From f714e86b0cc6a223e79b5965dadb3d46785f5b9b Mon Sep 17 00:00:00 2001 From: Kannan KVS Date: Mon, 31 Dec 2018 04:48:29 -0800 Subject: [PATCH] mgmt_vrf_namespace: management vrf using namespace solution --- config/main.py | 161 ++++++++++++++++++++++++++++++++++++++++++++++++- show/main.py | 86 ++++++++++++++++++++++++++ 2 files changed, 246 insertions(+), 1 deletion(-) diff --git a/config/main.py b/config/main.py index bbd715b6e7..741fb80be3 100755 --- a/config/main.py +++ b/config/main.py @@ -6,6 +6,9 @@ import json import subprocess import netaddr +import syslog +import logging +import logging.handlers import re from swsssdk import ConfigDBConnector from natsort import natsorted @@ -662,6 +665,96 @@ def del_vlan_member(ctx, vid, interface_name): db.set_entry('VLAN_MEMBER', (vlan_name, interface_name), None) +def vrf_add_management_vrf(): + """Enable management vrf""" + + cmd = 'sonic-cfggen -d --var-json "MGMT_VRF_CONFIG"' + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + res = p.communicate() + if p.returncode == 0 : + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + mvrf_dict = json.loads(p.stdout.read()) + + # if the mgmtVrfEnabled attribute is configured, check the value + # and modify only if it is changed. + if 'mgmtVrfEnabled' in mvrf_dict['vrf_global']: + if (mvrf_dict['vrf_global']['mgmtVrfEnabled'] == "true"): + click.echo("ManagementVRF is already Enabled.") + return None + + config_db = ConfigDBConnector() + config_db.connect() + config_db.mod_entry('MGMT_VRF_CONFIG',"vrf_global",{"mgmtVrfEnabled": "true"}) + + +def vrf_delete_management_vrf(): + """Disable management vrf""" + + cmd = 'sonic-cfggen -d --var-json "MGMT_VRF_CONFIG"' + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + res = p.communicate() + if p.returncode == 0 : + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + mvrf_dict = json.loads(p.stdout.read()) + + # if the mgmtVrfEnabled attribute is configured, check the value + # and modify only if it is changed. + if 'mgmtVrfEnabled' in mvrf_dict['vrf_global']: + if (mvrf_dict['vrf_global']['mgmtVrfEnabled'] == "false"): + click.echo("ManagementVRF is already Disabled.") + return None + else: + click.echo("ManagementVRF is already Disabled.") + return None + + config_db = ConfigDBConnector() + config_db.connect() + config_db.mod_entry('MGMT_VRF_CONFIG',"vrf_global",{"mgmtVrfEnabled": "false"}) + + +# +# 'vrf' group ('config vrf ...') +# + +@config.group('vrf') +def vrf(): + """VRF-related configuration tasks""" + pass + + +@vrf.command('add') +@click.argument('vrfname', metavar='. Type mgmt for management VRF', required=True) +@click.pass_context +def vrf_add (ctx, vrfname): + """VRF ADD""" + if vrfname == 'mgmt' or vrfname == 'management': + vrf_add_management_vrf() + else: + click.echo("Creation of data vrf={} is not yet supported".format(vrfname)) + + +@vrf.command('del') +@click.argument('vrfname', metavar='. Type mgmt for management VRF', required=False) +@click.pass_context +def vrf_del (ctx, vrfname): + """VRF Delete""" + if vrfname == 'mgmt' or vrfname == 'management': + vrf_delete_management_vrf() + else: + click.echo("Deletion of data vrf={} is not yet supported".format(vrfname)) + + +@config.command('clear_mgmt') +@click.pass_context +def clear_mgmt(ctx): + MGMT_TABLE_NAMES = [ + 'MGMT_INTERFACE', + 'MGMT_VRF_CONFIG'] + config_db = ConfigDBConnector() + config_db.connect() + for mgmt_table in MGMT_TABLE_NAMES: + config_db.delete_table(mgmt_table) + # # 'bgp' group # @@ -787,6 +880,42 @@ def speed(ctx, verbose): command += " -vv" run_command(command, display_cmd=verbose) +def _get_all_mgmtinterface_keys(): + """Returns list of strings containing mgmt interface keys + """ + config_db = ConfigDBConnector() + config_db.connect() + return config_db.get_table('MGMT_INTERFACE').keys() + +def is_address_in_network(network, address): + """ + Determine whether the provided address is within a network range. + + :param network (str): CIDR presentation format. For example, + '192.168.1.0/24'. + :param address: An individual IPv4 or IPv6 address without a net + mask or subnet prefix. For example, '192.168.1.1'. + :returns boolean: Flag indicating whether address is in network. + """ + try: + network = netaddr.IPNetwork(network) + # There wont be any exception if the IPNetwork is valid + except (netaddr.core.AddrFormatError, ValueError): + raise ValueError("Network (%s) is not in CIDR presentation format" % + network) + + try: + address = netaddr.IPAddress(address) + # There wont be any exception if the IPAddress is valid + except (netaddr.core.AddrFormatError, ValueError): + raise ValueError("Address (%s) is not in correct presentation format" % + address) + + if address in network: + return True + else: + return False + # # 'ip' subgroup # @@ -803,14 +932,42 @@ def ip(ctx): @ip.command() @click.argument("ip_addr", metavar="", required=True) +@click.argument('gw1', metavar='', required=False) @click.pass_context -def add(ctx, ip_addr): +def add(ctx, ip_addr, gw1): """Add an IP address towards the interface""" config_db = ctx.obj["config_db"] interface_name = ctx.obj["interface_name"] if interface_name.startswith("Ethernet"): config_db.set_entry("INTERFACE", (interface_name, ip_addr), {"NULL": "NULL"}) + elif interface_name.startswith("eth0"): + # Validate the ip/mask + ipaddress= ip_addr.split("/") + if is_address_in_network(ip_addr, ipaddress[0]) == False: + click.echo ("Its an invalid ip/mask value") + return + + # Configuring more than 1 IPv4 or more than 1 IPv6 address fails. + # Allow only one IPv4 and only one IPv6 address to be configured for IPv6. + # If a row already exist, overwrite it (by doing delete and add). + mgmtintf_key_list = _get_all_mgmtinterface_keys() + + for key in mgmtintf_key_list: + # For loop runs for max 2 rows, once for IPv4 and once for IPv6. + if ':' in ip_addr and ':' in key[1]: + # If user has configured IPv6 address and the already available row is also IPv6, delete it here. + config_db.set_entry("MGMT_INTERFACE", ("eth0", key[1]), None) + elif ':' not in ip_addr and ':' not in key[1]: + # If user has configured IPv4 address and the already available row is also IPv6, delete it here. + config_db.set_entry("MGMT_INTERFACE", ("eth0", key[1]), None) + + # Set the new row with new value + if not gw1: + config_db.set_entry("MGMT_INTERFACE", (interface_name, ip_addr), {"NULL": "NULL"}) + else: + config_db.set_entry("MGMT_INTERFACE", (interface_name, ip_addr), {"gwaddr": gw1}) + elif interface_name.startswith("PortChannel"): config_db.set_entry("PORTCHANNEL_INTERFACE", (interface_name, ip_addr), {"NULL": "NULL"}) @@ -828,6 +985,8 @@ def remove(ctx, ip_addr): if interface_name.startswith("Ethernet"): config_db.set_entry("INTERFACE", (interface_name, ip_addr), None) + elif interface_name.startswith("eth0"): + config_db.set_entry("MGMT_INTERFACE", (interface_name, ip_addr), None) elif interface_name.startswith("PortChannel"): config_db.set_entry("PORTCHANNEL_INTERFACE", (interface_name, ip_addr), None) # diff --git a/show/main.py b/show/main.py index 97cbd73c3d..a656797501 100755 --- a/show/main.py +++ b/show/main.py @@ -390,6 +390,92 @@ def ndp(ip6address, iface, verbose): run_command(cmd, display_cmd=verbose) +# +# 'mgmt-vrf' group ("show mgmt-vrf ...") +# + +@cli.group('mgmt-vrf', invoke_without_command=True) +@click.pass_context +def mgmt_vrf(ctx): + + """Show management VRF attributes""" + + if ctx.invoked_subcommand is None: + cmd = 'sonic-cfggen -d --var-json "MGMT_VRF_CONFIG"' + + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + res = p.communicate() + if p.returncode == 0 : + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + mvrf_dict = json.loads(p.stdout.read()) + + # if the mgmtVrfEnabled attribute is configured, check the value + # and print Enabled or Disabled accordingly. + if 'mgmtVrfEnabled' in mvrf_dict['vrf_global']: + if (mvrf_dict['vrf_global']['mgmtVrfEnabled'] == "true"): + click.echo("\nManagementVRF : Enabled") + else: + click.echo("\nManagementVRF : Disabled") + + click.echo("\nNameSpaces in Linux:") + cmd = "sudo ip netns list" + run_command(cmd) + +@mgmt_vrf.command('interfaces') +def mgmt_vrf_interfaces (): + """Show management VRF attributes""" + + click.echo("\nInterfaces in Management VRF:") + cmd = "sudo ip netns exec mgmt ifconfig" + run_command(cmd) + return None + +@mgmt_vrf.command('route') +def mgmt_vrf_route (): + """Show management VRF routes""" + + click.echo("\nRoutes in Management VRF Routing Table:") + cmd = "sudo ip netns exec mgmt ip route show" + run_command(cmd) + return None + + +@mgmt_vrf.command('addresses') +def mgmt_vrf_addresses (): + """Show management VRF addresses""" + + click.echo("\nIP Addresses for interfaces in Management VRF:") + cmd = "sudo ip netns exec mgmt ip address show" + run_command(cmd) + return None + + + +# +# 'management_interface' group ("show management_interface ...") +# + +@cli.group(cls=AliasedGroup, default_if_no_args=False) +def management_interface(): + """Show management interface parameters""" + pass + +# 'address' subcommand ("show management_interface address") +@management_interface.command() +def address (): + """Show IP address configured for management interface""" + + config_db = ConfigDBConnector() + config_db.connect() + header = ['IFNAME', 'IP Address', 'PrefixLen',] + body = [] + + # Fetching data from config_db for MGMT_INTERFACE + mgmt_ip_data = config_db.get_table('MGMT_INTERFACE') + for key in natsorted(mgmt_ip_data.keys()): + click.echo("Management IP address = {0}".format(key[1])) + click.echo("Management NetWork Default Gateway = {0}".format(mgmt_ip_data[key]['gwaddr'])) + # # 'interfaces' group ("show interfaces ...") #