Skip to content

Commit

Permalink
[macsec] Parse masec_enabled and macsec_profile from minigraph (#10917)
Browse files Browse the repository at this point in the history
* Updates needed to parse the macsec config from minigraph
* Add unit tests in tests/test_cfggen.py::TestCfgGen, and updates
  • Loading branch information
judyjoseph committed Jun 29, 2022
1 parent 6e0559d commit 05f1c6f
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 24 deletions.
41 changes: 35 additions & 6 deletions src/sonic-config-engine/minigraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,7 @@ def parse_meta(meta, hname):
switch_type = None
max_cores = None
kube_data = {}
macsec_profile = {}
device_metas = meta.find(str(QName(ns, "Devices")))
for device in device_metas.findall(str(QName(ns1, "DeviceMetadata"))):
if device.find(str(QName(ns1, "Name"))).text.lower() == hname.lower():
Expand Down Expand Up @@ -930,7 +931,9 @@ def parse_meta(meta, hname):
kube_data["enable"] = value
elif name == "KubernetesServerIp":
kube_data["ip"] = value
return syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type, downstream_subrole, switch_id, switch_type, max_cores, kube_data
elif name == 'MacSecProfile':
macsec_profile = parse_macsec_profile(value)
return syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type, downstream_subrole, switch_id, switch_type, max_cores, kube_data, macsec_profile


def parse_system_defaults(meta):
Expand Down Expand Up @@ -979,6 +982,7 @@ def parse_linkmeta(meta, hname):
upper_tor_hostname = ''
lower_tor_hostname = ''
auto_negotiation = None
macsec_enabled = False

properties = linkmeta.find(str(QName(ns1, "Properties")))
for device_property in properties.findall(str(QName(ns1, "DeviceProperty"))):
Expand All @@ -994,6 +998,8 @@ def parse_linkmeta(meta, hname):
lower_tor_hostname = value
elif name == "AutoNegotiation":
auto_negotiation = value
elif name == "MacSecEnabled":
macsec_enabled = value

linkmetas[port] = {}
if fec_disabled:
Expand All @@ -1005,14 +1011,28 @@ def parse_linkmeta(meta, hname):
linkmetas[port]["PeerSwitch"] = upper_tor_hostname
if auto_negotiation:
linkmetas[port]["AutoNegotiation"] = auto_negotiation
if macsec_enabled:
linkmetas[port]["MacSecEnabled"] = macsec_enabled
return linkmetas

def parse_macsec_profile(val_string):
macsec_profile = {}
values = val_string.strip().split()
for val in values:
keys = val.strip().split('=')
if keys[0] == 'PrimaryKey':
macsec_profile['PrimaryKey'] = keys[1].strip('\"')
elif keys[0] == 'FallbackKey':
macsec_profile['FallbackKey'] = keys[1].strip('\"')

return macsec_profile

def parse_asic_meta(meta, hname):
sub_role = None
switch_id = None
switch_type = None
max_cores = None
macsec_profile = {}
device_metas = meta.find(str(QName(ns, "Devices")))
for device in device_metas.findall(str(QName(ns1, "DeviceMetadata"))):
if device.find(str(QName(ns1, "Name"))).text.lower() == hname.lower():
Expand All @@ -1028,7 +1048,10 @@ def parse_asic_meta(meta, hname):
switch_type = value
elif name == "MaxCores":
max_cores = value
return sub_role, switch_id, switch_type, max_cores
elif name == 'MacSecProfile':
macsec_profile = parse_macsec_profile(value)

return sub_role, switch_id, switch_type, max_cores, macsec_profile

def parse_deviceinfo(meta, hwsku):
port_speeds = {}
Expand Down Expand Up @@ -1289,6 +1312,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
kube_data = {}
static_routes = {}
system_defaults = {}
macsec_profile = {}

hwsku_qn = QName(ns, "HwSku")
hostname_qn = QName(ns, "Hostname")
Expand Down Expand Up @@ -1319,7 +1343,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
elif child.tag == str(QName(ns, "UngDec")):
(u_neighbors, u_devices, _, _, _, _, _, _) = parse_png(child, hostname, None)
elif child.tag == str(QName(ns, "MetadataDeclaration")):
(syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type, downstream_subrole, switch_id, switch_type, max_cores, kube_data) = parse_meta(child, hostname)
(syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type, downstream_subrole, switch_id, switch_type, max_cores, kube_data, macsec_profile) = parse_meta(child, hostname)
elif child.tag == str(QName(ns, "LinkMetadataDeclaration")):
linkmetas = parse_linkmeta(child, hostname)
elif child.tag == str(QName(ns, "DeviceInfos")):
Expand All @@ -1335,7 +1359,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
elif child.tag == str(QName(ns, "PngDec")):
(neighbors, devices, port_speed_png) = parse_asic_png(child, asic_name, hostname)
elif child.tag == str(QName(ns, "MetadataDeclaration")):
(sub_role, switch_id, switch_type, max_cores ) = parse_asic_meta(child, asic_name)
(sub_role, switch_id, switch_type, max_cores, macsec_profile) = parse_asic_meta(child, asic_name)
elif child.tag == str(QName(ns, "LinkMetadataDeclaration")):
linkmetas = parse_linkmeta(child, hostname)
elif child.tag == str(QName(ns, "DeviceInfos")):
Expand Down Expand Up @@ -1538,6 +1562,11 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
if autoneg:
port['autoneg'] = 'on' if autoneg.lower() == 'true' else 'off'

# If macsec is enabled on interface, and profile is valid, add the profile to port
macsec_enabled = linkmetas.get(alias, {}).get('MacSecEnabled')
if macsec_enabled and 'PrimaryKey' in macsec_profile:
port['macsec'] = macsec_profile['PrimaryKey']

# If connected to a smart cable, get the connection position
for port_name, port in ports.items():
if port_name in mux_cable_ports:
Expand Down Expand Up @@ -1872,15 +1901,15 @@ def parse_asic_sub_role(filename, asic_name):
root = ET.parse(filename).getroot()
for child in root:
if child.tag == str(QName(ns, "MetadataDeclaration")):
sub_role, _, _, _ = parse_asic_meta(child, asic_name)
sub_role, _, _, _, _= parse_asic_meta(child, asic_name)
return sub_role

def parse_asic_switch_type(filename, asic_name):
if os.path.isfile(filename):
root = ET.parse(filename).getroot()
for child in root:
if child.tag == str(QName(ns, "MetadataDeclaration")):
_, _, switch_type, _ = parse_asic_meta(child, asic_name)
_, _, switch_type, _, _ = parse_asic_meta(child, asic_name)
return switch_type
return None

Expand Down
3 changes: 3 additions & 0 deletions src/sonic-config-engine/tests/common_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def validate(self, argument):
parser.add_argument("-n", "--namespace", help="namespace name", nargs='?', const=None, default=None)
parser.add_argument("-p", "--port-config", help="port config file, used with -m or -k", nargs='?', const=None)
parser.add_argument("-S", "--hwsku-config", help="hwsku config file, used with -p and -m or -k", nargs='?', const=None)
parser.add_argument("-j", "--json", help="additional json file input, used with -p, -S and -m or -k", nargs='?', const=None)
args, unknown = parser.parse_known_args(shlex.split(argument))

print('\n Validating yang schema')
Expand All @@ -73,6 +74,8 @@ def validate(self, argument):
cmd += ' -p ' + args.port_config
if args.namespace is not None:
cmd += ' -n ' + args.namespace
if args.json is not None:
cmd += ' -j ' + args.json
cmd += ' --print-data'
output = subprocess.check_output(cmd, shell=True).decode()
try:
Expand Down
22 changes: 22 additions & 0 deletions src/sonic-config-engine/tests/macsec_profile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"MACSEC_PROFILE":{
"macsec-profile": {
"cipher_suite": "GCM-AES-XPN-256",
"primary_cak": "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
"primary_ckn": "6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435",
"fallback_cak": "0000000000000000000000000000000000000000000000000000000000000000",
"fallback_ckn": "1111111111111111111111111111111111111111111111111111111111111111",
"priority": "0",
"rekey_period": "60"
},
"macsec-profile2": {
"cipher_suite": "GCM-AES-XPN-256",
"primary_cak": "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
"primary_ckn": "6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435",
"fallback_cak": "0000000000000000000000000000000000000000000000000000000000000000",
"fallback_ckn": "1111111111111111111111111111111111111111111111111111111111111111",
"priority": "0",
"rekey_period": "60"
}
}
}
41 changes: 30 additions & 11 deletions src/sonic-config-engine/tests/sample-voq-graph.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@
<DownstreamSummarySet xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution"/>
</DeviceDataPlaneInfo>
</DpgDec>
<LinkMetadataDeclaration>
<Link xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution">
<a:LinkMetadata>
<a:Name i:nil="true"/>
<a:Properties>
<a:DeviceProperty>
<a:Name>MacSecEnabled</a:Name>
<a:Value>True</a:Value>
</a:DeviceProperty>
</a:Properties>
<a:Key>linecard-1:Ethernet1/1;ARISTA01-RH:Ethernet1/1</a:Key>
</a:LinkMetadata>
</Link>
<Properties xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution"/>
</LinkMetadataDeclaration>
<PngDec>
<DeviceInterfaceLinks/>
<Devices>
Expand Down Expand Up @@ -144,6 +159,10 @@
<a:Reference i:nil="true"/>
<a:Value>16</a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>MacSecProfile</a:Name>
<a:Value>PrimaryKey="macsec-profile" FallbackKey="macsec-profile2" MacsecPolicy=""</a:Value>
</a:DeviceProperty>
</a:Properties>
</a:DeviceMetadata>
</Devices>
Expand All @@ -157,44 +176,44 @@
<AlternateSpeeds i:nil="true"/>
<EnableFlowControl>true</EnableFlowControl>
<Index>1</Index>
<InterfaceName>fortyGigE0/0</InterfaceName>
<InterfaceName>Ethernet1/1</InterfaceName>
<InterfaceType i:nil="true"/>
<MultiPortsInterface>false</MultiPortsInterface>
<PortName>0</PortName>
<Priority>0</Priority>
<Speed>10000</Speed>
<Speed>100000</Speed>
</a:EthernetInterface>
<a:EthernetInterface>
<ElementType>DeviceInterface</ElementType>
<AlternateSpeeds i:nil="true"/>
<EnableFlowControl>true</EnableFlowControl>
<Index>1</Index>
<InterfaceName>fortyGigE0/4</InterfaceName>
<InterfaceName>Ethernet2/1</InterfaceName>
<InterfaceType i:nil="true"/>
<MultiPortsInterface>false</MultiPortsInterface>
<PortName>0</PortName>
<Priority>0</Priority>
<Speed>25000</Speed>
<Speed>100000</Speed>
</a:EthernetInterface>
<a:EthernetInterface>
<ElementType>DeviceInterface</ElementType>
<AlternateSpeeds i:nil="true"/>
<EnableFlowControl>true</EnableFlowControl>
<Index>1</Index>
<InterfaceName>fortyGigE0/8</InterfaceName>
<InterfaceName>Ethernet3/1</InterfaceName>
<InterfaceType i:nil="true"/>
<MultiPortsInterface>false</MultiPortsInterface>
<PortName>0</PortName>
<Priority>0</Priority>
<Speed>40000</Speed>
<Speed>100000</Speed>
<Description>Interface description</Description>
</a:EthernetInterface>
<a:EthernetInterface>
<ElementType>DeviceInterface</ElementType>
<AlternateSpeeds i:nil="true"/>
<EnableFlowControl>true</EnableFlowControl>
<Index>1</Index>
<InterfaceName>fortyGigE0/12</InterfaceName>
<InterfaceName>Ethernet4/1</InterfaceName>
<InterfaceType i:nil="true"/>
<MultiPortsInterface>false</MultiPortsInterface>
<PortName>0</PortName>
Expand All @@ -207,7 +226,7 @@
<AlternateSpeeds i:nil="true"/>
<EnableFlowControl>true</EnableFlowControl>
<Index>1</Index>
<InterfaceName>fortyGigE0/16</InterfaceName>
<InterfaceName>Ethernet5/1</InterfaceName>
<InterfaceType i:nil="true"/>
<MultiPortsInterface>false</MultiPortsInterface>
<PortName>0</PortName>
Expand All @@ -219,7 +238,7 @@
<AlternateSpeeds i:nil="true"/>
<EnableFlowControl>true</EnableFlowControl>
<Index>1</Index>
<InterfaceName>fortyGigE0/20</InterfaceName>
<InterfaceName>Ethernet6/1</InterfaceName>
<InterfaceType i:nil="true"/>
<MultiPortsInterface>false</MultiPortsInterface>
<PortName>0</PortName>
Expand All @@ -231,7 +250,7 @@
<AlternateSpeeds i:nil="true"/>
<EnableFlowControl>true</EnableFlowControl>
<Index>1</Index>
<InterfaceName>fortyGigE0/24</InterfaceName>
<InterfaceName>Ethernet7/1</InterfaceName>
<InterfaceType i:nil="true"/>
<MultiPortsInterface>false</MultiPortsInterface>
<PortName>0</PortName>
Expand All @@ -243,7 +262,7 @@
<AlternateSpeeds i:nil="true"/>
<EnableFlowControl>true</EnableFlowControl>
<Index>1</Index>
<InterfaceName>fortyGigE0/28</InterfaceName>
<InterfaceName>Ethernetr8/1</InterfaceName>
<InterfaceType i:nil="true"/>
<MultiPortsInterface>false</MultiPortsInterface>
<PortName>0</PortName>
Expand Down
22 changes: 15 additions & 7 deletions src/sonic-config-engine/tests/test_cfggen.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def setUp(self):
self.voq_port_config = os.path.join(self.test_dir, 'voq-sample-port-config.ini')
self.packet_chassis_graph = os.path.join(self.test_dir, 'sample-chassis-packet-lc-graph.xml')
self.packet_chassis_port_ini = os.path.join(self.test_dir, 'sample-chassis-packet-lc-port-config.ini')
self.macsec_profile = os.path.join(self.test_dir, 'macsec_profile.json')
# To ensure that mock config_db data is used for unit-test cases
os.environ["CFGGEN_UNIT_TESTING"] = "2"

