Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to support acl-loader and mirror-session config commands for multi-npu platforms. #908

Merged
merged 2 commits into from
May 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 = {}
jleveque marked this conversation as resolved.
Show resolved Hide resolved

# 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 = {}
jleveque marked this conversation as resolved.
Show resolved Hide resolved

# 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]
judyjoseph marked this conversation as resolved.
Show resolved Hide resolved
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]
judyjoseph marked this conversation as resolved.
Show resolved Hide resolved
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)

jleveque marked this conversation as resolved.
Show resolved Hide resolved

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