[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:
parent
8431d3ab36
commit
3b128ec7e8
@ -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)
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
Reference in New Issue
Block a user