Expand Down Expand Up @@ -839,15 +840,15 @@ def test_show_run_interfaces(self):
self.assertEqual(output, '')

def test_minigraph_voq_metadata(self):
argument = "-m {} -p {} --var-json DEVICE_METADATA".format(self.sample_graph_voq, self.voq_port_config)
argument = "-j {} -m {} -p {} --var-json DEVICE_METADATA".format(self.macsec_profile, self.sample_graph_voq, self.voq_port_config)
output = json.loads(self.run_script(argument))
self.assertEqual(output['localhost']['asic_name'], 'Asic0')
self.assertEqual(output['localhost']['switch_id'], '0')
self.assertEqual(output['localhost']['switch_type'], 'voq')
self.assertEqual(output['localhost']['max_cores'], '16')

def test_minigraph_voq_system_ports(self):
argument = "-m {} -p {} --var-json SYSTEM_PORT".format(self.sample_graph_voq, self.voq_port_config)
argument = "-j {} -m {} -p {} --var-json SYSTEM_PORT".format(self.macsec_profile, self.sample_graph_voq, self.voq_port_config)
self.assertDictEqual(
json.loads(self.run_script(argument)),
{
Expand All @@ -865,8 +866,16 @@ def test_minigraph_voq_system_ports(self):
}
)

