Skip to content

Commit

Permalink
[pytest][qos][config] Added pytests for "config qos reload" commands (#…
Browse files Browse the repository at this point in the history
…1266)

 Added pytests for "config qos reload" commands.
  • Loading branch information
smaheshm authored Dec 23, 2020
1 parent f555e9e commit 6202a81
Show file tree
Hide file tree
Showing 28 changed files with 2,124 additions and 17 deletions.
45 changes: 36 additions & 9 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@
from . import vxlan
from .config_mgmt import ConfigMgmtDPB

# mock masic APIs for unit test
try:
if os.environ["UTILITIES_UNIT_TESTING"] == "1" or os.environ["UTILITIES_UNIT_TESTING"] == "2":
modules_path = os.path.join(os.path.dirname(__file__), "..")
tests_path = os.path.join(modules_path, "tests")
sys.path.insert(0, modules_path)
sys.path.insert(0, tests_path)
import mock_tables.dbconnector
if os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] == "multi_asic":
import mock_tables.mock_multi_asic
mock_tables.dbconnector.load_namespace_config()
except KeyError:
pass


CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help', '-?'])

SONIC_GENERATED_SERVICE_PATH = '/etc/sonic/generated_services.conf'
Expand Down Expand Up @@ -1641,12 +1656,24 @@ def _update_buffer_calculation_model(config_db, model):
@qos.command('reload')
@click.pass_context
@click.option('--no-dynamic-buffer', is_flag=True, help="Disable dynamic buffer calculation")
def reload(ctx, no_dynamic_buffer):
@click.option(
'--json-data', type=click.STRING,
help="json string with additional data, valid with --dry-run option"
)
@click.option(
'--dry_run', type=click.STRING,
help="Dry run, writes config to the given file"
)
def reload(ctx, no_dynamic_buffer, dry_run, json_data):
"""Reload QoS configuration"""
log.log_info("'qos reload' executing...")
_clear_qos()

_, hwsku_path = device_info.get_paths_to_platform_and_hwsku_dirs()
sonic_version_file = device_info.get_sonic_version_file()
from_db = "-d --write-to-db"
if dry_run:
from_db = "--additional-data \'{}\'".format(json_data) if json_data else ""

namespace_list = [DEFAULT_NAMESPACE]
if multi_asic.get_num_asics() > 1:
Expand Down Expand Up @@ -1683,17 +1710,17 @@ def reload(ctx, no_dynamic_buffer):
buffer_template_file = os.path.join(hwsku_path, asic_id_suffix, "buffers.json.j2")
if asic_type in vendors_supporting_dynamic_buffer:
buffer_model_updated |= _update_buffer_calculation_model(config_db, "traditional")

if os.path.isfile(buffer_template_file):
qos_template_file = os.path.join(hwsku_path, asic_id_suffix, "qos.json.j2")
qos_template_file = os.path.join(
hwsku_path, asic_id_suffix, "qos.json.j2"
)
if os.path.isfile(qos_template_file):
cmd_ns = "" if ns is DEFAULT_NAMESPACE else "-n {}".format(ns)
sonic_version_file = os.path.join('/', "etc", "sonic", "sonic_version.yml")
command = "{} {} -d -t {},config-db -t {},config-db -y {} --write-to-db".format(
SONIC_CFGGEN_PATH,
cmd_ns,
buffer_template_file,
qos_template_file,
sonic_version_file
fname = "{}{}".format(dry_run, asic_id_suffix) if dry_run else "config-db"
command = "{} {} {} -t {},{} -t {},{} -y {}".format(
SONIC_CFGGEN_PATH, cmd_ns, from_db, buffer_template_file,
fname, qos_template_file, fname, sonic_version_file
)
# Apply the configurations only when both buffer and qos
# configuration files are present
Expand Down
2 changes: 2 additions & 0 deletions pfcwd/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import imp
import sys

import click
Expand All @@ -21,6 +22,7 @@
import mock_tables.dbconnector
if os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] == "multi_asic":
import mock_tables.mock_multi_asic
imp.reload(mock_tables.mock_multi_asic)
mock_tables.dbconnector.load_namespace_config()

