From 4497ce563bea205db80630bee098182417bd91ca Mon Sep 17 00:00:00 2001 From: jingwenxie Date: Sun, 8 May 2022 19:26:06 -0700 Subject: [PATCH 1/2] cherry-pick override --- config/main.py | 71 ++++++++- tests/config_override_input/empty_input.json | 113 +++++++++++++ .../full_config_override.json | 122 ++++++++++++++ .../new_feature_config.json | 124 +++++++++++++++ .../partial_config_override.json | 130 +++++++++++++++ tests/config_override_test.py | 150 ++++++++++++++++++ tests/config_test.py | 21 +++ 7 files changed, 730 insertions(+), 1 deletion(-) create mode 100644 tests/config_override_input/empty_input.json create mode 100644 tests/config_override_input/full_config_override.json create mode 100644 tests/config_override_input/new_feature_config.json create mode 100644 tests/config_override_input/partial_config_override.json create mode 100644 tests/config_override_test.py diff --git a/config/main.py b/config/main.py index 79d0284c83..59cfd6914c 100644 --- a/config/main.py +++ b/config/main.py @@ -13,7 +13,7 @@ import itertools from collections import OrderedDict -from minigraph import parse_device_desc_xml +from minigraph import parse_device_desc_xml, minigraph_encoder from natsort import natsorted from portconfig import get_child_ports from socket import AF_INET, AF_INET6 @@ -24,6 +24,8 @@ from utilities_common.db import Db from utilities_common.intf_filter import parse_interface_in_filter import utilities_common.cli as clicommon +from utilities_common.general import load_module_from_source + from .utils import log from . import aaa @@ -63,6 +65,7 @@ DEFAULT_CONFIG_DB_FILE = '/etc/sonic/config_db.json' NAMESPACE_PREFIX = 'asic' INTF_KEY = "interfaces" +DEFAULT_GOLDEN_CONFIG_DB_FILE = '/etc/sonic/golden_config_db.json' INIT_CFG_FILE = '/etc/sonic/init_cfg.json' @@ -89,6 +92,9 @@ QUEUE_RANGE = click.IntRange(min=0, max=255) GRE_TYPE_RANGE = click.IntRange(min=0, max=65535) +# Load sonic-cfggen from source since /usr/local/bin/sonic-cfggen does not have .py extension. +sonic_cfggen = load_module_from_source('sonic_cfggen', '/usr/local/bin/sonic-cfggen') + # # Helper functions # @@ -1355,6 +1361,10 @@ def load_minigraph(db, no_service_restart): cfggen_namespace_option = " -n {}".format(namespace) clicommon.run_command(db_migrator + ' -o set_version' + cfggen_namespace_option) + # Load golden_config_db.json + if os.path.isfile(DEFAULT_GOLDEN_CONFIG_DB_FILE): + override_config_by(DEFAULT_GOLDEN_CONFIG_DB_FILE) + # We first run "systemctl reset-failed" to remove the "failed" # status from all services before we attempt to restart them if not no_service_restart: @@ -1403,6 +1413,65 @@ def load_port_config(config_db, port_config_path): port_name), display_cmd=True) return + +def override_config_by(golden_config_path): + # Override configDB with golden config + clicommon.run_command('config override-config-table {}'.format( + golden_config_path), display_cmd=True) + return + + +# +# 'override-config-table' command ('config override-config-table ...') +# +@config.command('override-config-table') +@click.argument('input-config-db', required=True) +@click.option( + '--dry-run', is_flag=True, default=False, + help='test out the command without affecting config state' +) +@clicommon.pass_db +def override_config_table(db, input_config_db, dry_run): + """Override current configDB with input config.""" + + try: + # Load golden config json + config_input = read_json_file(input_config_db) + except Exception as e: + click.secho("Bad format: json file broken. {}".format(str(e)), + fg='magenta') + sys.exit(1) + + # Validate if the input is dict + if not isinstance(config_input, dict): + click.secho("Bad format: input_config_db is not a dict", + fg='magenta') + sys.exit(1) + + config_db = db.cfgdb + + if dry_run: + # Read config from configDB + current_config = config_db.get_config() + # Serialize to the same format as json input + sonic_cfggen.FormatConverter.to_serialized(current_config) + # Override current config with golden config + for table in config_input: + current_config[table] = config_input[table] + print(json.dumps(current_config, sort_keys=True, + indent=4, cls=minigraph_encoder)) + else: + # Deserialized golden config to DB recognized format + sonic_cfggen.FormatConverter.to_deserialized(config_input) + # Delete table from DB then mod_config to apply golden config + click.echo("Removing configDB overriden table first ...") + for table in config_input: + config_db.delete_table(table) + click.echo("Overriding input config to configDB ...") + data = sonic_cfggen.FormatConverter.output_to_db(config_input) + config_db.mod_config(data) + click.echo("Overriding completed. No service is restarted.") + # # 'hostname' command # diff --git a/tests/config_override_input/empty_input.json b/tests/config_override_input/empty_input.json new file mode 100644 index 0000000000..ced851cfb8 --- /dev/null +++ b/tests/config_override_input/empty_input.json @@ -0,0 +1,113 @@ +{ + "running_config": { + "ACL_TABLE": { + "DATAACL": { + "policy_desc": "DATAACL", + "ports": [ + "Ethernet4" + ], + "stage": "ingress", + "type": "L3" + }, + "NTP_ACL": { + "policy_desc": "NTP_ACL", + "services": [ + "NTP" + ], + "stage": "ingress", + "type": "CTRLPLANE" + } + }, + "AUTO_TECHSUPPORT_FEATURE": { + "bgp": { + "rate_limit_interval": "600", + "state": "enabled" + }, + "database": { + "rate_limit_interval": "600", + "state": "enabled" + } + }, + "PORT": { + "Ethernet4": { + "admin_status": "up", + "alias": "fortyGigE0/4", + "description": "Servers0:eth0", + "index": "1", + "lanes": "29,30,31,32", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + }, + "Ethernet8": { + "admin_status": "up", + "alias": "fortyGigE0/8", + "description": "Servers1:eth0", + "index": "2", + "lanes": "33,34,35,36", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + } + } + }, + "golden_config": { + + }, + "expected_config": { + "ACL_TABLE": { + "DATAACL": { + "policy_desc": "DATAACL", + "ports": [ + "Ethernet4" + ], + "stage": "ingress", + "type": "L3" + }, + "NTP_ACL": { + "policy_desc": "NTP_ACL", + "services": [ + "NTP" + ], + "stage": "ingress", + "type": "CTRLPLANE" + } + }, + "AUTO_TECHSUPPORT_FEATURE": { + "bgp": { + "rate_limit_interval": "600", + "state": "enabled" + }, + "database": { + "rate_limit_interval": "600", + "state": "enabled" + } + }, + "PORT": { + "Ethernet4": { + "admin_status": "up", + "alias": "fortyGigE0/4", + "description": "Servers0:eth0", + "index": "1", + "lanes": "29,30,31,32", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + }, + "Ethernet8": { + "admin_status": "up", + "alias": "fortyGigE0/8", + "description": "Servers1:eth0", + "index": "2", + "lanes": "33,34,35,36", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + } + } + } +} \ No newline at end of file diff --git a/tests/config_override_input/full_config_override.json b/tests/config_override_input/full_config_override.json new file mode 100644 index 0000000000..1b589acb50 --- /dev/null +++ b/tests/config_override_input/full_config_override.json @@ -0,0 +1,122 @@ +{ + "running_config": { + "ACL_TABLE": { + "DATAACL": { + "policy_desc": "DATAACL", + "ports": [ + "Ethernet4" + ], + "stage": "ingress", + "type": "L3" + }, + "NTP_ACL": { + "policy_desc": "NTP_ACL", + "services": [ + "NTP" + ], + "stage": "ingress", + "type": "CTRLPLANE" + } + }, + "AUTO_TECHSUPPORT_FEATURE": { + "bgp": { + "rate_limit_interval": "600", + "state": "enabled" + }, + "database": { + "rate_limit_interval": "600", + "state": "enabled" + } + }, + "PORT": { + "Ethernet4": { + "admin_status": "up", + "alias": "fortyGigE0/4", + "description": "Servers0:eth0", + "index": "1", + "lanes": "29,30,31,32", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + }, + "Ethernet8": { + "admin_status": "up", + "alias": "fortyGigE0/8", + "description": "Servers1:eth0", + "index": "2", + "lanes": "33,34,35,36", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + } + } + }, + "golden_config": { + "ACL_TABLE": { + "EVERFLOWV6": { + "policy_desc": "EVERFLOWV6", + "ports": [ + "Ethernet12" + ], + "stage": "ingress", + "type": "MIRRORV6" + } + }, + "AUTO_TECHSUPPORT_FEATURE": { + "bgp": { + "state": "disabled" + }, + "database": { + "state": "disabled" + } + }, + "PORT": { + "Ethernet12": { + "admin_status": "up", + "alias": "fortyGigE0/12", + "description": "Servers2:eth0", + "index": "3", + "lanes": "37,38,39,40", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + } + } + }, + "expected_config": { + "ACL_TABLE": { + "EVERFLOWV6": { + "policy_desc": "EVERFLOWV6", + "ports": [ + "Ethernet12" + ], + "stage": "ingress", + "type": "MIRRORV6" + } + }, + "AUTO_TECHSUPPORT_FEATURE": { + "bgp": { + "state": "disabled" + }, + "database": { + "state": "disabled" + } + }, + "PORT": { + "Ethernet12": { + "admin_status": "up", + "alias": "fortyGigE0/12", + "description": "Servers2:eth0", + "index": "3", + "lanes": "37,38,39,40", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + } + } + } +} \ No newline at end of file diff --git a/tests/config_override_input/new_feature_config.json b/tests/config_override_input/new_feature_config.json new file mode 100644 index 0000000000..b8894f078c --- /dev/null +++ b/tests/config_override_input/new_feature_config.json @@ -0,0 +1,124 @@ +{ + "running_config": { + "ACL_TABLE": { + "DATAACL": { + "policy_desc": "DATAACL", + "ports": [ + "Ethernet4" + ], + "stage": "ingress", + "type": "L3" + }, + "NTP_ACL": { + "policy_desc": "NTP_ACL", + "services": [ + "NTP" + ], + "stage": "ingress", + "type": "CTRLPLANE" + } + }, + "AUTO_TECHSUPPORT_FEATURE": { + "bgp": { + "rate_limit_interval": "600", + "state": "enabled" + }, + "database": { + "rate_limit_interval": "600", + "state": "enabled" + } + }, + "PORT": { + "Ethernet4": { + "admin_status": "up", + "alias": "fortyGigE0/4", + "description": "Servers0:eth0", + "index": "1", + "lanes": "29,30,31,32", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + }, + "Ethernet8": { + "admin_status": "up", + "alias": "fortyGigE0/8", + "description": "Servers1:eth0", + "index": "2", + "lanes": "33,34,35,36", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + } + } + }, + "golden_config": { + "NEW_FEATURE_TABLE": { + "entry": { + "field": "value", + "state": "disabled" + } + } + }, + "expected_config": { + "ACL_TABLE": { + "DATAACL": { + "policy_desc": "DATAACL", + "ports": [ + "Ethernet4" + ], + "stage": "ingress", + "type": "L3" + }, + "NTP_ACL": { + "policy_desc": "NTP_ACL", + "services": [ + "NTP" + ], + "stage": "ingress", + "type": "CTRLPLANE" + } + }, + "AUTO_TECHSUPPORT_FEATURE": { + "bgp": { + "rate_limit_interval": "600", + "state": "enabled" + }, + "database": { + "rate_limit_interval": "600", + "state": "enabled" + } + }, + "PORT": { + "Ethernet4": { + "admin_status": "up", + "alias": "fortyGigE0/4", + "description": "Servers0:eth0", + "index": "1", + "lanes": "29,30,31,32", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + }, + "Ethernet8": { + "admin_status": "up", + "alias": "fortyGigE0/8", + "description": "Servers1:eth0", + "index": "2", + "lanes": "33,34,35,36", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + } + }, + "NEW_FEATURE_TABLE": { + "entry": { + "field": "value", + "state": "disabled" + } + } + } +} \ No newline at end of file diff --git a/tests/config_override_input/partial_config_override.json b/tests/config_override_input/partial_config_override.json new file mode 100644 index 0000000000..2021ea282b --- /dev/null +++ b/tests/config_override_input/partial_config_override.json @@ -0,0 +1,130 @@ +{ + "running_config": { + "ACL_TABLE": { + "DATAACL": { + "policy_desc": "DATAACL", + "ports": [ + "Ethernet4" + ], + "stage": "ingress", + "type": "L3" + }, + "NTP_ACL": { + "policy_desc": "NTP_ACL", + "services": [ + "NTP" + ], + "stage": "ingress", + "type": "CTRLPLANE" + } + }, + "AUTO_TECHSUPPORT_FEATURE": { + "bgp": { + "rate_limit_interval": "600", + "state": "enabled" + }, + "database": { + "rate_limit_interval": "600", + "state": "enabled" + } + }, + "PORT": { + "Ethernet4": { + "admin_status": "up", + "alias": "fortyGigE0/4", + "description": "Servers0:eth0", + "index": "1", + "lanes": "29,30,31,32", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + }, + "Ethernet8": { + "admin_status": "up", + "alias": "fortyGigE0/8", + "description": "Servers1:eth0", + "index": "2", + "lanes": "33,34,35,36", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + } + } + }, + "golden_config": { + "ACL_TABLE": { + "EVERFLOW": { + "policy_desc": "EVERFLOW", + "ports": [ + "Ethernet8" + ], + "stage": "ingress", + "type": "MIRROR" + }, + "NTP_ACL": { + "policy_desc": "NTP_ACL", + "services": [ + "NTP" + ], + "stage": "ingress", + "type": "CTRLPLANE" + } + } + }, + "expected_config": { + "ACL_TABLE": { + "EVERFLOW": { + "policy_desc": "EVERFLOW", + "ports": [ + "Ethernet8" + ], + "stage": "ingress", + "type": "MIRROR" + }, + "NTP_ACL": { + "policy_desc": "NTP_ACL", + "services": [ + "NTP" + ], + "stage": "ingress", + "type": "CTRLPLANE" + } + }, + "AUTO_TECHSUPPORT_FEATURE": { + "bgp": { + "rate_limit_interval": "600", + "state": "enabled" + }, + "database": { + "rate_limit_interval": "600", + "state": "enabled" + } + }, + "PORT": { + "Ethernet4": { + "admin_status": "up", + "alias": "fortyGigE0/4", + "description": "Servers0:eth0", + "index": "1", + "lanes": "29,30,31,32", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + }, + "Ethernet8": { + "admin_status": "up", + "alias": "fortyGigE0/8", + "description": "Servers1:eth0", + "index": "2", + "lanes": "33,34,35,36", + "mtu": "9100", + "pfc_asym": "off", + "speed": "40000", + "tpid": "0x8100" + } + } + } +} \ No newline at end of file diff --git a/tests/config_override_test.py b/tests/config_override_test.py new file mode 100644 index 0000000000..bee2b44192 --- /dev/null +++ b/tests/config_override_test.py @@ -0,0 +1,150 @@ +import os +import json +import filecmp +import config.main as config + +from click.testing import CliRunner +from unittest import mock +from utilities_common.db import Db +from utilities_common.general import load_module_from_source +from minigraph import minigraph_encoder + +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) +DATA_DIR = os.path.join(SCRIPT_DIR, "config_override_input") +EMPTY_INPUT = os.path.join(DATA_DIR, "empty_input.json") +PARTIAL_CONFIG_OVERRIDE = os.path.join(DATA_DIR, "partial_config_override.json") +NEW_FEATURE_CONFIG = os.path.join(DATA_DIR, "new_feature_config.json") +FULL_CONFIG_OVERRIDE = os.path.join(DATA_DIR, "full_config_override.json") + +# Load sonic-cfggen from source since /usr/local/bin/sonic-cfggen does not have .py extension. +sonic_cfggen = load_module_from_source('sonic_cfggen', '/usr/local/bin/sonic-cfggen') + + +def write_init_config_db(cfgdb, config): + tables = cfgdb.get_config() + # delete all tables then write config to configDB + for table in tables: + cfgdb.delete_table(table) + data = dict() + sonic_cfggen.deep_update( + data, sonic_cfggen.FormatConverter.to_deserialized(config)) + cfgdb.mod_config(sonic_cfggen.FormatConverter.output_to_db(data)) + return + + +def read_config_db(cfgdb): + data = dict() + sonic_cfggen.deep_update( + data, sonic_cfggen.FormatConverter.db_to_output(cfgdb.get_config())) + return sonic_cfggen.FormatConverter.to_serialized(data) + + +def write_config_to_file(cfgdb, file): + with open(file, 'w') as f: + json.dump(cfgdb, f, sort_keys=True, indent=4, cls=minigraph_encoder) + return + + +class TestConfigOverride(object): + @classmethod + def setup_class(cls): + print("SETUP") + os.environ["UTILITIES_UNIT_TESTING"] = "1" + return + + def test_broken_json(self): + def read_json_file_side_effect(filename): + return {{"TABLE"}} + with mock.patch('config.main.read_json_file', + mock.MagicMock(side_effect=read_json_file_side_effect)): + db = Db() + runner = CliRunner() + result = runner.invoke(config.config.commands["override-config-table"], + ['golden_config_db.json'], obj=db) + + assert result.exit_code == 1 + assert "Bad format: json file broken" in result.output + + def test_json_is_not_dict(self): + def read_json_file_side_effect(filename): + return [{}] + with mock.patch('config.main.read_json_file', + mock.MagicMock(side_effect=read_json_file_side_effect)): + db = Db() + runner = CliRunner() + result = runner.invoke(config.config.commands["override-config-table"], + ['golden_config_db.json'], obj=db) + + assert result.exit_code == 1 + assert "Bad format: input_config_db is not a dict" in result.output + + def test_dry_run(self): + def read_json_file_side_effect(filename): + return {} + db = Db() + current_config = read_config_db(db.cfgdb) + with mock.patch('config.main.read_json_file', + mock.MagicMock(side_effect=read_json_file_side_effect)): + runner = CliRunner() + result = runner.invoke(config.config.commands["override-config-table"], + ['golden_config_db.json', '--dry-run']) + + assert result.exit_code == 0 + assert json.loads(result.output) == current_config + + def test_golden_config_db_empty(self): + db = Db() + with open(EMPTY_INPUT, "r") as f: + read_data = json.load(f) + self.check_override_config_table( + db, config, read_data['running_config'], read_data['golden_config'], + read_data['expected_config']) + + def test_golden_config_db_partial(self): + """Golden Config only modify ACL_TABLE""" + db = Db() + with open(PARTIAL_CONFIG_OVERRIDE, "r") as f: + read_data = json.load(f) + self.check_override_config_table( + db, config, read_data['running_config'], read_data['golden_config'], + read_data['expected_config']) + + def test_golden_config_db_new_feature(self): + """Golden Config append NEW_FEATURE_TABLE""" + db = Db() + with open(NEW_FEATURE_CONFIG, "r") as f: + read_data = json.load(f) + self.check_override_config_table( + db, config, read_data['running_config'], read_data['golden_config'], + read_data['expected_config']) + + def test_golden_config_db_full(self): + """Golden Config makes change to every table in configDB""" + db = Db() + with open(FULL_CONFIG_OVERRIDE, "r") as f: + read_data = json.load(f) + self.check_override_config_table( + db, config, read_data['running_config'], read_data['golden_config'], + read_data['expected_config']) + + def check_override_config_table(self, db, config, running_config, + golden_config, expected_config): + def read_json_file_side_effect(filename): + return golden_config + with mock.patch('config.main.read_json_file', + mock.MagicMock(side_effect=read_json_file_side_effect)): + write_init_config_db(db.cfgdb, running_config) + + runner = CliRunner() + result = runner.invoke(config.config.commands["override-config-table"], + ['golden_config_db.json'], obj=db) + + current_config = read_config_db(db.cfgdb) + assert result.exit_code == 0 + assert current_config == expected_config + + @classmethod + def teardown_class(cls): + print("TEARDOWN") + os.environ["UTILITIES_UNIT_TESTING"] = "0" + return diff --git a/tests/config_test.py b/tests/config_test.py index d9a49d9b97..52ee7165cd 100644 --- a/tests/config_test.py +++ b/tests/config_test.py @@ -118,6 +118,27 @@ def is_file_side_effect(filename): assert result.exit_code == 0 assert expected_output in result.output + def test_load_minigraph_with_golden_config(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() + golden_config = {} + self.check_golden_config(db, config, golden_config, + "config override-config-table /etc/sonic/golden_config_db.json") + + def check_golden_config(self, db, config, golden_config, expected_output): + def is_file_side_effect(filename): + return True if 'golden_config' in filename else False + with mock.patch('os.path.isfile', mock.MagicMock(side_effect=is_file_side_effect)): + runner = CliRunner() + result = runner.invoke(config.config.commands["load_minigraph"], ["-y"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert expected_output in result.output + @classmethod def teardown_class(cls): os.environ['UTILITIES_UNIT_TESTING'] = "0" From b1fed6cb192fe339ff17bb99935b1cdf6d633d10 Mon Sep 17 00:00:00 2001 From: Jingwen Xie Date: Wed, 22 Jun 2022 09:56:50 +0000 Subject: [PATCH 2/2] change to load_source --- config/main.py | 11 ++++++----- tests/config_override_test.py | 10 ++++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/config/main.py b/config/main.py index 59cfd6914c..cdde017e15 100644 --- a/config/main.py +++ b/config/main.py @@ -24,8 +24,6 @@ from utilities_common.db import Db from utilities_common.intf_filter import parse_interface_in_filter import utilities_common.cli as clicommon -from utilities_common.general import load_module_from_source - from .utils import log from . import aaa @@ -41,6 +39,12 @@ from . import vxlan from .config_mgmt import ConfigMgmtDPB +# Using load_source to 'import /usr/local/bin/sonic-cfggen as sonic_cfggen' +# since /usr/local/bin/sonic-cfggen does not have .py extension. +from imp import load_source +load_source('sonic_cfggen', '/usr/local/bin/sonic-cfggen') +import sonic_cfggen + # mock masic APIs for unit test try: if os.environ["UTILITIES_UNIT_TESTING"] == "1" or os.environ["UTILITIES_UNIT_TESTING"] == "2": @@ -92,9 +96,6 @@ QUEUE_RANGE = click.IntRange(min=0, max=255) GRE_TYPE_RANGE = click.IntRange(min=0, max=65535) -# Load sonic-cfggen from source since /usr/local/bin/sonic-cfggen does not have .py extension. -sonic_cfggen = load_module_from_source('sonic_cfggen', '/usr/local/bin/sonic-cfggen') - # # Helper functions # diff --git a/tests/config_override_test.py b/tests/config_override_test.py index bee2b44192..066b7948fe 100644 --- a/tests/config_override_test.py +++ b/tests/config_override_test.py @@ -6,9 +6,14 @@ from click.testing import CliRunner from unittest import mock from utilities_common.db import Db -from utilities_common.general import load_module_from_source from minigraph import minigraph_encoder +# Using load_source to 'import /usr/local/bin/sonic-cfggen as sonic_cfggen' +# since /usr/local/bin/sonic-cfggen does not have .py extension. +from imp import load_source +load_source('sonic_cfggen', '/usr/local/bin/sonic-cfggen') +import sonic_cfggen + SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) DATA_DIR = os.path.join(SCRIPT_DIR, "config_override_input") EMPTY_INPUT = os.path.join(DATA_DIR, "empty_input.json") @@ -16,9 +21,6 @@ NEW_FEATURE_CONFIG = os.path.join(DATA_DIR, "new_feature_config.json") FULL_CONFIG_OVERRIDE = os.path.join(DATA_DIR, "full_config_override.json") -# Load sonic-cfggen from source since /usr/local/bin/sonic-cfggen does not have .py extension. -sonic_cfggen = load_module_from_source('sonic_cfggen', '/usr/local/bin/sonic-cfggen') - def write_init_config_db(cfgdb, config): tables = cfgdb.get_config()