From 83d14187473bfecd4a939911e95365a0c69a7eb5 Mon Sep 17 00:00:00 2001 From: judyjoseph <53951155+judyjoseph@users.noreply.github.com> Date: Thu, 27 Apr 2023 08:51:28 -0700 Subject: [PATCH] [macsec]: show macsec: add --profile option, include profile name in show command output (#13940) This PR is to add the following Add a new options "--profile" to the show macsec command, to show all profiles in device Update the currentl show macsec command, to show profile in each interface o/p. This will tell which macsec profile the interface is attached to. --- .../cli-plugin-tests/config_db.json | 75 +++++++++++++ .../cli-plugin-tests/test_show_macsec.py | 4 + .../cli/show/plugins/show_macsec.py | 104 +++++++++++++++--- 3 files changed, 168 insertions(+), 15 deletions(-) create mode 100644 dockers/docker-macsec/cli-plugin-tests/config_db.json diff --git a/dockers/docker-macsec/cli-plugin-tests/config_db.json b/dockers/docker-macsec/cli-plugin-tests/config_db.json new file mode 100644 index 000000000000..8c6b6893e560 --- /dev/null +++ b/dockers/docker-macsec/cli-plugin-tests/config_db.json @@ -0,0 +1,75 @@ +{ + "MACSEC_PROFILE|macsec_profile": { + "cipher_suite": "GCM-AES-XPN-256", + "policy": "security", + "primary_cak": "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF", + "primary_ckn": "6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435", + "priority": "0", + "rekey_period": "900", + "send_sci": "true" + }, + "PORT|Ethernet0": { + "alias": "Ethernet1/1", + "asic_port_name": "Eth0-ASIC0", + "coreid": "1", + "coreportid": "1", + "description": "Ethernet1/1", + "index": "1", + "lanes": "72,73,74,75,76,77,78,79", + "macsec": "macsec_profile", + "mtu": "9100", + "numvoq": "8", + "pfc_asym": "off", + "role": "Ext", + "speed": "400000", + "tpid": "0x8100" + }, + "PORT|Ethernet1": { + "alias": "Ethernet2/1", + "asic_port_name": "Eth0-ASIC0", + "coreid": "1", + "coreportid": "1", + "description": "Ethernet2/1", + "index": "1", + "lanes": "72,73,74,75,76,77,78,79", + "macsec": "macsec_profile", + "mtu": "9100", + "numvoq": "8", + "pfc_asym": "off", + "role": "Ext", + "speed": "400000", + "tpid": "0x8100" + }, + "PORT|Ethernet4": { + "alias": "Ethernet5/1", + "asic_port_name": "Eth0-ASIC0", + "coreid": "1", + "coreportid": "1", + "description": "Ethernet5/1", + "index": "1", + "lanes": "72,73,74,75,76,77,78,79", + "macsec": "macsec_profile", + "mtu": "9100", + "numvoq": "8", + "pfc_asym": "off", + "role": "Ext", + "speed": "400000", + "tpid": "0x8100" + }, + "PORT|Ethernet5": { + "alias": "Ethernet6/1", + "asic_port_name": "Eth0-ASIC0", + "coreid": "1", + "coreportid": "1", + "description": "Ethernet6/1", + "index": "1", + "lanes": "72,73,74,75,76,77,78,79", + "macsec": "macsec_profile", + "mtu": "9100", + "numvoq": "8", + "pfc_asym": "off", + "role": "Ext", + "speed": "400000", + "tpid": "0x8100" + } +} diff --git a/dockers/docker-macsec/cli-plugin-tests/test_show_macsec.py b/dockers/docker-macsec/cli-plugin-tests/test_show_macsec.py index 39b3a39ea7a9..2345c55b0627 100644 --- a/dockers/docker-macsec/cli-plugin-tests/test_show_macsec.py +++ b/dockers/docker-macsec/cli-plugin-tests/test_show_macsec.py @@ -23,3 +23,7 @@ def test_show_one_port(self): result = runner.invoke(show_macsec.macsec,["Ethernet1"]) assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info) + def test_show_profile(self): + runner = CliRunner() + result = runner.invoke(show_macsec.macsec,["--profile"]) + assert result.exit_code == 0, "exit code: {}, Exception: {}, Traceback: {}".format(result.exit_code, result.exception, result.exc_info) diff --git a/dockers/docker-macsec/cli/show/plugins/show_macsec.py b/dockers/docker-macsec/cli/show/plugins/show_macsec.py index 9789b32bac6b..f8a1265b07e9 100644 --- a/dockers/docker-macsec/cli/show/plugins/show_macsec.py +++ b/dockers/docker-macsec/cli/show/plugins/show_macsec.py @@ -18,6 +18,17 @@ DB_CONNECTOR = None COUNTER_TABLE = None +class MACsecCfgMeta(object): + def __init__(self, *args) -> None: + SEPARATOR = DB_CONNECTOR.get_db_separator(DB_CONNECTOR.CONFIG_DB) + self.key = self.__class__.get_cfg_table_name() + SEPARATOR + \ + SEPARATOR.join(args) + self.cfgMeta = DB_CONNECTOR.get_all( + DB_CONNECTOR.CONFIG_DB, self.key) + if len(self.cfgMeta) == 0: + raise ValueError("No such MACsecCfgMeta: {}".format(self.key)) + for k, v in self.cfgMeta.items(): + setattr(self, k, v) class MACsecAppMeta(object): def __init__(self, *args) -> None: @@ -126,23 +137,55 @@ def get_header(self): return "MACsec Egress SC ({})\n".format(self.sci) -class MACsecPort(MACsecAppMeta): +class MACsecPort(MACsecAppMeta, MACsecCfgMeta): def __init__(self, port_name: str) -> None: self.port_name = port_name - super(MACsecPort, self).__init__(port_name) + MACsecAppMeta.__init__(self, port_name) + MACsecCfgMeta.__init__(self, port_name) @classmethod def get_appl_table_name(cls) -> str: return "MACSEC_PORT_TABLE" + @classmethod + def get_cfg_table_name(cls) -> str: + return "PORT" + def dump_str(self, cache = None) -> str: buffer = self.get_header() + + # Add the profile information to the meta dict from config meta dict + self.meta["profile"] = self.cfgMeta["macsec"] + buffer += tabulate(sorted(self.meta.items(), key=lambda x: x[0])) return buffer def get_header(self) -> str: return "MACsec port({})\n".format(self.port_name) +class MACsecProfile(MACsecCfgMeta): + def __init__(self, profile_name: str) -> None: + self.profile_name = profile_name + super(MACsecProfile, self).__init__(profile_name) + + @classmethod + def get_cfg_table_name(cls) -> str: + return "MACSEC_PROFILE" + + def dump_str(self, cache = None) -> str: + buffer = self.get_header() + + # Don't display the primary and fallback CAK + if 'primary_cak' in self.cfgMeta: del self.cfgMeta['primary_cak'] + if 'fallback_cak' in self.cfgMeta: del self.cfgMeta['fallback_cak'] + + t_buffer = tabulate(sorted(self.cfgMeta.items(), key=lambda x: x[0])) + t_buffer = "\n".join(["\t" + line for line in t_buffer.splitlines()]) + buffer += t_buffer + return buffer + + def get_header(self) -> str: + return "MACsec profile : {}\n".format(self.profile_name) def create_macsec_obj(key: str) -> MACsecAppMeta: attr = key.split(":") @@ -161,6 +204,14 @@ def create_macsec_obj(key: str) -> MACsecAppMeta: except ValueError as e: return None +def create_macsec_profile_obj(key: str) -> MACsecCfgMeta: + attr = key.split("|") + try: + if attr[0] == MACsecProfile.get_cfg_table_name(): + return MACsecProfile(attr[1]) + raise TypeError("Unknown MACsec object type") + except ValueError as e: + return None def create_macsec_objs(interface_name: str) -> typing.List[MACsecAppMeta]: objs = [] @@ -192,6 +243,12 @@ def create_macsec_objs(interface_name: str) -> typing.List[MACsecAppMeta]: return objs +def create_macsec_profiles_objs(profile_name: str) -> typing.List[MACsecCfgMeta]: + objs = [] + objs.append(create_macsec_profile_obj(MACsecProfile.get_cfg_table_name() + "|" + profile_name)) + return objs + + def cache_find(cache: dict, target: MACsecAppMeta) -> MACsecAppMeta: if not cache or not cache["objs"]: return None @@ -207,10 +264,14 @@ def cache_find(cache: dict, target: MACsecAppMeta) -> MACsecAppMeta: @click.command() @click.argument('interface_name', required=False) -@click.option('--dump-file', is_flag=True, required=False, default=False) +@click.option('--profile', is_flag=True, required=False, default=False, help="show all macsec profiles") +@click.option('--dump-file', is_flag=True, required=False, default=False, help="store show output to a file") @multi_asic_util.multi_asic_click_options -def macsec(interface_name, dump_file, namespace, display): - MacsecContext(namespace, display).show(interface_name, dump_file) +def macsec(interface_name, dump_file, namespace, display, profile): + if interface_name is not None and profile: + click.echo('Interface name is not valid with profile option') + return + MacsecContext(namespace, display).show(interface_name, dump_file, profile) class MacsecContext(object): @@ -218,23 +279,36 @@ def __init__(self, namespace_option, display_option): self.db = None self.multi_asic = multi_asic_util.MultiAsic( display_option, namespace_option) + self.macsec_profiles = [] @multi_asic_util.run_on_multi_asic - def show(self, interface_name, dump_file): + def show(self, interface_name, dump_file, profile): global DB_CONNECTOR global COUNTER_TABLE DB_CONNECTOR = self.db - COUNTER_TABLE = CounterTable(self.db.get_redis_client(self.db.COUNTERS_DB)) - interface_names = [name.split(":")[1] for name in self.db.keys(self.db.APPL_DB, "MACSEC_PORT*")] - if interface_name is not None: - if interface_name not in interface_names: - return - interface_names = [interface_name] - objs = [] + if not profile: + COUNTER_TABLE = CounterTable(self.db.get_redis_client(self.db.COUNTERS_DB)) + + interface_names = [name.split(":")[1] for name in self.db.keys(self.db.APPL_DB, "MACSEC_PORT*")] + if interface_name is not None: + if interface_name not in interface_names: + return + interface_names = [interface_name] + objs = [] - for interface_name in natsorted(interface_names): - objs += create_macsec_objs(interface_name) + for interface_name in natsorted(interface_names): + objs += create_macsec_objs(interface_name) + else: + profile_names = [name.split("|")[1] for name in self.db.keys(self.db.CONFIG_DB, "MACSEC_PROFILE*")] + objs = [] + + for profile_name in natsorted(profile_names): + # Check if this macsec profile is already added to profile list. This is in case of + # multi-asic devices where all namespaces will have the same macsec profile defined. + if profile_name not in self.macsec_profiles and not dump_file: + self.macsec_profiles.append(profile_name) + objs += create_macsec_profiles_objs(profile_name) cache = {} if os.path.isfile(CACHE_FILE.format(self.multi_asic.current_namespace)):