Skip to content

Commit

Permalink
Changes to support acl-loader and mirror-session config commands for …
Browse files Browse the repository at this point in the history
…multi-npu platforms. (sonic-net#908)

* Changes to support acl-loader command for multi-npu platforms.

Move multi-npu related utility functions from config/main.py
to sonic-device-util.py so that it can be used by acl-loader/any other
module.

Updated Mirror Session add/remove for multi-npu platforms.
Needed for Everflow ACL rule programming.

* Address review comment to change comment from """ to #
except for doc string
  • Loading branch information
abdosi authored May 13, 2020
1 parent 7e52ef9 commit ffd0bd1
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 94 deletions.
120 changes: 103 additions & 17 deletions acl_loader/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
import syslog
import tabulate
from natsort import natsorted
import sonic_device_util

import openconfig_acl
import pyangbind.lib.pybindJSON as pybindJSON
from swsssdk import ConfigDBConnector
from swsssdk import SonicV2Connector
from swsssdk import SonicDBConfig


def info(msg):
Expand Down Expand Up @@ -114,12 +116,39 @@ def __init__(self):
self.tables_db_info = {}
self.rules_db_info = {}
self.rules_info = {}
# Load global db config. This call is no-op in single npu platforms
SonicDBConfig.load_sonic_global_db_config()
self.sessions_db_info = {}
self.configdb = ConfigDBConnector()
self.configdb.connect()
self.statedb = SonicV2Connector(host="127.0.0.1")
self.statedb.connect(self.statedb.STATE_DB)

# For multi-npu architecture we will have both global and per front asic namespace.
# Global namespace will be used for Control plane ACL which are via IPTables.
# Per ASIC namespace will be used for Data and Everflow ACL's.
# Global Configdb will have all ACL information for both Ctrl and Data/Evereflow ACL's
# and will be used as souurce of truth for ACL modification to config DB which will be done to both Global DB and
# front asic namespace

self.per_npu_configdb = {}

# State DB are used for to get mirror Session monitor port.
# For multi-npu platforms each asic namespace can have different monitor port
# dependinding on which route to session destination ip. So for multi-npu
# platforms we get state db for all front asic namespace in addition to

self.per_npu_statedb = {}

# Getting all front asic namespace and correspding config and state DB connector

namespaces = sonic_device_util.get_all_namespaces()
for front_asic_namespaces in namespaces['front_ns']:
self.per_npu_configdb[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces)
self.per_npu_configdb[front_asic_namespaces].connect()
self.per_npu_statedb[front_asic_namespaces] = SonicV2Connector(use_unix_socket_path=True, namespace=front_asic_namespaces)
self.per_npu_statedb[front_asic_namespaces].connect(self.per_npu_statedb[front_asic_namespaces].STATE_DB)

self.read_tables_info()
self.read_rules_info()
self.read_sessions_info()
Expand Down Expand Up @@ -150,7 +179,14 @@ def read_policers_info(self):
Read POLICER table from configuration database
:return:
"""
self.policers_db_info = self.configdb.get_table(self.POLICER)

# For multi-npu platforms we will read from any one of front asic namespace
# config db as the information should be same across all config db
if self.per_npu_configdb:
namespace_configdb = (self.per_npu_configdb.values())[0]
self.policers_db_info = namespace_configdb.get_table(self.POLICER)
else:
self.policers_db_info = self.configdb.get_table(self.POLICER)

def get_policers_db_info(self):
return self.policers_db_info
Expand All @@ -160,17 +196,30 @@ def read_sessions_info(self):
Read MIRROR_SESSION table from configuration database
:return:
"""
self.sessions_db_info = self.configdb.get_table(self.CFG_MIRROR_SESSION_TABLE)

# For multi-npu platforms we will read from any one of front asic namespace
# config db as the information should be same across all config db
if self.per_npu_configdb:
namespace_configdb = (self.per_npu_configdb.values())[0]
self.sessions_db_info = namespace_configdb.get_table(self.CFG_MIRROR_SESSION_TABLE)
else:
self.sessions_db_info = self.configdb.get_table(self.CFG_MIRROR_SESSION_TABLE)
for key in self.sessions_db_info.keys():
state_db_info = self.statedb.get_all(self.statedb.STATE_DB, "{}|{}".format(self.STATE_MIRROR_SESSION_TABLE, key))
monitor_port = ""
if state_db_info:
status = state_db_info.get("status", "inactive")
monitor_port = state_db_info.get("monitor_port", "")
if self.per_npu_statedb:
# For multi-npu platforms we will read from all front asic name space
# statedb as the monitor port will be differnt for each asic
# and it's status also might be different (ideally should not happen)
# We will store them as dict of 'asic' : value
self.sessions_db_info[key]["status"] = {}
self.sessions_db_info[key]["monitor_port"] = {}
for namespace_key, namespace_statedb in self.per_npu_statedb.iteritems():
state_db_info = namespace_statedb.get_all(self.statedb.STATE_DB, "{}|{}".format(self.STATE_MIRROR_SESSION_TABLE, key))
self.sessions_db_info[key]["status"][namespace_key] = state_db_info.get("status", "inactive") if state_db_info else "error"
self.sessions_db_info[key]["monitor_port"][namespace_key] = state_db_info.get("monitor_port", "") if state_db_info else ""
else:
status = "error"
self.sessions_db_info[key]["status"] = status
self.sessions_db_info[key]["monitor_port"] = monitor_port
state_db_info = self.statedb.get_all(self.statedb.STATE_DB, "{}|{}".format(self.STATE_MIRROR_SESSION_TABLE, key))
self.sessions_db_info[key]["status"] = state_db_info.get("status", "inactive") if state_db_info else "error"
self.sessions_db_info[key]["monitor_port"] = state_db_info.get("monitor_port", "") if state_db_info else ""

def get_sessions_db_info(self):
return self.sessions_db_info
Expand Down Expand Up @@ -309,7 +358,17 @@ def validate_actions(self, table_name, action_props):
raise AclLoaderException("Table {} does not exist".format(table_name))

stage = self.tables_db_info[table_name].get("stage", Stage.INGRESS)
capability = self.statedb.get_all(self.statedb.STATE_DB, "{}|switch".format(self.SWITCH_CAPABILITY_TABLE))

# check if per npu state db is there then read using first state db
# else read from global statedb
if self.per_npu_statedb:
# For multi-npu we will read using anyone statedb connector for front asic namespace.
# Same information should be there in all state DB's
# as it is static information about switch capability
namespace_statedb = (self.per_npu_statedb.values())[0]
capability = namespace_statedb.get_all(self.statedb.STATE_DB, "{}|switch".format(self.SWITCH_CAPABILITY_TABLE))
else:
capability = self.statedb.get_all(self.statedb.STATE_DB, "{}|switch".format(self.SWITCH_CAPABILITY_TABLE))
for action_key in dict(action_props):
key = "{}|{}".format(self.ACL_ACTIONS_CAPABILITY_FIELD, stage.upper())
if key not in capability:
Expand Down Expand Up @@ -518,9 +577,16 @@ def full_update(self):
"""
for key in self.rules_db_info.keys():
if self.current_table is None or self.current_table == key[0]:
self.configdb.mod_entry(self.ACL_RULE, key, None)
self.configdb.mod_entry(self.ACL_RULE, key, None)
# Program for per front asic namespace also if present
for namespace_configdb in self.per_npu_configdb.values():
namespace_configdb.mod_entry(self.ACL_RULE, key, None)


self.configdb.mod_config({self.ACL_RULE: self.rules_info})
# Program for per front asic namespace also if present
for namespace_configdb in self.per_npu_configdb.values():
namespace_configdb.mod_config({self.ACL_RULE: self.rules_info})

def incremental_update(self):
"""
Expand Down Expand Up @@ -559,25 +625,43 @@ def incremental_update(self):
# Remove all existing dataplane rules
for key in current_dataplane_rules:
self.configdb.mod_entry(self.ACL_RULE, key, None)
# Program for per-asic namespace also if present
for namespace_configdb in self.per_npu_configdb.values():
namespace_configdb.mod_entry(self.ACL_RULE, key, None)


# Add all new dataplane rules
for key in new_dataplane_rules:
self.configdb.mod_entry(self.ACL_RULE, key, self.rules_info[key])
# Program for per-asic namespace corresponding to front asic also if present.
for namespace_configdb in self.per_npu_configdb.values():
namespace_configdb.mod_entry(self.ACL_RULE, key, self.rules_info[key])

added_controlplane_rules = new_controlplane_rules.difference(current_controlplane_rules)
removed_controlplane_rules = current_controlplane_rules.difference(new_controlplane_rules)
existing_controlplane_rules = new_rules.intersection(current_controlplane_rules)

for key in added_controlplane_rules:
self.configdb.mod_entry(self.ACL_RULE, key, self.rules_info[key])
# Program for per-asic namespace corresponding to front asic also if present.
# For control plane ACL it's not needed but to keep all db in sync program everywhere
for namespace_configdb in self.per_npu_configdb.values():
namespace_configdb.mod_entry(self.ACL_RULE, key, self.rules_info[key])

for key in removed_controlplane_rules:
self.configdb.mod_entry(self.ACL_RULE, key, None)
# Program for per-asic namespace corresponding to front asic also if present.
# For control plane ACL it's not needed but to keep all db in sync program everywhere
for namespace_configdb in self.per_npu_configdb.values():
namespace_configdb.mod_entry(self.ACL_RULE, key, None)

for key in existing_controlplane_rules:
if cmp(self.rules_info[key], self.rules_db_info[key]) != 0:
self.configdb.set_entry(self.ACL_RULE, key, self.rules_info[key])

# Program for per-asic namespace corresponding to front asic also if present.
# For control plane ACL it's not needed but to keep all db in sync program everywhere
for namespace_configdb in self.per_npu_configdb.values():
namespace_configdb.set_entry(self.ACL_RULE, key, self.rules_info[key])

def delete(self, table=None, rule=None):
"""
Expand All @@ -589,8 +673,10 @@ def delete(self, table=None, rule=None):
if not table or table == key[0]:
if not rule or rule == key[1]:
self.configdb.set_entry(self.ACL_RULE, key, None)


# Program for per-asic namespace corresponding to front asic also if present.
for namespace_configdb in self.per_npu_configdb.values():
namespace_configdb.set_entry(self.ACL_RULE, key, None)

def show_table(self, table_name):
"""
Show ACL table configuration.
Expand Down Expand Up @@ -626,7 +712,6 @@ def show_table(self, table_name):

print(tabulate.tabulate(data, headers=header, tablefmt="simple", missingval=""))


def show_session(self, session_name):
"""
Show mirror session configuration.
Expand All @@ -639,7 +724,8 @@ def show_session(self, session_name):
for key, val in self.get_sessions_db_info().iteritems():
if session_name and key != session_name:
continue

# For multi-mpu platform status and monitor port will be dict()
# of 'asic-x':value
data.append([key, val["status"], val["src_ip"], val["dst_ip"],
val.get("gre_type", ""), val.get("dscp", ""),
val.get("ttl", ""), val.get("queue", ""), val.get("policer", ""),
Expand Down
Loading

0 comments on commit ffd0bd1

Please sign in to comment.