|
14 | 14 | import itertools
|
15 | 15 | import copy
|
16 | 16 |
|
| 17 | +from jsonpatch import JsonPatchConflict |
17 | 18 | from collections import OrderedDict
|
18 | 19 | from generic_config_updater.generic_updater import GenericUpdater, ConfigFormat
|
19 | 20 | from minigraph import parse_device_desc_xml, minigraph_encoder
|
|
31 | 32 | import utilities_common.cli as clicommon
|
32 | 33 | from utilities_common.helper import get_port_pbh_binding, get_port_acl_binding
|
33 | 34 | from utilities_common.general import load_db_config, load_module_from_source
|
| 35 | +from .validated_config_db_connector import ValidatedConfigDBConnector |
34 | 36 | import utilities_common.multi_asic as multi_asic_util
|
35 | 37 |
|
36 | 38 | from .utils import log
|
|
104 | 106 | TTL_RANGE = click.IntRange(min=0, max=255)
|
105 | 107 | QUEUE_RANGE = click.IntRange(min=0, max=255)
|
106 | 108 | GRE_TYPE_RANGE = click.IntRange(min=0, max=65535)
|
| 109 | +ADHOC_VALIDATION = True |
107 | 110 |
|
108 | 111 | # Load sonic-cfggen from source since /usr/local/bin/sonic-cfggen does not have .py extension.
|
109 | 112 | sonic_cfggen = load_module_from_source('sonic_cfggen', '/usr/local/bin/sonic-cfggen')
|
@@ -2040,51 +2043,64 @@ def portchannel(db, ctx, namespace):
|
2040 | 2043 | @click.pass_context
|
2041 | 2044 | def add_portchannel(ctx, portchannel_name, min_links, fallback, fast_rate):
|
2042 | 2045 | """Add port channel"""
|
2043 |
| - if is_portchannel_name_valid(portchannel_name) != True: |
2044 |
| - ctx.fail("{} is invalid!, name should have prefix '{}' and suffix '{}'" |
2045 |
| - .format(portchannel_name, CFG_PORTCHANNEL_PREFIX, CFG_PORTCHANNEL_NO)) |
2046 |
| - |
2047 |
| - db = ctx.obj['db'] |
2048 |
| - |
2049 |
| - if is_portchannel_present_in_db(db, portchannel_name): |
2050 |
| - ctx.fail("{} already exists!".format(portchannel_name)) |
2051 |
| - |
| 2046 | + |
2052 | 2047 | fvs = {
|
2053 | 2048 | 'admin_status': 'up',
|
2054 | 2049 | 'mtu': '9100',
|
2055 | 2050 | 'lacp_key': 'auto',
|
2056 | 2051 | 'fast_rate': fast_rate.lower(),
|
2057 | 2052 | }
|
| 2053 | + |
2058 | 2054 | if min_links != 0:
|
2059 | 2055 | fvs['min_links'] = str(min_links)
|
2060 | 2056 | if fallback != 'false':
|
2061 | 2057 | fvs['fallback'] = 'true'
|
2062 |
| - db.set_entry('PORTCHANNEL', portchannel_name, fvs) |
2063 |
| - |
| 2058 | + |
| 2059 | + if ADHOC_VALIDATION: |
| 2060 | + db = ctx.obj['db'] |
| 2061 | + if is_portchannel_name_valid(portchannel_name) != True: |
| 2062 | + ctx.fail("{} is invalid!, name should have prefix '{}' and suffix '{}'" |
| 2063 | + .format(portchannel_name, CFG_PORTCHANNEL_PREFIX, CFG_PORTCHANNEL_NO)) |
| 2064 | + if is_portchannel_present_in_db(db, portchannel_name): |
| 2065 | + ctx.fail("{} already exists!".format(portchannel_name)) # TODO: MISSING CONSTRAINT IN YANG MODEL |
| 2066 | + else: |
| 2067 | + db = ValidatedConfigDBConnector(ctx.obj['db']) |
| 2068 | + |
| 2069 | + try: |
| 2070 | + db.set_entry('PORTCHANNEL', portchannel_name, fvs) |
| 2071 | + except ValueError: |
| 2072 | + ctx.fail("{} is invalid!, name should have prefix '{}' and suffix '{}'".format(portchannel_name, CFG_PORTCHANNEL_PREFIX, CFG_PORTCHANNEL_NO)) |
| 2073 | + |
2064 | 2074 | @portchannel.command('del')
|
2065 | 2075 | @click.argument('portchannel_name', metavar='<portchannel_name>', required=True)
|
2066 | 2076 | @click.pass_context
|
2067 | 2077 | def remove_portchannel(ctx, portchannel_name):
|
2068 | 2078 | """Remove port channel"""
|
2069 |
| - if is_portchannel_name_valid(portchannel_name) != True: |
2070 |
| - ctx.fail("{} is invalid!, name should have prefix '{}' and suffix '{}'" |
2071 |
| - .format(portchannel_name, CFG_PORTCHANNEL_PREFIX, CFG_PORTCHANNEL_NO)) |
2072 |
| - |
2073 |
| - db = ctx.obj['db'] |
2074 |
| - |
2075 |
| - # Dont proceed if the port channel does not exist |
2076 |
| - if is_portchannel_present_in_db(db, portchannel_name) is False: |
2077 |
| - ctx.fail("{} is not present.".format(portchannel_name)) |
2078 |
| - |
2079 |
| - # Dont let to remove port channel if vlan membership exists |
2080 |
| - for k,v in db.get_table('VLAN_MEMBER'): |
2081 |
| - if v == portchannel_name: |
2082 |
| - ctx.fail("{} has vlan {} configured, remove vlan membership to proceed".format(portchannel_name, str(k))) |
2083 |
| - |
2084 |
| - if len([(k, v) for k, v in db.get_table('PORTCHANNEL_MEMBER') if k == portchannel_name]) != 0: |
2085 |
| - click.echo("Error: Portchannel {} contains members. Remove members before deleting Portchannel!".format(portchannel_name)) |
| 2079 | + |
| 2080 | + if ADHOC_VALIDATION: |
| 2081 | + db = ctx.obj['db'] |
| 2082 | + if is_portchannel_name_valid(portchannel_name) != True: |
| 2083 | + ctx.fail("{} is invalid!, name should have prefix '{}' and suffix '{}'" |
| 2084 | + .format(portchannel_name, CFG_PORTCHANNEL_PREFIX, CFG_PORTCHANNEL_NO)) |
| 2085 | + |
| 2086 | + # Don't proceed if the port channel does not exist |
| 2087 | + if is_portchannel_present_in_db(db, portchannel_name) is False: |
| 2088 | + ctx.fail("{} is not present.".format(portchannel_name)) |
| 2089 | + |
| 2090 | + # Dont let to remove port channel if vlan membership exists |
| 2091 | + for k,v in db.get_table('VLAN_MEMBER'): # TODO: MISSING CONSTRAINT IN YANG MODEL |
| 2092 | + if v == portchannel_name: |
| 2093 | + ctx.fail("{} has vlan {} configured, remove vlan membership to proceed".format(portchannel_name, str(k))) |
| 2094 | + |
| 2095 | + if len([(k, v) for k, v in db.get_table('PORTCHANNEL_MEMBER') if k == portchannel_name]) != 0: # TODO: MISSING CONSTRAINT IN YANG MODEL |
| 2096 | + ctx.fail("Error: Portchannel {} contains members. Remove members before deleting Portchannel!".format(portchannel_name)) |
2086 | 2097 | else:
|
| 2098 | + db = ValidatedConfigDBConnector(ctx.obj['db']) |
| 2099 | + |
| 2100 | + try: |
2087 | 2101 | db.set_entry('PORTCHANNEL', portchannel_name, None)
|
| 2102 | + except JsonPatchConflict: |
| 2103 | + ctx.fail("{} is not present.".format(portchannel_name)) |
2088 | 2104 |
|
2089 | 2105 | @portchannel.group(cls=clicommon.AbbreviationGroup, name='member')
|
2090 | 2106 | @click.pass_context
|
@@ -2113,8 +2129,8 @@ def add_portchannel_member(ctx, portchannel_name, port_name):
|
2113 | 2129 | # Dont proceed if the port channel does not exist
|
2114 | 2130 | if is_portchannel_present_in_db(db, portchannel_name) is False:
|
2115 | 2131 | ctx.fail("{} is not present.".format(portchannel_name))
|
2116 |
| - |
2117 |
| - # Dont allow a port to be member of port channel if it is configured with an IP address |
| 2132 | + |
| 2133 | + # Don't allow a port to be member of port channel if it is configured with an IP address |
2118 | 2134 | for key,value in db.get_table('INTERFACE').items():
|
2119 | 2135 | if type(key) == tuple:
|
2120 | 2136 | continue
|
@@ -6157,36 +6173,48 @@ def loopback(ctx, redis_unix_socket_path):
|
6157 | 6173 | @click.argument('loopback_name', metavar='<loopback_name>', required=True)
|
6158 | 6174 | @click.pass_context
|
6159 | 6175 | def add_loopback(ctx, loopback_name):
|
6160 |
| - config_db = ctx.obj['db'] |
6161 |
| - if is_loopback_name_valid(loopback_name) is False: |
6162 |
| - ctx.fail("{} is invalid, name should have prefix '{}' and suffix '{}' " |
6163 |
| - .format(loopback_name, CFG_LOOPBACK_PREFIX, CFG_LOOPBACK_NO)) |
6164 |
| - |
6165 |
| - lo_intfs = [k for k, v in config_db.get_table('LOOPBACK_INTERFACE').items() if type(k) != tuple] |
6166 |
| - if loopback_name in lo_intfs: |
6167 |
| - ctx.fail("{} already exists".format(loopback_name)) |
6168 |
| - |
6169 |
| - config_db.set_entry('LOOPBACK_INTERFACE', loopback_name, {"NULL" : "NULL"}) |
| 6176 | + if ADHOC_VALIDATION: |
| 6177 | + config_db = ctx.obj['db'] |
| 6178 | + if is_loopback_name_valid(loopback_name) is False: |
| 6179 | + ctx.fail("{} is invalid, name should have prefix '{}' and suffix '{}' " |
| 6180 | + .format(loopback_name, CFG_LOOPBACK_PREFIX, CFG_LOOPBACK_NO)) |
| 6181 | + |
| 6182 | + lo_intfs = [k for k, v in config_db.get_table('LOOPBACK_INTERFACE').items() if type(k) != tuple] |
| 6183 | + if loopback_name in lo_intfs: |
| 6184 | + ctx.fail("{} already exists".format(loopback_name)) # TODO: MISSING CONSTRAINT IN YANG VALIDATION |
| 6185 | + else: |
| 6186 | + config_db = ValidatedConfigDBConnector(ctx.obj['db']) |
| 6187 | + |
| 6188 | + try: |
| 6189 | + config_db.set_entry('LOOPBACK_INTERFACE', loopback_name, {"NULL" : "NULL"}) |
| 6190 | + except ValueError: |
| 6191 | + ctx.fail("{} is invalid, name should have prefix '{}' and suffix '{}' ".format(loopback_name, CFG_LOOPBACK_PREFIX, CFG_LOOPBACK_NO)) |
6170 | 6192 |
|
6171 | 6193 | @loopback.command('del')
|
6172 | 6194 | @click.argument('loopback_name', metavar='<loopback_name>', required=True)
|
6173 | 6195 | @click.pass_context
|
6174 | 6196 | def del_loopback(ctx, loopback_name):
|
6175 | 6197 | config_db = ctx.obj['db']
|
6176 |
| - if is_loopback_name_valid(loopback_name) is False: |
6177 |
| - ctx.fail("{} is invalid, name should have prefix '{}' and suffix '{}' " |
6178 |
| - .format(loopback_name, CFG_LOOPBACK_PREFIX, CFG_LOOPBACK_NO)) |
6179 |
| - |
6180 | 6198 | lo_config_db = config_db.get_table('LOOPBACK_INTERFACE')
|
6181 |
| - lo_intfs = [k for k, v in lo_config_db.items() if type(k) != tuple] |
6182 |
| - if loopback_name not in lo_intfs: |
6183 |
| - ctx.fail("{} does not exists".format(loopback_name)) |
| 6199 | + |
| 6200 | + if ADHOC_VALIDATION: |
| 6201 | + if is_loopback_name_valid(loopback_name) is False: |
| 6202 | + ctx.fail("{} is invalid, name should have prefix '{}' and suffix '{}' " |
| 6203 | + .format(loopback_name, CFG_LOOPBACK_PREFIX, CFG_LOOPBACK_NO)) |
| 6204 | + lo_intfs = [k for k, v in lo_config_db.items() if type(k) != tuple] |
| 6205 | + if loopback_name not in lo_intfs: |
| 6206 | + ctx.fail("{} does not exist".format(loopback_name)) |
| 6207 | + else: |
| 6208 | + config_db = ValidatedConfigDBConnector(ctx.obj['db']) |
6184 | 6209 |
|
6185 | 6210 | ips = [ k[1] for k in lo_config_db if type(k) == tuple and k[0] == loopback_name ]
|
6186 | 6211 | for ip in ips:
|
6187 | 6212 | config_db.set_entry('LOOPBACK_INTERFACE', (loopback_name, ip), None)
|
6188 |
| - |
6189 |
| - config_db.set_entry('LOOPBACK_INTERFACE', loopback_name, None) |
| 6213 | + |
| 6214 | + try: |
| 6215 | + config_db.set_entry('LOOPBACK_INTERFACE', loopback_name, None) |
| 6216 | + except JsonPatchConflict: |
| 6217 | + ctx.fail("{} does not exist".format(loopback_name)) |
6190 | 6218 |
|
6191 | 6219 |
|
6192 | 6220 | @config.group(cls=clicommon.AbbreviationGroup)
|
|
0 commit comments