From b1ac2ef1c5ca4d3724603d813413ae98a768a5e5 Mon Sep 17 00:00:00 2001 From: Yaqiang Zhu Date: Mon, 11 Sep 2023 01:12:00 -0400 Subject: [PATCH 01/20] [sonic-package-manager] Increate timeout for sonic-package-manager migrate (#2973) What I did When we migrate package via sonic-package-manager in some low-performance device, getting docker image from old image via docker socket may timeout by default timeout setting (60s) client.py. This PR is to increase timeout to 120s. How I did it Increase docker client timeout from 60s to 120s. How to verify it ut passed. Build image with INCLUDE_MACSEC, and install it, macsec image was not include. And the build image with this PR, install it. Migrate package successfully. Signed-off-by: Yaqiang Zhu --- sonic_package_manager/manager.py | 7 +++++- tests/sonic_package_manager/test_manager.py | 28 +++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/sonic_package_manager/manager.py b/sonic_package_manager/manager.py index 7ed2be0a9e..d01ed9cb44 100644 --- a/sonic_package_manager/manager.py +++ b/sonic_package_manager/manager.py @@ -659,6 +659,10 @@ def reset(self, name: str, force: bool = False, skip_host_plugins: bool = False) allow_downgrade=True, skip_host_plugins=skip_host_plugins) + @under_lock + def get_docker_client(self, dockerd_sock:str): + return docker.DockerClient(base_url=f'unix://{dockerd_sock}', timeout=120) + @under_lock def migrate_packages(self, old_package_database: PackageDatabase, @@ -701,7 +705,8 @@ def migrate_package(old_package_entry, # dockerd_sock is defined, so use docked_sock to connect to # dockerd and fetch package image from it. log.info(f'installing {name} from old docker library') - docker_api = DockerApi(docker.DockerClient(base_url=f'unix://{dockerd_sock}')) + docker_client = self.get_docker_client(dockerd_sock) + docker_api = DockerApi(docker_client) image = docker_api.get_image(old_package_entry.image_id) diff --git a/tests/sonic_package_manager/test_manager.py b/tests/sonic_package_manager/test_manager.py index ad365f946d..db9a79b309 100644 --- a/tests/sonic_package_manager/test_manager.py +++ b/tests/sonic_package_manager/test_manager.py @@ -356,3 +356,31 @@ def test_manager_migration(package_manager, fake_db_for_migration): call('test-package-6=2.0.0')], any_order=True ) + + +def mock_get_docker_client(dockerd_sock): + class DockerClient: + def __init__(self, dockerd_sock): + class Image: + def __init__(self, image_id): + self.image_id = image_id + + def save(self, named): + return ["named: {}".format(named).encode()] + + image = Image("dummy_id") + self.images = { + "Azure/docker-test-3:1.6.0": image, + "Azure/docker-test-6:2.0.0": image + } + self.dockerd_sock = dockerd_sock + + return DockerClient(dockerd_sock) + + +def test_manager_migration_dockerd(package_manager, fake_db_for_migration, mock_docker_api): + package_manager.install = Mock() + package_manager.get_docker_client = Mock(side_effect=mock_get_docker_client) + package_manager.migrate_packages(fake_db_for_migration, '/var/run/docker.sock') + package_manager.get_docker_client.assert_has_calls([ + call('/var/run/docker.sock')], any_order=True) From 82a4d7163fe7fbf71e988d460f891cd683e75ee8 Mon Sep 17 00:00:00 2001 From: Saikrishna Arcot Date: Wed, 13 Sep 2023 14:11:33 -0700 Subject: [PATCH 02/20] Remove command to install libhiredis deb file (#2980) libhiredis is now used as-is from the slave container, since we're not making any changes to this package. See also sonic-net/sonic-buildimage#15633. Signed-off-by: Saikrishna Arcot --- azure-pipelines.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index eecf1c9e53..1f0f354436 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -52,12 +52,11 @@ stages: - script: | set -xe - sudo apt-get -y purge libhiredis-dev libnl-3-dev libnl-route-3-dev || true + sudo apt-get -y purge libnl-3-dev libnl-route-3-dev || true sudo dpkg -i libnl-3-200_*.deb sudo dpkg -i libnl-genl-3-200_*.deb sudo dpkg -i libnl-route-3-200_*.deb sudo dpkg -i libnl-nf-3-200_*.deb - sudo dpkg -i libhiredis0.14_*.deb sudo dpkg -i libyang_1.0.73_amd64.deb sudo dpkg -i libyang-cpp_1.0.73_amd64.deb sudo dpkg -i python3-yang_1.0.73_amd64.deb From e2c184188ba3cdd73b81ba4c50463a515ccf99d4 Mon Sep 17 00:00:00 2001 From: abdosi <58047199+abdosi@users.noreply.github.com> Date: Thu, 14 Sep 2023 09:28:46 -0700 Subject: [PATCH 03/20] Added support to display only nonzero queue counter. (#2978) Added new option --nonzero to display given {port,queue} or {system-port, voq} counter only if any of counter value is non zero. --- scripts/queuestat | 73 +- show/main.py | 6 +- tests/mock_tables/counters_db.json | 24 +- tests/queue_counter_test.py | 1150 +++++++++++++++++++++++++--- 4 files changed, 1120 insertions(+), 133 deletions(-) diff --git a/scripts/queuestat b/scripts/queuestat index d82e7e4a6a..8f95554481 100755 --- a/scripts/queuestat +++ b/scripts/queuestat @@ -198,7 +198,7 @@ class Queuestat(object): cnstat_dict[queue] = get_counters(queue_map[queue]) return cnstat_dict - def cnstat_print(self, port, cnstat_dict, json_opt): + def cnstat_print(self, port, cnstat_dict, json_opt, non_zero): """ Print the cnstat. If JSON option is True, return data in JSON format. @@ -211,19 +211,22 @@ class Queuestat(object): if json_opt: json_output[port][key] = data continue - table.append((port, data['queuetype'] + str(data['queueindex']), - data['totalpacket'], data['totalbytes'], - data['droppacket'], data['dropbytes'])) + if not non_zero or data['totalpacket'] != '0' or data['totalbytes'] != '0' or \ + data['droppacket'] != '0' or data['dropbytes'] != '0': + table.append((port, data['queuetype'] + str(data['queueindex']), + data['totalpacket'], data['totalbytes'], + data['droppacket'], data['dropbytes'])) if json_opt: json_output[port].update(build_json(port, table)) return json_output else: hdr = voq_header if self.voq else header - print(tabulate(table, hdr, tablefmt='simple', stralign='right')) - print() + if table: + print(tabulate(table, hdr, tablefmt='simple', stralign='right')) + print() - def cnstat_diff_print(self, port, cnstat_new_dict, cnstat_old_dict, json_opt): + def cnstat_diff_print(self, port, cnstat_new_dict, cnstat_old_dict, json_opt, non_zero): """ Print the difference between two cnstat results. If JSON option is True, return data in JSON format. @@ -241,25 +244,32 @@ class Queuestat(object): old_cntr = cnstat_old_dict.get(key) if old_cntr is not None: - table.append((port, cntr['queuetype'] + str(cntr['queueindex']), - ns_diff(cntr['totalpacket'], old_cntr['totalpacket']), - ns_diff(cntr['totalbytes'], old_cntr['totalbytes']), - ns_diff(cntr['droppacket'], old_cntr['droppacket']), - ns_diff(cntr['dropbytes'], old_cntr['dropbytes']))) - else: - table.append((port, cntr['queuetype'] + str(cntr['queueindex']), - cntr['totalpacket'], cntr['totalbytes'], - cntr['droppacket'], cntr['dropbytes'])) + if not non_zero or ns_diff(cntr['totalpacket'], old_cntr['totalpacket']) != '0' or \ + ns_diff(cntr['totalbytes'], old_cntr['totalbytes']) != '0' or \ + ns_diff(cntr['droppacket'], old_cntr['droppacket']) != '0' or \ + ns_diff(cntr['dropbytes'], old_cntr['dropbytes']) != '0': + table.append((port, cntr['queuetype'] + str(cntr['queueindex']), + ns_diff(cntr['totalpacket'], old_cntr['totalpacket']), + ns_diff(cntr['totalbytes'], old_cntr['totalbytes']), + ns_diff(cntr['droppacket'], old_cntr['droppacket']), + ns_diff(cntr['dropbytes'], old_cntr['dropbytes']))) + elif not non_zero or cntr['totalpacket'] != '0' or cntr['totalbytes'] != '0' or \ + cntr['droppacket'] != '0' or cntr['dropbytes'] != '0': + table.append((port, cntr['queuetype'] + str(cntr['queueindex']), + cntr['totalpacket'], cntr['totalbytes'], + cntr['droppacket'], cntr['dropbytes'])) if json_opt: json_output[port].update(build_json(port, table)) return json_output else: hdr = voq_header if self.voq else header - print(tabulate(table, hdr, tablefmt='simple', stralign='right')) - print() + if table: + print(port + " Last cached time was " + str(cnstat_old_dict.get('time'))) + print(tabulate(table, hdr, tablefmt='simple', stralign='right')) + print() - def get_print_all_stat(self, json_opt): + def get_print_all_stat(self, json_opt, non_zero): """ Get stat for each port If JSON option is True, collect data for each port and @@ -276,22 +286,21 @@ class Queuestat(object): cnstat_cached_dict = json.load(open(cnstat_fqn_file_name, 'r')) if json_opt: json_output[port].update({"cached_time":cnstat_cached_dict.get('time')}) - json_output.update(self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt)) + json_output.update(self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt, non_zero)) else: - print(port + " Last cached time was " + str(cnstat_cached_dict.get('time'))) - self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt) + self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt, non_zero) except IOError as e: print(e.errno, e) else: if json_opt: - json_output.update(self.cnstat_print(port, cnstat_dict, json_opt)) + json_output.update(self.cnstat_print(port, cnstat_dict, json_opt, non_zero)) else: - self.cnstat_print(port, cnstat_dict, json_opt) + self.cnstat_print(port, cnstat_dict, json_opt, non_zero) if json_opt: print(json_dump(json_output)) - def get_print_port_stat(self, port, json_opt): + def get_print_port_stat(self, port, json_opt, non_zero): """ Get stat for the port If JSON option is True print data in JSON format @@ -310,17 +319,17 @@ class Queuestat(object): cnstat_cached_dict = json.load(open(cnstat_fqn_file_name, 'r')) if json_opt: json_output[port].update({"cached_time":cnstat_cached_dict.get('time')}) - json_output.update(self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt)) + json_output.update(self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt, non_zero)) else: print("Last cached time was " + str(cnstat_cached_dict.get('time'))) - self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt) + self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt, non_zero) except IOError as e: print(e.errno, e) else: if json_opt: - json_output.update(self.cnstat_print(port, cnstat_dict, json_opt)) + json_output.update(self.cnstat_print(port, cnstat_dict, json_opt, non_zero)) else: - self.cnstat_print(port, cnstat_dict, json_opt) + self.cnstat_print(port, cnstat_dict, json_opt, non_zero) if json_opt: print(json_dump(json_output)) @@ -358,6 +367,7 @@ Examples: parser.add_argument('-j', '--json_opt', action='store_true', help='Print in JSON format') parser.add_argument('-V', '--voq', action='store_true', help='display voq stats') parser.add_argument('-n','--namespace', default=None, help='Display queue counters for specific namespace') + parser.add_argument('-nz','--non_zero', action='store_true', help='Display non-zero queue counters') args = parser.parse_args() save_fresh_stats = args.clear @@ -365,6 +375,7 @@ Examples: voq = args.voq json_opt = args.json_opt namespace = args.namespace + non_zero = args.non_zero port_to_show_stats = args.port @@ -383,9 +394,9 @@ Examples: sys.exit(0) if port_to_show_stats!=None: - queuestat.get_print_port_stat(port_to_show_stats, json_opt) + queuestat.get_print_port_stat(port_to_show_stats, json_opt, non_zero) else: - queuestat.get_print_all_stat(json_opt) + queuestat.get_print_all_stat(json_opt, non_zero) sys.exit(0) diff --git a/show/main.py b/show/main.py index 35303b6eda..725556e6e8 100755 --- a/show/main.py +++ b/show/main.py @@ -718,7 +718,8 @@ def queue(): @click.option('--verbose', is_flag=True, help="Enable verbose output") @click.option('--json', is_flag=True, help="JSON output") @click.option('--voq', is_flag=True, help="VOQ counters") -def counters(interfacename, namespace, display, verbose, json, voq): +@click.option('--nonzero', is_flag=True, help="Non Zero Counters") +def counters(interfacename, namespace, display, verbose, json, voq, nonzero): """Show queue counters""" cmd = ["queuestat"] @@ -739,6 +740,9 @@ def counters(interfacename, namespace, display, verbose, json, voq): if voq: cmd += ["-V"] + if nonzero: + cmd += ["-nz"] + run_command(cmd, display_cmd=verbose) # diff --git a/tests/mock_tables/counters_db.json b/tests/mock_tables/counters_db.json index c0c37880df..d62c34cb3c 100644 --- a/tests/mock_tables/counters_db.json +++ b/tests/mock_tables/counters_db.json @@ -1,9 +1,9 @@ { "COUNTERS:oid:0x15000000000357": { - "SAI_QUEUE_STAT_BYTES": "30", - "SAI_QUEUE_STAT_DROPPED_BYTES": "74", - "SAI_QUEUE_STAT_DROPPED_PACKETS": "56", - "SAI_QUEUE_STAT_PACKETS": "68", + "SAI_QUEUE_STAT_BYTES": "0", + "SAI_QUEUE_STAT_DROPPED_BYTES": "0", + "SAI_QUEUE_STAT_DROPPED_PACKETS": "0", + "SAI_QUEUE_STAT_PACKETS": "0", "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "61" }, "COUNTERS:oid:0x15000000000358": { @@ -266,10 +266,10 @@ "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "49" }, "COUNTERS:oid:0x150000000003a7": { - "SAI_QUEUE_STAT_BYTES": "5", - "SAI_QUEUE_STAT_DROPPED_BYTES": "56", - "SAI_QUEUE_STAT_DROPPED_PACKETS": "36", - "SAI_QUEUE_STAT_PACKETS": "19", + "SAI_QUEUE_STAT_BYTES": "0", + "SAI_QUEUE_STAT_DROPPED_BYTES": "0", + "SAI_QUEUE_STAT_DROPPED_PACKETS": "0", + "SAI_QUEUE_STAT_PACKETS": "0", "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "65" }, "COUNTERS:oid:0x150000000003a8": { @@ -399,10 +399,10 @@ "SAI_QUEUE_STAT_SHARED_WATERMARK_BYTES": "81" }, "COUNTERS:oid:0x15000000000657": { - "SAI_QUEUE_STAT_BYTES": "30", - "SAI_QUEUE_STAT_DROPPED_BYTES": "74", - "SAI_QUEUE_STAT_DROPPED_PACKETS": "56", - "SAI_QUEUE_STAT_PACKETS": "68" + "SAI_QUEUE_STAT_BYTES": "0", + "SAI_QUEUE_STAT_DROPPED_BYTES": "0", + "SAI_QUEUE_STAT_DROPPED_PACKETS": "0", + "SAI_QUEUE_STAT_PACKETS": "0" }, "COUNTERS:oid:0x15000000000658": { "SAI_QUEUE_STAT_BYTES": "43", diff --git a/tests/queue_counter_test.py b/tests/queue_counter_test.py index a5fbf0b81b..20b9516fbc 100644 --- a/tests/queue_counter_test.py +++ b/tests/queue_counter_test.py @@ -24,7 +24,107 @@ show_queue_counters = """\ Port TxQ Counter/pkts Counter/bytes Drop/pkts Drop/bytes --------- ----- -------------- --------------- ----------- ------------ -Ethernet0 UC0 68 30 56 74 +Ethernet0 UC0 0 0 0 0 +Ethernet0 UC1 60 43 39 1 +Ethernet0 UC2 82 7 39 21 +Ethernet0 UC3 52 70 19 76 +Ethernet0 UC4 11 59 12 94 +Ethernet0 UC5 36 62 35 40 +Ethernet0 UC6 49 91 2 88 +Ethernet0 UC7 33 17 94 74 +Ethernet0 UC8 40 71 95 33 +Ethernet0 UC9 54 8 93 78 +Ethernet0 MC10 83 96 74 9 +Ethernet0 MC11 15 60 61 31 +Ethernet0 MC12 45 52 82 94 +Ethernet0 MC13 55 88 89 52 +Ethernet0 MC14 14 70 95 79 +Ethernet0 MC15 68 60 66 81 +Ethernet0 MC16 63 4 48 76 +Ethernet0 MC17 41 73 77 74 +Ethernet0 MC18 60 21 56 54 +Ethernet0 MC19 57 31 12 39 +Ethernet0 ALL20 N/A N/A N/A N/A +Ethernet0 ALL21 N/A N/A N/A N/A +Ethernet0 ALL22 N/A N/A N/A N/A +Ethernet0 ALL23 N/A N/A N/A N/A +Ethernet0 ALL24 N/A N/A N/A N/A +Ethernet0 ALL25 N/A N/A N/A N/A +Ethernet0 ALL26 N/A N/A N/A N/A +Ethernet0 ALL27 N/A N/A N/A N/A +Ethernet0 ALL28 N/A N/A N/A N/A +Ethernet0 ALL29 N/A N/A N/A N/A + + Port TxQ Counter/pkts Counter/bytes Drop/pkts Drop/bytes +--------- ----- -------------- --------------- ----------- ------------ +Ethernet4 UC0 41 96 70 98 +Ethernet4 UC1 18 49 63 36 +Ethernet4 UC2 99 90 3 15 +Ethernet4 UC3 60 89 48 41 +Ethernet4 UC4 8 84 82 94 +Ethernet4 UC5 83 15 75 92 +Ethernet4 UC6 84 26 50 71 +Ethernet4 UC7 27 19 49 80 +Ethernet4 UC8 13 89 13 33 +Ethernet4 UC9 43 48 86 31 +Ethernet4 MC10 50 1 57 82 +Ethernet4 MC11 67 99 84 59 +Ethernet4 MC12 4 58 27 5 +Ethernet4 MC13 74 5 57 39 +Ethernet4 MC14 21 59 4 14 +Ethernet4 MC15 24 61 19 53 +Ethernet4 MC16 51 15 15 32 +Ethernet4 MC17 98 18 23 15 +Ethernet4 MC18 41 34 9 57 +Ethernet4 MC19 57 7 18 99 +Ethernet4 ALL20 N/A N/A N/A N/A +Ethernet4 ALL21 N/A N/A N/A N/A +Ethernet4 ALL22 N/A N/A N/A N/A +Ethernet4 ALL23 N/A N/A N/A N/A +Ethernet4 ALL24 N/A N/A N/A N/A +Ethernet4 ALL25 N/A N/A N/A N/A +Ethernet4 ALL26 N/A N/A N/A N/A +Ethernet4 ALL27 N/A N/A N/A N/A +Ethernet4 ALL28 N/A N/A N/A N/A +Ethernet4 ALL29 N/A N/A N/A N/A + + Port TxQ Counter/pkts Counter/bytes Drop/pkts Drop/bytes +--------- ----- -------------- --------------- ----------- ------------ +Ethernet8 UC0 0 0 0 0 +Ethernet8 UC1 38 17 68 91 +Ethernet8 UC2 16 65 79 51 +Ethernet8 UC3 11 97 63 72 +Ethernet8 UC4 54 89 62 62 +Ethernet8 UC5 13 84 30 59 +Ethernet8 UC6 49 67 99 85 +Ethernet8 UC7 2 63 38 88 +Ethernet8 UC8 0 82 93 43 +Ethernet8 UC9 80 17 91 61 +Ethernet8 MC10 81 63 76 73 +Ethernet8 MC11 29 16 29 66 +Ethernet8 MC12 32 12 61 35 +Ethernet8 MC13 79 17 72 93 +Ethernet8 MC14 23 21 67 50 +Ethernet8 MC15 37 10 97 14 +Ethernet8 MC16 30 17 74 43 +Ethernet8 MC17 0 63 54 84 +Ethernet8 MC18 69 88 24 79 +Ethernet8 MC19 20 12 84 3 +Ethernet8 ALL20 N/A N/A N/A N/A +Ethernet8 ALL21 N/A N/A N/A N/A +Ethernet8 ALL22 N/A N/A N/A N/A +Ethernet8 ALL23 N/A N/A N/A N/A +Ethernet8 ALL24 N/A N/A N/A N/A +Ethernet8 ALL25 N/A N/A N/A N/A +Ethernet8 ALL26 N/A N/A N/A N/A +Ethernet8 ALL27 N/A N/A N/A N/A +Ethernet8 ALL28 N/A N/A N/A N/A +Ethernet8 ALL29 N/A N/A N/A N/A + +""" +show_queue_counters_nz = """\ + Port TxQ Counter/pkts Counter/bytes Drop/pkts Drop/bytes +--------- ----- -------------- --------------- ----------- ------------ Ethernet0 UC1 60 43 39 1 Ethernet0 UC2 82 7 39 21 Ethernet0 UC3 52 70 19 76 @@ -90,7 +190,6 @@ Port TxQ Counter/pkts Counter/bytes Drop/pkts Drop/bytes --------- ----- -------------- --------------- ----------- ------------ -Ethernet8 UC0 19 5 36 56 Ethernet8 UC1 38 17 68 91 Ethernet8 UC2 16 65 79 51 Ethernet8 UC3 11 97 63 72 @@ -227,7 +326,41 @@ show_queue_counters_port = """\ Port TxQ Counter/pkts Counter/bytes Drop/pkts Drop/bytes --------- ----- -------------- --------------- ----------- ------------ -Ethernet8 UC0 19 5 36 56 +Ethernet8 UC0 0 0 0 0 +Ethernet8 UC1 38 17 68 91 +Ethernet8 UC2 16 65 79 51 +Ethernet8 UC3 11 97 63 72 +Ethernet8 UC4 54 89 62 62 +Ethernet8 UC5 13 84 30 59 +Ethernet8 UC6 49 67 99 85 +Ethernet8 UC7 2 63 38 88 +Ethernet8 UC8 0 82 93 43 +Ethernet8 UC9 80 17 91 61 +Ethernet8 MC10 81 63 76 73 +Ethernet8 MC11 29 16 29 66 +Ethernet8 MC12 32 12 61 35 +Ethernet8 MC13 79 17 72 93 +Ethernet8 MC14 23 21 67 50 +Ethernet8 MC15 37 10 97 14 +Ethernet8 MC16 30 17 74 43 +Ethernet8 MC17 0 63 54 84 +Ethernet8 MC18 69 88 24 79 +Ethernet8 MC19 20 12 84 3 +Ethernet8 ALL20 N/A N/A N/A N/A +Ethernet8 ALL21 N/A N/A N/A N/A +Ethernet8 ALL22 N/A N/A N/A N/A +Ethernet8 ALL23 N/A N/A N/A N/A +Ethernet8 ALL24 N/A N/A N/A N/A +Ethernet8 ALL25 N/A N/A N/A N/A +Ethernet8 ALL26 N/A N/A N/A N/A +Ethernet8 ALL27 N/A N/A N/A N/A +Ethernet8 ALL28 N/A N/A N/A N/A +Ethernet8 ALL29 N/A N/A N/A N/A + +""" +show_queue_counters_port_nz = """\ + Port TxQ Counter/pkts Counter/bytes Drop/pkts Drop/bytes +--------- ----- -------------- --------------- ----------- ------------ Ethernet8 UC1 38 17 68 91 Ethernet8 UC2 16 65 79 51 Ethernet8 UC3 11 97 63 72 @@ -384,10 +517,10 @@ "totalpacket": "57" }, "UC0": { - "dropbytes": "74", - "droppacket": "56", - "totalbytes": "30", - "totalpacket": "68" + "dropbytes": "0", + "droppacket": "0", + "totalbytes": "0", + "totalpacket": "0" }, "UC1": { "dropbytes": "1", @@ -748,10 +881,10 @@ "totalpacket": "20" }, "UC0": { - "dropbytes": "56", - "droppacket": "36", - "totalbytes": "5", - "totalpacket": "19" + "dropbytes": "0", + "droppacket": "0", + "totalbytes": "0", + "totalpacket": "0" }, "UC1": { "dropbytes": "91", @@ -810,9 +943,9 @@ } }""" -show_queue_counters_port_json = """\ +show_queue_counters_json_nz = """\ { - "Ethernet8": { + "Ethernet0": { "ALL20": { "dropbytes": "N/A", "droppacket": "N/A", @@ -874,96 +1007,816 @@ "totalpacket": "N/A" }, "MC10": { - "dropbytes": "73", - "droppacket": "76", - "totalbytes": "63", - "totalpacket": "81" + "dropbytes": "9", + "droppacket": "74", + "totalbytes": "96", + "totalpacket": "83" }, "MC11": { - "dropbytes": "66", - "droppacket": "29", - "totalbytes": "16", - "totalpacket": "29" + "dropbytes": "31", + "droppacket": "61", + "totalbytes": "60", + "totalpacket": "15" }, "MC12": { - "dropbytes": "35", - "droppacket": "61", - "totalbytes": "12", - "totalpacket": "32" + "dropbytes": "94", + "droppacket": "82", + "totalbytes": "52", + "totalpacket": "45" }, "MC13": { - "dropbytes": "93", - "droppacket": "72", - "totalbytes": "17", - "totalpacket": "79" + "dropbytes": "52", + "droppacket": "89", + "totalbytes": "88", + "totalpacket": "55" }, "MC14": { - "dropbytes": "50", - "droppacket": "67", - "totalbytes": "21", - "totalpacket": "23" + "dropbytes": "79", + "droppacket": "95", + "totalbytes": "70", + "totalpacket": "14" }, "MC15": { - "dropbytes": "14", - "droppacket": "97", - "totalbytes": "10", - "totalpacket": "37" + "dropbytes": "81", + "droppacket": "66", + "totalbytes": "60", + "totalpacket": "68" }, "MC16": { - "dropbytes": "43", - "droppacket": "74", - "totalbytes": "17", - "totalpacket": "30" + "dropbytes": "76", + "droppacket": "48", + "totalbytes": "4", + "totalpacket": "63" }, "MC17": { - "dropbytes": "84", - "droppacket": "54", - "totalbytes": "63", - "totalpacket": "0" + "dropbytes": "74", + "droppacket": "77", + "totalbytes": "73", + "totalpacket": "41" }, "MC18": { - "dropbytes": "79", - "droppacket": "24", - "totalbytes": "88", - "totalpacket": "69" + "dropbytes": "54", + "droppacket": "56", + "totalbytes": "21", + "totalpacket": "60" }, "MC19": { - "dropbytes": "3", - "droppacket": "84", - "totalbytes": "12", - "totalpacket": "20" - }, - "UC0": { - "dropbytes": "56", - "droppacket": "36", - "totalbytes": "5", - "totalpacket": "19" + "dropbytes": "39", + "droppacket": "12", + "totalbytes": "31", + "totalpacket": "57" }, "UC1": { - "dropbytes": "91", - "droppacket": "68", - "totalbytes": "17", - "totalpacket": "38" + "dropbytes": "1", + "droppacket": "39", + "totalbytes": "43", + "totalpacket": "60" }, "UC2": { - "dropbytes": "51", - "droppacket": "79", - "totalbytes": "65", - "totalpacket": "16" + "dropbytes": "21", + "droppacket": "39", + "totalbytes": "7", + "totalpacket": "82" }, "UC3": { - "dropbytes": "72", - "droppacket": "63", - "totalbytes": "97", - "totalpacket": "11" + "dropbytes": "76", + "droppacket": "19", + "totalbytes": "70", + "totalpacket": "52" }, "UC4": { - "dropbytes": "62", - "droppacket": "62", - "totalbytes": "89", - "totalpacket": "54" - }, - "UC5": { + "dropbytes": "94", + "droppacket": "12", + "totalbytes": "59", + "totalpacket": "11" + }, + "UC5": { + "dropbytes": "40", + "droppacket": "35", + "totalbytes": "62", + "totalpacket": "36" + }, + "UC6": { + "dropbytes": "88", + "droppacket": "2", + "totalbytes": "91", + "totalpacket": "49" + }, + "UC7": { + "dropbytes": "74", + "droppacket": "94", + "totalbytes": "17", + "totalpacket": "33" + }, + "UC8": { + "dropbytes": "33", + "droppacket": "95", + "totalbytes": "71", + "totalpacket": "40" + }, + "UC9": { + "dropbytes": "78", + "droppacket": "93", + "totalbytes": "8", + "totalpacket": "54" + } + }, + "Ethernet4": { + "ALL20": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL21": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL22": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL23": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL24": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL25": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL26": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL27": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL28": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL29": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "MC10": { + "dropbytes": "82", + "droppacket": "57", + "totalbytes": "1", + "totalpacket": "50" + }, + "MC11": { + "dropbytes": "59", + "droppacket": "84", + "totalbytes": "99", + "totalpacket": "67" + }, + "MC12": { + "dropbytes": "5", + "droppacket": "27", + "totalbytes": "58", + "totalpacket": "4" + }, + "MC13": { + "dropbytes": "39", + "droppacket": "57", + "totalbytes": "5", + "totalpacket": "74" + }, + "MC14": { + "dropbytes": "14", + "droppacket": "4", + "totalbytes": "59", + "totalpacket": "21" + }, + "MC15": { + "dropbytes": "53", + "droppacket": "19", + "totalbytes": "61", + "totalpacket": "24" + }, + "MC16": { + "dropbytes": "32", + "droppacket": "15", + "totalbytes": "15", + "totalpacket": "51" + }, + "MC17": { + "dropbytes": "15", + "droppacket": "23", + "totalbytes": "18", + "totalpacket": "98" + }, + "MC18": { + "dropbytes": "57", + "droppacket": "9", + "totalbytes": "34", + "totalpacket": "41" + }, + "MC19": { + "dropbytes": "99", + "droppacket": "18", + "totalbytes": "7", + "totalpacket": "57" + }, + "UC0": { + "dropbytes": "98", + "droppacket": "70", + "totalbytes": "96", + "totalpacket": "41" + }, + "UC1": { + "dropbytes": "36", + "droppacket": "63", + "totalbytes": "49", + "totalpacket": "18" + }, + "UC2": { + "dropbytes": "15", + "droppacket": "3", + "totalbytes": "90", + "totalpacket": "99" + }, + "UC3": { + "dropbytes": "41", + "droppacket": "48", + "totalbytes": "89", + "totalpacket": "60" + }, + "UC4": { + "dropbytes": "94", + "droppacket": "82", + "totalbytes": "84", + "totalpacket": "8" + }, + "UC5": { + "dropbytes": "92", + "droppacket": "75", + "totalbytes": "15", + "totalpacket": "83" + }, + "UC6": { + "dropbytes": "71", + "droppacket": "50", + "totalbytes": "26", + "totalpacket": "84" + }, + "UC7": { + "dropbytes": "80", + "droppacket": "49", + "totalbytes": "19", + "totalpacket": "27" + }, + "UC8": { + "dropbytes": "33", + "droppacket": "13", + "totalbytes": "89", + "totalpacket": "13" + }, + "UC9": { + "dropbytes": "31", + "droppacket": "86", + "totalbytes": "48", + "totalpacket": "43" + } + }, + "Ethernet8": { + "ALL20": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL21": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL22": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL23": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL24": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL25": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL26": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL27": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL28": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL29": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "MC10": { + "dropbytes": "73", + "droppacket": "76", + "totalbytes": "63", + "totalpacket": "81" + }, + "MC11": { + "dropbytes": "66", + "droppacket": "29", + "totalbytes": "16", + "totalpacket": "29" + }, + "MC12": { + "dropbytes": "35", + "droppacket": "61", + "totalbytes": "12", + "totalpacket": "32" + }, + "MC13": { + "dropbytes": "93", + "droppacket": "72", + "totalbytes": "17", + "totalpacket": "79" + }, + "MC14": { + "dropbytes": "50", + "droppacket": "67", + "totalbytes": "21", + "totalpacket": "23" + }, + "MC15": { + "dropbytes": "14", + "droppacket": "97", + "totalbytes": "10", + "totalpacket": "37" + }, + "MC16": { + "dropbytes": "43", + "droppacket": "74", + "totalbytes": "17", + "totalpacket": "30" + }, + "MC17": { + "dropbytes": "84", + "droppacket": "54", + "totalbytes": "63", + "totalpacket": "0" + }, + "MC18": { + "dropbytes": "79", + "droppacket": "24", + "totalbytes": "88", + "totalpacket": "69" + }, + "MC19": { + "dropbytes": "3", + "droppacket": "84", + "totalbytes": "12", + "totalpacket": "20" + }, + "UC1": { + "dropbytes": "91", + "droppacket": "68", + "totalbytes": "17", + "totalpacket": "38" + }, + "UC2": { + "dropbytes": "51", + "droppacket": "79", + "totalbytes": "65", + "totalpacket": "16" + }, + "UC3": { + "dropbytes": "72", + "droppacket": "63", + "totalbytes": "97", + "totalpacket": "11" + }, + "UC4": { + "dropbytes": "62", + "droppacket": "62", + "totalbytes": "89", + "totalpacket": "54" + }, + "UC5": { + "dropbytes": "59", + "droppacket": "30", + "totalbytes": "84", + "totalpacket": "13" + }, + "UC6": { + "dropbytes": "85", + "droppacket": "99", + "totalbytes": "67", + "totalpacket": "49" + }, + "UC7": { + "dropbytes": "88", + "droppacket": "38", + "totalbytes": "63", + "totalpacket": "2" + }, + "UC8": { + "dropbytes": "43", + "droppacket": "93", + "totalbytes": "82", + "totalpacket": "0" + }, + "UC9": { + "dropbytes": "61", + "droppacket": "91", + "totalbytes": "17", + "totalpacket": "80" + } + } +}""" + + +show_queue_counters_port_json = """\ +{ + "Ethernet8": { + "ALL20": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL21": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL22": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL23": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL24": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL25": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL26": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL27": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL28": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL29": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "MC10": { + "dropbytes": "73", + "droppacket": "76", + "totalbytes": "63", + "totalpacket": "81" + }, + "MC11": { + "dropbytes": "66", + "droppacket": "29", + "totalbytes": "16", + "totalpacket": "29" + }, + "MC12": { + "dropbytes": "35", + "droppacket": "61", + "totalbytes": "12", + "totalpacket": "32" + }, + "MC13": { + "dropbytes": "93", + "droppacket": "72", + "totalbytes": "17", + "totalpacket": "79" + }, + "MC14": { + "dropbytes": "50", + "droppacket": "67", + "totalbytes": "21", + "totalpacket": "23" + }, + "MC15": { + "dropbytes": "14", + "droppacket": "97", + "totalbytes": "10", + "totalpacket": "37" + }, + "MC16": { + "dropbytes": "43", + "droppacket": "74", + "totalbytes": "17", + "totalpacket": "30" + }, + "MC17": { + "dropbytes": "84", + "droppacket": "54", + "totalbytes": "63", + "totalpacket": "0" + }, + "MC18": { + "dropbytes": "79", + "droppacket": "24", + "totalbytes": "88", + "totalpacket": "69" + }, + "MC19": { + "dropbytes": "3", + "droppacket": "84", + "totalbytes": "12", + "totalpacket": "20" + }, + "UC0": { + "dropbytes": "0", + "droppacket": "0", + "totalbytes": "0", + "totalpacket": "0" + }, + "UC1": { + "dropbytes": "91", + "droppacket": "68", + "totalbytes": "17", + "totalpacket": "38" + }, + "UC2": { + "dropbytes": "51", + "droppacket": "79", + "totalbytes": "65", + "totalpacket": "16" + }, + "UC3": { + "dropbytes": "72", + "droppacket": "63", + "totalbytes": "97", + "totalpacket": "11" + }, + "UC4": { + "dropbytes": "62", + "droppacket": "62", + "totalbytes": "89", + "totalpacket": "54" + }, + "UC5": { + "dropbytes": "59", + "droppacket": "30", + "totalbytes": "84", + "totalpacket": "13" + }, + "UC6": { + "dropbytes": "85", + "droppacket": "99", + "totalbytes": "67", + "totalpacket": "49" + }, + "UC7": { + "dropbytes": "88", + "droppacket": "38", + "totalbytes": "63", + "totalpacket": "2" + }, + "UC8": { + "dropbytes": "43", + "droppacket": "93", + "totalbytes": "82", + "totalpacket": "0" + }, + "UC9": { + "dropbytes": "61", + "droppacket": "91", + "totalbytes": "17", + "totalpacket": "80" + } + } +}""" + + +show_queue_counters_port_json_nz = """\ +{ + "Ethernet8": { + "ALL20": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL21": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL22": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL23": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL24": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL25": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL26": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL27": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL28": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "ALL29": { + "dropbytes": "N/A", + "droppacket": "N/A", + "totalbytes": "N/A", + "totalpacket": "N/A" + }, + "MC10": { + "dropbytes": "73", + "droppacket": "76", + "totalbytes": "63", + "totalpacket": "81" + }, + "MC11": { + "dropbytes": "66", + "droppacket": "29", + "totalbytes": "16", + "totalpacket": "29" + }, + "MC12": { + "dropbytes": "35", + "droppacket": "61", + "totalbytes": "12", + "totalpacket": "32" + }, + "MC13": { + "dropbytes": "93", + "droppacket": "72", + "totalbytes": "17", + "totalpacket": "79" + }, + "MC14": { + "dropbytes": "50", + "droppacket": "67", + "totalbytes": "21", + "totalpacket": "23" + }, + "MC15": { + "dropbytes": "14", + "droppacket": "97", + "totalbytes": "10", + "totalpacket": "37" + }, + "MC16": { + "dropbytes": "43", + "droppacket": "74", + "totalbytes": "17", + "totalpacket": "30" + }, + "MC17": { + "dropbytes": "84", + "droppacket": "54", + "totalbytes": "63", + "totalpacket": "0" + }, + "MC18": { + "dropbytes": "79", + "droppacket": "24", + "totalbytes": "88", + "totalpacket": "69" + }, + "MC19": { + "dropbytes": "3", + "droppacket": "84", + "totalbytes": "12", + "totalpacket": "20" + }, + "UC1": { + "dropbytes": "91", + "droppacket": "68", + "totalbytes": "17", + "totalpacket": "38" + }, + "UC2": { + "dropbytes": "51", + "droppacket": "79", + "totalbytes": "65", + "totalpacket": "16" + }, + "UC3": { + "dropbytes": "72", + "droppacket": "63", + "totalbytes": "97", + "totalpacket": "11" + }, + "UC4": { + "dropbytes": "62", + "droppacket": "62", + "totalbytes": "89", + "totalpacket": "54" + }, + "UC5": { "dropbytes": "59", "droppacket": "30", "totalbytes": "84", @@ -996,10 +1849,46 @@ } }""" + show_queue_voq_counters = """\ Port Voq Counter/pkts Counter/bytes Drop/pkts Drop/bytes ---------------- ----- -------------- --------------- ----------- ------------ -testsw|Ethernet0 VOQ0 68 30 56 74 +testsw|Ethernet0 VOQ0 0 0 0 0 +testsw|Ethernet0 VOQ1 60 43 39 1 +testsw|Ethernet0 VOQ2 82 7 39 21 +testsw|Ethernet0 VOQ3 11 59 12 94 +testsw|Ethernet0 VOQ4 36 62 35 40 +testsw|Ethernet0 VOQ5 49 91 2 88 +testsw|Ethernet0 VOQ6 33 17 94 74 +testsw|Ethernet0 VOQ7 40 71 95 33 + + Port Voq Counter/pkts Counter/bytes Drop/pkts Drop/bytes +---------------- ----- -------------- --------------- ----------- ------------ +testsw|Ethernet4 VOQ0 54 8 93 78 +testsw|Ethernet4 VOQ1 83 96 74 9 +testsw|Ethernet4 VOQ2 15 60 61 31 +testsw|Ethernet4 VOQ3 45 52 82 94 +testsw|Ethernet4 VOQ4 55 88 89 52 +testsw|Ethernet4 VOQ5 14 70 95 79 +testsw|Ethernet4 VOQ6 68 60 66 81 +testsw|Ethernet4 VOQ7 63 4 48 76 + + Port Voq Counter/pkts Counter/bytes Drop/pkts Drop/bytes +---------------- ----- -------------- --------------- ----------- ------------ +testsw|Ethernet8 VOQ0 41 73 77 74 +testsw|Ethernet8 VOQ1 60 21 56 54 +testsw|Ethernet8 VOQ2 57 31 12 39 +testsw|Ethernet8 VOQ3 41 96 70 98 +testsw|Ethernet8 VOQ4 18 49 63 36 +testsw|Ethernet8 VOQ5 99 90 3 15 +testsw|Ethernet8 VOQ6 8 84 82 94 +testsw|Ethernet8 VOQ7 83 15 75 92 + +""" + +show_queue_voq_counters_nz = """\ + Port Voq Counter/pkts Counter/bytes Drop/pkts Drop/bytes +---------------- ----- -------------- --------------- ----------- ------------ testsw|Ethernet0 VOQ1 60 43 39 1 testsw|Ethernet0 VOQ2 82 7 39 21 testsw|Ethernet0 VOQ3 11 59 12 94 @@ -1071,7 +1960,20 @@ show_queue_port_voq_counters = """\ Port Voq Counter/pkts Counter/bytes Drop/pkts Drop/bytes ---------------- ----- -------------- --------------- ----------- ------------ -testsw|Ethernet0 VOQ0 68 30 56 74 +testsw|Ethernet0 VOQ0 0 0 0 0 +testsw|Ethernet0 VOQ1 60 43 39 1 +testsw|Ethernet0 VOQ2 82 7 39 21 +testsw|Ethernet0 VOQ3 11 59 12 94 +testsw|Ethernet0 VOQ4 36 62 35 40 +testsw|Ethernet0 VOQ5 49 91 2 88 +testsw|Ethernet0 VOQ6 33 17 94 74 +testsw|Ethernet0 VOQ7 40 71 95 33 + +""" + +show_queue_port_voq_counters_nz = """\ + Port Voq Counter/pkts Counter/bytes Drop/pkts Drop/bytes +---------------- ----- -------------- --------------- ----------- ------------ testsw|Ethernet0 VOQ1 60 43 39 1 testsw|Ethernet0 VOQ2 82 7 39 21 testsw|Ethernet0 VOQ3 11 59 12 94 @@ -1086,10 +1988,10 @@ { "testsw|Ethernet0": { "VOQ0": { - "dropbytes": "74", - "droppacket": "56", - "totalbytes": "30", - "totalpacket": "68" + "dropbytes": "0", + "droppacket": "0", + "totalbytes": "0", + "totalpacket": "0" }, "VOQ1": { "dropbytes": "1", @@ -1240,10 +2142,10 @@ { "testsw|Ethernet0": { "VOQ0": { - "dropbytes": "74", - "droppacket": "56", - "totalbytes": "30", - "totalpacket": "68" + "dropbytes": "0", + "droppacket": "0", + "totalbytes": "0", + "totalpacket": "0" }, "VOQ1": { "dropbytes": "1", @@ -1307,6 +2209,16 @@ def test_queue_counters(self): assert result.exit_code == 0 assert result.output == show_queue_counters + def test_queue_counters_nonzero(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["queue"].commands["counters"], + ["--nonzero"] + ) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_queue_counters_nz + def test_queue_counters_with_clear(self): runner = CliRunner() result = runner.invoke(clear.cli.commands['queuecounters'], []) @@ -1335,6 +2247,16 @@ def test_queue_counters_port(self): assert result.exit_code == 0 assert result.output == show_queue_counters_port + def test_queue_counters_port_nz(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["queue"].commands["counters"], + ["Ethernet8", "--nonzero"] + ) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_queue_counters_port_nz + def test_queue_counters_json(self): runner = CliRunner() result = runner.invoke( @@ -1348,7 +2270,22 @@ def test_queue_counters_json(self): # remove "time" from the output for _, v in json_output.items(): del v["time"] - assert json_dump(json_output) == show_queue_counters_json + assert json_dump(json_output) == show_queue_counters_json + + def test_queue_counters_json_nz(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["queue"].commands["counters"], + ["--json", "--nonzero"] + ) + assert result.exit_code == 0 + print(result.output) + json_output = json.loads(result.output) + + # remove "time" from the output + for _, v in json_output.items(): + del v["time"] + assert json_dump(json_output) == show_queue_counters_json_nz def test_queue_counters_port_json(self): runner = CliRunner() @@ -1363,7 +2300,22 @@ def test_queue_counters_port_json(self): # remove "time" from the output for _, v in json_output.items(): del v["time"] - assert json_dump(json_output) == show_queue_counters_port_json + assert json_dump(json_output) == show_queue_counters_port_json + + def test_queue_counters_port_json_nz(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["queue"].commands["counters"], + ["Ethernet8", "--json", "--nonzero"] + ) + assert result.exit_code == 0 + print(result.output) + json_output = json.loads(result.output) + + # remove "time" from the output + for _, v in json_output.items(): + del v["time"] + assert json_dump(json_output) == show_queue_counters_port_json_nz def test_queue_voq_counters(self): runner = CliRunner() @@ -1375,6 +2327,16 @@ def test_queue_voq_counters(self): assert result.exit_code == 0 assert result.output == show_queue_voq_counters + def test_queue_voq_counters_nz(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["queue"].commands["counters"], + ["--voq", "--nonzero"] + ) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_queue_voq_counters_nz + def test_queue_voq_counters_with_clear(self): runner = CliRunner() result = runner.invoke(clear.cli.commands['queuecounters'], []) @@ -1403,6 +2365,16 @@ def test_queue_port_voq_counters(self): assert result.exit_code == 0 assert result.output == show_queue_port_voq_counters + def test_queue_port_voq_counters_nz(self): + runner = CliRunner() + result = runner.invoke( + show.cli.commands["queue"].commands["counters"], + ["testsw|Ethernet0", "--voq", "--nonzero"] + ) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_queue_port_voq_counters_nz + def test_queue_voq_counters_json(self): runner = CliRunner() result = runner.invoke( From ff6f8f32f708bac41f13383ca08d56936aa78d21 Mon Sep 17 00:00:00 2001 From: mihirpat1 <112018033+mihirpat1@users.noreply.github.com> Date: Thu, 14 Sep 2023 12:41:33 -0700 Subject: [PATCH 04/20] Include /var/log.tmpfs in techsupport (#2979) Signed-off-by: Mihir Patel --- scripts/generate_dump | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/scripts/generate_dump b/scripts/generate_dump index dd98302a27..ac9528ff35 100755 --- a/scripts/generate_dump +++ b/scripts/generate_dump @@ -1441,8 +1441,26 @@ save_log_files() { trap enable_logrotate HUP INT QUIT TERM KILL ABRT ALRM start_t=$(date +%s%3N) + log_dir_1="/var/log/" + log_dir_2="/var/log.tmpfs/" + file_list="" + + if [ -d "$log_dir_1" ]; then + file_list_1=$(find_files ${log_dir_1}) + file_list="${file_list} ${file_list_1}" + fi + + if [ -d "$log_dir_2" ]; then + file_list_2=$(find_files ${log_dir_2}) + file_list="${file_list} ${file_list_2}" + fi + # gzip up all log files individually before placing them in the incremental tarball - for file in $(find_files "/var/log/"); do + for file in $file_list; do + dest_dir="log" + if [[ $file == *"tmpfs"* ]]; then + dest_dir="log.tmpfs" + fi # ignore the sparse file lastlog if [ "$file" = "/var/log/lastlog" ]; then continue @@ -1450,9 +1468,9 @@ save_log_files() { # don't gzip already-gzipped log files :) # do not append the individual files to the main tarball if [ -z "${file##*.gz}" ]; then - save_file $file log false + save_file $file $dest_dir false else - save_file $file log true + save_file $file $dest_dir true fi done From 572902878cf4bb3345de37791d231456b9df95cb Mon Sep 17 00:00:00 2001 From: Mai Bui Date: Fri, 15 Sep 2023 10:19:38 -0400 Subject: [PATCH 05/20] Fix del vlan command (#2982) Resolves https://github.com/sonic-net/sonic-buildimage/issues/16542 #### What I did Update str -> list[str] commands which were missed in https://github.com/sonic-net/sonic-utilities/pull/2718 #### How I did it #### How to verify it Pass UT. Manual test, issue resolved, tested in internal.79435802-3bbd91c86e version Signed-off-by: Mai Bui --- config/vlan.py | 14 +++++++------- tests/vlan_test.py | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/config/vlan.py b/config/vlan.py index 7206868db7..fd70b027cb 100644 --- a/config/vlan.py +++ b/config/vlan.py @@ -176,21 +176,21 @@ def del_vlan(db, vid, multiple, no_restart_dhcp_relay): vlans = db.cfgdb.get_keys('VLAN') if not vlans: - docker_exec_cmd = "docker exec -i swss {}" - _, rc = clicommon.run_command(docker_exec_cmd.format("supervisorctl status ndppd"), ignore_error=True, return_cmd=True) + docker_exec_cmd = ['docker', 'exec', '-i', 'swss'] + _, rc = clicommon.run_command(docker_exec_cmd + ['supervisorctl', 'status', 'ndppd'], ignore_error=True, return_cmd=True) if rc == 0: click.echo("No VLANs remaining, stopping ndppd service") - clicommon.run_command(docker_exec_cmd.format("supervisorctl stop ndppd"), ignore_error=True, return_cmd=True) - clicommon.run_command(docker_exec_cmd.format("rm -f /etc/supervisor/conf.d/ndppd.conf"), ignore_error=True, return_cmd=True) - clicommon.run_command(docker_exec_cmd.format("supervisorctl update"), return_cmd=True) + clicommon.run_command(docker_exec_cmd + ['supervisorctl', 'stop', 'ndppd'], ignore_error=True, return_cmd=True) + clicommon.run_command(docker_exec_cmd + ['rm', '-f', '/etc/supervisor/conf.d/ndppd.conf'], ignore_error=True, return_cmd=True) + clicommon.run_command(docker_exec_cmd + ['supervisorctl', 'update'], return_cmd=True) def restart_ndppd(): verify_swss_running_cmd = ['docker', 'container', 'inspect', '-f', '{{.State.Status}}', 'swss'] docker_exec_cmd = ['docker', 'exec', '-i', 'swss'] ndppd_config_gen_cmd = ['sonic-cfggen', '-d', '-t', '/usr/share/sonic/templates/ndppd.conf.j2,/etc/ndppd.conf'] - ndppd_restart_cmd =['supervisorctl', 'restart', 'ndppd'] - ndppd_status_cmd= ["supervisorctl", "status", "ndppd"] + ndppd_restart_cmd = ['supervisorctl', 'restart', 'ndppd'] + ndppd_status_cmd = ["supervisorctl", "status", "ndppd"] ndppd_conf_copy_cmd = ['cp', '/usr/share/sonic/templates/ndppd.conf', '/etc/supervisor/conf.d/'] supervisor_update_cmd = ['supervisorctl', 'update'] diff --git a/tests/vlan_test.py b/tests/vlan_test.py index e00d3c40a8..456bb5dd11 100644 --- a/tests/vlan_test.py +++ b/tests/vlan_test.py @@ -630,10 +630,10 @@ def test_config_vlan_del_last_vlan(self): print(result.exit_code) print(result.output) mock_run_command.assert_has_calls([ - mock.call("docker exec -i swss supervisorctl status ndppd", ignore_error=True, return_cmd=True), - mock.call("docker exec -i swss supervisorctl stop ndppd", ignore_error=True, return_cmd=True), - mock.call("docker exec -i swss rm -f /etc/supervisor/conf.d/ndppd.conf", ignore_error=True, return_cmd=True), - mock.call("docker exec -i swss supervisorctl update", return_cmd=True) + mock.call(['docker', 'exec', '-i', 'swss', 'supervisorctl', 'status', 'ndppd'], ignore_error=True, return_cmd=True), + mock.call(['docker', 'exec', '-i', 'swss', 'supervisorctl', 'stop', 'ndppd'], ignore_error=True, return_cmd=True), + mock.call(['docker', 'exec', '-i', 'swss', 'rm', '-f', '/etc/supervisor/conf.d/ndppd.conf'], ignore_error=True, return_cmd=True), + mock.call(['docker', 'exec', '-i', 'swss', 'supervisorctl', 'update'], return_cmd=True) ]) assert result.exit_code == 0 From 701994f5dea8e380810a7d94d902095ab6baae93 Mon Sep 17 00:00:00 2001 From: mihirpat1 <112018033+mihirpat1@users.noreply.github.com> Date: Mon, 18 Sep 2023 21:53:50 -0700 Subject: [PATCH 06/20] Handle NotImplementedError exception while changing optoe write max (#2985) * Handle NotImplementedError exception while changing optoe write max Signed-off-by: Mihir Patel * Added unit test for more coverage * Removed unused import --------- Signed-off-by: Mihir Patel --- sfputil/main.py | 10 ++++++++-- tests/sfputil_test.py | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/sfputil/main.py b/sfputil/main.py index 51d5af4895..af16ddcff5 100644 --- a/sfputil/main.py +++ b/sfputil/main.py @@ -1325,7 +1325,10 @@ def download_firmware(port_name, filepath): sys.exit(EXIT_FAIL) # Increase the optoe driver's write max to speed up firmware download - sfp.set_optoe_write_max(SMBUS_BLOCK_WRITE_SIZE) + try: + sfp.set_optoe_write_max(SMBUS_BLOCK_WRITE_SIZE) + except NotImplementedError: + click.echo("Platform doesn't implement optoe write max change. Skipping value increase.") with click.progressbar(length=file_size, label="Downloading ...") as bar: address = 0 @@ -1351,7 +1354,10 @@ def download_firmware(port_name, filepath): remaining -= count # Restore the optoe driver's write max to '1' (default value) - sfp.set_optoe_write_max(1) + try: + sfp.set_optoe_write_max(1) + except NotImplementedError: + click.echo("Platform doesn't implement optoe write max change. Skipping value restore!") status = api.cdb_firmware_download_complete() update_firmware_info_to_state_db(port_name) diff --git a/tests/sfputil_test.py b/tests/sfputil_test.py index f8917d9c44..0de4a46026 100644 --- a/tests/sfputil_test.py +++ b/tests/sfputil_test.py @@ -825,6 +825,23 @@ def test_show_fwversion_Rj45(self, mock_chassis): assert result.output == 'Show firmware version is not applicable for RJ45 port Ethernet0.\n' assert result.exit_code == EXIT_FAIL + @patch('builtins.open') + @patch('sfputil.main.platform_chassis') + @patch('sfputil.main.logical_port_to_physical_port_index', MagicMock(return_value=1)) + @patch('sfputil.main.update_firmware_info_to_state_db', MagicMock()) + def test_download_firmware(self, mock_chassis, mock_file): + mock_file.return_value.tell.return_value = 0 + mock_sfp = MagicMock() + mock_api = MagicMock() + mock_sfp.get_xcvr_api = MagicMock(return_value=mock_api) + mock_chassis.get_sfp = MagicMock(return_value=mock_sfp) + mock_api.get_module_fw_mgmt_feature.return_value = {'status': True, 'feature': (0, 0, False, False, 0)} + mock_api.cdb_start_firmware_download.return_value = 1 + mock_api.cdb_firmware_download_complete.return_value = 1 + mock_sfp.set_optoe_write_max = MagicMock(side_effect=NotImplementedError) + status = sfputil.download_firmware("Ethernet0", "test.bin") + assert status == 1 + @patch('sfputil.main.platform_chassis') @patch('sfputil.main.logical_port_to_physical_port_index', MagicMock(return_value=1)) def test_run_firmwre(self, mock_chassis): From 3df1f18b0a339ab9d92cd8c28ad703a491b30b85 Mon Sep 17 00:00:00 2001 From: Yaqiang Zhu Date: Wed, 20 Sep 2023 17:20:33 +0800 Subject: [PATCH 07/20] [vlan][dhcp_relay] Fix error while delete vlan (#2987) Why I did Use static str to run_command would encounter error because change in this PR: #2718. How I did it Change command from static str to list. How to verify it UT passed. Build whl and install in testbed. Signed-off-by: Yaqiang Zhu --- config/vlan.py | 3 ++- tests/mock_tables/config_db.json | 5 +++++ tests/vlan_test.py | 34 +++++++++++++++++++++++++++++ utilities_common/dhcp_relay_util.py | 6 ++--- 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/config/vlan.py b/config/vlan.py index fd70b027cb..2f99f3ccfe 100644 --- a/config/vlan.py +++ b/config/vlan.py @@ -30,7 +30,8 @@ def set_dhcp_relay_table(table, config_db, vlan_name, value): def is_dhcp_relay_running(): - out, _ = clicommon.run_command("systemctl show dhcp_relay.service --property ActiveState --value", return_cmd=True) + out, _ = clicommon.run_command(["systemctl", "show", "dhcp_relay.service", "--property", "ActiveState", "--value"], + return_cmd=True) return out.strip() == "active" def is_dhcpv6_relay_config_exist(db, vlan_name): diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index 07fc66db9e..f622184e26 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -2723,5 +2723,10 @@ "alias": "Fabric2", "isolateStatus": "False", "lanes": "2" + }, + "DHCP_RELAY|Vlan1000": { + "dhcpv6_servers": [ + "fc02:2000::1" + ] } } diff --git a/tests/vlan_test.py b/tests/vlan_test.py index 456bb5dd11..5212a7b026 100644 --- a/tests/vlan_test.py +++ b/tests/vlan_test.py @@ -1377,3 +1377,37 @@ def teardown_class(cls): os.environ['UTILITIES_UNIT_TESTING'] = "0" bgp_util.run_bgp_command = cls._old_run_bgp_command print("TEARDOWN") + + def test_config_vlan_del_dhcp_relay_restart(self): + runner = CliRunner() + db = Db() + obj = {"config_db": db.cfgdb} + + # remove vlan IP`s + result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], + ["Vlan1000", "192.168.0.1/21"], obj=obj) + print(result.exit_code, result.output) + assert result.exit_code != 0 + + result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["remove"], + ["Vlan1000", "fc02:1000::1/64"], obj=obj) + print(result.exit_code, result.output) + assert result.exit_code != 0 + + # remove vlan members + vlan_member = db.cfgdb.get_table("VLAN_MEMBER") + keys = [(k, v) for k, v in vlan_member if k == "Vlan{}".format(1000)] + for _, v in keys: + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["del"], ["1000", v], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + origin_run_command_func = config.vlan.clicommon.run_command + config.vlan.clicommon.run_command = mock.MagicMock(return_value=("active", 0)) + result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1000"], obj=db) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + + config.vlan.clicommon.run_command = origin_run_command_func diff --git a/utilities_common/dhcp_relay_util.py b/utilities_common/dhcp_relay_util.py index b9c0b4e20f..4a0ab5a2e4 100644 --- a/utilities_common/dhcp_relay_util.py +++ b/utilities_common/dhcp_relay_util.py @@ -7,9 +7,9 @@ def restart_dhcp_relay_service(): Restart dhcp_relay service """ click.echo("Restarting DHCP relay service...") - clicommon.run_command("systemctl stop dhcp_relay", display_cmd=False) - clicommon.run_command("systemctl reset-failed dhcp_relay", display_cmd=False) - clicommon.run_command("systemctl start dhcp_relay", display_cmd=False) + clicommon.run_command(["systemctl", "stop", "dhcp_relay"], display_cmd=False) + clicommon.run_command(["systemctl", "reset-failed", "dhcp_relay"], display_cmd=False) + clicommon.run_command(["systemctl", "start", "dhcp_relay"], display_cmd=False) def handle_restart_dhcp_relay_service(): From d82a4900790dd662fe261a2f8d0e218da4f6c995 Mon Sep 17 00:00:00 2001 From: Zhijian Li Date: Sat, 23 Sep 2023 07:11:42 -0700 Subject: [PATCH 08/20] [acl-loader] Identity ICMP v4/v6 based on IP_PROTOCOL for custom ACL table types (#2994) What is the motivation for this PR? When adding ICMPv6 ACL rules in custom ACL table type, current acl-loader will incorrectly treat the ACL table as IPv4. I open this PR to fix this bug and let acl-loader identify ICMP v4 or v6 based on IP_PROTOCOL. Also fixed some typo in UT of acl-loader to avoid confusion. How did you do it? In function convert_icmp, add one step to identify the rule is v4 or v6 based on IP_PROTOCOL. How did you verify/test it? Verified by UT. Signed-off-by: Zhijian Li --- acl_loader/main.py | 8 +++++++ tests/acl_input/acl1.json | 44 ++++++++++++++++++++++++++++++++++++++- tests/acl_loader_test.py | 27 ++++++++++++++++++++++-- 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/acl_loader/main.py b/acl_loader/main.py index 5bacfe7d8f..e81e05d9b7 100644 --- a/acl_loader/main.py +++ b/acl_loader/main.py @@ -598,6 +598,14 @@ def convert_icmp(self, table_name, rule_idx, rule): is_rule_v6 = True except Exception as e: pass + else: + # get the IP version type using IP_PROTOCOL. + try: + ip_protocol = rule.ip.config.protocol + if ip_protocol == "IP_ICMPV6" or int(ip_protocol) == self.ip_protocol_map["IP_ICMPV6"]: + is_rule_v6 = True + except Exception as e: + pass type_key = "ICMPV6_TYPE" if is_rule_v6 else "ICMP_TYPE" code_key = "ICMPV6_CODE" if is_rule_v6 else "ICMP_CODE" diff --git a/tests/acl_input/acl1.json b/tests/acl_input/acl1.json index 4bcd8049be..586661bbc8 100644 --- a/tests/acl_input/acl1.json +++ b/tests/acl_input/acl1.json @@ -235,7 +235,7 @@ } } }, - "2": { + "100": { "config": { "sequence-id": 100 }, @@ -285,6 +285,27 @@ } } } + }, + "2": { + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + }, + "config": { + "sequence-id": 2 + }, + "ip": { + "config": { + "protocol": "1" + } + }, + "icmp": { + "config": { + "type": "136", + "code": "0" + } + } } } }, @@ -310,6 +331,27 @@ "destination-ip-address": "fc02::/64" } } + }, + "2": { + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + }, + "config": { + "sequence-id": 2 + }, + "ip": { + "config": { + "protocol": "58" + } + }, + "icmp": { + "config": { + "type": "136", + "code": "0" + } + } } } }, diff --git a/tests/acl_loader_test.py b/tests/acl_loader_test.py index c4d2e0b9ea..599e47461a 100644 --- a/tests/acl_loader_test.py +++ b/tests/acl_loader_test.py @@ -150,7 +150,6 @@ def test_icmp_translation(self, acl_loader): def test_icmpv6_translation(self, acl_loader): acl_loader.rules_info = {} acl_loader.load_rules_from_file(os.path.join(test_path, 'acl_input/acl1.json')) - print(acl_loader.rules_info) assert acl_loader.rules_info[("DATAACL_2", "RULE_1")] == { "ICMPV6_TYPE": 1, "ICMPV6_CODE": 0, @@ -171,6 +170,30 @@ def test_icmpv6_translation(self, acl_loader): "PRIORITY": "9900" } + def test_icmp_translation_in_custom_acl_table_type(self, acl_loader): + acl_loader.rules_info = {} + acl_loader.load_rules_from_file(os.path.join(test_path, 'acl_input/acl1.json')) + assert acl_loader.rules_info[("BMC_ACL_NORTHBOUND", "RULE_2")] + assert acl_loader.rules_info[("BMC_ACL_NORTHBOUND", "RULE_2")] == { + "ICMP_TYPE": 136, + "ICMP_CODE": 0, + "IP_PROTOCOL": 1, + "PACKET_ACTION": "FORWARD", + "PRIORITY": "9998" + } + + def test_icmpv6_translation_in_custom_acl_table_type(self, acl_loader): + acl_loader.rules_info = {} + acl_loader.load_rules_from_file(os.path.join(test_path, 'acl_input/acl1.json')) + assert acl_loader.rules_info[("BMC_ACL_NORTHBOUND_V6", "RULE_2")] + assert acl_loader.rules_info[("BMC_ACL_NORTHBOUND_V6", "RULE_2")] == { + "ICMPV6_TYPE": 136, + "ICMPV6_CODE": 0, + "IP_PROTOCOL": 58, + "PACKET_ACTION": "FORWARD", + "PRIORITY": "9998" + } + def test_ingress_default_deny_rule(self, acl_loader): acl_loader.set_mirror_stage("ingress") acl_loader.get_session_name = mock.MagicMock(return_value="everflow_session_mock") @@ -250,7 +273,7 @@ def ttest_icmp_fields_with_non_icmpv6_protocol(self, acl_loader): assert not acl_loader.rules_info.get("RULE_1") - def test_icmp_fields_with_non_tcp_protocol(self, acl_loader): + def test_tcp_fields_with_non_tcp_protocol(self, acl_loader): acl_loader.rules_info = {} acl_loader.load_rules_from_file(os.path.join(test_path, 'acl_input/tcp_bad_protocol_number.json')) assert not acl_loader.rules_info.get("RULE_1") From 7d7a971e5d9f63f529694dc773ae4a77625990f3 Mon Sep 17 00:00:00 2001 From: Alpesh Patel Date: Sat, 23 Sep 2023 20:10:01 -0400 Subject: [PATCH 09/20] mmuconfig to set threshold for profiles (#2775) Extended the mmuconfig utility with a "-s / --staticth" keyword to configure the threshold configured via pg_profile_lookup.ini The utility had ability to configure "-a/alpha" for dynamic threshold --- config/main.py | 16 ++ mmuconfig | 199 ++++++++++++++++++ scripts/mmuconfig | 45 ++-- tests/buffer_input/buffer_test_vectors.py | 14 ++ .../mmuconfig_input/mmuconfig_test_vectors.py | 112 ++++++++++ tests/mmuconfig_test.py | 86 ++++++++ tests/mock_tables/config_db.json | 10 + 7 files changed, 467 insertions(+), 15 deletions(-) create mode 100755 mmuconfig create mode 100644 tests/mmuconfig_input/mmuconfig_test_vectors.py create mode 100644 tests/mmuconfig_test.py diff --git a/config/main.py b/config/main.py index 5f7e41a36f..f49773be7c 100644 --- a/config/main.py +++ b/config/main.py @@ -5984,6 +5984,22 @@ def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, rdrop, ydrop, gdrop, verbos clicommon.run_command(command, display_cmd=verbose) +# +# 'mmu' command ('config mmu...') +# +@config.command() +@click.option('-p', metavar='', type=str, required=True, help="Profile name") +@click.option('-a', metavar='', type=click.IntRange(-8,8), help="Set alpha for profile type dynamic") +@click.option('-s', metavar='', type=int, help="Set staticth for profile type static") +def mmu(p, a, s): + """mmuconfig configuration tasks""" + log.log_info("'mmuconfig -p {}' executing...".format(p)) + command = ['mmuconfig', '-p', str(p)] + if a is not None: command += ['-a', str(a)] + if s is not None: command += ['-s', str(s)] + clicommon.run_command(command) + + # # 'pfc' group ('config interface pfc ...') # diff --git a/mmuconfig b/mmuconfig new file mode 100755 index 0000000000..f9dc178625 --- /dev/null +++ b/mmuconfig @@ -0,0 +1,199 @@ +#!/usr/bin/python3 + +""" +mmuconfig is the utility to show and change mmu configuration + +usage: mmuconfig [-h] [-v] [-l] [-p PROFILE] [-a ALPHA] [-s staticth] [-vv] + +optional arguments: + -h --help show this help message and exit + -v --version show program's version number and exit + -vv --verbose verbose output + -l --list show mmu configuration + -p --profile specify buffer profile name + -a --alpha set n for dyanmic threshold alpha 2^(n) + -s --staticth set static threshold + +""" + +import os +import sys +import argparse +import tabulate +import traceback + +BUFFER_POOL_TABLE_NAME = "BUFFER_POOL" +BUFFER_PROFILE_TABLE_NAME = "BUFFER_PROFILE" +DEFAULT_LOSSLESS_BUFFER_PARAMETER_NAME = "DEFAULT_LOSSLESS_BUFFER_PARAMETER" + +DYNAMIC_THRESHOLD = "dynamic_th" +STATIC_THRESHOLD = "static_th" +BUFFER_PROFILE_FIELDS = { + "alpha": DYNAMIC_THRESHOLD, + "staticth": STATIC_THRESHOLD +} + +# mock the redis for unit test purposes # +try: + if 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 + +except KeyError: + pass + +from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector + +BUFFER_POOL_TABLE_NAME = "BUFFER_POOL" +BUFFER_PROFILE_TABLE_NAME = "BUFFER_PROFILE" + +''' +DYNAMIC_THRESHOLD = "dynamic_th" +BUFFER_PROFILE_FIELDS = { + "alpha": DYNAMIC_THRESHOLD +} +''' + +class MmuConfig(object): + def __init__(self, verbose, config): + self.verbose = verbose + self.config = config + + # Set up db connections + if self.config: + self.db = ConfigDBConnector() + self.db.connect() + else: + self.db = SonicV2Connector(use_unix_socket_path=False) + self.db.connect(self.db.STATE_DB, False) + + def get_table(self, tablename): + if self.config: + return self.db.get_table(tablename) + + entries = {} + keys = self.db.keys(self.db.STATE_DB, tablename + '*') + + if not keys: + return None + + for key in keys: + entries[key.split('|')[1]] = self.db.get_all(self.db.STATE_DB, key) + + return entries + + def list(self): + lossless_traffic_pattern = self.get_table(DEFAULT_LOSSLESS_BUFFER_PARAMETER_NAME) + if lossless_traffic_pattern: + for _, pattern in lossless_traffic_pattern.items(): + config = [] + + print("Lossless traffic pattern:") + for field, value in pattern.items(): + config.append([field, value]) + print(tabulate.tabulate(config) + "\n") + + buf_pools = self.get_table(BUFFER_POOL_TABLE_NAME) + if buf_pools: + for pool_name, pool_data in buf_pools.items(): + config = [] + + print("Pool: " + pool_name) + for field, value in pool_data.items(): + config.append([field, value]) + print(tabulate.tabulate(config) + "\n") + if self.verbose: + print("Total pools: %d\n\n" % len(buf_pools)) + else: + print("No buffer pool information available") + + buf_profs = self.get_table(BUFFER_PROFILE_TABLE_NAME) + if buf_profs: + for prof_name, prof_data in buf_profs.items(): + config = [] + + print("Profile: " + prof_name) + for field, value in prof_data.items(): + config.append([field, value]) + print(tabulate.tabulate(config) + "\n") + if self.verbose: + print("Total profiles: %d" % len(buf_profs)) + else: + print("No buffer profile information available") + + def set(self, profile, field_alias, value): + if os.geteuid() != 0: + sys.exit("Root privileges required for this operation") + + field = BUFFER_PROFILE_FIELDS[field_alias] + buf_profs = self.db.get_table(BUFFER_PROFILE_TABLE_NAME) + v = int(value) + if field == DYNAMIC_THRESHOLD: + if v < -8 or v > 8: + sys.exit("Invalid alpha value: 2^(%s)" % (value)) + + if profile in buf_profs and DYNAMIC_THRESHOLD not in buf_profs[profile]: + sys.exit("%s not using dynamic thresholding" % (profile)) + elif field == STATIC_THRESHOLD: + if v < 0: + sys.exit("Invalid static threshold value: (%s)" % (value)) + + buf_profs = self.db.get_table(BUFFER_PROFILE_TABLE_NAME) + if profile in buf_profs and STATIC_THRESHOLD not in buf_profs[profile]: + sys.exit("%s not using static threshold" % (profile)) + else: + sys.exit("Set field %s not supported" % (field)) + + if self.verbose: + print("Setting %s %s value to %s" % (profile, field, value)) + self.db.mod_entry(BUFFER_PROFILE_TABLE_NAME, profile, {field: value}) + + +def main(config): + if config: + parser = argparse.ArgumentParser(description='Show and change: mmu configuration', + formatter_class=argparse.RawTextHelpFormatter) + + parser.add_argument('-l', '--list', action='store_true', help='show mmu configuration') + parser.add_argument('-p', '--profile', type=str, help='specify buffer profile name', default=None) + parser.add_argument('-a', '--alpha', type=str, help='set n for dyanmic threshold alpha 2^(n)', default=None) + parser.add_argument('-s', '--staticth', type=str, help='set n for static threshold', default=None) + parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0') + else: + parser = argparse.ArgumentParser(description='Show buffer state', + formatter_class=argparse.RawTextHelpFormatter) + + parser.add_argument('-l', '--list', action='store_true', help='show buffer state') + parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0') + + parser.add_argument('-vv', '--verbose', action='store_true', help='verbose output', default=False) + + args = parser.parse_args() + + try: + mmu_cfg = MmuConfig(args.verbose, config) + if args.list: + mmu_cfg.list() + elif config and args.profile: + import pdb; pdb.set_trace() + if args.alpha: + mmu_cfg.set(args.profile, "alpha", args.alpha) + elif args.staticth: + mmu_cfg.set(args.profile, "staticth", args.staticth) + else: + parser.print_help() + sys.exit(1) + + except Exception as e: + print("Exception caught: ", str(e), file=sys.stderr) + traceback.print_exc() + sys.exit(1) + +if __name__ == "__main__": + if sys.argv[0].split('/')[-1] == "mmuconfig": + main(True) + else: + main(False) diff --git a/scripts/mmuconfig b/scripts/mmuconfig index c0338a1762..ebeb74fdaf 100755 --- a/scripts/mmuconfig +++ b/scripts/mmuconfig @@ -3,7 +3,7 @@ """ mmuconfig is the utility to show and change mmu configuration -usage: mmuconfig [-h] [-v] [-l] [-p PROFILE] [-a ALPHA] [-vv] +usage: mmuconfig [-h] [-v] [-l] [-p PROFILE] [-a ALPHA] [-vv] [-s staticth] optional arguments: -h --help show this help message and exit @@ -12,6 +12,7 @@ optional arguments: -l --list show mmu configuration -p --profile specify buffer profile name -a --alpha set n for dyanmic threshold alpha 2^(n) + -s --staticth set static threshold """ @@ -20,14 +21,17 @@ import sys import argparse import tabulate import traceback +import json BUFFER_POOL_TABLE_NAME = "BUFFER_POOL" BUFFER_PROFILE_TABLE_NAME = "BUFFER_PROFILE" DEFAULT_LOSSLESS_BUFFER_PARAMETER_NAME = "DEFAULT_LOSSLESS_BUFFER_PARAMETER" DYNAMIC_THRESHOLD = "dynamic_th" +STATIC_THRESHOLD = "static_th" BUFFER_PROFILE_FIELDS = { - "alpha": DYNAMIC_THRESHOLD + "alpha": DYNAMIC_THRESHOLD, + "staticth" : STATIC_THRESHOLD } # mock the redis for unit test purposes # @@ -44,18 +48,11 @@ except KeyError: from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector -BUFFER_POOL_TABLE_NAME = "BUFFER_POOL" -BUFFER_PROFILE_TABLE_NAME = "BUFFER_PROFILE" - -DYNAMIC_THRESHOLD = "dynamic_th" -BUFFER_PROFILE_FIELDS = { - "alpha": DYNAMIC_THRESHOLD -} - class MmuConfig(object): - def __init__(self, verbose, config): + def __init__(self, verbose, config, filename): self.verbose = verbose self.config = config + self.filename = filename # Set up db connections if self.config: @@ -120,24 +117,34 @@ class MmuConfig(object): print("No buffer profile information available") def set(self, profile, field_alias, value): - if os.geteuid() != 0: + if os.geteuid() != 0 and os.environ.get("UTILITIES_UNIT_TESTING", "0") != "2": sys.exit("Root privileges required for this operation") field = BUFFER_PROFILE_FIELDS[field_alias] + buf_profs = self.db.get_table(BUFFER_PROFILE_TABLE_NAME) + v = int(value) if field == DYNAMIC_THRESHOLD: - v = int(value) if v < -8 or v > 8: sys.exit("Invalid alpha value: 2^(%s)" % (value)) - buf_profs = self.db.get_table(BUFFER_PROFILE_TABLE_NAME) if profile in buf_profs and DYNAMIC_THRESHOLD not in buf_profs[profile]: sys.exit("%s not using dynamic thresholding" % (profile)) + elif field == STATIC_THRESHOLD: + if v < 0: + sys.exit("Invalid static threshold value: (%s)" % (value)) + + if profile in buf_profs and STATIC_THRESHOLD not in buf_profs[profile]: + sys.exit("%s not using static threshold" % (profile)) else: sys.exit("Set field %s not supported" % (field)) if self.verbose: print("Setting %s %s value to %s" % (profile, field, value)) self.db.mod_entry(BUFFER_PROFILE_TABLE_NAME, profile, {field: value}) + if self.filename is not None: + prof_table = self.db.get_table(BUFFER_PROFILE_TABLE_NAME) + with open(self.filename, "w") as fd: + json.dump(prof_table, fd) def main(config): @@ -148,6 +155,7 @@ def main(config): parser.add_argument('-l', '--list', action='store_true', help='show mmu configuration') parser.add_argument('-p', '--profile', type=str, help='specify buffer profile name', default=None) parser.add_argument('-a', '--alpha', type=str, help='set n for dyanmic threshold alpha 2^(n)', default=None) + parser.add_argument('-s', '--staticth', type=str, help='set static threshold', default=None) parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0') else: parser = argparse.ArgumentParser(description='Show buffer state', @@ -157,16 +165,23 @@ def main(config): parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0') parser.add_argument('-vv', '--verbose', action='store_true', help='verbose output', default=False) + parser.add_argument('-f', '--filename', help='file used by mock tests', type=str, default=None) + + if os.environ.get("UTILITIES_UNIT_TESTING", "0") == "2": + sys.argv.extend(['-f', '/tmp/mmuconfig']) + args = parser.parse_args() try: - mmu_cfg = MmuConfig(args.verbose, config) + mmu_cfg = MmuConfig(args.verbose, config, args.filename) if args.list: mmu_cfg.list() elif config and args.profile: if args.alpha: mmu_cfg.set(args.profile, "alpha", args.alpha) + elif args.staticth: + mmu_cfg.set(args.profile, "staticth", args.staticth) else: parser.print_help() sys.exit(1) diff --git a/tests/buffer_input/buffer_test_vectors.py b/tests/buffer_input/buffer_test_vectors.py index b94d428a39..baa99608a1 100644 --- a/tests/buffer_input/buffer_test_vectors.py +++ b/tests/buffer_input/buffer_test_vectors.py @@ -30,6 +30,13 @@ type ingress ---- ------- +Pool: ingress_lossless_pool_hbm +---- --------- +mode static +size 139458240 +type ingress +---- --------- + Profile: ingress_lossy_profile ---------- ------------------ dynamic_th 3 @@ -37,6 +44,13 @@ size 0 ---------- ------------------ +Profile: ingress_lossless_profile_hbm +--------- ------------------------- +static_th 12121212 +pool ingress_lossless_pool_hbm +size 0 +--------- ------------------------- + Profile: headroom_profile ---------- --------------------- dynamic_th 0 diff --git a/tests/mmuconfig_input/mmuconfig_test_vectors.py b/tests/mmuconfig_input/mmuconfig_test_vectors.py new file mode 100644 index 0000000000..c20a964516 --- /dev/null +++ b/tests/mmuconfig_input/mmuconfig_test_vectors.py @@ -0,0 +1,112 @@ +show_mmu_config = """\ +Lossless traffic pattern: +-------------------- - +default_dynamic_th 0 +over_subscribe_ratio 2 +-------------------- - + +Pool: egress_lossless_pool +---- -------- +mode dynamic +size 13945824 +type egress +---- -------- + +Pool: egress_lossy_pool +---- ------- +mode dynamic +type egress +---- ------- + +Pool: ingress_lossless_pool +---- ------- +mode dynamic +type ingress +---- ------- + +Pool: ingress_lossy_pool +---- ------- +mode dynamic +type ingress +---- ------- + +Pool: ingress_lossless_pool_hbm +---- --------- +mode static +size 139458240 +type ingress +---- --------- + +Profile: ingress_lossy_profile +---------- ------------------ +dynamic_th 3 +pool ingress_lossy_pool +size 0 +---------- ------------------ + +Profile: ingress_lossless_profile_hbm +--------- ------------------------- +static_th 12121212 +pool ingress_lossless_pool_hbm +size 0 +--------- ------------------------- + +Profile: headroom_profile +---------- --------------------- +dynamic_th 0 +pool ingress_lossless_pool +xon 18432 +xoff 32768 +size 51200 +---------- --------------------- + +Profile: alpha_profile +------------- --------------------- +dynamic_th 0 +pool ingress_lossless_pool +headroom_type dynamic +------------- --------------------- + +Profile: egress_lossless_profile +---------- -------------------- +dynamic_th 0 +pool egress_lossless_pool +size 0 +---------- -------------------- + +Profile: egress_lossy_profile +---------- ----------------- +dynamic_th 0 +pool egress_lossy_pool +size 0 +---------- ----------------- + +""" + +testData = { + 'mmuconfig_list' : {'cmd' : ['show'], + 'args' : [], + 'rc' : 0, + 'rc_output': show_mmu_config + }, + 'mmu_cfg_static_th' : {'cmd' : ['config'], + 'args' : ['-p', 'ingress_lossless_profile_hbm', '-s', '12121213'], + 'rc' : 0, + 'db_table' : 'BUFFER_PROFILE', + 'cmp_args' : ['ingress_lossless_profile_hbm,static_th,12121213'], + 'rc_msg' : '' + }, + 'mmu_cfg_alpha' : {'cmd' : ['config'], + 'args' : ['-p', 'alpha_profile', '-a', '2'], + 'rc' : 0, + 'db_table' : 'BUFFER_PROFILE', + 'cmp_args' : ['alpha_profile,dynamic_th,2'], + 'rc_msg' : '' + }, + 'mmu_cfg_alpha_invalid' : {'cmd' : ['config'], + 'args' : ['-p', 'alpha_profile', '-a', '12'], + 'rc' : 2, + 'rc_msg' : 'Usage: mmu [OPTIONS]\nTry "mmu --help" for help.\n\nError: Invalid value for "-a": 12 is not in the valid range of -8 to 8.\n' + } + + } diff --git a/tests/mmuconfig_test.py b/tests/mmuconfig_test.py new file mode 100644 index 0000000000..7218270e36 --- /dev/null +++ b/tests/mmuconfig_test.py @@ -0,0 +1,86 @@ +import os +import sys +import json +import pytest + +from click.testing import CliRunner +import config.main as config +import show.main as show +from utilities_common.db import Db +from .mmuconfig_input.mmuconfig_test_vectors import * + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +scripts_path = os.path.join(modules_path, "scripts") +sys.path.insert(0, test_path) +sys.path.insert(0, modules_path) + + +class Testmmuconfig(object): + @classmethod + def setup_class(cls): + os.environ["PATH"] += os.pathsep + scripts_path + os.environ['UTILITIES_UNIT_TESTING'] = "2" + print("SETUP") + + def test_mmu_show_config(self): + self.executor(testData['mmuconfig_list']) + + def test_mmu_alpha_config(self): + self.executor(testData['mmu_cfg_alpha']) + + def test_mmu_alpha_invalid_config(self): + self.executor(testData['mmu_cfg_alpha_invalid']) + + def test_mmu_staticth_config(self): + self.executor(testData['mmu_cfg_static_th']) + + def executor(self, input): + runner = CliRunner() + + if 'db_table' in input: + db = Db() + data_list = list(db.cfgdb.get_table(input['db_table'])) + input['rc_msg'] = input['rc_msg'].format(",".join(data_list)) + + if 'show' in input['cmd']: + exec_cmd = show.cli.commands["mmu"] + result = runner.invoke(exec_cmd, input['args']) + exit_code = result.exit_code + output = result.output + elif 'config' in input['cmd']: + exec_cmd = config.config.commands["mmu"] + result = runner.invoke(exec_cmd, input['args'], catch_exceptions=False) + exit_code = result.exit_code + output = result.output + + print(exit_code) + print(output) + + if input['rc'] == 0: + assert exit_code == 0 + else: + assert exit_code != 0 + + if 'cmp_args' in input: + fd = open('/tmp/mmuconfig', 'r') + cmp_data = json.load(fd) + for args in input['cmp_args']: + profile, name, value = args.split(',') + assert(cmp_data[profile][name] == value) + fd.close() + + if 'rc_msg' in input: + assert input['rc_msg'] in output + + if 'rc_output' in input: + assert output == input['rc_output'] + + + @classmethod + def teardown_class(cls): + os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) + os.environ['UTILITIES_UNIT_TESTING'] = "0" + if os.path.isfile('/tmp/mmuconfig'): + os.remove('/tmp/mmuconfig') + print("TEARDOWN") diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index f622184e26..ae5d7f97df 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -1934,11 +1934,21 @@ "mode": "dynamic", "type": "ingress" }, + "BUFFER_POOL|ingress_lossless_pool_hbm": { + "mode": "static", + "size": "139458240", + "type": "ingress" + }, "BUFFER_PROFILE|ingress_lossy_profile": { "dynamic_th": "3", "pool": "ingress_lossy_pool", "size": "0" }, + "BUFFER_PROFILE|ingress_lossless_profile_hbm": { + "static_th": "12121212", + "pool": "ingress_lossless_pool_hbm", + "size": "0" + }, "BUFFER_PROFILE|headroom_profile": { "dynamic_th": "0", "pool": "ingress_lossless_pool", From 7a12424b2fc22e9981758e07a06312e40a580ffa Mon Sep 17 00:00:00 2001 From: Stepan Blyshchak <38952541+stepanblyschak@users.noreply.github.com> Date: Sun, 24 Sep 2023 03:57:22 +0300 Subject: [PATCH 10/20] [sonic-installer] print output from spm migrate (#2960) ### What I did Don't capture output from migrate command for better debugging. #### How I did it Added ```nocapture``` flag to ```run_command_or_raise```. Pass ```nocapture``` when running ```spm migrate```. #### How to verify it Run sonic to sonic upgrade and observe the output of migrate command. --- sonic_installer/common.py | 10 +++++++--- sonic_installer/main.py | 2 +- tests/test_sonic_installer.py | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/sonic_installer/common.py b/sonic_installer/common.py index f220a4b57e..50b736901f 100644 --- a/sonic_installer/common.py +++ b/sonic_installer/common.py @@ -41,16 +41,20 @@ def run_command(command, stdout=subprocess.PIPE, env=None, shell=False): sys.exit(proc.returncode) # Run bash command and return output, raise if it fails -def run_command_or_raise(argv, raise_exception=True): +def run_command_or_raise(argv, raise_exception=True, capture=True): click.echo(click.style("Command: ", fg='cyan') + click.style(' '.join(argv), fg='green')) - proc = subprocess.Popen(argv, text=True, stdout=subprocess.PIPE) + stdout = subprocess.PIPE if capture else None + proc = subprocess.Popen(argv, text=True, stdout=stdout) out, _ = proc.communicate() if proc.returncode != 0 and raise_exception: raise SonicRuntimeException("Failed to run command '{0}'".format(argv)) - return out.rstrip("\n") + if out is not None: + out = out.rstrip("\n") + + return out # Needed to prevent "broken pipe" error messages when piping # output of multiple commands using subprocess.Popen() diff --git a/sonic_installer/main.py b/sonic_installer/main.py index 9737e57de7..341111f265 100644 --- a/sonic_installer/main.py +++ b/sonic_installer/main.py @@ -384,7 +384,7 @@ def migrate_sonic_packages(bootloader, binary_image_version): run_command_or_raise(["chroot", new_image_mount, SONIC_PACKAGE_MANAGER, "migrate", os.path.join("/", TMP_DIR, packages_file), "--dockerd-socket", os.path.join("/", TMP_DIR, DOCKERD_SOCK), - "-y"]) + "-y"], capture=False) finally: if docker_started: run_command_or_raise(["chroot", new_image_mount, DOCKER_CTL_SCRIPT, "stop"], raise_exception=False) diff --git a/tests/test_sonic_installer.py b/tests/test_sonic_installer.py index 3e257ae3b0..e9cb727d37 100644 --- a/tests/test_sonic_installer.py +++ b/tests/test_sonic_installer.py @@ -91,7 +91,7 @@ def rootfs_path_mock(path): call(["cp", f"{mounted_image_folder}/etc/resolv.conf", "/tmp/resolv.conf.backup"]), call(["cp", "/etc/resolv.conf", f"{mounted_image_folder}/etc/resolv.conf"]), call(["chroot", mounted_image_folder, "sh", "-c", "command -v sonic-package-manager"]), - call(["chroot", mounted_image_folder, "sonic-package-manager", "migrate", "/tmp/packages.json", "--dockerd-socket", "/tmp/docker.sock", "-y"]), + call(["chroot", mounted_image_folder, "sonic-package-manager", "migrate", "/tmp/packages.json", "--dockerd-socket", "/tmp/docker.sock", "-y"], capture=False), call(["chroot", mounted_image_folder, "/usr/lib/docker/docker.sh", "stop"], raise_exception=False), call(["cp", "/tmp/resolv.conf.backup", f"{mounted_image_folder}/etc/resolv.conf"], raise_exception=False), call(["umount", "-f", "-R", mounted_image_folder], raise_exception=False), From 6ba6ddf6d559e7f0c6f3cfd3d9918eeab9ab3b52 Mon Sep 17 00:00:00 2001 From: jingwenxie Date: Tue, 26 Sep 2023 14:40:25 +0800 Subject: [PATCH 11/20] Remove syslog service validator in GCU (#2991) ### What I did Remove rsyslog related service validator in GCU because it has been supported in latest image #### How I did it Remove related code #### How to verify it Unit test --- .../gcu_services_validator.conf.json | 6 ------ generic_config_updater/services_validator.py | 8 -------- .../service_validator_test.py | 18 +----------------- 3 files changed, 1 insertion(+), 31 deletions(-) diff --git a/generic_config_updater/gcu_services_validator.conf.json b/generic_config_updater/gcu_services_validator.conf.json index 852b587286..b504cf5d26 100644 --- a/generic_config_updater/gcu_services_validator.conf.json +++ b/generic_config_updater/gcu_services_validator.conf.json @@ -31,9 +31,6 @@ "PORT": { "services_to_validate": [ "port_service" ] }, - "SYSLOG_SERVER":{ - "services_to_validate": [ "rsyslog" ] - }, "DHCP_RELAY": { "services_to_validate": [ "dhcp-relay" ] }, @@ -60,9 +57,6 @@ "port_service": { "validate_commands": [ ] }, - "rsyslog": { - "validate_commands": [ "generic_config_updater.services_validator.rsyslog_validator" ] - }, "dhcp-relay": { "validate_commands": [ "generic_config_updater.services_validator.dhcp_validator" ] }, diff --git a/generic_config_updater/services_validator.py b/generic_config_updater/services_validator.py index 497cb4ee74..29a887da89 100644 --- a/generic_config_updater/services_validator.py +++ b/generic_config_updater/services_validator.py @@ -48,14 +48,6 @@ def _service_restart(svc_name): return rc == 0 -def rsyslog_validator(old_config, upd_config, keys): - rc = os.system("/usr/bin/rsyslog-config.sh") - if rc != 0: - return _service_restart("rsyslog") - else: - return True - - def dhcp_validator(old_config, upd_config, keys): return _service_restart("dhcp_relay") diff --git a/tests/generic_config_updater/service_validator_test.py b/tests/generic_config_updater/service_validator_test.py index f14a3ad7b0..802bc2dc15 100644 --- a/tests/generic_config_updater/service_validator_test.py +++ b/tests/generic_config_updater/service_validator_test.py @@ -6,7 +6,7 @@ from collections import defaultdict from unittest.mock import patch -from generic_config_updater.services_validator import vlan_validator, rsyslog_validator, caclmgrd_validator, vlanintf_validator +from generic_config_updater.services_validator import vlan_validator, caclmgrd_validator, vlanintf_validator import generic_config_updater.gu_common @@ -142,16 +142,6 @@ def mock_time_sleep_call(sleep_time): }, ] -test_rsyslog_fail = [ - # Fail the calls, to get the entire fail path calls invoked - # - { "cmd": "/usr/bin/rsyslog-config.sh", "rc": 1 }, # config update; fails - { "cmd": "systemctl restart rsyslog", "rc": 1 }, # rsyslog restart; fails - { "cmd": "systemctl reset-failed rsyslog", "rc": 1 }, # reset; failure here just logs - { "cmd": "systemctl restart rsyslog", "rc": 1 }, # restart again; fails - { "cmd": "systemctl restart rsyslog", "rc": 1 }, # restart again; fails - ] - test_vlanintf_data = [ { "old": {}, "upd": {}, "cmd": "" }, { @@ -211,12 +201,6 @@ def test_change_apply_os_system(self, mock_os_sys): # Test failure case # - os_system_calls = test_rsyslog_fail - os_system_call_index = 0 - - rc = rsyslog_validator("", "", "") - assert not rc, "rsyslog_validator expected to fail" - os_system_calls = [] os_system_call_index = 0 for entry in test_vlanintf_data: From ba179fb5194b051a02668a1363cbb2b625b67d09 Mon Sep 17 00:00:00 2001 From: Stepan Blyshchak <38952541+stepanblyschak@users.noreply.github.com> Date: Mon, 2 Oct 2023 19:46:05 +0300 Subject: [PATCH 12/20] [warm-reboot] remove ISSU bank check (#2958) ### What I did I removed issu bank check: 1. The file in which SDK holds ISSU bank information has changed and is no longer issu_bank.txt 2. We shouldn't check the SDK dump content from SONiC as that information and its format is constantly changing. Overwriting it might be risky when SONiC and SDK are out of sync regarding the format of the file 3. ISSU bank information is written by pre_shutdown request anyway. Verifying whether the information was indeed written right after that seems redundant 4. The check is made after ```set +e```, so at the moment we "detect" a problem it is already too late #### How I did it Removed the relevant code. #### How to verify it Run warm-reboot on device. --- scripts/fast-reboot | 42 +++--------------------------------------- 1 file changed, 3 insertions(+), 39 deletions(-) diff --git a/scripts/fast-reboot b/scripts/fast-reboot index 1c47664e09..922d217e3f 100755 --- a/scripts/fast-reboot +++ b/scripts/fast-reboot @@ -193,7 +193,7 @@ function request_pre_shutdown() { if [ -x ${DEVPATH}/${PLATFORM}/${PLATFORM_REBOOT_PRE_CHECK} ]; then debug "Requesting platform reboot pre-check ..." - ${DEVPATH}/${PLATFORM}/${PLATFORM_REBOOT_PRE_CHECK} ${REBOOT_TYPE} + ${DEVPATH}/${PLATFORM}/${PLATFORM_REBOOT_PRE_CHECK} ${REBOOT_TYPE} fi debug "Requesting pre-shutdown ..." STATE=$(timeout 5s docker exec syncd /usr/bin/syncd_request_shutdown --pre &> /dev/null; if [[ $? == 124 ]]; then echo "timed out"; fi) @@ -202,34 +202,6 @@ function request_pre_shutdown() fi } -function recover_issu_bank_file() -{ - debug "Recovering the (${ISSU_BANK_FILE}) file" - docker exec -i syncd sx_api_dbg_generate_dump.py - issu_bank_value=`docker exec -i syncd cat /tmp/sdkdump | grep 'ISSU Bank' | grep -o -E '[0-9]+'` - printf $issu_bank_value > /host/warmboot/issu_bank.txt -} - -function check_issu_bank_file() -{ - ISSU_BANK_FILE=/host/warmboot/issu_bank.txt - - if [[ ! -s "$ISSU_BANK_FILE" ]]; then - error "(${ISSU_BANK_FILE}) does NOT exist or empty ..." - recover_issu_bank_file - return - fi - - issu_file_chars_count=`stat -c %s ${ISSU_BANK_FILE}`; - issu_file_content=`awk '{print $0}' ${ISSU_BANK_FILE}` - - if [[ $issu_file_chars_count != 1 ]] || - [[ "$issu_file_content" != "0" && "$issu_file_content" != "1" ]]; then - error "(${ISSU_BANK_FILE}) is broken ..." - recover_issu_bank_file - fi -} - function wait_for_pre_shutdown_complete_or_fail() { debug "Waiting for pre-shutdown ..." @@ -464,7 +436,7 @@ function invoke_kexec() { function load_kernel() { # Load kernel into the memory - invoke_kexec -a + invoke_kexec -a } function load_kernel_secure() { @@ -630,7 +602,7 @@ fi if is_secureboot && grep -q aboot_machine= /host/machine.conf; then load_aboot_secureboot_kernel else - # check if secure boot is enable in UEFI + # check if secure boot is enable in UEFI CHECK_SECURE_UPGRADE_ENABLED=0 SECURE_UPGRADE_ENABLED=$(bootctl status 2>/dev/null | grep -c "Secure Boot: enabled") || CHECK_SECURE_UPGRADE_ENABLED=$? if [[ CHECK_SECURE_UPGRADE_ENABLED -ne 0 ]]; then @@ -773,17 +745,9 @@ for service in ${SERVICES_TO_STOP}; do # Pre-shutdown syncd initialize_pre_shutdown - if [[ "x$sonic_asic_type" == x"mellanox" ]]; then - check_issu_bank_file - fi - request_pre_shutdown wait_for_pre_shutdown_complete_or_fail - - if [[ "x$sonic_asic_type" == x"mellanox" ]]; then - check_issu_bank_file - fi fi if [[ "$REBOOT_TYPE" = "fastfast-reboot" || "$REBOOT_TYPE" = "fast-reboot" ]]; then From 4f26a4dff9ea34045287d973093f0346d0ef4aee Mon Sep 17 00:00:00 2001 From: Sudharsan Dhamal Gopalarathnam Date: Tue, 3 Oct 2023 12:52:49 -0700 Subject: [PATCH 13/20] [portconfig]Auto FEC initial changes (#2965) * Auto FEC initial changes * Enhancing show interface command to handle auto FEC * Revert "Enhancing show interface command to handle auto FEC" This reverts commit 63461722deedbd3274a5700ce3394d4361c5e325. * Adding new CLI for show FEC status * Updating command reference * Update Command-Reference.md --- doc/Command-Reference.md | 24 +++++++++++++ scripts/intfutil | 60 ++++++++++++++++++++++++++++++++ show/interfaces/__init__.py | 34 +++++++++++++++++- tests/config_an_test.py | 2 +- tests/intfutil_test.py | 24 ++++++++++++- tests/mock_tables/appl_db.json | 2 +- tests/mock_tables/config_db.json | 3 +- tests/mock_tables/state_db.json | 8 ++++- utilities_common/constants.py | 2 +- 9 files changed, 152 insertions(+), 7 deletions(-) diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 429fe39bbf..9cb0cfbb54 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -456,6 +456,7 @@ The same syntax applies to all subgroups of `show` which themselves contain subc Commands: counters Show interface counters description Show interface status, protocol and... + fec Show interface fec information link-training Show interface link-training information naming_mode Show interface naming_mode status neighbor Show neighbor related information @@ -4450,6 +4451,29 @@ This command displays the key fields of the interfaces such as Operational Statu Ethernet4 down up hundredGigE1/2 T0-2:hundredGigE1/30 ``` +**show interfaces fec status (Versions >= 202311)** + +This command is to display the FEC status of the selected interfaces. If **interface_name** is not specicied, this command shows the FEC status of all interfaces. + +- Usage: + ``` + show interfaces fec status [] + ``` + +- Example: +``` + admin@sonic:~$ show interfaces fec status + Interface FEC Oper FEC Admin + ----------- ---------- ----------- + Ethernet0 N/A rs + Ethernet32 N/A rs + Ethernet36 N/A N/A +Ethernet112 N/A rs +Ethernet116 N/A rs +Ethernet120 N/A rs +Ethernet124 rs auto +``` + **show interfaces link-training (Versions >= 202211)** This command is to display the link-training status of the selected interfaces. If **interface_name** is not specicied, this command shows the link-training status of all interfaces. diff --git a/scripts/intfutil b/scripts/intfutil index 5988b7f43d..f80523c0c4 100755 --- a/scripts/intfutil +++ b/scripts/intfutil @@ -806,6 +806,63 @@ class IntfLinkTrainingStatus(object): appl_db_port_status_get(self.db, key, PORT_ADMIN_STATUS))) return table +# ========================== FEC logic ========================== +header_fec = ['Interface', 'FEC Oper', 'FEC Admin'] + +class IntfFecStatus(object): + + def __init__(self, intf_name, namespace_option, display_option): + self.db = None + self.config_db = None + self.table = [] + self.multi_asic = multi_asic_util.MultiAsic( + display_option, namespace_option) + + if intf_name is not None and intf_name == SUB_PORT: + self.intf_name = None + else: + self.intf_name = intf_name + + def display_fec_status(self): + self.get_intf_fec_status() + # Sorting and tabulating the result table. + sorted_table = natsorted(self.table) + print(tabulate(sorted_table, header_fec, tablefmt="simple", stralign='right')) + + @multi_asic_util.run_on_multi_asic + def get_intf_fec_status(self): + self.front_panel_ports_list = get_frontpanel_port_list(self.config_db) + self.appl_db_keys = appl_db_keys_get(self.db, self.front_panel_ports_list, self.intf_name) + if self.appl_db_keys: + self.table += self.generate_fec_status() + + def generate_fec_status(self): + """ + Generate FEC output + """ + + i = {} + table = [] + key = [] + + # + # Iterate through all the keys and append port's associated state to + # the result table. + # + for i in self.appl_db_keys: + key = re.split(':', i, maxsplit=1)[-1].strip() + if key in self.front_panel_ports_list: + if self.multi_asic.skip_display(constants.PORT_OBJ, key): + continue + admin_fec = appl_db_port_status_get(self.db, key, PORT_FEC) + oper_fec = self.db.get(self.db.STATE_DB, PORT_STATE_TABLE_PREFIX + key, PORT_FEC) + oper_status = self.db.get(self.db.APPL_DB, PORT_STATUS_TABLE_PREFIX + key, PORT_OPER_STATUS) + if oper_status != "up" or oper_fec is None: + oper_fec= "N/A" + oper_status = self.db.get(self.db.APPL_DB, PORT_STATUS_TABLE_PREFIX + key, PORT_OPER_STATUS) + table.append((key, oper_fec, admin_fec)) + return table + def main(): parser = argparse.ArgumentParser(description='Display Interface information', formatter_class=argparse.RawTextHelpFormatter) @@ -829,6 +886,9 @@ def main(): elif args.command == "link_training": interface_lt_status = IntfLinkTrainingStatus(args.interface, args.namespace, args.display) interface_lt_status.display_link_training_status() + elif args.command == "fec": + interface_fec_status = IntfFecStatus(args.interface, args.namespace, args.display) + interface_fec_status.display_fec_status() sys.exit(0) diff --git a/show/interfaces/__init__.py b/show/interfaces/__init__.py index 8757d23aec..b1d3c69b6f 100644 --- a/show/interfaces/__init__.py +++ b/show/interfaces/__init__.py @@ -764,7 +764,39 @@ def link_training_status(interfacename, namespace, display, verbose): cmd += ['-n', str(namespace)] clicommon.run_command(cmd, display_cmd=verbose) +# +# fec group (show interfaces fec ...) +# +@interfaces.group(name='fec', cls=clicommon.AliasedGroup) +def fec(): + """Show interface fec information""" + pass + + +# 'fec status' subcommand ("show interfaces fec status") +@fec.command(name='status') +@click.argument('interfacename', required=False) +@multi_asic_util.multi_asic_click_options +@click.option('--verbose', is_flag=True, help="Enable verbose output") +def fec_status(interfacename, namespace, display, verbose): + """Show interface fec status""" + + ctx = click.get_current_context() + + cmd = ['intfutil', '-c', 'fec'] + #ignore the display option when interface name is passed + if interfacename is not None: + interfacename = try_convert_interfacename_from_alias(ctx, interfacename) + + cmd += ['-i', str(interfacename)] + else: + cmd += ['-d', str(display)] + + if namespace is not None: + cmd += ['-n', str(namespace)] + + clicommon.run_command(cmd, display_cmd=verbose) # # switchport group (show interfaces switchport ...) @@ -830,4 +862,4 @@ def tablelize(keys): return table header = ['Interface', 'Mode'] - click.echo(tabulate(tablelize(keys), header,tablefmt="simple", stralign='left')) \ No newline at end of file + click.echo(tabulate(tablelize(keys), header,tablefmt="simple", stralign='left')) diff --git a/tests/config_an_test.py b/tests/config_an_test.py index 0af19898c6..210b66d846 100644 --- a/tests/config_an_test.py +++ b/tests/config_an_test.py @@ -110,7 +110,7 @@ def test_config_fec(self, ctx): assert "fec fc is not in ['rs', 'none', 'test']" in result.output # Negative case: set a fec mode which is not default on an interface without supported_fecs result = self.basic_check("fec", ["Ethernet4", "test"], ctx, operator.ne) - assert "fec test is not in ['rs', 'fc', 'none']" in result.output + assert "fec test is not in ['rs', 'fc', 'none', 'auto']" in result.output # Negative case: set a fec mode on a port where setting fec is not supported result = self.basic_check("fec", ["Ethernet112", "test"], ctx, operator.ne) assert "Setting fec is not supported" in result.output diff --git a/tests/intfutil_test.py b/tests/intfutil_test.py index bb83dd31df..6e48c9724b 100644 --- a/tests/intfutil_test.py +++ b/tests/intfutil_test.py @@ -22,7 +22,7 @@ Ethernet112 93,94,95,96 40G 9100 rs etp29 PortChannel0001 up up N/A off Ethernet116 89,90,91,92 40G 9100 rs etp30 PortChannel0002 up up N/A off Ethernet120 101,102,103,104 40G 9100 rs etp31 PortChannel0003 up up N/A off - Ethernet124 97,98,99,100 40G 9100 rs etp32 PortChannel0004 up up N/A off + Ethernet124 97,98,99,100 40G 9100 auto etp32 PortChannel0004 up up N/A off PortChannel0001 N/A 40G 9100 N/A N/A routed down up N/A N/A PortChannel0002 N/A 40G 9100 N/A N/A routed up up N/A N/A PortChannel0003 N/A 40G 9100 N/A N/A routed up up N/A N/A @@ -112,6 +112,21 @@ Ethernet124 N/A N/A up up """ +show_interface_fec_status_output = """\ + Interface FEC Oper FEC Admin +----------- ---------- ----------- + Ethernet0 N/A rs + Ethernet16 N/A N/A + Ethernet24 N/A N/A + Ethernet28 N/A N/A + Ethernet32 N/A rs + Ethernet36 N/A N/A +Ethernet112 N/A rs +Ethernet116 N/A rs +Ethernet120 N/A rs +Ethernet124 rs auto +""" + class TestIntfutil(TestCase): @classmethod def setup_class(cls): @@ -338,6 +353,13 @@ def test_show_interfaces_link_training_status(self): assert result.exit_code == 0 assert result.output == show_interface_link_training_status_output + def test_show_interfaces_fec_status(self): + result = self.runner.invoke(show.cli.commands["interfaces"].commands["fec"].commands["status"], []) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert result.output == show_interface_fec_status_output + @classmethod def teardown_class(cls): print("TEARDOWN") diff --git a/tests/mock_tables/appl_db.json b/tests/mock_tables/appl_db.json index 71b9e7f249..2c3d14da1e 100644 --- a/tests/mock_tables/appl_db.json +++ b/tests/mock_tables/appl_db.json @@ -155,7 +155,7 @@ "pfc_asym": "off", "mtu": "9100", "tpid": "0x8100", - "fec": "rs", + "fec": "auto", "admin_status": "up" }, "PORT_TABLE:Ethernet200": { diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index ae5d7f97df..8786051e36 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -399,7 +399,8 @@ "tpid": "0x8100", "mode": "routed", "pfc_asym": "off", - "speed": "40000" + "speed": "40000", + "fec" : "auto" }, "VLAN_SUB_INTERFACE|Ethernet0.10": { "admin_status": "up" diff --git a/tests/mock_tables/state_db.json b/tests/mock_tables/state_db.json index 8d7f27f7ca..993e05c480 100644 --- a/tests/mock_tables/state_db.json +++ b/tests/mock_tables/state_db.json @@ -1356,7 +1356,8 @@ "speed" : "100000", "supported_speeds": "10000,25000,40000,100000", "supported_fecs": "rs,none,test", - "link_training_status": "not_trained" + "link_training_status": "not_trained", + "fec": "rs" }, "PORT_TABLE|Ethernet32": { "link_training_status": "trained" @@ -1366,6 +1367,11 @@ "supported_fecs": "N/A", "link_training_status": "off" }, + "PORT_TABLE|Ethernet124": { + "speed": "40000", + "supported_fecs": "rs,none,fc,auto", + "fec": "rs" + }, "PCIE_DEVICE|00:01.0": { "correctable|BadDLLP": "0", "correctable|BadTLP": "0", diff --git a/utilities_common/constants.py b/utilities_common/constants.py index f5c157941c..25858b785e 100644 --- a/utilities_common/constants.py +++ b/utilities_common/constants.py @@ -1,7 +1,7 @@ #All the constant used in sonic-utilities DEFAULT_NAMESPACE = '' -DEFAULT_SUPPORTED_FECS_LIST = [ 'rs', 'fc', 'none'] +DEFAULT_SUPPORTED_FECS_LIST = [ 'rs', 'fc', 'none', 'auto'] DISPLAY_ALL = 'all' DISPLAY_EXTERNAL = 'frontend' BGP_NEIGH_OBJ = 'BGP_NEIGH' From 583b4987b925e1ddd69fe53378381255f19ac644 Mon Sep 17 00:00:00 2001 From: Mai Bui Date: Thu, 5 Oct 2023 16:08:31 -0400 Subject: [PATCH 14/20] Update str -> list[str] in config/main and fast-reboot-filter-routes script (#3005) ### What I did Update str -> list[str] in config/main and fast-reboot-filter-routes script ### How to verify it Add UT --- config/main.py | 2 +- scripts/fast-reboot-filter-routes.py | 2 +- tests/config_test.py | 8 ++++++++ tests/fast_reboot_filter_routes_test.py | 19 +++++++++++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 tests/fast_reboot_filter_routes_test.py diff --git a/config/main.py b/config/main.py index f49773be7c..30d004b520 100644 --- a/config/main.py +++ b/config/main.py @@ -7243,7 +7243,7 @@ def clock(): def get_tzs(ctx, args, incomplete): - ret = clicommon.run_command('timedatectl list-timezones', + ret = clicommon.run_command(['timedatectl', 'list-timezones'], display_cmd=False, ignore_error=False, return_cmd=True) if len(ret) == 0: diff --git a/scripts/fast-reboot-filter-routes.py b/scripts/fast-reboot-filter-routes.py index 9328b79ed2..c66972bf48 100755 --- a/scripts/fast-reboot-filter-routes.py +++ b/scripts/fast-reboot-filter-routes.py @@ -12,7 +12,7 @@ ROUTE_IDX = 1 def get_connected_routes(): - cmd = 'sudo vtysh -c "show ip route connected json"' + cmd = ['sudo', 'vtysh', '-c', "show ip route connected json"] connected_routes = [] try: output, ret = clicommon.run_command(cmd, return_cmd=True) diff --git a/tests/config_test.py b/tests/config_test.py index 332bfe14ec..81c4e404d4 100644 --- a/tests/config_test.py +++ b/tests/config_test.py @@ -2314,6 +2314,14 @@ def setup_class(cls): import config.main importlib.reload(config.main) + @patch('utilities_common.cli.run_command') + def test_get_tzs(self, mock_run_command): + runner = CliRunner() + obj = {'db': Db().cfgdb} + + runner.invoke(config.config.commands['clock'].commands['timezone'], ['Atlantis'], obj=obj) + mock_run_command.assert_called_with(['timedatectl', 'list-timezones'], display_cmd=False, ignore_error=False, return_cmd=True) + @patch('config.main.get_tzs', mock.Mock(return_value=timezone_test_val)) def test_timezone_good(self): runner = CliRunner() diff --git a/tests/fast_reboot_filter_routes_test.py b/tests/fast_reboot_filter_routes_test.py new file mode 100644 index 0000000000..5da0862881 --- /dev/null +++ b/tests/fast_reboot_filter_routes_test.py @@ -0,0 +1,19 @@ +import pytest +import importlib +from unittest import mock +from mock import patch +fast_reboot_filter_routes = importlib.import_module("scripts.fast-reboot-filter-routes") + +class TestFastRebootFilterRoutes(object): + def setup(self): + print("SETUP") + + @patch('utilities_common.cli.run_command') + def test_get_connected_routes(self, mock_run_command): + mock_run_command.return_value = (None, 0) + output = fast_reboot_filter_routes.get_connected_routes() + mock_run_command.assert_called_with(['sudo', 'vtysh', '-c', "show ip route connected json"], return_cmd=True) + assert output == [] + + def teardown(self): + print("TEAR DOWN") From 1c1620cc7b182f6497cd60673870586113763018 Mon Sep 17 00:00:00 2001 From: Qi Luo Date: Fri, 6 Oct 2023 17:54:37 -0700 Subject: [PATCH 15/20] Revert "Switch Port Modes and VLAN CLI Enhancement (#2419)" (#3007) #### What I did Revert "Switch Port Modes and VLAN CLI Enhancement (#2419)". The reason is PORT/mode is not yang modelled. --- config/main.py | 38 +- config/switchport.py | 138 ---- config/vlan.py | 370 ++++------ doc/Command-Reference.md | 180 +---- scripts/db_migrator.py | 47 +- scripts/intfutil | 2 +- show/interfaces/__init__.py | 66 -- .../config_db/port-an-expected.json | 3 - .../config_db/portchannel-expected.json | 5 - .../config_db/switchport-expected.json | 144 ---- .../config_db/switchport-input.json | 138 ---- tests/db_migrator_test.py | 24 - tests/interfaces_test.py | 97 --- tests/intfutil_test.py | 2 +- tests/ipv6_link_local_test.py | 2 +- tests/mock_tables/asic0/config_db.json | 1 - tests/mock_tables/config_db.json | 32 - tests/vlan_test.py | 631 +----------------- utilities_common/cli.py | 269 ++------ 19 files changed, 208 insertions(+), 1981 deletions(-) delete mode 100644 config/switchport.py delete mode 100644 tests/db_migrator_input/config_db/switchport-expected.json delete mode 100644 tests/db_migrator_input/config_db/switchport-input.json diff --git a/config/main.py b/config/main.py index 30d004b520..1d2c33605f 100644 --- a/config/main.py +++ b/config/main.py @@ -56,7 +56,6 @@ from .config_mgmt import ConfigMgmtDPB, ConfigMgmt from . import mclag from . import syslog -from . import switchport from . import dns # mock masic APIs for unit test @@ -102,8 +101,6 @@ CFG_PORTCHANNEL_NO="<0-9999>" PORT_MTU = "mtu" -PORT_MODE= "switchport_mode" - PORT_SPEED = "speed" PORT_TPID = "tpid" DEFAULT_TPID = "0x8100" @@ -1193,7 +1190,6 @@ def config(ctx): config.add_command(nat.nat) config.add_command(vlan.vlan) config.add_command(vxlan.vxlan) -config.add_command(switchport.switchport) #add mclag commands config.add_command(mclag.mclag) @@ -4503,39 +4499,19 @@ def add(ctx, interface_name, ip_addr, gw): if interface_name is None: ctx.fail("'interface_name' is None!") + # Add a validation to check this interface is not a member in vlan before + # changing it to a router port + vlan_member_table = config_db.get_table('VLAN_MEMBER') + if (interface_is_in_vlan(vlan_member_table, interface_name)): + click.echo("Interface {} is a member of vlan\nAborting!".format(interface_name)) + return + portchannel_member_table = config_db.get_table('PORTCHANNEL_MEMBER') if interface_is_in_portchannel(portchannel_member_table, interface_name): ctx.fail("{} is configured as a member of portchannel." .format(interface_name)) - - - # Add a validation to check this interface is in routed mode before - # assigning an IP address to it - - sub_intf = False - if clicommon.is_valid_port(config_db, interface_name): - is_port = True - elif clicommon.is_valid_portchannel(config_db, interface_name): - is_port = False - else: - sub_intf = True - - if not sub_intf: - interface_mode = "routed" - if is_port: - interface_data = config_db.get_entry('PORT',interface_name) - elif not is_port: - interface_data = config_db.get_entry('PORTCHANNEL',interface_name) - - if "mode" in interface_data: - interface_mode = interface_data["mode"] - - if interface_mode != "routed": - ctx.fail("Interface {} is not in routed mode!".format(interface_name)) - return - try: ip_address = ipaddress.ip_interface(ip_addr) except ValueError as err: diff --git a/config/switchport.py b/config/switchport.py deleted file mode 100644 index fe51ccaf79..0000000000 --- a/config/switchport.py +++ /dev/null @@ -1,138 +0,0 @@ -import click -from .utils import log -import utilities_common.cli as clicommon - -# -# 'switchport' mode ('config switchport ...') -# - - -@click.group(cls=clicommon.AbbreviationGroup, name='switchport') -def switchport(): - """Switchport mode configuration tasks""" - pass - - -@switchport.command("mode") -@click.argument("type", metavar="", required=True, type=click.Choice(["access", "trunk", "routed"])) -@click.argument("port", metavar="port", required=True) -@clicommon.pass_db -def switchport_mode(db, type, port): - """switchport mode help commands.Mode_type can be access or trunk or routed""" - - ctx = click.get_current_context() - - log.log_info("'switchport mode {} {}' executing...".format(type, port)) - mode_exists_status = True - - # checking if port name with alias exists - if clicommon.get_interface_naming_mode() == "alias": - alias = port - iface_alias_converter = clicommon.InterfaceAliasConverter(db) - port = iface_alias_converter.alias_to_name(port) - if port is None: - ctx.fail("cannot find port name for alias {}".format(alias)) - - if clicommon.is_port_mirror_dst_port(db.cfgdb, port): - ctx.fail("{} is configured as mirror destination port".format(port)) - - - if clicommon.is_valid_port(db.cfgdb, port): - is_port = True - elif clicommon.is_valid_portchannel(db.cfgdb, port): - is_port = False - else: - ctx.fail("{} does not exist".format(port)) - - portchannel_member_table = db.cfgdb.get_table('PORTCHANNEL_MEMBER') - - if (is_port and clicommon.interface_is_in_portchannel(portchannel_member_table, port)): - ctx.fail("{} is part of portchannel!".format(port)) - - if is_port: - port_data = db.cfgdb.get_entry('PORT',port) - else: - port_data = db.cfgdb.get_entry('PORTCHANNEL',port) - - # mode type is either access or trunk - if type != "routed": - - if "mode" in port_data: - existing_mode = port_data["mode"] - else: - existing_mode = "routed" - mode_exists_status = False - if (is_port and clicommon.is_port_router_interface(db.cfgdb, port)) or \ - (not is_port and clicommon.is_pc_router_interface(db.cfgdb, port)): - ctx.fail("Remove IP from {} to change mode!".format(port)) - - if existing_mode == "routed": - if mode_exists_status: - # if the port in an interface - if is_port: - db.cfgdb.mod_entry("PORT", port, {"mode": "{}".format(type)}) - # if not port then is a port channel - elif not is_port: - db.cfgdb.mod_entry("PORTCHANNEL", port, {"mode": "{}".format(type)}) - - if not mode_exists_status: - port_data["mode"] = type - if is_port: - db.cfgdb.set_entry("PORT", port, port_data) - # if not port then is a port channel - elif not is_port: - db.cfgdb.set_entry("PORTCHANNEL", port, port_data) - - if existing_mode == type: - ctx.fail("{} is already in the {} mode".format(port,type)) - else: - if existing_mode == "access" and type == "trunk": - pass - if existing_mode == "trunk" and type == "access": - if clicommon.interface_is_tagged_member(db.cfgdb,port): - ctx.fail("{} is in {} mode and have tagged member(s).\nRemove tagged member(s) from {} to switch to {} mode".format(port,existing_mode,port,type)) - if is_port: - db.cfgdb.mod_entry("PORT", port, {"mode": "{}".format(type)}) - # if not port then is a port channel - elif not is_port: - db.cfgdb.mod_entry("PORTCHANNEL", port, {"mode": "{}".format(type)}) - - click.echo("{} switched from {} to {} mode".format(port, existing_mode, type)) - - # if mode type is routed - else: - - if clicommon.interface_is_tagged_member(db.cfgdb,port): - ctx.fail("{} has tagged member(s). \nRemove them to change mode to {}".format(port,type)) - - if clicommon.interface_is_untagged_member(db.cfgdb,port): - ctx.fail("{} has untagged member. \nRemove it to change mode to {}".format(port,type)) - - if "mode" in port_data: - existing_mode = port_data["mode"] - else: - existing_mode = "routed" - mode_exists_status = False - - if not mode_exists_status: - port_data["mode"] = type - if is_port: - db.cfgdb.set_entry("PORT", port, port_data) - - # if not port then is a port channel - elif not is_port: - db.cfgdb.set_entry("PORTCHANNEL", port, port_data) - pass - - elif mode_exists_status and existing_mode == type: - ctx.fail("{} is already in {} mode".format(port,type)) - - else: - if is_port: - db.cfgdb.mod_entry("PORT", port, {"mode": "{}".format(type)}) - # if not port then is a port channel - elif not is_port: - db.cfgdb.mod_entry("PORTCHANNEL", port, {"mode": "{}".format(type)}) - - click.echo("{} switched from {} to {} mode".format(port,existing_mode,type)) - diff --git a/config/vlan.py b/config/vlan.py index 2f99f3ccfe..6cc3193ec0 100644 --- a/config/vlan.py +++ b/config/vlan.py @@ -12,19 +12,15 @@ DHCP_RELAY_TABLE = "DHCP_RELAY" DHCPV6_SERVERS = "dhcpv6_servers" - # # 'vlan' group ('config vlan ...') # - - @click.group(cls=clicommon.AbbreviationGroup, name='vlan') def vlan(): """VLAN-related configuration tasks""" pass - def set_dhcp_relay_table(table, config_db, vlan_name, value): config_db.set_entry(table, vlan_name, value) @@ -34,68 +30,41 @@ def is_dhcp_relay_running(): return_cmd=True) return out.strip() == "active" -def is_dhcpv6_relay_config_exist(db, vlan_name): - keys = db.cfgdb.get_keys(DHCP_RELAY_TABLE) - if len(keys) == 0 or vlan_name not in keys: - return False - - table = db.cfgdb.get_entry("DHCP_RELAY", vlan_name) - dhcpv6_servers = table.get(DHCPV6_SERVERS, []) - if len(dhcpv6_servers) > 0: - return True - @vlan.command('add') -@click.argument('vid', metavar='', required=True) -@click.option('-m', '--multiple', is_flag=True, help="Add Multiple Vlan(s) in Range or in Comma separated list") +@click.argument('vid', metavar='', required=True, type=int) @clicommon.pass_db -def add_vlan(db, vid, multiple): +def add_vlan(db, vid): """Add VLAN""" ctx = click.get_current_context() - - config_db = ValidatedConfigDBConnector(db.cfgdb) - - vid_list = [] - # parser will parse the vid input if there are syntax errors it will throw error - if multiple: - vid_list = clicommon.multiple_vlan_parser(ctx, vid) - else: - if not vid.isdigit(): - ctx.fail("{} is not integer".format(vid)) - vid_list.append(int(vid)) + vlan = 'Vlan{}'.format(vid) + config_db = ValidatedConfigDBConnector(db.cfgdb) if ADHOC_VALIDATION: + if not clicommon.is_vlanid_in_range(vid): + ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid)) - # loop will execute till an exception occurs - for vid in vid_list: - - vlan = 'Vlan{}'.format(vid) - - # default vlan checker - if vid == 1: - # TODO: MISSING CONSTRAINT IN YANG MODEL - ctx.fail("{} is default VLAN.".format(vlan)) + if vid == 1: + ctx.fail("{} is default VLAN".format(vlan)) # TODO: MISSING CONSTRAINT IN YANG MODEL - log.log_info("'vlan add {}' executing...".format(vid)) + if clicommon.check_if_vlanid_exist(db.cfgdb, vlan): # TODO: MISSING CONSTRAINT IN YANG MODEL + ctx.fail("{} already exists".format(vlan)) + if clicommon.check_if_vlanid_exist(db.cfgdb, vlan, "DHCP_RELAY"): + ctx.fail("DHCPv6 relay config for {} already exists".format(vlan)) + # set dhcpv4_relay table + set_dhcp_relay_table('VLAN', config_db, vlan, {'vlanid': str(vid)}) - if not clicommon.is_vlanid_in_range(vid): - ctx.fail("Invalid VLAN ID {} (2-4094)".format(vid)) - # TODO: MISSING CONSTRAINT IN YANG MODEL - if clicommon.check_if_vlanid_exist(db.cfgdb, vlan): - log.log_info("{} already exists".format(vlan)) - ctx.fail("{} already exists, Aborting!!!".format(vlan)) - - if clicommon.check_if_vlanid_exist(db.cfgdb, vlan, "DHCP_RELAY"): - ctx.fail("DHCPv6 relay config for {} already exists".format(vlan)) - - try: - # set dhcpv4_relay / VLAN table - config_db.set_entry('VLAN', vlan, {'vlanid': str(vid)}) +def is_dhcpv6_relay_config_exist(db, vlan_name): + keys = db.cfgdb.get_keys(DHCP_RELAY_TABLE) + if len(keys) == 0 or vlan_name not in keys: + return False - except ValueError: - ctx.fail("Invalid VLAN ID {} (2-4094)".format(vid)) + table = db.cfgdb.get_entry("DHCP_RELAY", vlan_name) + dhcpv6_servers = table.get(DHCPV6_SERVERS, []) + if len(dhcpv6_servers) > 0: + return True def delete_state_db_entry(entry_name): @@ -107,74 +76,57 @@ def delete_state_db_entry(entry_name): @vlan.command('del') -@click.argument('vid', metavar='', required=True) -@click.option('-m', '--multiple', is_flag=True, help="Add Multiple Vlan(s) in Range or in Comma separated list") +@click.argument('vid', metavar='', required=True, type=int) @click.option('--no_restart_dhcp_relay', is_flag=True, type=click.BOOL, required=False, default=False, help="If no_restart_dhcp_relay is True, do not restart dhcp_relay while del vlan and \ - require dhcpv6 relay of this is empty") + require dhcpv6 relay of this is empty") @clicommon.pass_db -def del_vlan(db, vid, multiple, no_restart_dhcp_relay): +def del_vlan(db, vid, no_restart_dhcp_relay): """Delete VLAN""" + log.log_info("'vlan del {}' executing...".format(vid)) + ctx = click.get_current_context() + vlan = 'Vlan{}'.format(vid) + if no_restart_dhcp_relay: + if is_dhcpv6_relay_config_exist(db, vlan): + ctx.fail("Can't delete {} because related DHCPv6 Relay config is exist".format(vlan)) + config_db = ValidatedConfigDBConnector(db.cfgdb) + if ADHOC_VALIDATION: + if not clicommon.is_vlanid_in_range(vid): + ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid)) - vid_list = [] - # parser will parse the vid input if there are syntax errors it will throw error - if multiple: - vid_list = clicommon.multiple_vlan_parser(ctx, vid) - else: - if not vid.isdigit(): - ctx.fail("{} is not integer".format(vid)) - vid_list.append(int(vid)) + if clicommon.check_if_vlanid_exist(db.cfgdb, vlan) == False: + ctx.fail("{} does not exist".format(vlan)) + + intf_table = db.cfgdb.get_table('VLAN_INTERFACE') + for intf_key in intf_table: + if ((type(intf_key) is str and intf_key == 'Vlan{}'.format(vid)) or # TODO: MISSING CONSTRAINT IN YANG MODEL + (type(intf_key) is tuple and intf_key[0] == 'Vlan{}'.format(vid))): + ctx.fail("{} can not be removed. First remove IP addresses assigned to this VLAN".format(vlan)) + + keys = [ (k, v) for k, v in db.cfgdb.get_table('VLAN_MEMBER') if k == 'Vlan{}'.format(vid) ] + + if keys: # TODO: MISSING CONSTRAINT IN YANG MODEL + ctx.fail("VLAN ID {} can not be removed. First remove all members assigned to this VLAN.".format(vid)) + + vxlan_table = db.cfgdb.get_table('VXLAN_TUNNEL_MAP') + for vxmap_key, vxmap_data in vxlan_table.items(): + if vxmap_data['vlan'] == 'Vlan{}'.format(vid): + ctx.fail("vlan: {} can not be removed. First remove vxlan mapping '{}' assigned to VLAN".format(vid, '|'.join(vxmap_key)) ) + + # set dhcpv4_relay table + set_dhcp_relay_table('VLAN', config_db, vlan, None) + + if not no_restart_dhcp_relay and is_dhcpv6_relay_config_exist(db, vlan): + # set dhcpv6_relay table + set_dhcp_relay_table('DHCP_RELAY', config_db, vlan, None) + # We need to restart dhcp_relay service after dhcpv6_relay config change + if is_dhcp_relay_running(): + dhcp_relay_util.handle_restart_dhcp_relay_service() + delete_state_db_entry(vlan) - if ADHOC_VALIDATION: - # loop will execute till an exception occurs - for vid in vid_list: - - log.log_info("'vlan del {}' executing...".format(vid)) - - if not clicommon.is_vlanid_in_range(vid): - ctx.fail("Invalid VLAN ID {} (2-4094)".format(vid)) - - vlan = 'Vlan{}'.format(vid) - - if no_restart_dhcp_relay: - if is_dhcpv6_relay_config_exist(db, vlan): - ctx.fail("Can't delete {} because related DHCPv6 Relay config is exist".format(vlan)) - - if clicommon.check_if_vlanid_exist(db.cfgdb, vlan) == False: - log.log_info("{} does not exist".format(vlan)) - ctx.fail("{} does not exist, Aborting!!!".format(vlan)) - - intf_table = db.cfgdb.get_table('VLAN_INTERFACE') - for intf_key in intf_table: - if ((type(intf_key) is str and intf_key == 'Vlan{}'.format(vid)) or # TODO: MISSING CONSTRAINT IN YANG MODEL - (type(intf_key) is tuple and intf_key[0] == 'Vlan{}'.format(vid))): - ctx.fail("{} can not be removed. First remove IP addresses assigned to this VLAN".format(vlan)) - - keys = [(k, v) for k, v in db.cfgdb.get_table('VLAN_MEMBER') if k == 'Vlan{}'.format(vid)] - - if keys: # TODO: MISSING CONSTRAINT IN YANG MODEL - ctx.fail("VLAN ID {} can not be removed. First remove all members assigned to this VLAN.".format(vid)) - - vxlan_table = db.cfgdb.get_table('VXLAN_TUNNEL_MAP') - for vxmap_key, vxmap_data in vxlan_table.items(): - if vxmap_data['vlan'] == 'Vlan{}'.format(vid): - ctx.fail("vlan: {} can not be removed. First remove vxlan mapping '{}' assigned to VLAN".format(vid, '|'.join(vxmap_key))) - - # set dhcpv4_relay / VLAN table - config_db.set_entry('VLAN', 'Vlan{}'.format(vid), None) - - if not no_restart_dhcp_relay and is_dhcpv6_relay_config_exist(db, vlan): - # set dhcpv6_relay table - set_dhcp_relay_table('DHCP_RELAY', config_db, vlan, None) - # We need to restart dhcp_relay service after dhcpv6_relay config change - if is_dhcp_relay_running(): - dhcp_relay_util.handle_restart_dhcp_relay_service() - - delete_state_db_entry(vlan) - vlans = db.cfgdb.get_keys('VLAN') if not vlans: docker_exec_cmd = ['docker', 'exec', '-i', 'swss'] @@ -184,7 +136,6 @@ def del_vlan(db, vid, multiple, no_restart_dhcp_relay): clicommon.run_command(docker_exec_cmd + ['supervisorctl', 'stop', 'ndppd'], ignore_error=True, return_cmd=True) clicommon.run_command(docker_exec_cmd + ['rm', '-f', '/etc/supervisor/conf.d/ndppd.conf'], ignore_error=True, return_cmd=True) clicommon.run_command(docker_exec_cmd + ['supervisorctl', 'update'], return_cmd=True) - def restart_ndppd(): verify_swss_running_cmd = ['docker', 'container', 'inspect', '-f', '{{.State.Status}}', 'swss'] @@ -231,162 +182,103 @@ def config_proxy_arp(db, vid, mode): db.cfgdb.mod_entry('VLAN_INTERFACE', vlan, {"proxy_arp": mode}) click.echo('Proxy ARP setting saved to ConfigDB') restart_ndppd() - - # # 'member' group ('config vlan member ...') # - - @vlan.group(cls=clicommon.AbbreviationGroup, name='member') def vlan_member(): pass - @vlan_member.command('add') -@click.argument('vid', metavar='', required=True) +@click.argument('vid', metavar='', required=True, type=int) @click.argument('port', metavar='port', required=True) -@click.option('-u', '--untagged', is_flag=True, help="Untagged status") -@click.option('-m', '--multiple', is_flag=True, help="Add Multiple Vlan(s) in Range or in Comma separated list") -@click.option('-e', '--except_flag', is_flag=True, help="Skips the given vlans and adds all other existing vlans.") +@click.option('-u', '--untagged', is_flag=True) @clicommon.pass_db -def add_vlan_member(db, vid, port, untagged, multiple, except_flag): +def add_vlan_member(db, vid, port, untagged): """Add VLAN member""" ctx = click.get_current_context() + + log.log_info("'vlan member add {} {}' executing...".format(vid, port)) + + vlan = 'Vlan{}'.format(vid) + config_db = ValidatedConfigDBConnector(db.cfgdb) + if ADHOC_VALIDATION: + if not clicommon.is_vlanid_in_range(vid): + ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid)) - # parser will parse the vid input if there are syntax errors it will throw error + if clicommon.check_if_vlanid_exist(db.cfgdb, vlan) == False: + ctx.fail("{} does not exist".format(vlan)) - vid_list = clicommon.vlan_member_input_parser(ctx, "add", db, except_flag, multiple, vid, port) + if clicommon.get_interface_naming_mode() == "alias": # TODO: MISSING CONSTRAINT IN YANG MODEL + alias = port + iface_alias_converter = clicommon.InterfaceAliasConverter(db) + port = iface_alias_converter.alias_to_name(alias) + if port is None: + ctx.fail("cannot find port name for alias {}".format(alias)) - # multiple vlan command cannot be used to add multiple untagged vlan members - if untagged and (multiple or except_flag or vid == "all"): - ctx.fail("{} cannot have more than one untagged Vlan.".format(port)) + if clicommon.is_port_mirror_dst_port(db.cfgdb, port): # TODO: MISSING CONSTRAINT IN YANG MODEL + ctx.fail("{} is configured as mirror destination port".format(port)) - if ADHOC_VALIDATION: - for vid in vid_list: - - # default vlan checker - if vid == 1: - ctx.fail("{} is default VLAN".format(vlan)) - - log.log_info("'vlan member add {} {}' executing...".format(vid, port)) - - if not clicommon.is_vlanid_in_range(vid): - ctx.fail("Invalid VLAN ID {} (2-4094)".format(vid)) - - vlan = 'Vlan{}'.format(vid) - if clicommon.check_if_vlanid_exist(db.cfgdb, vlan) == False: - log.log_info("{} does not exist".format(vlan)) - ctx.fail("{} does not exist".format(vlan)) - - if clicommon.get_interface_naming_mode() == "alias": # TODO: MISSING CONSTRAINT IN YANG MODEL - alias = port - iface_alias_converter = clicommon.InterfaceAliasConverter(db) - port = iface_alias_converter.alias_to_name(alias) - if port is None: - ctx.fail("cannot find port name for alias {}".format(alias)) - - # TODO: MISSING CONSTRAINT IN YANG MODEL - if clicommon.is_port_mirror_dst_port(db.cfgdb, port): - ctx.fail("{} is configured as mirror destination port".format(port)) - - # TODO: MISSING CONSTRAINT IN YANG MODEL - if clicommon.is_port_vlan_member(db.cfgdb, port, vlan): - log.log_info("{} is already a member of {}, Aborting!!!".format(port, vlan)) - ctx.fail("{} is already a member of {}, Aborting!!!".format(port, vlan)) - - - if clicommon.is_valid_port(db.cfgdb, port): - is_port = True - elif clicommon.is_valid_portchannel(db.cfgdb, port): - is_port = False - else: - ctx.fail("{} does not exist".format(port)) - - portchannel_member_table = db.cfgdb.get_table('PORTCHANNEL_MEMBER') - - # TODO: MISSING CONSTRAINT IN YANG MODEL - if (is_port and clicommon.interface_is_in_portchannel(portchannel_member_table, port)): - ctx.fail("{} is part of portchannel!".format(port)) - - # TODO: MISSING CONSTRAINT IN YANG MODEL - if (clicommon.interface_is_untagged_member(db.cfgdb, port) and untagged): - ctx.fail("{} is already untagged member!".format(port)) - - # checking mode status of port if its access, trunk or routed - if is_port: - port_data = config_db.get_entry('PORT',port) - - # if not port then is a port channel - elif not is_port: - port_data = config_db.get_entry('PORTCHANNEL',port) - - if "mode" not in port_data: - ctx.fail("{} is in routed mode!\nUse switchport mode command to change port mode".format(port)) - else: - existing_mode = port_data["mode"] - - if existing_mode == "routed": - ctx.fail("{} is in routed mode!\nUse switchport mode command to change port mode".format(port)) - - mode_type = "access" if untagged else "trunk" - if existing_mode == "access" and mode_type == "trunk": # TODO: MISSING CONSTRAINT IN YANG MODEL - ctx.fail("{} is in access mode! Tagged Members cannot be added".format(port)) - - elif existing_mode == mode_type or (existing_mode == "trunk" and mode_type == "access"): - pass - - # in case of exception in list last added member will be shown to user - try: - config_db.set_entry('VLAN_MEMBER', (vlan, port), {'tagging_mode': "untagged" if untagged else "tagged"}) - except ValueError: - ctx.fail("{} invalid or does not exist, or {} invalid or does not exist".format(vlan, port)) + if clicommon.is_port_vlan_member(db.cfgdb, port, vlan): # TODO: MISSING CONSTRAINT IN YANG MODEL + ctx.fail("{} is already a member of {}".format(port, vlan)) + + if clicommon.is_valid_port(db.cfgdb, port): + is_port = True + elif clicommon.is_valid_portchannel(db.cfgdb, port): + is_port = False + else: + ctx.fail("{} does not exist".format(port)) + + if (is_port and clicommon.is_port_router_interface(db.cfgdb, port)) or \ + (not is_port and clicommon.is_pc_router_interface(db.cfgdb, port)): # TODO: MISSING CONSTRAINT IN YANG MODEL + ctx.fail("{} is a router interface!".format(port)) + + portchannel_member_table = db.cfgdb.get_table('PORTCHANNEL_MEMBER') + + if (is_port and clicommon.interface_is_in_portchannel(portchannel_member_table, port)): # TODO: MISSING CONSTRAINT IN YANG MODEL + ctx.fail("{} is part of portchannel!".format(port)) + + if (clicommon.interface_is_untagged_member(db.cfgdb, port) and untagged): # TODO: MISSING CONSTRAINT IN YANG MODEL + ctx.fail("{} is already untagged member!".format(port)) + try: + config_db.set_entry('VLAN_MEMBER', (vlan, port), {'tagging_mode': "untagged" if untagged else "tagged" }) + except ValueError: + ctx.fail("{} invalid or does not exist, or {} invalid or does not exist".format(vlan, port)) @vlan_member.command('del') -@click.argument('vid', metavar='', required=True) +@click.argument('vid', metavar='', required=True, type=int) @click.argument('port', metavar='', required=True) -@click.option('-m', '--multiple', is_flag=True, help="Add Multiple Vlan(s) in Range or in Comma separated list") -@click.option('-e', '--except_flag', is_flag=True, help="Skips the given vlans and adds all other existing vlans.") @clicommon.pass_db -def del_vlan_member(db, vid, port, multiple, except_flag): +def del_vlan_member(db, vid, port): """Delete VLAN member""" ctx = click.get_current_context() + log.log_info("'vlan member del {} {}' executing...".format(vid, port)) + vlan = 'Vlan{}'.format(vid) + config_db = ValidatedConfigDBConnector(db.cfgdb) - - # parser will parse the vid input if there are syntax errors it will throw error - - vid_list = clicommon.vlan_member_input_parser(ctx,"del", db, except_flag, multiple, vid, port) - if ADHOC_VALIDATION: - for vid in vid_list: - - log.log_info("'vlan member del {} {}' executing...".format(vid, port)) - - if not clicommon.is_vlanid_in_range(vid): - ctx.fail("Invalid VLAN ID {} (2-4094)".format(vid)) + if not clicommon.is_vlanid_in_range(vid): + ctx.fail("Invalid VLAN ID {} (1-4094)".format(vid)) - vlan = 'Vlan{}'.format(vid) - if clicommon.check_if_vlanid_exist(db.cfgdb, vlan) == False: - log.log_info("{} does not exist, Aborting!!!".format(vlan)) - ctx.fail("{} does not exist, Aborting!!!".format(vlan)) + if clicommon.check_if_vlanid_exist(db.cfgdb, vlan) == False: + ctx.fail("{} does not exist".format(vlan)) - if clicommon.get_interface_naming_mode() == "alias": # TODO: MISSING CONSTRAINT IN YANG MODEL - alias = port - iface_alias_converter = clicommon.InterfaceAliasConverter(db) - port = iface_alias_converter.alias_to_name(alias) - if port is None: - ctx.fail("cannot find port name for alias {}".format(alias)) + if clicommon.get_interface_naming_mode() == "alias": # TODO: MISSING CONSTRAINT IN YANG MODEL + alias = port + iface_alias_converter = clicommon.InterfaceAliasConverter(db) + port = iface_alias_converter.alias_to_name(alias) + if port is None: + ctx.fail("cannot find port name for alias {}".format(alias)) - # TODO: MISSING CONSTRAINT IN YANG MODEL - if not clicommon.is_port_vlan_member(db.cfgdb, port, vlan): - ctx.fail("{} is not a member of {}".format(port, vlan)) + if not clicommon.is_port_vlan_member(db.cfgdb, port, vlan): # TODO: MISSING CONSTRAINT IN YANG MODEL + ctx.fail("{} is not a member of {}".format(port, vlan)) - try: - config_db.set_entry('VLAN_MEMBER', (vlan, port), None) + try: + config_db.set_entry('VLAN_MEMBER', (vlan, port), None) + except JsonPatchConflict: + ctx.fail("{} invalid or does not exist, or {} is not a member of {}".format(vlan, port, vlan)) - except JsonPatchConflict: - ctx.fail("{} invalid or does not exist, or {} is not a member of {}".format(vlan, port, vlan)) diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 9cb0cfbb54..019499b0c9 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -158,8 +158,6 @@ * [Subinterfaces](#subinterfaces) * [Subinterfaces Show Commands](#subinterfaces-show-commands) * [Subinterfaces Config Commands](#subinterfaces-config-commands) -* [Switchport Modes](#switchport-modes) - * [Switchport Mode config commands](#switchport modes-config-commands) * [Syslog](#syslog) * [Syslog show commands](#syslog-show-commands) * [Syslog config commands](#syslog-config-commands) @@ -2844,23 +2842,23 @@ This command is used to show ipv6 dhcp_relay counters. - Example: ``` admin@sonic:~$ sudo sonic-clear dhcp_relay counters - Message Type Vlan1000 - ------------------- ---------- - Unknown 0 - Solicit 0 - Advertise 0 - Request 5 - Confirm 0 - Renew 0 - Rebind 0 - Reply 0 - Release 0 - Decline 0 - Reconfigure 0 - Information-Request 0 - Relay-Forward 0 - Relay-Reply 0 - Malformed 0 +      Message Type    Vlan1000 + -------------------  ---------- +             Unknown           0 +             Solicit           0 +           Advertise           0 +             Request           5 +             Confirm           0 +               Renew           0 +              Rebind           0 +               Reply           0 +             Release           0 +             Decline           0 +         Reconfigure           0 + Information-Request           0 +       Relay-Forward           0 +         Relay-Reply           0 +           Malformed           0 ``` ### DHCP Relay clear commands @@ -4215,7 +4213,6 @@ Subsequent pages explain each of these commands in detail. neighbor Show neighbor related information portchannel Show PortChannel information status Show Interface status information - switchport Show Interface switchport information tpid Show Interface tpid information transceiver Show SFP Transceiver information ``` @@ -4681,50 +4678,6 @@ This command displays some more fields such as Lanes, Speed, MTU, Type, Asymmetr Ethernet180 105,106,107,108 100G 9100 hundredGigE46 down down N/A N/A ``` - -**show interface switchport status** - -This command displays switchport modes status of the interfaces - -- Usage: - ``` - show interfaces switchport status - ``` - -- Example (show interface switchport status of all interfaces): - ``` - admin@sonic:~$ show interfaces switchport status - Interface Mode - ----------- -------- - Ethernet0 access - Ethernet4 trunk - Ethernet8 routed - - ``` - -**show interface switchport config** - -This command displays switchport modes configuration of the interfaces - -- Usage: - ``` - show interfaces switchport config - ``` - -- Example (show interface switchport config of all interfaces): - ``` - admin@sonic:~$ show interfaces switchport config - Interface Mode Untagged Tagged - ----------- -------- -------- ------- - Ethernet0 access 2 - Ethernet4 trunk 3 4,5,6 - Ethernet8 routed - - ``` - - -For details please refer [Switchport Mode HLD](https://github.com/sonic-net/SONiC/pull/912/files#diff-03597c34684d527192f76a6e975792fcfc83f54e20dde63f159399232d148397) to know more about this command. - **show interfaces transceiver** This command is already explained [here](#Transceivers) @@ -9831,40 +9784,6 @@ This sub-section explains how to configure subinterfaces. Go Back To [Beginning of the document](#) or [Beginning of this section](#subinterfaces) -## Switchport Modes - -### Switchport Modes Config Commands - -This subsection explains how to configure switchport modes on Port/PortChannel. - -**config switchport** -mode -Usage: - ``` - config switchport mode - ``` - -- Example (Config switchport mode access on "Ethernet0): - ``` - admin@sonic:~$ sudo config switchport mode access Ethernet0 - ``` - -- Example (Config switchport mode trunk on "Ethernet4"): - ``` - admin@sonic:~$ sudo config switchport mode trunk Ethernet4 - ``` - -- Example (Config switchport mode routed on "Ethernet12"): - ``` - admin@sonic:~$ sudo config switchport mode routed Ethernet12 - ``` - - - -Go Back To [Beginning of the document](#) or [Beginning of this section](#switchport-modes) - - - ## Syslog ### Syslog Show Commands @@ -10550,29 +10469,6 @@ This command is used to add or delete the vlan. admin@sonic:~$ sudo config vlan add 100 ``` -**config vlan add/del -m** - -This command is used to add or delete multiple vlans via single command. - -- Usage: - ``` - config vlan (add | del) -m - ``` - -- Example01 (Create the VLAN "Vlan100, Vlan101, Vlan102, Vlan103" if these does not already exist) - - ``` - admin@sonic:~$ sudo config vlan add -m 100-103 - ``` - - -- Example02 (Create the VLAN "Vlan105, Vlan106, Vlan107, Vlan108" if these does not already exist): - - ``` - admin@sonic:~$ sudo config vlan add -m 105,106,107,108 - ``` - - **config vlan member add/del** This command is to add or delete a member port into the already created vlan. @@ -10594,48 +10490,6 @@ This command is to add or delete a member port into the already created vlan. This command will add Ethernet4 as member of the vlan 100. ``` - -**config vlan member add/del -m -e** - -This command is to add or delete a member port into multiple already created vlans. - -- Usage: - ``` - config vlan member add/del [-m] [-e] - ``` - -*NOTE: -m flag multiple Vlans in range or comma separted list can be added as a member port.* - - -*NOTE: -e is used as an except flag as explaied with examples below.* - - -- Example: - ``` - admin@sonic:~$ sudo config vlan member add -m 100-103 Ethernet0 - This command will add Ethernet0 as member of the vlan 100, vlan 101, vlan 102, vlan 103 - ``` - - ``` - admin@sonic:~$ sudo config vlan member add -m 100,101,102 Ethernet4 - This command will add Ethernet4 as member of the vlan 100, vlan 101, vlan 102 - ``` - - ``` - admin@sonic:~$ sudo config vlan member add -e -m 104,105 Ethernet8 - Suppose vlan 100, vlan 101, vlan 102, vlan 103, vlan 104, vlan 105 are exisiting vlans. This command will add Ethernet8 as member of vlan 100, vlan 101, vlan 102, vlan 103 - ``` - - ``` - admin@sonic:~$ sudo config vlan member add -e 100 Ethernet12 - Suppose vlan 100, vlan 101, vlan 102, vlan 103, vlan 104, vlan 105 are exisiting vlans. This command will add Ethernet12 as member of vlan 101, vlan 102, vlan 103, vlan 104, vlan 105 - ``` - - ``` - admin@sonic:~$ sudo config vlan member add all Ethernet20 - Suppose vlan 100, vlan 101, vlan 102, vlan 103, vlan 104, vlan 105 are exisiting vlans. This command will add Ethernet20 as member of vlan 100, vlan 101, vlan 102, vlan 103, vlan 104, vlan 105 - ``` - **config proxy_arp enabled/disabled** This command is used to enable or disable proxy ARP for a VLAN interface diff --git a/scripts/db_migrator.py b/scripts/db_migrator.py index 8df892b2d7..00f2d7cc0a 100755 --- a/scripts/db_migrator.py +++ b/scripts/db_migrator.py @@ -456,39 +456,6 @@ def migrate_config_db_port_table_for_auto_neg(self): elif value['autoneg'] == '0': self.configDB.set(self.configDB.CONFIG_DB, '{}|{}'.format(table_name, key), 'autoneg', 'off') - - def migrate_config_db_switchport_mode(self): - port_table = self.configDB.get_table('PORT') - portchannel_table = self.configDB.get_table('PORTCHANNEL') - vlan_member_table = self.configDB.get_table('VLAN_MEMBER') - - vlan_member_keys= [] - for _,key in vlan_member_table: - vlan_member_keys.append(key) - - for p_key, p_value in port_table.items(): - if 'mode' in p_value: - self.configDB.set(self.configDB.CONFIG_DB, '{}|{}'.format("PORT", p_key), 'mode', p_value['mode']) - else: - if p_key in vlan_member_keys: - p_value["mode"] = "trunk" - self.configDB.set_entry("PORT", p_key, p_value) - else: - p_value["mode"] = "routed" - self.configDB.set_entry("PORT", p_key, p_value) - - for pc_key, pc_value in portchannel_table.items(): - if 'mode' in pc_value: - self.configDB.set(self.configDB.CONFIG_DB, '{}|{}'.format("PORTCHANNEL", pc_key), 'mode', pc_value['mode']) - else: - if pc_key in vlan_member_keys: - pc_value["mode"] = "trunk" - self.configDB.set_entry("PORTCHANNEL", pc_key, pc_value) - else: - pc_value["mode"] = "routed" - self.configDB.set_entry("PORTCHANNEL", pc_key, pc_value) - - def migrate_qos_db_fieldval_reference_remove(self, table_list, db, db_num, db_delimeter): for pair in table_list: table_name, fields_list = pair @@ -1054,7 +1021,7 @@ def version_4_0_1(self): self.migrate_feature_timer() self.set_version('version_4_0_2') return 'version_4_0_2' - + def version_4_0_2(self): """ Version 4_0_2. @@ -1080,19 +1047,9 @@ def version_4_0_3(self): def version_4_0_4(self): """ Version 4_0_4. - """ - log.log_info('Handling version_4_0_4') - - self.migrate_config_db_switchport_mode() - self.set_version('version_4_0_4') - return 'version_4_0_5' - - def version_4_0_5(self): - """ - Version 4_0_5. This is the latest version for master branch """ - log.log_info('Handling version_4_0_5') + log.log_info('Handling version_4_0_4') return None def get_version(self): diff --git a/scripts/intfutil b/scripts/intfutil index f80523c0c4..eb40a49186 100755 --- a/scripts/intfutil +++ b/scripts/intfutil @@ -893,4 +893,4 @@ def main(): sys.exit(0) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/show/interfaces/__init__.py b/show/interfaces/__init__.py index b1d3c69b6f..a5a3734664 100644 --- a/show/interfaces/__init__.py +++ b/show/interfaces/__init__.py @@ -797,69 +797,3 @@ def fec_status(interfacename, namespace, display, verbose): cmd += ['-n', str(namespace)] clicommon.run_command(cmd, display_cmd=verbose) - -# -# switchport group (show interfaces switchport ...) -# -@interfaces.group(name='switchport', cls=clicommon.AliasedGroup) -def switchport(): - """Show interface switchport information""" - pass - - -@switchport.command(name="config") -@clicommon.pass_db -def switchport_mode_config(db): - """Show interface switchport config information""" - - port_data = list(db.cfgdb.get_table('PORT').keys()) - portchannel_data = list(db.cfgdb.get_table('PORTCHANNEL').keys()) - - portchannel_member_table = db.cfgdb.get_table('PORTCHANNEL_MEMBER') - - for interface in port_data: - if clicommon.interface_is_in_portchannel(portchannel_member_table,interface): - port_data.remove(interface) - - - keys = port_data + portchannel_data - - def tablelize(keys): - table = [] - - for key in natsorted(keys): - r = [clicommon.get_interface_name_for_display(db, key), clicommon.get_interface_switchport_mode(db,key), clicommon.get_interface_untagged_vlan_members(db,key), clicommon.get_interface_tagged_vlan_members(db,key)] - table.append(r) - - return table - - header = ['Interface', 'Mode', 'Untagged', 'Tagged'] - click.echo(tabulate(tablelize(keys), header, tablefmt="simple", stralign='left')) - -@switchport.command(name="status") -@clicommon.pass_db -def switchport_mode_status(db): - """Show interface switchport status information""" - - port_data = list(db.cfgdb.get_table('PORT').keys()) - portchannel_data = list(db.cfgdb.get_table('PORTCHANNEL').keys()) - - portchannel_member_table = db.cfgdb.get_table('PORTCHANNEL_MEMBER') - - for interface in port_data: - if clicommon.interface_is_in_portchannel(portchannel_member_table,interface): - port_data.remove(interface) - - keys = port_data + portchannel_data - - def tablelize(keys): - table = [] - - for key in natsorted(keys): - r = [clicommon.get_interface_name_for_display(db, key), clicommon.get_interface_switchport_mode(db,key)] - table.append(r) - - return table - - header = ['Interface', 'Mode'] - click.echo(tabulate(tablelize(keys), header,tablefmt="simple", stralign='left')) diff --git a/tests/db_migrator_input/config_db/port-an-expected.json b/tests/db_migrator_input/config_db/port-an-expected.json index 19fb6a80dd..1ef2cf4916 100644 --- a/tests/db_migrator_input/config_db/port-an-expected.json +++ b/tests/db_migrator_input/config_db/port-an-expected.json @@ -3,7 +3,6 @@ "index": "0", "lanes": "0,1", "description": "etp1a", - "mode": "routed", "mtu": "9100", "alias": "etp1a", "pfc_asym": "off", @@ -17,7 +16,6 @@ "lanes": "2,3", "description": "Servers0:eth0", "admin_status": "up", - "mode": "routed", "mtu": "9100", "alias": "etp1b", "pfc_asym": "off", @@ -30,7 +28,6 @@ "lanes": "4,5", "description": "Servers1:eth0", "admin_status": "up", - "mode": "routed", "mtu": "9100", "alias": "etp2a", "pfc_asym": "off", diff --git a/tests/db_migrator_input/config_db/portchannel-expected.json b/tests/db_migrator_input/config_db/portchannel-expected.json index da97ad3f3d..2644e5f4e9 100644 --- a/tests/db_migrator_input/config_db/portchannel-expected.json +++ b/tests/db_migrator_input/config_db/portchannel-expected.json @@ -3,7 +3,6 @@ "admin_status": "up", "members@": "Ethernet0,Ethernet4", "min_links": "2", - "mode": "routed", "mtu": "9100", "lacp_key": "auto" }, @@ -11,7 +10,6 @@ "admin_status": "up", "members@": "Ethernet8,Ethernet12", "min_links": "2", - "mode": "routed", "mtu": "9100", "lacp_key": "auto" }, @@ -19,7 +17,6 @@ "admin_status": "up", "members@": "Ethernet16", "min_links": "1", - "mode": "routed", "mtu": "9100", "lacp_key": "auto" }, @@ -27,13 +24,11 @@ "admin_status": "up", "members@": "Ethernet20,Ethernet24", "min_links": "2", - "mode": "routed", "mtu": "9100", "lacp_key": "auto" }, "PORTCHANNEL|PortChannel9999": { "admin_status": "up", - "mode": "routed", "mtu": "9100", "lacp_key": "auto" }, diff --git a/tests/db_migrator_input/config_db/switchport-expected.json b/tests/db_migrator_input/config_db/switchport-expected.json deleted file mode 100644 index 0f49b5edf3..0000000000 --- a/tests/db_migrator_input/config_db/switchport-expected.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "PORT|Ethernet0": { - "admin_status": "up", - "alias": "fortyGigE0/0", - "index": "0", - "lanes": "25,26,27,28", - "mode": "trunk", - "mtu": "9100", - "speed": "40000" - }, - "PORT|Ethernet4": { - "admin_status": "up", - "alias": "fortyGigE0/4", - "index": "1", - "lanes": "29,30,31,32", - "mode": "routed", - "mtu": "9100", - "speed": "40000" - }, - "PORT|Ethernet8": { - "admin_status": "up", - "alias": "fortyGigE0/8", - "index": "2", - "lanes": "33,34,35,36", - "mode": "trunk", - "mtu": "9100", - "speed": "40000" - }, - "PORT|Ethernet12": { - "admin_status": "up", - "alias": "fortyGigE0/12", - "index": "3", - "lanes": "37,38,39,40", - "mode": "access", - "mtu": "9100", - "speed": "40000" - }, - "PORT|Ethernet16": { - "admin_status": "up", - "alias": "fortyGigE0/16", - "index": "4", - "lanes": "45,46,47,48", - "mode": "routed", - "mtu": "9100", - "speed": "40000" - }, - "PORT|Ethernet20": { - "admin_status": "up", - "alias": "fortyGigE0/20", - "index": "5", - "lanes": "41,42,43,44", - "mode": "trunk", - "mtu": "9100", - "speed": "40000" - }, - - "VLAN|Vlan2": { - "vlanid": "2" - }, - "VLAN|Vlan3": { - "vlanid": "3" - }, - "VLAN|Vlan4": { - "vlanid": "4" - }, - "VLAN|Vlan5": { - "vlanid": "5" - }, - "VLAN|Vlan6": { - "vlanid": "6" - }, - "VLAN|Vlan7": { - "vlanid": "7" - }, - - - "VLAN_MEMBER|Vlan2|Ethernet0": { - "tagging_mode": "tagged" - }, - "VLAN_MEMBER|Vlan3|Ethernet8": { - "tagging_mode": "tagged" - }, - "VLAN_MEMBER|Vlan4|Ethernet0": { - "tagging_mode": "tagged" - }, - "VLAN_MEMBER|Vlan6|Ethernet0": { - "tagging_mode": "tagged" - }, - "VLAN_MEMBER|Vlan6|Ethernet8": { - "tagging_mode": "untagged" - }, - "VLAN_MEMBER|Vlan7|Ethernet8": { - "tagging_mode": "tagged" - }, - "VLAN_MEMBER|Vlan5|Ethernet8": { - "tagging_mode": "untagged" - }, - "VLAN_MEMBER|Vlan3|PortChannel0003": { - "tagging_mode": "untagged" - }, - "VLAN_MEMBER|Vlan8|PortChannel0002": { - "tagging_mode": "tagged" - }, - "VLAN_MEMBER|Vlan9|PortChannel0002": { - "tagging_mode": "tagged" - }, - - "PORTCHANNEL|PortChannel0001": { - "admin_status": "up", - "fast_rate": "false", - "lacp_key": "auto", - "min_links": "1", - "mode": "access", - "mtu": "9100" - }, - "PORTCHANNEL|PortChannel0002": { - "admin_status": "up", - "fast_rate": "false", - "lacp_key": "auto", - "min_links": "1", - "mode": "trunk", - "mtu": "9100" - }, - "PORTCHANNEL|PortChannel0003": { - "admin_status": "up", - "fast_rate": "false", - "lacp_key": "auto", - "min_links": "1", - "mode": "trunk", - "mtu": "9100" - }, - "PORTCHANNEL|PortChannel0004": { - "admin_status": "up", - "fast_rate": "false", - "lacp_key": "auto", - "min_links": "1", - "mode": "routed", - "mtu": "9100" - }, - - "VERSIONS|DATABASE": { - "VERSION": "version_4_0_1" - } -} \ No newline at end of file diff --git a/tests/db_migrator_input/config_db/switchport-input.json b/tests/db_migrator_input/config_db/switchport-input.json deleted file mode 100644 index 9613f29e75..0000000000 --- a/tests/db_migrator_input/config_db/switchport-input.json +++ /dev/null @@ -1,138 +0,0 @@ -{ - "PORT|Ethernet0": { - "admin_status": "up", - "alias": "fortyGigE0/0", - "index": "0", - "lanes": "25,26,27,28", - "mtu": "9100", - "speed": "40000" - }, - "PORT|Ethernet4": { - "admin_status": "up", - "alias": "fortyGigE0/4", - "index": "1", - "lanes": "29,30,31,32", - "mode": "routed", - "mtu": "9100", - "speed": "40000" - }, - "PORT|Ethernet8": { - "admin_status": "up", - "alias": "fortyGigE0/8", - "index": "2", - "lanes": "33,34,35,36", - "mtu": "9100", - "speed": "40000" - }, - "PORT|Ethernet12": { - "admin_status": "up", - "alias": "fortyGigE0/12", - "index": "3", - "lanes": "37,38,39,40", - "mode": "access", - "mtu": "9100", - "speed": "40000" - }, - "PORT|Ethernet16": { - "admin_status": "up", - "alias": "fortyGigE0/16", - "index": "4", - "lanes": "45,46,47,48", - "mtu": "9100", - "speed": "40000" - }, - "PORT|Ethernet20": { - "admin_status": "up", - "alias": "fortyGigE0/20", - "index": "5", - "lanes": "41,42,43,44", - "mode": "trunk", - "mtu": "9100", - "speed": "40000" - }, - "VLAN|Vlan2": { - "vlanid": "2" - }, - "VLAN|Vlan3": { - "vlanid": "3" - }, - "VLAN|Vlan4": { - "vlanid": "4" - }, - "VLAN|Vlan5": { - "vlanid": "5" - }, - "VLAN|Vlan6": { - "vlanid": "6" - }, - "VLAN|Vlan7": { - "vlanid": "7" - }, - - "VLAN_MEMBER|Vlan2|Ethernet0": { - "tagging_mode": "tagged" - }, - "VLAN_MEMBER|Vlan3|Ethernet8": { - "tagging_mode": "tagged" - }, - "VLAN_MEMBER|Vlan4|Ethernet0": { - "tagging_mode": "tagged" - }, - "VLAN_MEMBER|Vlan6|Ethernet0": { - "tagging_mode": "tagged" - }, - "VLAN_MEMBER|Vlan6|Ethernet8": { - "tagging_mode": "untagged" - }, - "VLAN_MEMBER|Vlan7|Ethernet8": { - "tagging_mode": "tagged" - }, - "VLAN_MEMBER|Vlan5|Ethernet8": { - "tagging_mode": "untagged" - }, - "VLAN_MEMBER|Vlan3|PortChannel0003": { - "tagging_mode": "untagged" - }, - "VLAN_MEMBER|Vlan8|PortChannel0002": { - "tagging_mode": "tagged" - }, - "VLAN_MEMBER|Vlan9|PortChannel0002": { - "tagging_mode": "tagged" - }, - - - "PORTCHANNEL|PortChannel0001": { - "admin_status": "up", - "fast_rate": "false", - "lacp_key": "auto", - "min_links": "1", - "mode": "access", - "mtu": "9100" - }, - "PORTCHANNEL|PortChannel0002": { - "admin_status": "up", - "fast_rate": "false", - "lacp_key": "auto", - "min_links": "1", - "mode": "trunk", - "mtu": "9100" - }, - "PORTCHANNEL|PortChannel0003": { - "admin_status": "up", - "fast_rate": "false", - "lacp_key": "auto", - "min_links": "1", - "mtu": "9100" - }, - "PORTCHANNEL|PortChannel0004": { - "admin_status": "up", - "fast_rate": "false", - "lacp_key": "auto", - "min_links": "1", - "mtu": "9100" - }, - - "VERSIONS|DATABASE": { - "VERSION": "version_4_0_0" - } -} \ No newline at end of file diff --git a/tests/db_migrator_test.py b/tests/db_migrator_test.py index 3f15f2c2e0..e75f758947 100644 --- a/tests/db_migrator_test.py +++ b/tests/db_migrator_test.py @@ -246,30 +246,6 @@ def test_port_autoneg_migrator(self): assert dbmgtr.configDB.get_table('PORT') == expected_db.cfgdb.get_table('PORT') assert dbmgtr.configDB.get_table('VERSIONS') == expected_db.cfgdb.get_table('VERSIONS') -class TestSwitchPortMigrator(object): - @classmethod - def setup_class(cls): - os.environ['UTILITIES_UNIT_TESTING'] = "2" - - @classmethod - def teardown_class(cls): - os.environ['UTILITIES_UNIT_TESTING'] = "0" - dbconnector.dedicated_dbs['CONFIG_DB'] = None - - def test_switchport_mode_migrator(self): - dbconnector.dedicated_dbs['CONFIG_DB'] = os.path.join(mock_db_path, 'config_db', 'switchport-input') - import db_migrator - dbmgtr = db_migrator.DBMigrator(None) - dbmgtr.migrate() - - dbconnector.dedicated_dbs['CONFIG_DB'] = os.path.join(mock_db_path, 'config_db', 'switchport-expected') - expected_db = Db() - advance_version_for_expected_database(dbmgtr.configDB, expected_db.cfgdb, 'version_4_0_1') - - assert dbmgtr.configDB.get_table('PORT') == expected_db.cfgdb.get_table('PORT') - assert dbmgtr.configDB.get_table('PORTCHANNEL') == expected_db.cfgdb.get_table('PORTCHANNEL') - assert dbmgtr.configDB.get_table('VERSIONS') == expected_db.cfgdb.get_table('VERSIONS') - class TestInitConfigMigrator(object): @classmethod def setup_class(cls): diff --git a/tests/interfaces_test.py b/tests/interfaces_test.py index 84940f0bca..c3246ba026 100644 --- a/tests/interfaces_test.py +++ b/tests/interfaces_test.py @@ -144,85 +144,6 @@ 1001 PortChannel1001 N/A """ -show_interfaces_switchport_status_output="""\ -Interface Mode ---------------- ------ -Ethernet0 routed -Ethernet4 trunk -Ethernet8 routed -Ethernet12 routed -Ethernet16 trunk -Ethernet20 routed -Ethernet24 trunk -Ethernet28 trunk -Ethernet36 routed -Ethernet40 routed -Ethernet44 routed -Ethernet48 routed -Ethernet52 access -Ethernet56 access -Ethernet60 routed -Ethernet64 routed -Ethernet68 routed -Ethernet72 routed -Ethernet76 routed -Ethernet80 routed -Ethernet84 routed -Ethernet88 routed -Ethernet92 routed -Ethernet96 routed -Ethernet100 routed -Ethernet104 routed -Ethernet108 routed -Ethernet116 routed -Ethernet124 routed -PortChannel0001 routed -PortChannel0002 routed -PortChannel0003 routed -PortChannel0004 routed -PortChannel1001 trunk -""" - -show_interfaces_switchport_config_output = """\ -Interface Mode Untagged Tagged ---------------- ------ ---------- -------- -Ethernet0 routed -Ethernet4 trunk 1000 -Ethernet8 routed 1000 -Ethernet12 routed 1000 -Ethernet16 trunk 1000 -Ethernet20 routed -Ethernet24 trunk 2000 -Ethernet28 trunk 2000 -Ethernet36 routed -Ethernet40 routed -Ethernet44 routed -Ethernet48 routed -Ethernet52 access -Ethernet56 access -Ethernet60 routed -Ethernet64 routed -Ethernet68 routed -Ethernet72 routed -Ethernet76 routed -Ethernet80 routed -Ethernet84 routed -Ethernet88 routed -Ethernet92 routed -Ethernet96 routed -Ethernet100 routed -Ethernet104 routed -Ethernet108 routed -Ethernet116 routed -Ethernet124 routed -PortChannel0001 routed -PortChannel0002 routed -PortChannel0003 routed -PortChannel0004 routed -PortChannel1001 trunk 4000 -""" - - class TestInterfaces(object): @classmethod def setup_class(cls): @@ -416,24 +337,6 @@ def test_parse_interface_in_filter(self): assert len(intf_list) == 3 assert intf_list == ["Ethernet-BP10", "Ethernet-BP11", "Ethernet-BP12"] - def test_show_interfaces_switchport_status(self): - runner = CliRunner() - result = runner.invoke(show.cli.commands["interfaces"].commands["switchport"].commands["status"]) - print(result.exit_code) - print(result.output) - - assert result.exit_code == 0 - assert result.output == show_interfaces_switchport_status_output - - def test_show_interfaces_switchport_config(self): - runner = CliRunner() - result = runner.invoke(show.cli.commands["interfaces"].commands["switchport"].commands["config"]) - print(result.exit_code) - print(result.output) - - assert result.exit_code == 0 - assert result.output == show_interfaces_switchport_config_output - @classmethod def teardown_class(cls): print("TEARDOWN") diff --git a/tests/intfutil_test.py b/tests/intfutil_test.py index 6e48c9724b..469c73e071 100644 --- a/tests/intfutil_test.py +++ b/tests/intfutil_test.py @@ -364,4 +364,4 @@ def test_show_interfaces_fec_status(self): def teardown_class(cls): print("TEARDOWN") os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) - os.environ["UTILITIES_UNIT_TESTING"] = "0" \ No newline at end of file + os.environ["UTILITIES_UNIT_TESTING"] = "0" diff --git a/tests/ipv6_link_local_test.py b/tests/ipv6_link_local_test.py index bb9e53ac1a..50b691be6b 100644 --- a/tests/ipv6_link_local_test.py +++ b/tests/ipv6_link_local_test.py @@ -232,7 +232,7 @@ def test_vlan_member_add_on_link_local_interface(self): result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], ["4000", "Ethernet40"], obj=obj) print(result.output) assert result.exit_code != 0 - assert 'Error: Ethernet40 is in routed mode!\nUse switchport mode command to change port mode' in result.output + assert 'Error: Ethernet40 is a router interface!' in result.output @classmethod def teardown_class(cls): diff --git a/tests/mock_tables/asic0/config_db.json b/tests/mock_tables/asic0/config_db.json index 023e70299c..de20194a64 100644 --- a/tests/mock_tables/asic0/config_db.json +++ b/tests/mock_tables/asic0/config_db.json @@ -75,7 +75,6 @@ "admin_status": "up", "members@": "Ethernet0,Ethernet4", "min_links": "2", - "mode": "trunk", "mtu": "9100" }, "PORTCHANNEL|PortChannel4001": { diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index 8786051e36..e58119d905 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -29,7 +29,6 @@ "lanes": "25,26,27,28", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -41,7 +40,6 @@ "lanes": "29,30,31,32", "mtu": "9100", "tpid": "0x8100", - "mode": "trunk", "pfc_asym": "off", "speed": "40000" }, @@ -53,7 +51,6 @@ "lanes": "33,34,35,36", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -65,7 +62,6 @@ "lanes": "37,38,39,40", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -77,7 +73,6 @@ "lanes": "16", "mtu": "9100", "tpid": "0x8100", - "mode": "trunk", "pfc_asym": "off", "speed": "100" }, @@ -89,7 +84,6 @@ "lanes": "41,42,43,44", "mtu": "9100", "tpid": "0x9200", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -101,7 +95,6 @@ "lanes": "1,2,3,4", "mtu": "9100", "tpid": "0x8100", - "mode": "trunk", "pfc_asym": "off", "speed": "1000" }, @@ -113,7 +106,6 @@ "lanes": "5,6,7,8", "mtu": "9100", "tpid": "0x8100", - "mode": "trunk", "pfc_asym": "off", "speed": "1000" }, @@ -125,7 +117,6 @@ "lanes": "13,14,15,16", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -137,7 +128,6 @@ "lanes": "9,10,11,12", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "10" }, @@ -149,7 +139,6 @@ "lanes": "17,18,19,20", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -161,7 +150,6 @@ "lanes": "21,22,23,24", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -173,7 +161,6 @@ "lanes": "53,54,55,56", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -185,7 +172,6 @@ "lanes": "49,50,51,52", "mtu": "9100", "tpid": "0x8100", - "mode": "access", "pfc_asym": "off", "speed": "40000" }, @@ -197,7 +183,6 @@ "lanes": "57,58,59,60", "mtu": "9100", "tpid": "0x8100", - "mode": "access", "pfc_asym": "off", "speed": "40000" }, @@ -209,7 +194,6 @@ "lanes": "61,62,63,64", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -232,7 +216,6 @@ "lanes": "65,66,67,68", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -244,7 +227,6 @@ "lanes": "73,74,75,76", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -256,7 +238,6 @@ "lanes": "77,78,79,80", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -268,7 +249,6 @@ "lanes": "109,110,111,112", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -280,7 +260,6 @@ "lanes": "105,106,107,108", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -292,7 +271,6 @@ "lanes": "113,114,115,116", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -304,7 +282,6 @@ "lanes": "117,118,119,120", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -316,7 +293,6 @@ "lanes": "125,126,127,128", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -327,7 +303,6 @@ "lanes": "121,122,123,124", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -338,7 +313,6 @@ "lanes": "81,82,83,84", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -349,7 +323,6 @@ "lanes": "85,86,87,88", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -361,7 +334,6 @@ "lanes": "93,94,95,96", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -373,7 +345,6 @@ "lanes": "89,90,91,92", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -385,7 +356,6 @@ "lanes": "101,102,103,104", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000" }, @@ -397,7 +367,6 @@ "lanes": "97,98,99,100", "mtu": "9100", "tpid": "0x8100", - "mode": "routed", "pfc_asym": "off", "speed": "40000", "fec" : "auto" @@ -700,7 +669,6 @@ "admin_status": "up", "members@": "Ethernet32", "min_links": "1", - "mode":"trunk", "tpid": "0x8100", "mtu": "9100" }, diff --git a/tests/vlan_test.py b/tests/vlan_test.py index 5212a7b026..6dae8be86d 100644 --- a/tests/vlan_test.py +++ b/tests/vlan_test.py @@ -134,124 +134,6 @@ +-----------+-----------------+-----------------+----------------+-------------+ """ -test_config_add_del_multiple_vlan_and_vlan_member_output="""\ -+-----------+-----------------+-----------------+----------------+-------------+ -| VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | -+===========+=================+=================+================+=============+ -| 1000 | 192.168.0.1/21 | Ethernet4 | untagged | disabled | -| | fc02:1000::1/64 | Ethernet8 | untagged | | -| | | Ethernet12 | untagged | | -| | | Ethernet16 | untagged | | -+-----------+-----------------+-----------------+----------------+-------------+ -| 1001 | | Ethernet20 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 1002 | | Ethernet20 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 1003 | | Ethernet20 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 2000 | 192.168.0.10/21 | Ethernet24 | untagged | enabled | -| | fc02:1011::1/64 | Ethernet28 | untagged | | -+-----------+-----------------+-----------------+----------------+-------------+ -| 3000 | | | | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 4000 | | PortChannel1001 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -""" - -test_config_add_del_add_vlans_and_add_all_vlan_member_output="""\ -+-----------+-----------------+-----------------+----------------+-------------+ -| VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | -+===========+=================+=================+================+=============+ -| 1000 | 192.168.0.1/21 | Ethernet4 | untagged | disabled | -| | fc02:1000::1/64 | Ethernet8 | untagged | | -| | | Ethernet12 | untagged | | -| | | Ethernet16 | untagged | | -| | | Ethernet20 | tagged | | -+-----------+-----------------+-----------------+----------------+-------------+ -| 1001 | | Ethernet20 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 1002 | | Ethernet20 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 1003 | | Ethernet20 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 2000 | 192.168.0.10/21 | Ethernet20 | tagged | enabled | -| | fc02:1011::1/64 | Ethernet24 | untagged | | -| | | Ethernet28 | untagged | | -+-----------+-----------------+-----------------+----------------+-------------+ -| 3000 | | Ethernet20 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 4000 | | Ethernet20 | tagged | disabled | -| | | PortChannel1001 | tagged | | -+-----------+-----------------+-----------------+----------------+-------------+ -""" - -test_config_add_del_add_vlans_and_add_vlans_member_except_vlan_output = """\ -+-----------+-----------------+-----------------+----------------+-------------+ -| VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | -+===========+=================+=================+================+=============+ -| 1000 | 192.168.0.1/21 | Ethernet4 | untagged | disabled | -| | fc02:1000::1/64 | Ethernet8 | untagged | | -| | | Ethernet12 | untagged | | -| | | Ethernet16 | untagged | | -+-----------+-----------------+-----------------+----------------+-------------+ -| 1001 | | Ethernet20 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 1002 | | Ethernet20 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 2000 | 192.168.0.10/21 | Ethernet20 | tagged | enabled | -| | fc02:1011::1/64 | Ethernet24 | untagged | | -| | | Ethernet28 | untagged | | -+-----------+-----------------+-----------------+----------------+-------------+ -| 3000 | | Ethernet20 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 4000 | | PortChannel1001 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -""" - -test_config_add_del_add_vlans_and_add_vlans_member_except_vlan__after_del_member_output = """\ -+-----------+-----------------+-----------------+----------------+-------------+ -| VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | -+===========+=================+=================+================+=============+ -| 1000 | 192.168.0.1/21 | Ethernet4 | untagged | disabled | -| | fc02:1000::1/64 | Ethernet8 | untagged | | -| | | Ethernet12 | untagged | | -| | | Ethernet16 | untagged | | -+-----------+-----------------+-----------------+----------------+-------------+ -| 1001 | | Ethernet20 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 1002 | | Ethernet20 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 2000 | 192.168.0.10/21 | Ethernet24 | untagged | enabled | -| | fc02:1011::1/64 | Ethernet28 | untagged | | -+-----------+-----------------+-----------------+----------------+-------------+ -| 3000 | | | | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 4000 | | PortChannel1001 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -""" - -test_config_add_del_vlan_and_vlan_member_with_switchport_modes_output = """\ -+-----------+-----------------+-----------------+----------------+-------------+ -| VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | -+===========+=================+=================+================+=============+ -| 1000 | 192.168.0.1/21 | Ethernet4 | untagged | disabled | -| | fc02:1000::1/64 | Ethernet8 | untagged | | -| | | Ethernet12 | untagged | | -| | | Ethernet16 | untagged | | -| | | Ethernet20 | tagged | | -+-----------+-----------------+-----------------+----------------+-------------+ -| 1001 | | Ethernet20 | untagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 2000 | 192.168.0.10/21 | Ethernet24 | untagged | enabled | -| | fc02:1011::1/64 | Ethernet28 | untagged | | -+-----------+-----------------+-----------------+----------------+-------------+ -| 3000 | | | | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 4000 | | PortChannel1001 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -""" - - config_add_del_vlan_and_vlan_member_in_alias_mode_output="""\ +-----------+-----------------+-----------------+----------------+-------------+ | VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | @@ -272,27 +154,6 @@ +-----------+-----------------+-----------------+----------------+-------------+ """ -test_config_add_del_vlan_and_vlan_member_with_switchport_modes_and_change_mode_types_output = """\ -+-----------+-----------------+-----------------+----------------+-------------+ -| VLAN ID | IP Address | Ports | Port Tagging | Proxy ARP | -+===========+=================+=================+================+=============+ -| 1000 | 192.168.0.1/21 | Ethernet4 | untagged | disabled | -| | fc02:1000::1/64 | Ethernet8 | untagged | | -| | | Ethernet12 | untagged | | -| | | Ethernet16 | untagged | | -+-----------+-----------------+-----------------+----------------+-------------+ -| 1001 | | | | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 2000 | 192.168.0.10/21 | Ethernet24 | untagged | enabled | -| | fc02:1011::1/64 | Ethernet28 | untagged | | -+-----------+-----------------+-----------------+----------------+-------------+ -| 3000 | | | | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -| 4000 | | PortChannel1001 | tagged | disabled | -+-----------+-----------------+-----------------+----------------+-------------+ -""" - - class TestVlan(object): _old_run_bgp_command = None @@ -375,7 +236,7 @@ def test_config_vlan_add_vlan_with_invalid_vlanid(self): print(result.exit_code) print(result.output) assert result.exit_code != 0 - assert "Error: Invalid VLAN ID 4096 (2-4094)" in result.output + assert "Error: Invalid VLAN ID 4096 (1-4094)" in result.output def test_config_vlan_add_vlan_with_exist_vlanid(self): runner = CliRunner() @@ -391,7 +252,7 @@ def test_config_vlan_del_vlan_with_invalid_vlanid(self): print(result.exit_code) print(result.output) assert result.exit_code != 0 - assert "Error: Invalid VLAN ID 4096 (2-4094)" in result.output + assert "Error: Invalid VLAN ID 4096 (1-4094)" in result.output def test_config_vlan_del_vlan_with_nonexist_vlanid(self): runner = CliRunner() @@ -401,79 +262,13 @@ def test_config_vlan_del_vlan_with_nonexist_vlanid(self): assert result.exit_code != 0 assert "Error: Vlan1001 does not exist" in result.output - def test_config_vlan_add_vlan_with_multiple_vlanids(self, mock_restart_dhcp_relay_service): - runner = CliRunner() - result = runner.invoke(config.config.commands["vlan"].commands["add"], ["10,20,30,40", "--multiple"]) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - def test_config_vlan_add_vlan_with_multiple_vlanids_with_range(self, mock_restart_dhcp_relay_service): - runner = CliRunner() - result = runner.invoke(config.config.commands["vlan"].commands["add"], ["10-20", "--multiple"]) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - def test_config_vlan_add_vlan_with_multiple_vlanids_with_range_and_multiple_ids(self, mock_restart_dhcp_relay_service): - runner = CliRunner() - result = runner.invoke(config.config.commands["vlan"].commands["add"], ["10-15,20,25,30", "--multiple"]) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - def test_config_vlan_add_vlan_with_wrong_range(self): - runner = CliRunner() - result = runner.invoke(config.config.commands["vlan"].commands["add"], ["15-10", "--multiple"]) - print(result.exit_code) - print(result.output) - assert result.exit_code != 0 - assert "15 is greater than 10. List cannot be generated" in result.output - - def test_config_vlan_add_vlan_range_with_default_vlan(self): - runner = CliRunner() - result = runner.invoke(config.config.commands["vlan"].commands["add"], ["1-10", "--multiple"]) - print(result.exit_code) - print(result.output) - assert result.exit_code != 0 - assert "Vlan1 is default vlan" in result.output - - def test_config_vlan_add_vlan_is_digit_fail(self): - runner = CliRunner() - vid = "test_fail_case" - result = runner.invoke(config.config.commands["vlan"].commands["add"], [vid]) - print(result.exit_code) - print(result.output) - assert result.exit_code != 0 - assert "{} is not integer".format(vid) in result.output - - def test_config_vlan_add_vlan_is_default_vlan(self): - runner = CliRunner() - default_vid = "1" - vlan = "Vlan{}".format(default_vid) - result = runner.invoke(config.config.commands["vlan"].commands["add"], [default_vid]) - print(result.exit_code) - print(result.output) - assert result.exit_code != 0 - assert "{} is default VLAN.".format(vlan) in result.output - - def test_config_vlan_del_vlan_does_not_exist(self): - runner = CliRunner() - vid = "3010" - vlan = "Vlan{}".format(vid) - result = runner.invoke(config.config.commands["vlan"].commands["del"], [vid]) - print(result.exit_code) - print(result.output) - assert result.exit_code != 0 - assert "{} does not exist".format(vlan) in result.output - def test_config_vlan_add_member_with_invalid_vlanid(self): runner = CliRunner() result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], ["4096", "Ethernet4"]) print(result.exit_code) print(result.output) assert result.exit_code != 0 - assert "Error: Invalid VLAN ID 4096 (2-4094)" in result.output + assert "Error: Invalid VLAN ID 4096 (1-4094)" in result.output def test_config_vlan_add_member_with_nonexist_vlanid(self): runner = CliRunner() @@ -501,13 +296,6 @@ def test_config_vlan_add_nonexist_port_member(self): def test_config_vlan_add_nonexist_portchannel_member(self): runner = CliRunner() - #switch port mode for PortChannel1011 to trunk mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["trunk", "PortChannel1011"]) - print(result.exit_code) - print(result.output) - assert result.exit_code != 0 - assert "Error: PortChannel1011 does not exist" in result.output - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], \ ["1000", "PortChannel1011"]) print(result.exit_code) @@ -518,6 +306,7 @@ def test_config_vlan_add_nonexist_portchannel_member(self): def test_config_vlan_add_portchannel_member(self): runner = CliRunner() db = Db() + result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], \ ["1000", "PortChannel1001", "--untagged"], obj=db) print(result.exit_code) @@ -540,7 +329,7 @@ def test_config_vlan_add_rif_portchannel_member(self): print(result.exit_code) print(result.output) assert result.exit_code != 0 - assert "Error: PortChannel0001 is in routed mode!\nUse switchport mode command to change port mode" in result.output + assert "Error: PortChannel0001 is a router interface!" in result.output def test_config_vlan_with_vxlanmap_del_vlan(self, mock_restart_dhcp_relay_service): runner = CliRunner() @@ -657,22 +446,6 @@ def test_config_add_del_vlan_and_vlan_member(self, mock_restart_dhcp_relay_servi print(result.output) assert result.exit_code == 0 - # add Ethernet20 to vlan 1001 but Ethernet20 is in routed mode will give error - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["1001", "Ethernet20", "--untagged"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code != 0 - assert "Ethernet20 is in routed mode!\nUse switchport mode command to change port mode" in result.output - - # configure Ethernet20 from routed to access mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["access", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert "Ethernet20 switched from routed to access mode" in result.output - # add Ethernet20 to vlan 1001 result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], ["1001", "Ethernet20", "--untagged"], obj=db) @@ -718,22 +491,6 @@ def test_config_add_del_vlan_and_vlan_member_in_alias_mode(self, mock_restart_dh print(result.output) assert result.exit_code == 0 - # add etp6 to vlan 1001 but etp6 is in routed mode will give error - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["1001", "etp6", "--untagged"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code != 0 - assert "Ethernet20 is in routed mode!\nUse switchport mode command to change port mode" in result.output - - # configure etp6 from routed to access mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["access", "etp6"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert "Ethernet20 switched from routed to access mode" in result.output - # add etp6 to vlan 1001 result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], ["1001", "etp6", "--untagged"], obj=db) @@ -770,380 +527,6 @@ def test_config_add_del_vlan_and_vlan_member_in_alias_mode(self, mock_restart_dh os.environ['SONIC_CLI_IFACE_MODE'] = "default" - def test_config_add_del_multiple_vlan_and_vlan_member(self,mock_restart_dhcp_relay_service): - runner = CliRunner() - db = Db() - - # add vlan 1001 - result = runner.invoke(config.config.commands["vlan"].commands["add"], ["1001,1002,1003","--multiple"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # add Ethernet20 to vlan1001, vlan1002, vlan1003 multiple flag but Ethernet20 is in routed mode will give error - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["1001,1002,1003", "Ethernet20", "--multiple"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code != 0 - assert "Ethernet20 is in routed mode!\nUse switchport mode command to change port mode" in result.output - - # configure Ethernet20 from routed to access mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["trunk", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert "Ethernet20 switched from routed to trunk mode" in result.output - - # add Ethernet20 to vlan 1001 - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["1001,1002,1003", "Ethernet20", "--multiple"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code == 0 - - # show output - result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) - print(result.output) - assert result.output == test_config_add_del_multiple_vlan_and_vlan_member_output - - # remove vlan member - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["del"], - ["1001-1003", "Ethernet20", "--multiple"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # add del 1001 - result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001-1003","--multiple"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # show output - result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert result.output == show_vlan_brief_output - - def test_config_add_del_add_vlans_and_add_vlans_member_except_vlan(self, mock_restart_dhcp_relay_service): - runner = CliRunner() - db = Db() - - # add vlan 1001 - result = runner.invoke(config.config.commands["vlan"].commands["add"], ["1001,1002","--multiple"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # add Ethernet20 to vlan1001, vlan1002, vlan1003 multiple flag but Ethernet20 is in routed mode will give error - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["1000,4000", "Ethernet20", "--multiple", "--except_flag"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code != 0 - assert "Ethernet20 is in routed mode!\nUse switchport mode command to change port mode" in result.output - - # configure Ethernet20 from routed to access mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["trunk", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert "Ethernet20 switched from routed to trunk mode" in result.output - - # add Ethernet20 to vlan 1001 - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["1000,4000", "Ethernet20", "--multiple", "--except_flag"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code == 0 - - # show output - result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) - print(result.output) - assert result.output == test_config_add_del_add_vlans_and_add_vlans_member_except_vlan_output - - # remove vlan member except some - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["del"], - ["1001,1002", "Ethernet20", "--multiple", "--except_flag"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # show output - result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert result.output == test_config_add_del_add_vlans_and_add_vlans_member_except_vlan__after_del_member_output - - # remove vlan member - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["del"], - ["1001,1002", "Ethernet20", "--multiple"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # add del 1001 - result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001-1002","--multiple"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # show output - result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert result.output == show_vlan_brief_output - - - def test_config_add_del_add_vlans_and_add_all_vlan_member(self, mock_restart_dhcp_relay_service): - runner = CliRunner() - db = Db() - - # add vlan 1001 - result = runner.invoke(config.config.commands["vlan"].commands["add"], ["1001,1002,1003","--multiple"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # add Ethernet20 to vlan1001, vlan1002, vlan1003 multiple flag but Ethernet20 is in routed mode will give error - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["all", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code != 0 - assert "Ethernet20 is in routed mode!\nUse switchport mode command to change port mode" in result.output - - # configure Ethernet20 from routed to access mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["trunk", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert "Ethernet20 switched from routed to trunk mode" in result.output - - # add Ethernet20 to vlan 1001 - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["all", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code == 0 - - # show output - result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) - print(result.output) - assert result.output == test_config_add_del_add_vlans_and_add_all_vlan_member_output - - # remove vlan member - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["del"], - ["all", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # add del 1001 - result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001-1003","--multiple"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # show output - result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert result.output == show_vlan_brief_output - - def test_config_add_del_vlan_and_vlan_member_with_switchport_modes(self, mock_restart_dhcp_relay_service): - runner = CliRunner() - db = Db() - - # add vlan 1001 - result = runner.invoke(config.config.commands["vlan"].commands["add"], ["1001"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # add Ethernet20 to vlan 1001 but Ethernet20 is in routed mode will give error - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["1001", "Ethernet20", "--untagged"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code != 0 - assert "Ethernet20 is in routed mode!\nUse switchport mode command to change port mode" in result.output - - - # configure Ethernet20 from routed to access mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["access", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert "Ethernet20 switched from routed to access mode" in result.output - - # add Ethernet20 to vlan 1001 - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["1001", "Ethernet20", "--untagged"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code == 0 - - # add Ethernet20 to vlan 1001 as tagged member - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["1000", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code != 0 - assert "Ethernet20 is in access mode! Tagged Members cannot be added" in result.output - - # configure Ethernet20 from access to trunk mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["trunk", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert "Ethernet20 switched from access to trunk mode" in result.output - - # add Ethernet20 to vlan 1001 as tagged member - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["1000", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code == 0 - - # show output - result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) - print(result.output) - assert result.output == test_config_add_del_vlan_and_vlan_member_with_switchport_modes_output - - # configure Ethernet20 from trunk to routed mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["routed", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code != 0 - assert "Ethernet20 has tagged member(s). \nRemove them to change mode to routed" in result.output - - # remove vlan member - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["del"], - ["1000", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # configure Ethernet20 from trunk to routed mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["routed", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code != 0 - assert "Ethernet20 has untagged member. \nRemove it to change mode to routed" in result.output - - # remove vlan member - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["del"], - ["1001", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # configure Ethernet20 from trunk to routed mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["routed", "Ethernet20"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert "Ethernet20 switched from trunk to routed mode" in result.output - - # add del 1001 - result = runner.invoke(config.config.commands["vlan"].commands["del"], ["1001"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # show output - result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert result.output == show_vlan_brief_output - - - def test_config_add_del_vlan_and_vlan_member_with_switchport_modes_and_change_mode_types(self, mock_restart_dhcp_relay_service): - runner = CliRunner() - db = Db() - - # add vlan 1001 - result = runner.invoke(config.config.commands["vlan"].commands["add"], ["1001"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - - # configure Ethernet64 to routed mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["routed", "Ethernet64"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # add Ethernet64 to vlan 1001 but Ethernet64 is in routed mode will give error - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["1001", "Ethernet64"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code != 0 - assert "Ethernet64 is in routed mode!\nUse switchport mode command to change port mode" in result.output - - # configure Ethernet64 from routed to trunk mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["trunk", "Ethernet64"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert "Ethernet64 switched from routed to trunk mode" in result.output - - # add Ethernet64 to vlan 1001 - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["add"], - ["1001", "Ethernet64"], obj=db) - print(result.exit_code) - print(result.output) - traceback.print_tb(result.exc_info[2]) - assert result.exit_code == 0 - - # configure Ethernet64 from routed to access mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["access", "Ethernet64"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code != 0 - assert "Ethernet64 is in trunk mode and have tagged member(s).\nRemove tagged member(s) from Ethernet64 to switch to access mode" in result.output - - # remove vlan member - result = runner.invoke(config.config.commands["vlan"].commands["member"].commands["del"], - ["1001", "Ethernet64"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - - # configure Ethernet64 from routed to access mode - result = runner.invoke(config.config.commands["switchport"].commands["mode"],["access", "Ethernet64"], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert "Ethernet64 switched from trunk to access mode" in result.output - - # show output - result = runner.invoke(show.cli.commands["vlan"].commands["brief"], [], obj=db) - print(result.exit_code) - print(result.output) - assert result.exit_code == 0 - assert result.output == test_config_add_del_vlan_and_vlan_member_with_switchport_modes_and_change_mode_types_output - def test_config_vlan_proxy_arp_with_nonexist_vlan_intf_table(self): modes = ["enabled", "disabled"] runner = CliRunner() @@ -1232,8 +615,8 @@ def test_config_set_router_port_on_member_interface(self): result = runner.invoke(config.config.commands["interface"].commands["ip"].commands["add"], ["Ethernet4", "10.10.10.1/24"], obj=obj) print(result.exit_code, result.output) - assert result.exit_code != 0 - assert 'Interface Ethernet4 is not in routed mode!' in result.output + assert result.exit_code == 0 + assert 'Interface Ethernet4 is a member of vlan' in result.output def test_config_vlan_add_member_of_portchannel(self): runner = CliRunner() diff --git a/utilities_common/cli.py b/utilities_common/cli.py index 54e867c610..9d3cdae710 100644 --- a/utilities_common/cli.py +++ b/utilities_common/cli.py @@ -20,7 +20,6 @@ pass_db = click.make_pass_decorator(Db, ensure=True) - class AbbreviationGroup(click.Group): """This subclass of click.Group supports abbreviated subgroup/subcommand names """ @@ -77,11 +76,9 @@ def read_config(self, filename): except configparser.NoSectionError: pass - # Global Config object _config = None - class AliasedGroup(click.Group): """This subclass of click.Group supports abbreviations and looking up aliases in a config file with a bit of magic. @@ -120,7 +117,6 @@ def get_command(self, ctx, cmd_name): return click.Group.get_command(self, ctx, matches[0]) ctx.fail('Too many matches: %s' % ', '.join(sorted(matches))) - class InterfaceAliasConverter(object): """Class which handles conversion between interface name and alias""" @@ -135,6 +131,7 @@ def __init__(self, db=None): self.port_dict = self.config_db.get_table('PORT') self.alias_max_length = 0 + if not self.port_dict: self.port_dict = {} @@ -142,7 +139,7 @@ def __init__(self, db=None): try: if self.alias_max_length < len( self.port_dict[port_name]['alias']): - self.alias_max_length = len( + self.alias_max_length = len( self.port_dict[port_name]['alias']) except KeyError: break @@ -154,8 +151,7 @@ def name_to_alias(self, interface_name): vlan_id = '' sub_intf_sep_idx = -1 if interface_name is not None: - sub_intf_sep_idx = interface_name.find( - VLAN_SUB_INTERFACE_SEPARATOR) + sub_intf_sep_idx = interface_name.find(VLAN_SUB_INTERFACE_SEPARATOR) if sub_intf_sep_idx != -1: vlan_id = interface_name[sub_intf_sep_idx + 1:] # interface_name holds the parent port name @@ -164,7 +160,7 @@ def name_to_alias(self, interface_name): for port_name in self.port_dict: if interface_name == port_name: return self.port_dict[port_name]['alias'] if sub_intf_sep_idx == -1 \ - else self.port_dict[port_name]['alias'] + VLAN_SUB_INTERFACE_SEPARATOR + vlan_id + else self.port_dict[port_name]['alias'] + VLAN_SUB_INTERFACE_SEPARATOR + vlan_id # interface_name not in port_dict. Just return interface_name return interface_name if sub_intf_sep_idx == -1 else interface_name + VLAN_SUB_INTERFACE_SEPARATOR + vlan_id @@ -176,8 +172,7 @@ def alias_to_name(self, interface_alias): vlan_id = '' sub_intf_sep_idx = -1 if interface_alias is not None: - sub_intf_sep_idx = interface_alias.find( - VLAN_SUB_INTERFACE_SEPARATOR) + sub_intf_sep_idx = interface_alias.find(VLAN_SUB_INTERFACE_SEPARATOR) if sub_intf_sep_idx != -1: vlan_id = interface_alias[sub_intf_sep_idx + 1:] # interface_alias holds the parent port alias @@ -190,11 +185,8 @@ def alias_to_name(self, interface_alias): # interface_alias not in port_dict. Just return interface_alias return interface_alias if sub_intf_sep_idx == -1 else interface_alias + VLAN_SUB_INTERFACE_SEPARATOR + vlan_id - # Lazy global class instance for SONiC interface name to alias conversion -iface_alias_converter = lazy_object_proxy.Proxy( - lambda: InterfaceAliasConverter()) - +iface_alias_converter = lazy_object_proxy.Proxy(lambda: InterfaceAliasConverter()) def get_interface_naming_mode(): mode = os.getenv('SONIC_CLI_IFACE_MODE') @@ -202,7 +194,6 @@ def get_interface_naming_mode(): mode = "default" return mode - def is_ipaddress(val): """ Validate if an entry is a valid IP """ import netaddr @@ -214,7 +205,6 @@ def is_ipaddress(val): return False return True - def ipaddress_type(val): """ Return the IP address type """ if not val: @@ -227,7 +217,6 @@ def ipaddress_type(val): return ip_version.version - def is_ip_prefix_in_key(key): ''' Function to check if IP address is present in the key. If it @@ -236,7 +225,6 @@ def is_ip_prefix_in_key(key): ''' return (isinstance(key, tuple)) - def is_valid_port(config_db, port): """Check if port is in PORT table""" @@ -246,7 +234,6 @@ def is_valid_port(config_db, port): return False - def is_valid_portchannel(config_db, port): """Check if port is in PORT_CHANNEL table""" @@ -256,7 +243,6 @@ def is_valid_portchannel(config_db, port): return False - def is_vlanid_in_range(vid): """Check if vlan id is valid or not""" @@ -265,7 +251,6 @@ def is_vlanid_in_range(vid): return False - def check_if_vlanid_exist(config_db, vlan, table_name='VLAN'): """Check if vlan id exits in the config db or ot""" @@ -274,7 +259,6 @@ def check_if_vlanid_exist(config_db, vlan, table_name='VLAN'): return False - def is_port_vlan_member(config_db, port, vlan): """Check if port is a member of vlan""" @@ -285,134 +269,26 @@ def is_port_vlan_member(config_db, port, vlan): return False - -def vlan_range_list(ctx, vid_range: str) -> list: - - vid1, vid2 = map(int, vid_range.split("-")) - - if vid1 == 1 or vid2 == 1: - ctx.fail("Vlan1 is default vlan") - - if vid1 >= vid2: - ctx.fail("{} is greater than {}. List cannot be generated".format(vid1,vid2)) - - if is_vlanid_in_range(vid1) and is_vlanid_in_range(vid2): - return list(range(vid1, vid2+1)) - else: - ctx.fail("Invalid VLAN ID must be in (2-4094)") - - -def multiple_vlan_parser(ctx, s_input: str) -> list: - - vlan_list = [] - - vlan_map = map(str, s_input.replace(" ", "").split(",")) - for vlan in vlan_map: - if "-" in vlan: - vlan_list += vlan_range_list(ctx, vlan) - elif vlan.isdigit() and int(vlan) not in vlan_list: - vlan_list.append(int(vlan)) - elif not vlan.isdigit(): - ctx.fail("{} is not integer".format(vlan)) - - vlan_list.sort() - return vlan_list - - -def get_existing_vlan_id(db) -> list: - existing_vlans = [] - vlan_data = db.cfgdb.get_table('VLAN') - - for i in vlan_data.keys(): - existing_vlans.append(int(i.strip("Vlan"))) - - return sorted(existing_vlans) - -def get_existing_vlan_id_on_interface(db,port) -> list: - intf_vlans = [] - vlan_member_data = db.cfgdb.get_table('VLAN_MEMBER') - - for (k,v) in vlan_member_data.keys(): - if v == port: - intf_vlans.append(int(k.strip("Vlan"))) - - return sorted(intf_vlans) - - -def vlan_member_input_parser(ctx, command_mode, db, except_flag, multiple, vid, port) -> list: - vid_list = [] - if vid == "all": - if command_mode == "add": - return get_existing_vlan_id(db) # config vlan member add - if command_mode == "del": - return get_existing_vlan_id_on_interface(db,port) # config vlan member del - - if multiple: - vid_list = multiple_vlan_parser(ctx, vid) - - if except_flag: - if command_mode == "add": - comp_list = get_existing_vlan_id(db) # config vlan member add - - elif command_mode == "del": - comp_list = get_existing_vlan_id_on_interface(db,port) # config vlan member del - - if multiple: - for i in vid_list: - if i in comp_list: - comp_list.remove(i) - - else: - if not vid.isdigit(): - ctx.fail("Vlan is not integer.") - vid = int(vid) - if vid in comp_list: - comp_list.remove(vid) - vid_list = comp_list - - elif not multiple: - # if entered vlan is not a integer - if not vid.isdigit(): - ctx.fail("Vlan is not integer.") - vid_list.append(int(vid)) - - # sorting the vid_list - vid_list.sort() - return vid_list - -def interface_is_tagged_member(db, interface_name): - """ Check if interface has tagged members i.e. is in trunk mode""" - vlan_member_table = db.get_table('VLAN_MEMBER') - - for key, val in vlan_member_table.items(): - if(key[1] == interface_name): - if (val['tagging_mode'] == 'tagged'): - return True - return False - def interface_is_in_vlan(vlan_member_table, interface_name): """ Check if an interface is in a vlan """ - for _, intf in vlan_member_table: + for _,intf in vlan_member_table: if intf == interface_name: return True return False - def is_valid_vlan_interface(config_db, interface): """ Check an interface is a valid VLAN interface """ return interface in config_db.get_table("VLAN_INTERFACE") - def interface_is_in_portchannel(portchannel_member_table, interface_name): """ Check if an interface is part of portchannel """ - for _, intf in portchannel_member_table: + for _,intf in portchannel_member_table: if intf == interface_name: return True return False - def is_port_router_interface(config_db, port): """Check if port is a router interface""" @@ -423,7 +299,6 @@ def is_port_router_interface(config_db, port): return False - def is_pc_router_interface(config_db, pc): """Check if portchannel is a router interface""" @@ -434,65 +309,15 @@ def is_pc_router_interface(config_db, pc): return False -def get_vlan_id(vlan): - vlan_prefix, vid = vlan.split('Vlan') - return vid - -def get_interface_name_for_display(db ,interface): - interface_naming_mode = get_interface_naming_mode() - iface_alias_converter = InterfaceAliasConverter(db) - if interface_naming_mode == "alias" and interface: - return iface_alias_converter.name_to_alias(interface) - return interface - -def get_interface_untagged_vlan_members(db,interface): - untagged_vlans = [] - vlan_member = db.cfgdb.get_table('VLAN_MEMBER') - - for member in natsorted(list(vlan_member.keys())): - interface_vlan, interface_name = member - - if interface == interface_name and vlan_member[member]['tagging_mode'] == 'untagged': - untagged_vlans.append(get_vlan_id(interface_vlan)) - - return "\n".join(untagged_vlans) - -def get_interface_tagged_vlan_members(db,interface): - tagged_vlans = [] - formatted_tagged_vlans = [] - vlan_member = db.cfgdb.get_table('VLAN_MEMBER') - - for member in natsorted(list(vlan_member.keys())): - interface_vlan, interface_name = member - - if interface == interface_name and vlan_member[member]['tagging_mode'] == 'tagged': - tagged_vlans.append(get_vlan_id(interface_vlan)) - - for i in range(len(tagged_vlans)//5+1): - formatted_tagged_vlans.append(" ,".join([str(x) for x in tagged_vlans[i*5:(i+1)*5]])) - - return "\n".join(formatted_tagged_vlans) - -def get_interface_switchport_mode(db, interface): - port = db.cfgdb.get_entry('PORT',interface) - portchannel = db.cfgdb.get_entry('PORTCHANNEL',interface) - switchport_mode = 'routed' - if "mode" in port: - switchport_mode = port['mode'] - elif "mode" in portchannel: - switchport_mode = portchannel['mode'] - return switchport_mode - def is_port_mirror_dst_port(config_db, port): """Check if port is already configured as mirror destination port """ mirror_table = config_db.get_table('MIRROR_SESSION') - for _, v in mirror_table.items(): + for _,v in mirror_table.items(): if 'dst_port' in v and v['dst_port'] == port: return True return False - def vni_id_is_valid(vni): """Check if the vni id is in acceptable range (between 1 and 2^24) """ @@ -502,7 +327,6 @@ def vni_id_is_valid(vni): return True - def is_vni_vrf_mapped(db, vni): """Check if the vni is mapped to vrf """ @@ -511,22 +335,20 @@ def is_vni_vrf_mapped(db, vni): vrf_table = db.cfgdb.get_table('VRF') vrf_keys = vrf_table.keys() if vrf_keys is not None: - for vrf_key in vrf_keys: - if ('vni' in vrf_table[vrf_key] and vrf_table[vrf_key]['vni'] == vni): - found = 1 - break + for vrf_key in vrf_keys: + if ('vni' in vrf_table[vrf_key] and vrf_table[vrf_key]['vni'] == vni): + found = 1 + break if (found == 1): - print("VNI {} mapped to Vrf {}, Please remove VRF VNI mapping".format( - vni, vrf_key)) + print("VNI {} mapped to Vrf {}, Please remove VRF VNI mapping".format(vni, vrf_key)) return False return True - def interface_has_mirror_config(mirror_table, interface_name): """Check if port is already configured with mirror config """ - for _, v in mirror_table.items(): + for _,v in mirror_table.items(): if 'src_port' in v and v['src_port'] == interface_name: return True if 'dst_port' in v and v['dst_port'] == interface_name: @@ -534,7 +356,6 @@ def interface_has_mirror_config(mirror_table, interface_name): return False - def print_output_in_alias_mode(output, index): """Convert and print all instances of SONiC interface name to vendor-sepecific interface aliases. @@ -560,12 +381,12 @@ def print_output_in_alias_mode(output, index): interface_name = word[index] interface_name = interface_name.replace(':', '') for port_name in natsorted(list(iface_alias_converter.port_dict.keys())): - if interface_name == port_name: - alias_name = iface_alias_converter.port_dict[port_name]['alias'] + if interface_name == port_name: + alias_name = iface_alias_converter.port_dict[port_name]['alias'] if alias_name: if len(alias_name) < iface_alias_converter.alias_max_length: alias_name = alias_name.rjust( - iface_alias_converter.alias_max_length) + iface_alias_converter.alias_max_length) output = output.replace(interface_name, alias_name, 1) click.echo(output.rstrip('\n')) @@ -595,7 +416,7 @@ def run_command_in_alias_mode(command, shell=False): index = 0 if output.startswith("IFACE"): output = output.replace("IFACE", "IFACE".rjust( - iface_alias_converter.alias_max_length)) + iface_alias_converter.alias_max_length)) print_output_in_alias_mode(output, index) elif command_str.startswith("intfstat"): @@ -603,7 +424,7 @@ def run_command_in_alias_mode(command, shell=False): index = 0 if output.startswith("IFACE"): output = output.replace("IFACE", "IFACE".rjust( - iface_alias_converter.alias_max_length)) + iface_alias_converter.alias_max_length)) print_output_in_alias_mode(output, index) elif command_str == "pfcstat": @@ -611,11 +432,11 @@ def run_command_in_alias_mode(command, shell=False): index = 0 if output.startswith("Port Tx"): output = output.replace("Port Tx", "Port Tx".rjust( - iface_alias_converter.alias_max_length)) + iface_alias_converter.alias_max_length)) elif output.startswith("Port Rx"): output = output.replace("Port Rx", "Port Rx".rjust( - iface_alias_converter.alias_max_length)) + iface_alias_converter.alias_max_length)) print_output_in_alias_mode(output, index) elif (command_str.startswith("sudo sfputil show eeprom")): @@ -630,7 +451,7 @@ def run_command_in_alias_mode(command, shell=False): index = 0 if output.startswith("Port"): output = output.replace("Port", "Port".rjust( - iface_alias_converter.alias_max_length)) + iface_alias_converter.alias_max_length)) print_output_in_alias_mode(output, index) elif command_str == "sudo lldpshow": @@ -638,7 +459,7 @@ def run_command_in_alias_mode(command, shell=False): index = 0 if output.startswith("LocalPort"): output = output.replace("LocalPort", "LocalPort".rjust( - iface_alias_converter.alias_max_length)) + iface_alias_converter.alias_max_length)) print_output_in_alias_mode(output, index) elif command_str.startswith("queuestat"): @@ -646,7 +467,7 @@ def run_command_in_alias_mode(command, shell=False): index = 0 if output.startswith("Port"): output = output.replace("Port", "Port".rjust( - iface_alias_converter.alias_max_length)) + iface_alias_converter.alias_max_length)) print_output_in_alias_mode(output, index) elif command_str == "fdbshow": @@ -655,7 +476,7 @@ def run_command_in_alias_mode(command, shell=False): if output.startswith("No."): output = " " + output output = re.sub( - 'Type', ' Type', output) + 'Type', ' Type', output) elif output[0].isdigit(): output = " " + output print_output_in_alias_mode(output, index) @@ -670,8 +491,8 @@ def run_command_in_alias_mode(command, shell=False): """Show ip(v6) int""" index = 0 if output.startswith("Interface"): - output = output.replace("Interface", "Interface".rjust( - iface_alias_converter.alias_max_length)) + output = output.replace("Interface", "Interface".rjust( + iface_alias_converter.alias_max_length)) print_output_in_alias_mode(output, index) else: @@ -684,9 +505,8 @@ def run_command_in_alias_mode(command, shell=False): converted_output = raw_output for port_name in iface_alias_converter.port_dict: converted_output = re.sub(r"(^|\s){}($|,{{0,1}}\s)".format(port_name), - r"\1{}\2".format( - iface_alias_converter.name_to_alias(port_name)), - converted_output) + r"\1{}\2".format(iface_alias_converter.name_to_alias(port_name)), + converted_output) click.echo(converted_output.rstrip('\n')) rc = process.poll() @@ -721,7 +541,7 @@ def run_command(command, display_cmd=False, ignore_error=False, return_cmd=False if get_interface_naming_mode() == "alias" and not command_str.startswith("intfutil") and not re.search("show ip|ipv6 route", command_str): run_command_in_alias_mode(command, shell=shell) sys.exit(0) - + proc = subprocess.Popen(command, shell=shell, text=True, stdout=subprocess.PIPE) if return_cmd: @@ -773,21 +593,20 @@ def interface_is_untagged_member(db, interface_name): """ Check if interface is already untagged member""" vlan_member_table = db.get_table('VLAN_MEMBER') - for key, val in vlan_member_table.items(): + for key,val in vlan_member_table.items(): if(key[1] == interface_name): if (val['tagging_mode'] == 'untagged'): return True return False - def is_interface_in_config_db(config_db, interface_name): """ Check if an interface is in CONFIG DB """ if (not interface_name in config_db.get_keys('VLAN_INTERFACE') and not interface_name in config_db.get_keys('INTERFACE') and not interface_name in config_db.get_keys('PORTCHANNEL_INTERFACE') and not interface_name in config_db.get_keys('VLAN_SUB_INTERFACE') and - not interface_name == 'null'): - return False + not interface_name == 'null'): + return False return True @@ -804,11 +623,9 @@ def __init__(self, *args, **kwargs): def get_help_record(self, ctx): """Return help string with mutually_exclusive list added.""" - help_record = list( - super(MutuallyExclusiveOption, self).get_help_record(ctx)) + help_record = list(super(MutuallyExclusiveOption, self).get_help_record(ctx)) if self.mutually_exclusive: - mutually_exclusive_str = 'NOTE: this argument is mutually exclusive with arguments: %s' % ', '.join( - self.mutually_exclusive) + mutually_exclusive_str = 'NOTE: this argument is mutually exclusive with arguments: %s' % ', '.join(self.mutually_exclusive) if help_record[-1]: help_record[-1] += ' ' + mutually_exclusive_str else: @@ -820,9 +637,8 @@ def handle_parse_result(self, ctx, opts, args): for opt_name in self.mutually_exclusive: if opt_name in opts and opts[opt_name] is not None: raise click.UsageError( - "Illegal usage: %s is mutually exclusive with arguments %s" % ( - self.name, ', '.join(self.mutually_exclusive)) - ) + "Illegal usage: %s is mutually exclusive with arguments %s" % (self.name, ', '.join(self.mutually_exclusive)) + ) return super(MutuallyExclusiveOption, self).handle_parse_result(ctx, opts, args) @@ -871,10 +687,8 @@ def __init__(self, app_name=None, tag=None): tag (str): Tag the user cache. Different tags correspond to different cache directories even for the same user. """ self.uid = os.getuid() - self.app_name = os.path.basename( - sys.argv[0]) if app_name is None else app_name - self.cache_directory_suffix = str( - self.uid) if tag is None else f"{self.uid}-{tag}" + self.app_name = os.path.basename(sys.argv[0]) if app_name is None else app_name + self.cache_directory_suffix = str(self.uid) if tag is None else f"{self.uid}-{tag}" self.cache_directory_app = os.path.join(self.CACHE_DIR, self.app_name) prev_umask = os.umask(0) @@ -883,8 +697,7 @@ def __init__(self, app_name=None, tag=None): finally: os.umask(prev_umask) - self.cache_directory = os.path.join( - self.cache_directory_app, self.cache_directory_suffix) + self.cache_directory = os.path.join(self.cache_directory_app, self.cache_directory_suffix) os.makedirs(self.cache_directory, exist_ok=True) def get_directory(self): From 0e43e4dc441dd6bb545de2c767da05fa448fbaa7 Mon Sep 17 00:00:00 2001 From: Rajkumar-Marvell <54936542+rajkumar38@users.noreply.github.com> Date: Thu, 12 Oct 2023 02:42:14 +0530 Subject: [PATCH 16/20] [sflow] Added egress Sflow support. (#2790) ### What I did Added egress Sflow support. HLD : https://github.com/sonic-net/SONiC/pull/1268 #### How I did it New commands and migrator scripts are updated as per HLD. #### How to verify it Run UT and validate the commands. #### Previous command output (if the output of a command-line utility has changed) ``` show sflow sFlow Global Information: sFlow Admin State: up sFlow Polling Interval: 0 sFlow AgentID: default 2 Collectors configured: Name: prod IP addr: fe80::6e82:6aff:fe1e:cd8e UDP port: 6343 VRF: mgmt Name: ser5 IP addr: 172.21.35.15 UDP port: 6343 VRF: default show sflow interface +-------------+---------------+-----------------+ | Interface | Admin State | Sampling Rate | +=============+===============+=================+ | Ethernet0 | up | 2500 | +-------------+---------------+-----------------+ | Ethernet4 | up | 1000 | +-------------+---------------+-----------------+ | Ethernet112 | up | 1000 | +-------------+---------------+-----------------+ | Ethernet116 | up | 5000 | +-------------+---------------+-----------------+ ```` #### New command output (if the output of a command-line utility has changed) ``` show sflow sFlow Global Information: sFlow Admin State: up sFlow Sample Direction: both sFlow Polling Interval: 0 sFlow AgentID: default 2 Collectors configured: Name: prod IP addr: fe80::6e82:6aff:fe1e:cd8e UDP port: 6343 VRF: mgmt Name: ser5 IP addr: 172.21.35.15 UDP port: 6343 VRF: default show sflow interface +-------------+---------------+-----------------+----------------------+ | Interface | Admin State | Sampling Rate | Sampling Direction | +=============+===============+=================+======================+ | Ethernet0 | up | 2500 | both | +-------------+---------------+-----------------+----------------------+ | Ethernet4 | up | 1000 | tx | +-------------+---------------+-----------------+----------------------+ | Ethernet112 | up | 1000 | both | +-------------+---------------+-----------------+----------------------+ | Ethernet116 | up | 5000 | rx | +-------------+---------------+-----------------+----------------------+ ``` --- config/main.py | 69 +++++++++++++ doc/Command-Reference.md | 60 +++++++++--- show/sflow.py | 7 +- tests/mock_tables/appl_db.json | 12 ++- tests/mock_tables/config_db.json | 1 + tests/sflow_test.py | 161 ++++++++++++++++++++++++++++--- 6 files changed, 279 insertions(+), 31 deletions(-) diff --git a/config/main.py b/config/main.py index 1d2c33605f..f336fb5818 100644 --- a/config/main.py +++ b/config/main.py @@ -6662,6 +6662,41 @@ def polling_int(ctx, interval): except ValueError as e: ctx.fail("Invalid ConfigDB. Error: {}".format(e)) +def is_port_egress_sflow_supported(): + state_db = SonicV2Connector(use_unix_socket_path=True) + state_db.connect(state_db.STATE_DB, False) + entry_name="SWITCH_CAPABILITY|switch" + supported = state_db.get(state_db.STATE_DB, entry_name,"PORT_EGRESS_SAMPLE_CAPABLE") + return supported + +# +# 'sflow' command ('config sflow sample-direction ...') +# +@sflow.command('sample-direction') +@click.argument('direction', metavar='', required=True, type=str) +@click.pass_context +def global_sample_direction(ctx, direction): + """Set sampling direction """ + if ADHOC_VALIDATION: + if direction: + if direction not in ['rx', 'tx', 'both']: + ctx.fail("Error: Direction {} is invalid".format(direction)) + + if ((direction == 'tx' or direction == 'both') and (is_port_egress_sflow_supported() == 'false')): + ctx.fail("Sample direction {} is not supported on this platform".format(direction)) + + config_db = ValidatedConfigDBConnector(ctx.obj['db']) + sflow_tbl = config_db.get_table('SFLOW') + + if not sflow_tbl: + sflow_tbl = {'global': {'admin_state': 'down'}} + + sflow_tbl['global']['sample_direction'] = direction + try: + config_db.mod_entry('SFLOW', 'global', sflow_tbl['global']) + except ValueError as e: + ctx.fail("Invalid ConfigDB. Error: {}".format(e)) + def is_valid_sample_rate(rate): return rate.isdigit() and int(rate) in range(256, 8388608 + 1) @@ -6771,6 +6806,40 @@ def sample_rate(ctx, ifname, rate): except ValueError as e: ctx.fail("Invalid ConfigDB. Error: {}".format(e)) +# +# 'sflow' command ('config sflow interface sample-direction ...') +# +@interface.command('sample-direction') +@click.argument('ifname', metavar='', required=True, type=str) +@click.argument('direction', metavar='', required=True, type=str) +@click.pass_context +def interface_sample_direction(ctx, ifname, direction): + config_db = ValidatedConfigDBConnector(ctx.obj['db']) + if ADHOC_VALIDATION: + if not interface_name_is_valid(config_db, ifname) and ifname != 'all': + click.echo('Invalid interface name') + return + if direction: + if direction not in ['rx', 'tx', 'both']: + ctx.fail("Error: Direction {} is invalid".format(direction)) + + if (direction == 'tx' or direction == 'both') and (is_port_egress_sflow_supported() == 'false'): + ctx.fail("Sample direction {} is not supported on this platform".format(direction)) + + sess_dict = config_db.get_table('SFLOW_SESSION') + + if sess_dict and ifname in sess_dict.keys(): + sess_dict[ifname]['sample_direction'] = direction + try: + config_db.mod_entry('SFLOW_SESSION', ifname, sess_dict[ifname]) + except ValueError as e: + ctx.fail("Invalid ConfigDB. Error: {}".format(e)) + else: + try: + config_db.mod_entry('SFLOW_SESSION', ifname, {'sample_direction': direction}) + except ValueError as e: + ctx.fail("Invalid ConfigDB. Error: {}".format(e)) + # # 'sflow collector' group diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 019499b0c9..35e221ddc8 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -8988,6 +8988,7 @@ This command displays the global sFlow configuration that includes the admin sta admin@sonic:~# show sflow sFlow Global Information: sFlow Admin State: up + sFlow Sample Direction: both sFlow Polling Interval: default sFlow AgentID: lo @@ -9011,24 +9012,23 @@ This command displays the per-interface sflow admin status and the sampling rate admin@sonic:~# show sflow interface sFlow interface configurations - +-------------+---------------+-----------------+ - | Interface | Admin State | Sampling Rate | - +=============+===============+=================+ - | Ethernet0 | up | 4000 | - +-------------+---------------+-----------------+ - | Ethernet1 | up | 4000 | - +-------------+---------------+-----------------+ + +-------------+---------------+-----------------+----------------------+ + | Interface | Admin State | Sampling Rate | Sampling Direction | + +=============+===============+=================+======================+ + | Ethernet0 | up | 4000 | both | + +-------------+---------------+-----------------+----------------------| + | Ethernet1 | up | 4000 | tx | + +-------------+---------------+-----------------+----------------------+ ... - +-------------+---------------+-----------------+ - | Ethernet61 | up | 4000 | - +-------------+---------------+-----------------+ - | Ethernet62 | up | 4000 | - +-------------+---------------+-----------------+ - | Ethernet63 | up | 4000 | - +-------------+---------------+-----------------+ + +-------------+---------------+-----------------+----------------------+ + | Ethernet61 | up | 4000 | rx | + +-------------+---------------+-----------------+----------------------+ + | Ethernet62 | up | 4000 | tx | + +-------------+---------------+-----------------+----------------------+ + | Ethernet63 | up | 4000 | both | + +-------------+---------------+-----------------+----------------------+ ``` - ### sFlow Config commands **config sflow collector add** @@ -9097,6 +9097,18 @@ Globally, sFlow is disabled by default. When sFlow is enabled globally, the sflo ``` admin@sonic:~# sudo config sflow enable ``` +**config sflow sample-direction** + +This command takes global sflow sample direction. If not configured, default is "rx" for backward compatibility. Based on the direction, the sFlow is enabled at all the interface level at rx or tx or both. + +- Usage: + ``` + config sflow sample-direction + ``` +- Example: + ``` + admin@sonic:~# sudo config sflow sample-direction tx + ``` **config sflow interface** Enable/disable sflow at an interface level. By default, sflow is enabled on all interfaces at the interface level. Use this command to explicitly disable sFlow for a specific interface. An interface is sampled if sflow is enabled globally as well as at the interface level. Note that this configuration deals only with sFlow flow samples and not counter samples. @@ -9114,6 +9126,24 @@ Enable/disable sflow at an interface level. By default, sflow is enabled on all admin@sonic:~# sudo config sflow interface disable Ethernet40 ``` +**config sflow interface sample-direction** + +Set sample direction to determine ingress sampling or egress sampling or both. If not configured, default is "rx". + +- Usage: + ``` + config sflow sample-direction + ``` + + - Parameters: + - interface-name: specify the interface for which sFlow flow sample-direction has to be set. The “all” keyword is used as a convenience to set sflow sample-direction at the interface level for all the interfaces. + +- Example: + ``` + admin@sonic:~# sudo config sflow interface sample-direction Ethernet40 tx + ``` +Note: The local configuration applied to an interface has higher precedence over the global configuration provided through the "all" keyword. + **config sflow interface sample-rate** Configure the sample-rate for a specific interface. diff --git a/show/sflow.py b/show/sflow.py index a84a4bdff1..0cbc8c7c70 100644 --- a/show/sflow.py +++ b/show/sflow.py @@ -45,7 +45,7 @@ def show_sflow_interface(config_db): return click.echo("\nsFlow interface configurations") - header = ['Interface', 'Admin State', 'Sampling Rate'] + header = ['Interface', 'Admin State', 'Sampling Rate', 'Sampling Direction'] body = [] for pname in natsorted(list(port_tbl.keys())): intf_key = 'SFLOW_SESSION_TABLE:' + pname @@ -56,6 +56,7 @@ def show_sflow_interface(config_db): body_info = [pname] body_info.append(sess_info.get('admin_state')) body_info.append(sess_info.get('sample_rate')) + body_info.append(sess_info.get('sample_direction')) body.append(body_info) click.echo(tabulate(body, header, tablefmt='grid')) @@ -63,11 +64,15 @@ def show_sflow_interface(config_db): def show_sflow_global(config_db): sflow_info = config_db.get_table('SFLOW') global_admin_state = 'down' + global_sample_dir = 'rx' if sflow_info: global_admin_state = sflow_info['global']['admin_state'] + if ('sample_direction' in sflow_info['global']): + global_sample_dir = sflow_info['global']['sample_direction'] click.echo("\nsFlow Global Information:") click.echo(" sFlow Admin State:".ljust(30) + "{}".format(global_admin_state)) + click.echo(" sFlow Sample Direction:".ljust(30) + "{}".format(global_sample_dir)) click.echo(" sFlow Polling Interval:".ljust(30), nl=False) if (sflow_info and 'polling_interval' in sflow_info['global']): diff --git a/tests/mock_tables/appl_db.json b/tests/mock_tables/appl_db.json index 2c3d14da1e..2889e6b202 100644 --- a/tests/mock_tables/appl_db.json +++ b/tests/mock_tables/appl_db.json @@ -1,19 +1,23 @@ { "SFLOW_SESSION_TABLE:Ethernet0": { "admin_state": "up", - "sample_rate": "2500" + "sample_rate": "2500", + "sample_direction": "both" }, "SFLOW_SESSION_TABLE:Ethernet4": { "admin_state": "up", - "sample_rate": "1000" + "sample_rate": "1000", + "sample_direction": "tx" }, "SFLOW_SESSION_TABLE:Ethernet112": { "admin_state": "up", - "sample_rate": "1000" + "sample_rate": "1000", + "sample_direction": "both" }, "SFLOW_SESSION_TABLE:Ethernet116": { "admin_state": "up", - "sample_rate": "5000" + "sample_rate": "5000", + "sample_direction": "rx" }, "SFLOW_SESSION_TABLE:Ethernet8": { "BAD": "BAD" diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index e58119d905..24a250536a 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -1,6 +1,7 @@ { "SFLOW|global": { "admin_state": "up", + "sample_direction": "both", "polling_interval": "0" }, "SFLOW_COLLECTOR|prod": { diff --git a/tests/sflow_test.py b/tests/sflow_test.py index ecd622655e..4ee298dfca 100644 --- a/tests/sflow_test.py +++ b/tests/sflow_test.py @@ -18,6 +18,7 @@ show_sflow_output = """ sFlow Global Information: sFlow Admin State: up + sFlow Sample Direction: both sFlow Polling Interval: 0 sFlow AgentID: default @@ -29,17 +30,17 @@ # Expected output for 'show sflow interface' show_sflow_intf_output = """ sFlow interface configurations -+-------------+---------------+-----------------+ -| Interface | Admin State | Sampling Rate | -+=============+===============+=================+ -| Ethernet0 | up | 2500 | -+-------------+---------------+-----------------+ -| Ethernet4 | up | 1000 | -+-------------+---------------+-----------------+ -| Ethernet112 | up | 1000 | -+-------------+---------------+-----------------+ -| Ethernet116 | up | 5000 | -+-------------+---------------+-----------------+ ++-------------+---------------+-----------------+----------------------+ +| Interface | Admin State | Sampling Rate | Sampling Direction | ++=============+===============+=================+======================+ +| Ethernet0 | up | 2500 | both | ++-------------+---------------+-----------------+----------------------+ +| Ethernet4 | up | 1000 | tx | ++-------------+---------------+-----------------+----------------------+ +| Ethernet112 | up | 1000 | both | ++-------------+---------------+-----------------+----------------------+ +| Ethernet116 | up | 5000 | rx | ++-------------+---------------+-----------------+----------------------+ """ class TestShowSflow(object): @@ -425,6 +426,144 @@ def test_config_sflow_intf_sample_rate_default(self): return + def test_config_sflow_sample_direction(self): + # config sflow sample-direction + db = Db() + runner = CliRunner() + obj = {'db':db.cfgdb} + + #Sample direction : Invalid + result = runner.invoke(config.config.commands["sflow"].commands["sample-direction"], ["NA"], obj=obj) + print(result.output) + expected = "Error: Direction NA is invalid" + assert expected in result.output + + #disable + result = runner.invoke(config.config.commands["sflow"].commands["sample-direction"], ["tx"], obj=obj) + print(result.exit_code, result.output) + assert result.exit_code == 0 + + # change the output + global show_sflow_output + show_sflow_output_tx = show_sflow_output.replace( + 'Sample Direction: both', + 'Sample Direction: tx') + + show_sflow_output_rx = show_sflow_output.replace( + 'Sample Direction: both', + 'Sample Direction: rx') + + # run show and check + result = runner.invoke(show.cli.commands["sflow"], [], obj=db) + print(result.exit_code, result.output, show_sflow_output_tx) + assert result.exit_code == 0 + assert result.output == show_sflow_output_tx + + # sample-direction rx + with mock.patch("utilities_common.cli.run_command", mock.MagicMock()) as mock_run_command: + result = runner.invoke(config.config.commands["sflow"].commands["sample-direction"], ["rx"], obj=obj) + print(result.exit_code, result.output) + assert result.exit_code == 0 + + # run show and check + result = runner.invoke(show.cli.commands["sflow"], [], obj=db) + print(result.exit_code, result.output) + assert result.exit_code == 0 + assert result.output == show_sflow_output_rx + + return + + def test_config_all_intf_sample_direction(self): + db = Db() + runner = CliRunner() + obj = {'db':db.cfgdb} + + # set all direction to both + result = runner.invoke(config.config.commands["sflow"]. + commands["interface"].commands["sample-direction"], ["all","both"], obj=obj) + print(result.exit_code, result.output) + assert result.exit_code == 0 + + # verify in configDb + sflowSession = db.cfgdb.get_table('SFLOW_SESSION') + assert sflowSession["all"]["sample_direction"] == "both" + + # set all direction to tx + result = runner.invoke(config.config.commands["sflow"]. + commands["interface"].commands["sample-direction"], ["all","tx"], obj=obj) + print(result.exit_code, result.output) + assert result.exit_code == 0 + + # verify in configDb + sflowSession = db.cfgdb.get_table('SFLOW_SESSION') + assert sflowSession["all"]["sample_direction"] == "tx" + + # set all direction to rx + result = runner.invoke(config.config.commands["sflow"]. + commands["interface"].commands["sample-direction"], ["all","rx"], obj=obj) + print(result.exit_code, result.output) + assert result.exit_code == 0 + + # verify in configDb + sflowSession = db.cfgdb.get_table('SFLOW_SESSION') + assert sflowSession["all"]["sample_direction"] == "rx" + + return + + def test_config_sflow_intf_sample_direction(self): + db = Db() + runner = CliRunner() + obj = {'db':db.cfgdb} + + # mock interface_name_is_valid + config.interface_name_is_valid = mock.MagicMock(return_value = True) + + # set sample-direction to Invalid + result = runner.invoke(config.config.commands["sflow"]. + commands["interface"].commands["sample-direction"], + ["Ethernet2", "NA"], obj=obj) + print(result.exit_code, result.output) + expected = "Error: Direction NA is invalid" + assert result.exit_code == 2 + assert expected in result.output + + # set sample-direction to both + result = runner.invoke(config.config.commands["sflow"]. + commands["interface"].commands["sample-direction"], + ["Ethernet2", "both"], obj=obj) + print(result.exit_code, result.output) + assert result.exit_code == 0 + + # we can not use 'show sflow interface', becasue 'show sflow interface' + # gets data from appDB, we need to fetch data from configDB for verification + sflowSession = db.cfgdb.get_table('SFLOW_SESSION') + assert sflowSession["Ethernet2"]["sample_direction"] == "both" + + # set sample-direction to tx + result = runner.invoke(config.config.commands["sflow"]. + commands["interface"].commands["sample-direction"], + ["Ethernet2", "tx"], obj=obj) + print(result.exit_code, result.output) + assert result.exit_code == 0 + + # we can not use 'show sflow interface', becasue 'show sflow interface' + # gets data from appDB, we need to fetch data from configDB for verification + sflowSession = db.cfgdb.get_table('SFLOW_SESSION') + assert sflowSession["Ethernet2"]["sample_direction"] == "tx" + + # set sample-direction to rx + result = runner.invoke(config.config.commands["sflow"]. + commands["interface"].commands["sample-direction"], + ["Ethernet2", "rx"], obj=obj) + print(result.exit_code, result.output) + assert result.exit_code == 0 + + # we can not use 'show sflow interface', becasue 'show sflow interface' + # gets data from appDB, we need to fetch data from configDB for verification + sflowSession = db.cfgdb.get_table('SFLOW_SESSION') + assert sflowSession["Ethernet2"]["sample_direction"] == "rx" + + return @classmethod def teardown_class(cls): From bf9c07c4f57989cf9811ae596d59ec61a5c607ae Mon Sep 17 00:00:00 2001 From: Anoop Kamath <115578705+AnoopKamath@users.noreply.github.com> Date: Thu, 12 Oct 2023 12:31:54 -0700 Subject: [PATCH 17/20] Add target mode to sfputil firmware (#3002) * Add target mode to sfputil firmware * Remove duplicate code * Address review comments 1. Add int range to CLI arg 2. update port name to error messages * Add UT cases for sfputil * Update sfputil_test.py * Update Command-Reference.md Add CMIS firmware upgrade command * Update Command-Reference.md * Update main.py * Update Command-Reference.md * Update Command-Reference.md --- doc/Command-Reference.md | 137 +++++++++++++++++++++++++++++++++++++++ sfputil/main.py | 37 +++++++++++ tests/sfputil_test.py | 33 ++++++++++ 3 files changed, 207 insertions(+) diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 35e221ddc8..6784b8faf6 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -39,6 +39,10 @@ * [Console config commands](#console-config-commands) * [Console connect commands](#console-connect-commands) * [Console clear commands](#console-clear-commands) +* [CMIS firmware upgrade](#cmis-firmware-upgrade) + * [CMIS firmware version show commands](#cmis-firmware-version-show-commands) + * [CMIS firmware upgrade commands](#cmis-firmware-upgrade-commands) + * [CMIS firmware target mode commands](#cmis-firmware-target-mode-commands) * [DHCP Relay](#dhcp-relay) * [DHCP Relay show commands](#dhcp-relay-show-commands) * [DHCP Relay clear commands](#dhcp-relay-clear-commands) @@ -206,6 +210,7 @@ | Version | Modification Date | Details | | --- | --- | --- | +| v8 | Oct-09-2023 | Add CMIS firmware upgrade commands | | v7 | Jun-22-2023 | Add static DNS show and config commands | | v6 | May-06-2021 | Add SNMP show and config commands | | v5 | Nov-05-2020 | Add document for console commands | @@ -2785,6 +2790,138 @@ Optionally, you can clear with a remote device name by specifying the `-d` or `- Go Back To [Beginning of the document](#) or [Beginning of this section](#console) +## CMIS firmware upgrade + +### CMIS firmware version show commands + +The sfputil command shows the current major and minor versions of active/inactive firmware, running Image details. The output may vary based on the single vs dual bank supported modules. + +**sfputil show fwversion** + +- Usage: + ``` + sfputil show fwversion PORT_NAME + ``` + +- Example: + ``` + admin@sonic:~$ sfputil show fwversion Ethernet180 + Image A Version: 0.3.5 + Image B Version: 0.3.5 + Factory Image Version: 0.0.0 + Running Image: A + Committed Image: A + Active Firmware: 0.3.5 + Inactive Firmware: 0.3.5 + ``` + +### CMIS firmware upgrade commands + +The sfputil commands are used to download/upgrade firmware on transciver modules. The download/upgrade actually happens using set of CMIS CDB commands. The module may replace the exisiting image or copy into the inactive bank of the module. The host issues a download complete CDB command when the entire firmware image has been written to LPL or EPL pages. Each steps can be verified using the 'sfputil show fwversion PORT_NAME' + +**sfputil firmware download** + +This command is used for downloading firmware tp upgrade the transciever module. + +- Usage: + ``` + sfputil firmware download PORT_NAME FILE_PATH + ``` + +- Example: + ``` + admin@sonic:~$ sfputil firmware download Ethernet180 AEC_Camano_YCable__0.3.6_20230905.bin + CDB: Starting firmware download + Downloading ... [####################################] 100% + CDB: firmware download complete + Firmware download complete success + Total download Time: 0:01:55.731397 + + admin@sonic:~$ sfputil show fwversion Ethernet180 + Image A Version: 0.3.5 + Image B Version: 0.3.6 + Factory Image Version: 0.0.0 + Running Image: A + Committed Image: A + Active Firmware: 0.3.5 + Inactive Firmware: 0.3.6 + ``` +**sfputil firmware run** + +This command is used to start and run a downloaded image. This command transfers control from the currently running firmware to a new firmware. + +- Usage: + ``` + sfputil firmware run PORT_NAME + ``` + +- Example: + ``` + admin@sonic:~$ sfputil firmware run Ethernet180 + Running firmware: Non-hitless Reset to Inactive Image + Firmware run in mode=0 success + + admin@sonic:~$ sfputil show fwversion Ethernet180 + Image A Version: 0.3.5 + Image B Version: 0.3.6 + Factory Image Version: 0.0.0 + Running Image: B + Committed Image: A + Active Firmware: 0.3.6 + Inactive Firmware: 0.3.5 + ``` + +**sfputil firmware commit** + +This command to commit the running image so that the module will boot from it on future boots. + +- Usage: + ``` + sfputil firmware commit PORT_NAME + ``` + +- Example: + ``` + admin@sonic:~$ sfputil firmware commit Ethernet180 + Firmware commit successful + + admin@sonic:~$ sfputil show fwversion Ethernet180 + Image A Version: 0.3.5 + Image B Version: 0.3.6 + Factory Image Version: 0.0.0 + Running Image: B + Committed Image: B + Active Firmware: 0.3.6 + Inactive Firmware: 0.3.5 + ``` + +### CMIS firmware target mode commands + +This command is vendor-specific and supported on the modules to set the target mode to perform remote firmware upgrades. The target modes can be set as 0 (local- E0), 1 (remote end E1), or 2 (remote end E2). Depending on the mode set, the remote or local end will respond to CDB/I2C commands from host's E0 end. After setting the target mode, we can use **sfputil** firmware upgrade commands, will be executed on the module for which target mode is set. + +Example of the module supporting target mode + +![RMT_UPGRD](https://github.com/AnoopKamath/sonic-utilities_remote_upgrade/assets/115578705/c3b0bb62-eb14-4b05-b0a8-96b8c082455a) + +**sfputil firmware target** + +- Usage: + ``` + sfputil firmware target [OPTIONS] PORT_NAME TARGET + + Select target end for firmware download + 0-(local) + + 1-(remote-A) + + 2-(remote-B) + ``` + +- Example: + ``` + admin@sonic:~$ sfputil firmware target Ethernet180 1 + Target Mode set to 1 + ``` ## DHCP Relay diff --git a/sfputil/main.py b/sfputil/main.py index af16ddcff5..454287cae2 100644 --- a/sfputil/main.py +++ b/sfputil/main.py @@ -1526,6 +1526,43 @@ def version(): """Display version info""" click.echo("sfputil version {0}".format(VERSION)) +# 'target' subcommand +@firmware.command() +@click.argument('port_name', required=True, default=None) +@click.argument('target', type=click.IntRange(0, 2), required=True, default=None) +def target(port_name, target): + """Select target end for firmware download 0-(local) \n + 1-(remote-A) \n + 2-(remote-B) + """ + physical_port = logical_port_to_physical_port_index(port_name) + sfp = platform_chassis.get_sfp(physical_port) + + if is_port_type_rj45(port_name): + click.echo("{}: This functionality is not applicable for RJ45 port".format(port_name)) + sys.exit(EXIT_FAIL) + + if not is_sfp_present(port_name): + click.echo("{}: SFP EEPROM not detected\n".format(port_name)) + sys.exit(EXIT_FAIL) + + try: + api = sfp.get_xcvr_api() + except NotImplementedError: + click.echo("{}: This functionality is currently not implemented for this module".format(port_name)) + sys.exit(ERROR_NOT_IMPLEMENTED) + + try: + status = api.set_firmware_download_target_end(target) + except AttributeError: + click.echo("{}: This functionality is not applicable for this module".format(port_name)) + sys.exit(ERROR_NOT_IMPLEMENTED) + + if status: + click.echo("Target Mode set to {}". format(target)) + else: + click.echo("Target Mode set failed!") + sys.exit(EXIT_FAIL) if __name__ == '__main__': cli() diff --git a/tests/sfputil_test.py b/tests/sfputil_test.py index 0de4a46026..bed8e994d7 100644 --- a/tests/sfputil_test.py +++ b/tests/sfputil_test.py @@ -967,3 +967,36 @@ def test_update_firmware_info_to_state_db(self, mock_chassis): mock_sfp.get_transceiver_info_firmware_versions.return_value = ['a.b.c', 'd.e.f'] sfputil.update_firmware_info_to_state_db("Ethernet0") + + @patch('sfputil.main.platform_chassis') + @patch('sfputil.main.logical_port_to_physical_port_index', MagicMock(return_value=1)) + def test_target_firmware(self, mock_chassis): + mock_sfp = MagicMock() + mock_api = MagicMock() + mock_sfp.get_xcvr_api = MagicMock(return_value=mock_api) + mock_sfp.get_presence.return_value = True + mock_chassis.get_sfp = MagicMock(return_value=mock_sfp) + mock_api.set_firmware_download_target_end.return_value = 1 + runner = CliRunner() + result = runner.invoke(sfputil.cli.commands['firmware'].commands['target'], ["Ethernet0", "2"]) + assert result.output == 'Target Mode set to 2\n' + assert result.exit_code == 0 + + mock_sfp.get_presence.return_value = False + result = runner.invoke(sfputil.cli.commands['firmware'].commands['target'], ["Ethernet0", "2"]) + assert result.output == 'Ethernet0: SFP EEPROM not detected\n\n' + + mock_sfp.get_presence.return_value = True + mock_sfp.get_xcvr_api = MagicMock(side_effect=NotImplementedError) + result = runner.invoke(sfputil.cli.commands['firmware'].commands['target'], ["Ethernet0", "2"]) + assert result.output == 'Ethernet0: This functionality is currently not implemented for this module\n' + assert result.exit_code == ERROR_NOT_IMPLEMENTED + + mock_sfp.get_xcvr_api = MagicMock(return_value=mock_api) + mock_sfp.get_presence.return_value = True + mock_api.set_firmware_download_target_end.return_value = 0 + result = runner.invoke(sfputil.cli.commands['firmware'].commands['target'], ["Ethernet0", "1"]) + assert result.output == 'Target Mode set failed!\n' + assert result.exit_code == EXIT_FAIL + + From 424be9ca9f950f0d04996e291f5b3dac1fb02346 Mon Sep 17 00:00:00 2001 From: Kebo Liu Date: Thu, 19 Oct 2023 12:29:51 +0800 Subject: [PATCH 18/20] [fwutil] Fix python SyntaxWarning for 'is' with literals (#3013) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix python SyntaxWarning for ‘is’ with literals Signed-off-by: Kebo Liu * add unit test Signed-off-by: Kebo Liu --------- Signed-off-by: Kebo Liu --- fwutil/lib.py | 2 +- tests/fwutil_test.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/fwutil/lib.py b/fwutil/lib.py index d4f686bec1..95cced330e 100755 --- a/fwutil/lib.py +++ b/fwutil/lib.py @@ -899,7 +899,7 @@ def is_capable_auto_update(self, boot): if status_file is not None: data = self.read_au_status_file_if_exists(FW_AU_STATUS_FILE_PATH) if data is not None: - if boot is "none" or boot in data: + if boot == "none" or boot in data: click.echo("Allow firmware auto-update with boot_type {} again".format(boot)) return True diff --git a/tests/fwutil_test.py b/tests/fwutil_test.py index e7e192ee21..5dd68348b4 100644 --- a/tests/fwutil_test.py +++ b/tests/fwutil_test.py @@ -76,3 +76,21 @@ def test_unmount_next_image_fs(self, mock_check_call, mock_exists, mock_rmdir): def teardown(self): print('TEARDOWN') + + +class TestComponentUpdateProvider(object): + def setup(self): + print('SETUP') + + @patch("glob.glob", MagicMock(side_effect=[[], ['abc'], [], ['abc']])) + @patch("fwutil.lib.ComponentUpdateProvider.read_au_status_file_if_exists", MagicMock(return_value=['def'])) + @patch("fwutil.lib.ComponentUpdateProvider._ComponentUpdateProvider__validate_platform_schema", MagicMock()) + @patch("fwutil.lib.PlatformComponentsParser.parse_platform_components", MagicMock()) + @patch("os.mkdir", MagicMock()) + def test_is_capable_auto_update(self): + CUProvider = fwutil_lib.ComponentUpdateProvider() + assert CUProvider.is_capable_auto_update('none') == True + assert CUProvider.is_capable_auto_update('def') == True + + def teardown(self): + print('TEARDOWN') From d857eb0978d94bf24c5a32f5d0d3e679ddf94202 Mon Sep 17 00:00:00 2001 From: Vivek Date: Thu, 19 Oct 2023 09:22:09 -0700 Subject: [PATCH 19/20] [db_migrator] Fix the broken version chain (#3014) Signed-off-by: Vivek Reddy Karri Why I did Warm Reboot from 202205 -> latest labels is failing because of an extra version that was added to 202205 and that was missing in later branches. How I did it Add a placeholder for version 3_0_7 in latest labels How to verify it WR from 202205 to 202305 and try running db_migrator --- scripts/db_migrator.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/db_migrator.py b/scripts/db_migrator.py index 00f2d7cc0a..a4240eac4d 100755 --- a/scripts/db_migrator.py +++ b/scripts/db_migrator.py @@ -984,10 +984,19 @@ def version_3_0_5(self): def version_3_0_6(self): """ Version 3_0_6 - This is the latest version for 202211 branch """ log.log_info('Handling version_3_0_6') + self.set_version('version_3_0_7') + return 'version_3_0_7' + + def version_3_0_7(self): + """ + Version 3_0_7 + This is the latest version for 202205 branch + """ + + log.log_info('Handling version_3_0_7') self.set_version('version_4_0_0') return 'version_4_0_0' From 244ad2d63d0bb7d4e2e3928e53f8574f010c8d24 Mon Sep 17 00:00:00 2001 From: jingwenxie Date: Fri, 20 Oct 2023 09:09:42 +0800 Subject: [PATCH 20/20] Revert "Remove syslog service validator in GCU (#2991)" (#3015) This reverts commit 6ba6ddf6d559e7f0c6f3cfd3d9918eeab9ab3b52. --- .../gcu_services_validator.conf.json | 6 ++++++ generic_config_updater/services_validator.py | 8 ++++++++ .../service_validator_test.py | 18 +++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/generic_config_updater/gcu_services_validator.conf.json b/generic_config_updater/gcu_services_validator.conf.json index b504cf5d26..852b587286 100644 --- a/generic_config_updater/gcu_services_validator.conf.json +++ b/generic_config_updater/gcu_services_validator.conf.json @@ -31,6 +31,9 @@ "PORT": { "services_to_validate": [ "port_service" ] }, + "SYSLOG_SERVER":{ + "services_to_validate": [ "rsyslog" ] + }, "DHCP_RELAY": { "services_to_validate": [ "dhcp-relay" ] }, @@ -57,6 +60,9 @@ "port_service": { "validate_commands": [ ] }, + "rsyslog": { + "validate_commands": [ "generic_config_updater.services_validator.rsyslog_validator" ] + }, "dhcp-relay": { "validate_commands": [ "generic_config_updater.services_validator.dhcp_validator" ] }, diff --git a/generic_config_updater/services_validator.py b/generic_config_updater/services_validator.py index 29a887da89..497cb4ee74 100644 --- a/generic_config_updater/services_validator.py +++ b/generic_config_updater/services_validator.py @@ -48,6 +48,14 @@ def _service_restart(svc_name): return rc == 0 +def rsyslog_validator(old_config, upd_config, keys): + rc = os.system("/usr/bin/rsyslog-config.sh") + if rc != 0: + return _service_restart("rsyslog") + else: + return True + + def dhcp_validator(old_config, upd_config, keys): return _service_restart("dhcp_relay") diff --git a/tests/generic_config_updater/service_validator_test.py b/tests/generic_config_updater/service_validator_test.py index 802bc2dc15..f14a3ad7b0 100644 --- a/tests/generic_config_updater/service_validator_test.py +++ b/tests/generic_config_updater/service_validator_test.py @@ -6,7 +6,7 @@ from collections import defaultdict from unittest.mock import patch -from generic_config_updater.services_validator import vlan_validator, caclmgrd_validator, vlanintf_validator +from generic_config_updater.services_validator import vlan_validator, rsyslog_validator, caclmgrd_validator, vlanintf_validator import generic_config_updater.gu_common @@ -142,6 +142,16 @@ def mock_time_sleep_call(sleep_time): }, ] +test_rsyslog_fail = [ + # Fail the calls, to get the entire fail path calls invoked + # + { "cmd": "/usr/bin/rsyslog-config.sh", "rc": 1 }, # config update; fails + { "cmd": "systemctl restart rsyslog", "rc": 1 }, # rsyslog restart; fails + { "cmd": "systemctl reset-failed rsyslog", "rc": 1 }, # reset; failure here just logs + { "cmd": "systemctl restart rsyslog", "rc": 1 }, # restart again; fails + { "cmd": "systemctl restart rsyslog", "rc": 1 }, # restart again; fails + ] + test_vlanintf_data = [ { "old": {}, "upd": {}, "cmd": "" }, { @@ -201,6 +211,12 @@ def test_change_apply_os_system(self, mock_os_sys): # Test failure case # + os_system_calls = test_rsyslog_fail + os_system_call_index = 0 + + rc = rsyslog_validator("", "", "") + assert not rc, "rsyslog_validator expected to fail" + os_system_calls = [] os_system_call_index = 0 for entry in test_vlanintf_data: