Skip to content

Adding some sanity-checking to configdb.json parsing logic #1891

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
87 changes: 87 additions & 0 deletions src/sonic-config-engine/sonic-cfggen
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,87 @@ def unique_name(l):
new_list.append(item)
return new_list

def valid_config(data):
"""Verifies that proposed config-state (data) is semantically
correct/consistent.

Args:
data: config-data in dictionary form

Returns:
'true' if valid configuration, 'false' otherwise
"""

# Verifies that there is a valid entry in PORT table for every entry
# present in INTERFACE table. See that there's no need to verify the
# opposite direction as it's perfectly valid to have PORT entries with
# no matching entry in INTERFACE table.
#
# "INTERFACE": {
# "Ethernet112|10.1.2.2/24": {},
# "Ethernet112|fc00:1:2::2/64": {},
# }
#
if 'INTERFACE' in data and 'PORT' in data:
interface_table = data['INTERFACE']

for key in interface_table:
if key[0] not in data['PORT']:
print("Interface", key[0], "not found in PORT table.")
return False

# Verifies that VLAN_INTERFACE entries are relying on existing VLAN entities.
#
# "VLAN_INTERFACE": {
# "Vlan100|172.18.1.1/24": {
# "scope": "global",
# }
# }
if 'VLAN_INTERFACE' in data and 'VLAN' in data:
vlan_interface_table = data['VLAN_INTERFACE']

for key in vlan_interface_table:
if key[0] not in data['VLAN']:
print("Vlan interface", key[0], "not found in VLAN table.")
return False

# Verifies that VLAN table holds members that are present in PORT table.
#
# "VLAN": {
# "Vlan100": {
# ...
# "members": [
# "Ethernet16",
# "Ethernet17"
# }
# }
if 'VLAN' in data and 'PORT' in data:
vlan_table = data['VLAN']

for key, value in vlan_table.iteritems():
for member in value['members']:
if member not in data['PORT']:
print("VLAN member", member, "not found in PORT table.")
return False

# Verifies that each DEVICE_NEIGHBOR key matches a valid entry in PORT table.
#
# "DEVICE_NEIGHBOR": {
# "Ethernet112": {
# "name": "lnos-x1-a-csw01",
# "port": "Ethernet1"
# }
# }
if 'DEVICE_NEIGHBOR' in data and 'PORT' in data:
devnbr_table = data['DEVICE_NEIGHBOR']

for key in devnbr_table:
if key not in data['PORT']:
print("DEVICE_NEIGHBOR port", key, "not found in PORT table.")
return False

return True


class FormatConverter:
"""Convert config DB based schema to legacy minigraph based schema for backward capability.
Expand Down Expand Up @@ -154,6 +235,7 @@ def main():
group.add_argument("-t", "--template", help="render the data with the template file")
group.add_argument("-v", "--var", help="print the value of a variable, support jinja2 expression")
group.add_argument("--var-json", help="print the value of a variable, in json format")
group.add_argument("--check-json", help="check passed config -- to be used with -j", action='store_true')
group.add_argument("--write-to-db", help="write config into configdb", action='store_true')
group.add_argument("--print-data", help="print all data", action='store_true')
args = parser.parse_args()
Expand Down Expand Up @@ -241,6 +323,11 @@ def main():
configdb.connect(False)
configdb.mod_config(FormatConverter.output_to_db(data))

if args.check_json:
if valid_config(FormatConverter.output_to_db(data)) == False:
print("\nInvalid configuration detected. No configuration changes processed. Exiting...\n")
sys.exit(1)

if args.print_data:
print(json.dumps(FormatConverter.to_serialized(data), indent=4, cls=minigraph_encoder))

Expand Down