except KeyError:
Expand Down
4 changes: 2 additions & 2 deletions scripts/intfutil
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ try:
if os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] == "multi_asic":
import mock_tables.mock_multi_asic
mock_tables.dbconnector.load_namespace_config()

except KeyError:
pass

Expand Down Expand Up @@ -461,7 +461,7 @@ class IntfDescription(object):
self.intf_name = intf_name

def display_intf_description(self):

self.get_intf_description()

# Sorting and tabulating the result table.
Expand Down
105 changes: 103 additions & 2 deletions tests/config_test.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import filecmp
import imp
import os
import traceback
import json
from unittest import mock

import click
from click.testing import CliRunner

from sonic_py_common import device_info
from utilities_common.db import Db

load_minigraph_command_output="""\
Expand Down Expand Up @@ -61,8 +65,10 @@ class TestLoadMinigraph(object):
def setup_class(cls):
os.environ['UTILITIES_UNIT_TESTING'] = "1"
print("SETUP")
import config.main
imp.reload(config.main)

def test_load_minigraph(self, get_cmd_module, setup_single_broacom_asic):
def test_load_minigraph(self, get_cmd_module, setup_single_broadcom_asic):
with mock.patch("utilities_common.cli.run_command", mock.MagicMock(side_effect=mock_run_command_side_effect)) as mock_run_command:
(config, show) = get_cmd_module
runner = CliRunner()
Expand All @@ -74,7 +80,7 @@ def test_load_minigraph(self, get_cmd_module, setup_single_broacom_asic):
assert "\n".join([l.rstrip() for l in result.output.split('\n')]) == load_minigraph_command_output
assert mock_run_command.call_count == 38

def test_load_minigraph_with_disabled_telemetry(self, get_cmd_module, setup_single_broacom_asic):
def test_load_minigraph_with_disabled_telemetry(self, get_cmd_module, setup_single_broadcom_asic):
with mock.patch("utilities_common.cli.run_command", mock.MagicMock(side_effect=mock_run_command_side_effect)) as mock_run_command:
(config, show) = get_cmd_module
db = Db()
Expand All @@ -95,3 +101,98 @@ def test_load_minigraph_with_disabled_telemetry(self, get_cmd_module, setup_sing
def teardown_class(cls):
os.environ['UTILITIES_UNIT_TESTING'] = "0"
print("TEARDOWN")


class TestConfigQos(object):
@classmethod
def setup_class(cls):
print("SETUP")
os.environ['UTILITIES_UNIT_TESTING'] = "2"
import config.main
imp.reload(config.main)

def test_qos_reload_single(
self, get_cmd_module, setup_qos_mock_apis,
setup_single_broadcom_asic
):
(config, show) = get_cmd_module
runner = CliRunner()
output_file = os.path.join(os.sep, "tmp", "qos_config_output.json")
print("Saving output in {}".format(output_file))
try:
os.remove(output_file)
except OSError:
pass
json_data = '{"DEVICE_METADATA": {"localhost": {}}}'
result = runner.invoke(
config.config.commands["qos"],
["reload", "--dry_run", output_file, "--json-data", json_data]
)
print(result.exit_code)
print(result.output)
assert result.exit_code == 0

cwd = os.path.dirname(os.path.realpath(__file__))
expected_result = os.path.join(
cwd, "qos_config_input", "config_qos.json"
)
assert filecmp.cmp(output_file, expected_result, shallow=False)

@classmethod
def teardown_class(cls):
print("TEARDOWN")
os.environ['UTILITIES_UNIT_TESTING'] = "0"


class TestConfigQosMasic(object):
@classmethod
def setup_class(cls):
print("SETUP")
os.environ['UTILITIES_UNIT_TESTING'] = "2"
os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "multi_asic"
import config.main
imp.reload(config.main)

def test_qos_reload_masic(
self, get_cmd_module, setup_qos_mock_apis,
setup_multi_broadcom_masic
):
(config, show) = get_cmd_module
runner = CliRunner()
output_file = os.path.join(os.sep, "tmp", "qos_config_output.json")
print("Saving output in {}<0,1,2..>".format(output_file))
num_asic = device_info.get_num_npus()
for asic in range(num_asic):
try:
file = "{}{}".format(output_file, asic)
os.remove(file)
except OSError:
pass
json_data = '{"DEVICE_METADATA": {"localhost": {}}}'
result = runner.invoke(
config.config.commands["qos"],
["reload", "--dry_run", output_file, "--json-data", json_data]
)
print(result.exit_code)
print(result.output)
assert result.exit_code == 0

cwd = os.path.dirname(os.path.realpath(__file__))

for asic in range(num_asic):
expected_result = os.path.join(
cwd, "qos_config_input", str(asic), "config_qos.json"
)
file = "{}{}".format(output_file, asic)
assert filecmp.cmp(file, expected_result, shallow=False)

@classmethod
def teardown_class(cls):
print("TEARDOWN")
os.environ['UTILITIES_UNIT_TESTING'] = "0"
os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = ""
# change back to single asic config
from .mock_tables import dbconnector
from .mock_tables import mock_single_asic
imp.reload(mock_single_asic)
dbconnector.load_namespace_config()
35 changes: 32 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,47 @@ def get_cmd_module():

return (config, show)

def set_mock_apis():
import config.main as config
cwd = os.path.dirname(os.path.realpath(__file__))
config.asic_type = mock.MagicMock(return_value="broadcom")
config._get_device_type = mock.MagicMock(return_value="ToRRouter")

@pytest.fixture
def setup_qos_mock_apis():
cwd = os.path.dirname(os.path.realpath(__file__))
device_info.get_paths_to_platform_and_hwsku_dirs = mock.MagicMock(
return_value=(
os.path.join(cwd, "."), os.path.join(cwd, "qos_config_input")
)
)
device_info.get_sonic_version_file = mock.MagicMock(
return_value=os.path.join(cwd, "qos_config_input/sonic_version.yml")
)

@pytest.fixture
def setup_single_broacom_asic():
def setup_single_broadcom_asic():
import config.main as config
import show.main as show

set_mock_apis()
device_info.get_num_npus = mock.MagicMock(return_value=1)
config._get_sonic_generated_services = \
mock.MagicMock(return_value=(generated_services_list, []))

config.asic_type = mock.MagicMock(return_value="broadcom")
config._get_device_type = mock.MagicMock(return_value="ToRRouter")

@pytest.fixture
def setup_multi_broadcom_masic():
import config.main as config
import show.main as show

set_mock_apis()
device_info.get_num_npus = mock.MagicMock(return_value=2)

yield

device_info.get_num_npus = mock.MagicMock(return_value=1)


@pytest.fixture
def setup_t1_topo():
Expand Down
5 changes: 4 additions & 1 deletion tests/crm_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import imp
import os
import sys
from importlib import reload
Expand Down Expand Up @@ -1574,5 +1575,7 @@ def teardown_class(cls):
print("TEARDOWN")
os.environ["UTILITIES_UNIT_TESTING"] = "0"
os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = ""
from .mock_tables import dbconnector
from .mock_tables import mock_single_asic
reload(mock_single_asic)
imp.reload(mock_single_asic)
dbconnector.load_namespace_config()
2 changes: 2 additions & 0 deletions tests/mock_tables/mock_multi_asic.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ def mock_is_multi_asic():
def mock_get_namespace_list(namespace=None):
return ['asic0', 'asic1']


multi_asic.get_num_asics = mock_get_num_asics
multi_asic.is_multi_asic = mock_is_multi_asic
multi_asic.get_namespace_list = mock_get_namespace_list
multi_asic.get_namespaces_from_linux = mock_get_namespace_list
3 changes: 3 additions & 0 deletions tests/qos_config_input/0/buffers.json.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{%- set default_topo = 't1' %}
{%- include 'buffers_config.j2' %}

Loading

0 comments on commit 6202a81

Please sign in to comment.