def test_minigraph_voq_port_macsec_enabled(self):
argument = '-j "' + self.macsec_profile + '" -m "' + self.sample_graph_voq + '" -p "' + self.voq_port_config + '" -v "PORT[\'Ethernet0\']"'
output = self.run_script(argument)
self.assertEqual(
utils.to_dict(output.strip()),
utils.to_dict("{'lanes': '6,7', 'fec': 'rs', 'alias': 'Ethernet1/1', 'index': '1', 'role': 'Ext', 'speed': '100000', 'macsec': 'macsec-profile', 'description': 'Ethernet1/1', 'mtu': '9100', 'tpid': '0x8100', 'pfc_asym': 'off'}")
)

def test_minigraph_voq_inband_interface_vlan(self):
argument = "-m {} -p {} --var-json VOQ_INBAND_INTERFACE".format(self.sample_graph_voq, self.voq_port_config)
argument = "-j {} -m {} -p {} --var-json VOQ_INBAND_INTERFACE".format(self.macsec_profile, self.sample_graph_voq, self.voq_port_config)
output = self.run_script(argument)
output_dict = utils.to_dict(output.strip())
self.assertDictEqual(
Expand All @@ -879,7 +888,7 @@ def test_minigraph_voq_inband_interface_vlan(self):
)

