[macsec]: Add MACsec clear CLI support (#11731)

Why I did it
To support clear MACsec counters by sonic-clear macsec

How I did it
Add macsec sub-command in sonic-clear to cache the current macsec stats, and in the show macsec command to check the cache and return the diff with cache file.

How to verify it

admin@vlab-02:~$ show macsec  Ethernet0
MACsec port(Ethernet0)
---------------------  -----------
cipher_suite           GCM-AES-128
enable                 true
enable_encrypt         true
enable_protect         true
enable_replay_protect  false
replay_window          0
send_sci               true
---------------------  -----------
        MACsec Egress SC (52540067daa70001)
        -----------  -
        encoding_an  0
        -----------  -
                MACsec Egress SA (0)
                -------------------------------------  --------------------------------
                auth_key                               9DDD4C69220A1FA9B6763F229B75CB6F
                next_pn                                1
                sak                                    BA86574D054FCF48B9CD7CF54F21304A
                salt                                   000000000000000000000000
                ssci                                   0
                SAI_MACSEC_SA_ATTR_CURRENT_XPN         52
                SAI_MACSEC_SA_STAT_OCTETS_ENCRYPTED    0
                SAI_MACSEC_SA_STAT_OCTETS_PROTECTED    0
                SAI_MACSEC_SA_STAT_OUT_PKTS_ENCRYPTED  0
                SAI_MACSEC_SA_STAT_OUT_PKTS_PROTECTED  0
                -------------------------------------  --------------------------------
        MACsec Ingress SC (525400d4fd3f0001)
                MACsec Ingress SA (0)
                ---------------------------------------  --------------------------------
                active                                   true
                auth_key                                 9DDD4C69220A1FA9B6763F229B75CB6F
                lowest_acceptable_pn                     1
                sak                                      BA86574D054FCF48B9CD7CF54F21304A
                salt                                     000000000000000000000000
                ssci                                     0
                SAI_MACSEC_SA_ATTR_CURRENT_XPN           56
                SAI_MACSEC_SA_STAT_IN_PKTS_DELAYED       0
                SAI_MACSEC_SA_STAT_IN_PKTS_INVALID       0
                SAI_MACSEC_SA_STAT_IN_PKTS_LATE          0
                SAI_MACSEC_SA_STAT_IN_PKTS_NOT_USING_SA  0
                SAI_MACSEC_SA_STAT_IN_PKTS_NOT_VALID     0
                SAI_MACSEC_SA_STAT_IN_PKTS_OK            0
                SAI_MACSEC_SA_STAT_IN_PKTS_UNCHECKED     0
                SAI_MACSEC_SA_STAT_IN_PKTS_UNUSED_SA     0
                SAI_MACSEC_SA_STAT_OCTETS_ENCRYPTED      0
                SAI_MACSEC_SA_STAT_OCTETS_PROTECTED      0
                ---------------------------------------  --------------------------------

admin@vlab-02:~$ sonic-clear macsec
Clear MACsec counters

admin@vlab-02:~$ show macsec  Ethernet0
MACsec port(Ethernet0)
---------------------  -----------
cipher_suite           GCM-AES-128
enable                 true
enable_encrypt         true
enable_protect         true
enable_replay_protect  false
replay_window          0
send_sci               true
---------------------  -----------
        MACsec Egress SC (52540067daa70001)
        -----------  -
        encoding_an  0
        -----------  -
                MACsec Egress SA (0)
                -------------------------------------  --------------------------------
                auth_key                               9DDD4C69220A1FA9B6763F229B75CB6F
                next_pn                                1
                sak                                    BA86574D054FCF48B9CD7CF54F21304A
                salt                                   000000000000000000000000
                ssci                                   0
                SAI_MACSEC_SA_ATTR_CURRENT_XPN         52
                SAI_MACSEC_SA_STAT_OCTETS_ENCRYPTED    0
                SAI_MACSEC_SA_STAT_OCTETS_PROTECTED    0
                SAI_MACSEC_SA_STAT_OUT_PKTS_ENCRYPTED  0
                SAI_MACSEC_SA_STAT_OUT_PKTS_PROTECTED  0
                -------------------------------------  --------------------------------
        MACsec Ingress SC (525400d4fd3f0001)
                MACsec Ingress SA (0)
                ---------------------------------------  --------------------------------
                active                                   true
                auth_key                                 9DDD4C69220A1FA9B6763F229B75CB6F
                lowest_acceptable_pn                     1
                sak                                      BA86574D054FCF48B9CD7CF54F21304A
                salt                                     000000000000000000000000
                ssci                                     0
                SAI_MACSEC_SA_ATTR_CURRENT_XPN           0 <---this counters was cleared.
                SAI_MACSEC_SA_STAT_IN_PKTS_DELAYED       0
                SAI_MACSEC_SA_STAT_IN_PKTS_INVALID       0
                SAI_MACSEC_SA_STAT_IN_PKTS_LATE          0
                SAI_MACSEC_SA_STAT_IN_PKTS_NOT_USING_SA  0
                SAI_MACSEC_SA_STAT_IN_PKTS_NOT_VALID     0
                SAI_MACSEC_SA_STAT_IN_PKTS_OK            0
                SAI_MACSEC_SA_STAT_IN_PKTS_UNCHECKED     0
                SAI_MACSEC_SA_STAT_IN_PKTS_UNUSED_SA     0
                SAI_MACSEC_SA_STAT_OCTETS_ENCRYPTED      0
                SAI_MACSEC_SA_STAT_OCTETS_PROTECTED      0
                ---------------------------------------  --------------------------------


Signed-off-by: Ze Gan <ganze718@gmail.com>
Co-authored-by: Judy Joseph <jujoseph@microsoft.com>
This commit is contained in:
Ze Gan 2022-09-07 08:16:23 +08:00 committed by Ying Xie
parent 8431d3ab36
commit 3b128ec7e8
3 changed files with 94 additions and 15 deletions

View File

@ -0,0 +1,36 @@
import os
import click
import show.plugins.macsec as show_macsec
import utilities_common.cli as clicommon
from sonic_py_common import multi_asic
@click.group(cls=clicommon.AliasedGroup)
def macsec():
pass
@macsec.command('macsec')
@click.option('--clean-cache', type=bool, required=False, default=False, help="If the option of clean cache is true, next show commands will show the raw counters which based on the service booted instead of the last clear command.")
def macsec_clear_counters(clean_cache):
"""
Clear MACsec counts.
This clear command will generated a cache for next show commands which will base on this cache as the zero baseline to show the increment of counters.
"""
if clean_cache:
for namespace in multi_asic.get_namespace_list():
if os.path.isfile(show_macsec.CACHE_FILE.format(namespace)):
os.remove(show_macsec.CACHE_FILE.format(namespace))
print("Cleaned cache")
return
clicommon.run_command("show macsec --dump-file")
print("Clear MACsec counters")
def register(cli):
cli.add_command(macsec_clear_counters)
if __name__ == '__main__':
macsec_clear_counters(None)

View File

@ -1,12 +1,19 @@
import typing import typing
from natsort import natsorted from natsort import natsorted
import datetime
import pickle
import os
import copy
import click import click
from tabulate import tabulate from tabulate import tabulate
import utilities_common.multi_asic as multi_asic_util import utilities_common.multi_asic as multi_asic_util
from swsscommon.swsscommon import CounterTable, MacsecCounter from swsscommon.swsscommon import CounterTable, MacsecCounter
from utilities_common.cli import UserCache
CACHE_MANAGER = UserCache(app_name="macsec")
CACHE_FILE = os.path.join(CACHE_MANAGER.get_directory(), "macsecstats{}")
DB_CONNECTOR = None DB_CONNECTOR = None
COUNTER_TABLE = None COUNTER_TABLE = None
@ -15,12 +22,12 @@ COUNTER_TABLE = None
class MACsecAppMeta(object): class MACsecAppMeta(object):
def __init__(self, *args) -> None: def __init__(self, *args) -> None:
SEPARATOR = DB_CONNECTOR.get_db_separator(DB_CONNECTOR.APPL_DB) SEPARATOR = DB_CONNECTOR.get_db_separator(DB_CONNECTOR.APPL_DB)
key = self.__class__.get_appl_table_name() + SEPARATOR + \ self.key = self.__class__.get_appl_table_name() + SEPARATOR + \
SEPARATOR.join(args) SEPARATOR.join(args)
self.meta = DB_CONNECTOR.get_all( self.meta = DB_CONNECTOR.get_all(
DB_CONNECTOR.APPL_DB, key) DB_CONNECTOR.APPL_DB, self.key)
if len(self.meta) == 0: if len(self.meta) == 0:
raise ValueError("No such MACsecAppMeta: {}".format(key)) raise ValueError("No such MACsecAppMeta: {}".format(self.key))
for k, v in self.meta.items(): for k, v in self.meta.items():
setattr(self, k, v) setattr(self, k, v)
@ -39,10 +46,15 @@ class MACsecSA(MACsecAppMeta, MACsecCounters):
MACsecAppMeta.__init__(self, port_name, sci, an) MACsecAppMeta.__init__(self, port_name, sci, an)
MACsecCounters.__init__(self, port_name, sci, an) MACsecCounters.__init__(self, port_name, sci, an)
def dump_str(self) -> str: def dump_str(self, cache = None) -> str:
buffer = self.get_header() buffer = self.get_header()
meta = sorted(self.meta.items(), key=lambda x: x[0]) meta = sorted(self.meta.items(), key=lambda x: x[0])
counters = sorted(self.counters.items(), key=lambda x: x[0]) counters = copy.deepcopy(self.counters)
if cache:
for k, v in counters.items():
if k in cache.counters:
counters[k] = int(counters[k]) - int(cache.counters[k])
counters = sorted(counters.items(), key=lambda x: x[0])
buffer += tabulate(meta + counters) buffer += tabulate(meta + counters)
buffer = "\n".join(["\t\t" + line for line in buffer.splitlines()]) buffer = "\n".join(["\t\t" + line for line in buffer.splitlines()])
return buffer return buffer
@ -87,7 +99,7 @@ class MACsecIngressSC(MACsecSC):
def get_appl_table_name(cls) -> str: def get_appl_table_name(cls) -> str:
return "MACSEC_INGRESS_SC_TABLE" return "MACSEC_INGRESS_SC_TABLE"
def dump_str(self) -> str: def dump_str(self, cache = None) -> str:
buffer = self.get_header() buffer = self.get_header()
buffer = "\n".join(["\t" + line for line in buffer.splitlines()]) buffer = "\n".join(["\t" + line for line in buffer.splitlines()])
return buffer return buffer
@ -104,7 +116,7 @@ class MACsecEgressSC(MACsecSC):
def get_appl_table_name(cls) -> str: def get_appl_table_name(cls) -> str:
return "MACSEC_EGRESS_SC_TABLE" return "MACSEC_EGRESS_SC_TABLE"
def dump_str(self) -> str: def dump_str(self, cache = None) -> str:
buffer = self.get_header() buffer = self.get_header()
buffer += tabulate(sorted(self.meta.items(), key=lambda x: x[0])) buffer += tabulate(sorted(self.meta.items(), key=lambda x: x[0]))
buffer = "\n".join(["\t" + line for line in buffer.splitlines()]) buffer = "\n".join(["\t" + line for line in buffer.splitlines()])
@ -123,7 +135,7 @@ class MACsecPort(MACsecAppMeta):
def get_appl_table_name(cls) -> str: def get_appl_table_name(cls) -> str:
return "MACSEC_PORT_TABLE" return "MACSEC_PORT_TABLE"
def dump_str(self) -> str: def dump_str(self, cache = None) -> str:
buffer = self.get_header() buffer = self.get_header()
buffer += tabulate(sorted(self.meta.items(), key=lambda x: x[0])) buffer += tabulate(sorted(self.meta.items(), key=lambda x: x[0]))
return buffer return buffer
@ -149,6 +161,7 @@ def create_macsec_obj(key: str) -> MACsecAppMeta:
except ValueError as e: except ValueError as e:
return None return None
def create_macsec_objs(interface_name: str) -> typing.List[MACsecAppMeta]: def create_macsec_objs(interface_name: str) -> typing.List[MACsecAppMeta]:
objs = [] objs = []
objs.append(create_macsec_obj(MACsecPort.get_appl_table_name() + ":" + interface_name)) objs.append(create_macsec_obj(MACsecPort.get_appl_table_name() + ":" + interface_name))
@ -179,12 +192,25 @@ def create_macsec_objs(interface_name: str) -> typing.List[MACsecAppMeta]:
return objs return objs
def cache_find(cache: dict, target: MACsecAppMeta) -> MACsecAppMeta:
if not cache or not cache["objs"]:
return None
for obj in cache["objs"]:
if type(obj) == type(target) and obj.key == target.key:
# MACsec SA may be refreshed by a cycle that use the same key
# So, use the SA as the identifier
if isinstance(obj, MACsecSA) and obj.sak != target.sak:
continue
return obj
return None
@click.command() @click.command()
@click.argument('interface_name', required=False) @click.argument('interface_name', required=False)
@click.option('--dump-file', is_flag=True, required=False, default=False)
@multi_asic_util.multi_asic_click_options @multi_asic_util.multi_asic_click_options
def macsec(interface_name, namespace, display): def macsec(interface_name, dump_file, namespace, display):
MacsecContext(namespace, display).show(interface_name) MacsecContext(namespace, display).show(interface_name, dump_file)
class MacsecContext(object): class MacsecContext(object):
@ -194,7 +220,7 @@ class MacsecContext(object):
display_option, namespace_option) display_option, namespace_option)
@multi_asic_util.run_on_multi_asic @multi_asic_util.run_on_multi_asic
def show(self, interface_name): def show(self, interface_name, dump_file):
global DB_CONNECTOR global DB_CONNECTOR
global COUNTER_TABLE global COUNTER_TABLE
DB_CONNECTOR = self.db DB_CONNECTOR = self.db
@ -205,13 +231,29 @@ class MacsecContext(object):
if interface_name not in interface_names: if interface_name not in interface_names:
return return
interface_names = [interface_name] interface_names = [interface_name]
objs = [] objs = []
for interface_name in natsorted(interface_names): for interface_name in natsorted(interface_names):
objs += create_macsec_objs(interface_name) objs += create_macsec_objs(interface_name)
for obj in objs:
print(obj.dump_str())
cache = {}
if os.path.isfile(CACHE_FILE.format(self.multi_asic.current_namespace)):
cache = pickle.load(open(CACHE_FILE.format(self.multi_asic.current_namespace), "rb"))
if not dump_file:
if cache and cache["time"] and objs:
print("Last cached time was {}".format(cache["time"]))
for obj in objs:
cache_obj = cache_find(cache, obj)
print(obj.dump_str(cache_obj))
else:
dump_obj = {
"time": datetime.datetime.now(),
"objs": objs
}
with open(CACHE_FILE.format(self.multi_asic.current_namespace), 'wb') as dump_file:
pickle.dump(dump_obj, dump_file)
dump_file.flush()
def register(cli): def register(cli):
cli.add_command(macsec) cli.add_command(macsec)

View File

@ -44,5 +44,6 @@ $(DOCKER_MACSEC)_RUN_OPT += -v /host/warmboot:/var/warmboot
$(DOCKER_MACSEC)_CLI_CONFIG_PLUGIN = /cli/config/plugins/macsec.py $(DOCKER_MACSEC)_CLI_CONFIG_PLUGIN = /cli/config/plugins/macsec.py
$(DOCKER_MACSEC)_CLI_SHOW_PLUGIN = /cli/show/plugins/show_macsec.py $(DOCKER_MACSEC)_CLI_SHOW_PLUGIN = /cli/show/plugins/show_macsec.py
$(DOCKER_MACSEC)_CLI_CLEAR_PLUGIN = /cli/clear/plugins/clear_macsec_counter.py
$(DOCKER_MACSEC)_FILES += $(SUPERVISOR_PROC_EXIT_LISTENER_SCRIPT) $(DOCKER_MACSEC)_FILES += $(SUPERVISOR_PROC_EXIT_LISTENER_SCRIPT)