Skip to content

Commit e8254bb

Browse files
liuh-80liuh-80
and
liuh-80
authored
Porting util.py and port_util.py from py-swsssdk to sonic-py-common (#11347)
#### Why I did it To deprecate sonic-py-swsssdk, need move some code to sonic-py-common. #### How I did it Porting util.py and port_util.py from sonic-py-swsssdk to sonic-py-common. #### How to verify it Pass UT. Pass all E2E test. #### Which release branch to backport (provide reason below if selected) <!-- - Note we only backport fixes to a release branch, *not* features! - Please also provide a reason for the backporting below. - e.g. - [x] 202006 --> - [ ] 201811 - [ ] 201911 - [ ] 202006 - [ ] 202012 - [ ] 202106 - [ ] 202111 - [ ] 202205 #### Description for the changelog Porting util.py and port_util.py from sonic-py-swsssdk to sonic-py-common. Co-authored-by: liuh-80 <azureuser@liuh-dev-vm-02.5fg3zjdzj2xezlx1yazx5oxkzd.hx.internal.cloudapp.net>
1 parent a4b9838 commit e8254bb

File tree

3 files changed

+282
-0
lines changed

3 files changed

+282
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
"""
2+
Bridge/Port mapping utility library.
3+
"""
4+
from swsscommon import swsscommon
5+
import re
6+
7+
8+
SONIC_ETHERNET_RE_PATTERN = "^Ethernet(\d+)$"
9+
"""
10+
Ethernet-BP refers to BackPlane interfaces
11+
in multi-asic platform.
12+
"""
13+
SONIC_ETHERNET_BP_RE_PATTERN = "^Ethernet-BP(\d+)$"
14+
SONIC_VLAN_RE_PATTERN = "^Vlan(\d+)$"
15+
SONIC_PORTCHANNEL_RE_PATTERN = "^PortChannel(\d+)$"
16+
SONIC_MGMT_PORT_RE_PATTERN = "^eth(\d+)$"
17+
SONIC_ETHERNET_IB_RE_PATTERN = "^Ethernet-IB(\d+)$"
18+
SONIC_ETHERNET_REC_RE_PATTERN = "^Ethernet-Rec(\d+)$"
19+
20+
class BaseIdx:
21+
ethernet_base_idx = 1
22+
vlan_interface_base_idx = 2000
23+
ethernet_bp_base_idx = 9000
24+
portchannel_base_idx = 1000
25+
mgmt_port_base_idx = 10000
26+
ethernet_ib_base_idx = 11000
27+
ethernet_rec_base_idx = 12000
28+
29+
def get_index(if_name):
30+
"""
31+
OIDs are 1-based, interfaces are 0-based, return the 1-based index
32+
Ethernet N = N + 1
33+
Vlan N = N + 2000
34+
Ethernet_BP N = N + 9000
35+
PortChannel N = N + 1000
36+
eth N = N + 10000
37+
Ethernet_IB N = N + 11000
38+
Ethernet_Rec N = N + 12000
39+
"""
40+
return get_index_from_str(if_name.decode())
41+
42+
43+
def get_index_from_str(if_name):
44+
"""
45+
OIDs are 1-based, interfaces are 0-based, return the 1-based index
46+
Ethernet N = N + 1
47+
Vlan N = N + 2000
48+
Ethernet_BP N = N + 9000
49+
PortChannel N = N + 1000
50+
eth N = N + 10000
51+
Ethernet_IB N = N + 11000
52+
Ethernet_Rec N = N + 12000
53+
"""
54+
patterns = {
55+
SONIC_ETHERNET_RE_PATTERN: BaseIdx.ethernet_base_idx,
56+
SONIC_ETHERNET_BP_RE_PATTERN: BaseIdx.ethernet_bp_base_idx,
57+
SONIC_VLAN_RE_PATTERN: BaseIdx.vlan_interface_base_idx,
58+
SONIC_PORTCHANNEL_RE_PATTERN: BaseIdx.portchannel_base_idx,
59+
SONIC_MGMT_PORT_RE_PATTERN: BaseIdx.mgmt_port_base_idx,
60+
SONIC_ETHERNET_IB_RE_PATTERN: BaseIdx.ethernet_ib_base_idx,
61+
SONIC_ETHERNET_REC_RE_PATTERN: BaseIdx.ethernet_rec_base_idx
62+
}
63+
64+
for pattern, baseidx in patterns.items():
65+
match = re.match(pattern, if_name)
66+
if match:
67+
return int(match.group(1)) + baseidx
68+
69+
def get_interface_oid_map(db, blocking=True):
70+
"""
71+
Get the Interface names from Counters DB
72+
"""
73+
db.connect('COUNTERS_DB')
74+
if_name_map = db.get_all('COUNTERS_DB', 'COUNTERS_PORT_NAME_MAP', blocking=blocking)
75+
if_lag_name_map = db.get_all('COUNTERS_DB', 'COUNTERS_LAG_NAME_MAP', blocking=blocking)
76+
if_name_map.update(if_lag_name_map)
77+
78+
if not if_name_map:
79+
return {}, {}
80+
81+
oid_pfx = len("oid:0x")
82+
if_name_map = {if_name: sai_oid[oid_pfx:] for if_name, sai_oid in if_name_map.items()}
83+
84+
# TODO: remove the first branch after all SonicV2Connector are migrated to decode_responses
85+
if isinstance(db, swsscommon.SonicV2Connector) == False and db.dbintf.redis_kwargs.get('decode_responses', False) == False:
86+
get_index_func = get_index
87+
else:
88+
get_index_func = get_index_from_str
89+
90+
if_id_map = {sai_oid: if_name for if_name, sai_oid in if_name_map.items()
91+
# only map the interface if it's a style understood to be a SONiC interface.
92+
if get_index_func(if_name) is not None}
93+
94+
return if_name_map, if_id_map
95+
96+
def get_bridge_port_map(db):
97+
"""
98+
Get the Bridge port mapping from ASIC DB
99+
"""
100+
db.connect('ASIC_DB')
101+
br_port_str = db.keys('ASIC_DB', "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:*")
102+
if not br_port_str:
103+
return {}
104+
105+
if_br_oid_map = {}
106+
offset = len("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:")
107+
oid_pfx = len("oid:0x")
108+
for br_s in br_port_str:
109+
# Example output: ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:oid:0x3a000000000616
110+
br_port_id = br_s[(offset + oid_pfx):]
111+
ent = db.get_all('ASIC_DB', br_s, blocking=True)
112+
# TODO: remove the first branch after all SonicV2Connector are migrated to decode_responses
113+
if isinstance(db, swsscommon.SonicV2Connector) == False and db.dbintf.redis_kwargs.get('decode_responses', False) == False:
114+
if b"SAI_BRIDGE_PORT_ATTR_PORT_ID" in ent:
115+
port_id = ent[b"SAI_BRIDGE_PORT_ATTR_PORT_ID"][oid_pfx:]
116+
if_br_oid_map[br_port_id] = port_id
117+
else:
118+
if "SAI_BRIDGE_PORT_ATTR_PORT_ID" in ent:
119+
port_id = ent["SAI_BRIDGE_PORT_ATTR_PORT_ID"][oid_pfx:]
120+
if_br_oid_map[br_port_id] = port_id
121+
122+
return if_br_oid_map
123+
124+
def get_vlan_id_from_bvid(db, bvid):
125+
"""
126+
Get the Vlan Id from Bridge Vlan Object
127+
"""
128+
db.connect('ASIC_DB')
129+
vlan_obj = db.keys('ASIC_DB', str("ASIC_STATE:SAI_OBJECT_TYPE_VLAN:" + bvid))
130+
vlan_entry = db.get_all('ASIC_DB', vlan_obj[0], blocking=True)
131+
vlan_id = None
132+
# TODO: remove the first branch after all SonicV2Connector are migrated to decode_responses
133+
if isinstance(db, swsscommon.SonicV2Connector) == False and db.dbintf.redis_kwargs.get('decode_responses', False) == False:
134+
if b"SAI_VLAN_ATTR_VLAN_ID" in vlan_entry:
135+
vlan_id = vlan_entry[b"SAI_VLAN_ATTR_VLAN_ID"]
136+
else:
137+
if "SAI_VLAN_ATTR_VLAN_ID" in vlan_entry:
138+
vlan_id = vlan_entry["SAI_VLAN_ATTR_VLAN_ID"]
139+
140+
return vlan_id
141+
142+
def get_rif_port_map(db):
143+
"""
144+
Get the RIF port mapping from ASIC DB
145+
"""
146+
db.connect('ASIC_DB')
147+
rif_keys_str = db.keys('ASIC_DB', "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE:*")
148+
if not rif_keys_str:
149+
return {}
150+
151+
rif_port_oid_map = {}
152+
for rif_s in rif_keys_str:
153+
rif_id = rif_s[len("ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE:oid:0x"):]
154+
ent = db.get_all('ASIC_DB', rif_s, blocking=True)
155+
# TODO: remove the first branch after all SonicV2Connector are migrated to decode_responses
156+
if isinstance(db, swsscommon.SonicV2Connector) == False and db.dbintf.redis_kwargs.get('decode_responses', False) == False:
157+
if b"SAI_ROUTER_INTERFACE_ATTR_PORT_ID" in ent:
158+
port_id = ent[b"SAI_ROUTER_INTERFACE_ATTR_PORT_ID"].lstrip(b"oid:0x")
159+
rif_port_oid_map[rif_id] = port_id
160+
else:
161+
if "SAI_ROUTER_INTERFACE_ATTR_PORT_ID" in ent:
162+
port_id = ent["SAI_ROUTER_INTERFACE_ATTR_PORT_ID"].lstrip("oid:0x")
163+
rif_port_oid_map[rif_id] = port_id
164+
165+
return rif_port_oid_map
166+
167+
def get_vlan_interface_oid_map(db, blocking=True):
168+
"""
169+
Get Vlan Interface names and sai oids
170+
"""
171+
db.connect('COUNTERS_DB')
172+
173+
rif_name_map = db.get_all('COUNTERS_DB', 'COUNTERS_RIF_NAME_MAP', blocking=blocking)
174+
rif_type_name_map = db.get_all('COUNTERS_DB', 'COUNTERS_RIF_TYPE_MAP', blocking=blocking)
175+
176+
if not rif_name_map or not rif_type_name_map:
177+
return {}
178+
179+
oid_pfx = len("oid:0x")
180+
vlan_if_name_map = {}
181+
182+
# TODO: remove the first branch after all SonicV2Connector are migrated to decode_responses
183+
if isinstance(db, swsscommon.SonicV2Connector) == False and db.dbintf.redis_kwargs.get('decode_responses', False) == False:
184+
get_index_func = get_index
185+
else:
186+
get_index_func = get_index_from_str
187+
188+
for if_name, sai_oid in rif_name_map.items():
189+
# Check if RIF is l3 vlan interface
190+
# TODO: remove the first candidate after all SonicV2Connector are migrated to decode_responses
191+
if rif_type_name_map[sai_oid] in (b'SAI_ROUTER_INTERFACE_TYPE_VLAN', 'SAI_ROUTER_INTERFACE_TYPE_VLAN'):
192+
# Check if interface name is in style understood to be a SONiC interface
193+
if get_index_func(if_name):
194+
vlan_if_name_map[sai_oid[oid_pfx:]] = if_name
195+
196+
return vlan_if_name_map
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
Syslog and daemon script utility library.
3+
"""
4+
5+
from __future__ import print_function
6+
import json
7+
import logging
8+
import logging.config
9+
import sys
10+
from getopt import getopt
11+
12+
13+
# TODO: move to dbsync project.
14+
def usage(script_name):
15+
print('Usage: python ', script_name,
16+
'-t [host] -p [port] -s [unix_socket_path] -d [logging_level] -f [update_frequency] -h [help]')
17+
18+
19+
# TODO: move to dbsync project.
20+
def process_options(script_name):
21+
"""
22+
Process command line options
23+
"""
24+
options, remainders = getopt(sys.argv[1:], "t:p:s:d:f:h", ["host=", "port=", "unix_socket_path=", "debug=", "frequency=", "help"])
25+
26+
args = {}
27+
for (opt, arg) in options:
28+
try:
29+
if opt in ('-d', '--debug'):
30+
args['log_level'] = int(arg)
31+
elif opt in ('-t', '--host'):
32+
args['host'] = arg
33+
elif opt in ('-p', '--port'):
34+
args['port'] = int(arg)
35+
elif opt in ('-s', 'unix_socket_path'):
36+
args['unix_socket_path'] = arg
37+
elif opt in ('-f', '--frequency'):
38+
args['update_frequency'] = int(arg)
39+
elif opt in ('-h', '--help'):
40+
usage(script_name)
41+
except ValueError as e:
42+
print('Invalid option for {}: {}'.format(opt, e))
43+
sys.exit(1)
44+
45+
return args
46+
47+
48+
# TODO: move
49+
def setup_logging(config_file_path, log_level=logging.INFO):
50+
"""
51+
Logging configuration helper.
52+
53+
:param config_file_path: file path to logging configuration file.
54+
https://docs.python.org/3/library/logging.config.html#object-connections
55+
:param log_level: defaults to logging.INFO
56+
:return: None - access the logger by name as described in the config--or the "root" logger as a backup.
57+
"""
58+
try:
59+
with open(config_file_path, 'rt') as f:
60+
config = json.load(f)
61+
logging.config.dictConfig(config)
62+
except (ValueError, IOError, OSError):
63+
# json.JSONDecodeError is throwable in Python3.5+ -- subclass of ValueError
64+
logging.basicConfig(log_level=log_level)
65+
logging.root.exception(
66+
"Could not load specified logging configuration '{}'. Verify the filepath exists and is compliant with: "
67+
"[https://docs.python.org/3/library/logging.config.html#object-connections]".format(config_file_path))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import os
2+
import sys
3+
4+
if sys.version_info.major == 3:
5+
from unittest import mock
6+
else:
7+
import mock
8+
9+
modules_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
10+
sys.path.insert(0, os.path.join(modules_path, 'src'))
11+
12+
class TestPortUtil:
13+
def test_get_vlan_interface_oid_map(self):
14+
db = mock.MagicMock()
15+
db.get_all = mock.MagicMock()
16+
db.get_all.return_value = {}
17+
18+
from swsssdk.port_util import get_vlan_interface_oid_map
19+
assert not get_vlan_interface_oid_map(db, True)

0 commit comments

Comments
 (0)