def test_minigraph_voq_inband_interface_port(self):
argument = "-m {} -p {} --var-json VOQ_INBAND_INTERFACE".format(self.sample_graph_voq, self.voq_port_config)
argument = "-j {} -m {} -p {} --var-json VOQ_INBAND_INTERFACE".format(self.macsec_profile, self.sample_graph_voq, self.voq_port_config)
output = self.run_script(argument)
output_dict = utils.to_dict(output.strip())
self.assertDictEqual(
Expand All @@ -892,7 +901,7 @@ def test_minigraph_voq_inband_interface_port(self):
)

def test_minigraph_voq_inband_port(self):
argument = "-m {} -p {} --var-json PORT".format(self.sample_graph_voq, self.voq_port_config)
argument = "-j {} -m {} -p {} --var-json PORT".format(self.macsec_profile, self.sample_graph_voq, self.voq_port_config)
output = self.run_script(argument)
output_dict = utils.to_dict(output.strip())
self.assertDictEqual(
Expand All @@ -910,7 +919,7 @@ def test_minigraph_voq_inband_port(self):
})

def test_minigraph_voq_recirc_ports(self):
argument = "-m {} -p {} --var-json PORT".format(self.sample_graph_voq, self.voq_port_config)
argument = "-j {} -m {} -p {} --var-json PORT".format(self.macsec_profile, self.sample_graph_voq, self.voq_port_config)
output = self.run_script(argument)
output_dict = utils.to_dict(output.strip())
self.assertDictEqual(
Expand Down Expand Up @@ -968,4 +977,3 @@ def test_minigraph_bgp_packet_chassis_vlan_subintf(self):
utils.to_dict(output.strip()),
utils.to_dict("{('PortChannel32.2', '192.168.1.4/24'): {}, 'PortChannel32.2': {'admin_status': 'up'}, ('PortChannel33.2', '192.168.2.4/24'): {}, 'PortChannel33.2': {'admin_status': 'up'}}")
)

0 comments on commit 05f1c6f

Please sign in to comment.