|
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