Skip to content

Commit

Permalink
[sonic-utilities] managementVRF cli support(l3mdev)
Browse files Browse the repository at this point in the history
This commit adds CLI support for management VRF using l3dev. mVRF can
be enabled using config vrf add mgmt and deleted using config vrf del mgmt.
Show commands for management VRF are added which displays the linux command
output, will update show command display after concluding what would be the
output for the show commands.
Added cli to configure management interface(eth0), config interface ip eth0
add can be used to configure eth0 ip and config ip eth0 remove is used to
remove eth0 ip.

New cli config/show commands:

config vrf add mgmt
config vrf del mgmt
config interface eth0 ip add ip/mask gatewayIP
config interface eth0 ip remove ip/mask

show mgmt-vrf
show mgmt-vrf route
show mgmt-vrf addresses
show mgmt-vrf interfaces

Signed-off-by: Harish Venkatraman <[email protected]>
  • Loading branch information
Harish Venkatraman committed Jul 17, 2019
1 parent ee56d54 commit b5647b9
Show file tree
Hide file tree
Showing 2 changed files with 309 additions and 4 deletions.
215 changes: 213 additions & 2 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import json
import subprocess
import netaddr
import logging
import logging.handlers
import re
import syslog

Expand Down Expand Up @@ -789,6 +791,174 @@ 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"""

config_db = ConfigDBConnector()
config_db.connect()
entry = config_db.get_entry('MGMT_VRF_CONFIG', "vrf_global")
if entry and entry['mgmtVrfEnabled'] == 'true' :
click.echo("ManagementVRF is already Enabled.")
return None
config_db.mod_entry('MGMT_VRF_CONFIG',"vrf_global",{"mgmtVrfEnabled": "true"})
cmd="service ntp stop"
os.system (cmd)
cmd="systemctl restart interfaces-config"
os.system (cmd)
cmd="service ntp start"
os.system (cmd)


def vrf_delete_management_vrf():
"""Disable management vrf"""

config_db = ConfigDBConnector()
config_db.connect()
entry = config_db.get_entry('MGMT_VRF_CONFIG', "vrf_global")
if not entry or entry['mgmtVrfEnabled'] == 'false' :
click.echo("ManagementVRF is already Disabled.")
return None
config_db.mod_entry('MGMT_VRF_CONFIG',"vrf_global",{"mgmtVrfEnabled": "false"})
cmd="service ntp stop"
os.system (cmd)
cmd="systemctl restart interfaces-config"
os.system (cmd)
cmd="service ntp start"
os.system (cmd)


#
# 'vrf' group ('config vrf ...')
#

@config.group('vrf')
def vrf():
"""VRF-related configuration tasks"""
pass


@vrf.command('add')
@click.argument('vrfname', metavar='<vrfname>. 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='<vrfname>. 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)

@config.group()
def snmpagentaddress():
"""SNMP agentaddress Related Task"""
pass

@snmpagentaddress.command('add')
@click.argument('agentip', metavar='<SNMP AGENT IP Address>', required=True)
@click.argument('vrf', metavar='<VRF name mgmt/default/data>', required=False)
@click.pass_context

def add_snmp_agent_address(ctx, agentip, vrf):
"""Add the SNMP agent IP and VRF configuration"""

config_db = ConfigDBConnector()
config_db.connect()
if vrf:
entry = config_db.get_entry('MGMT_VRF_CONFIG', "vrf_global")
if entry == {}:
click.echo('Cannot set SNMP Agent ip address using when management VRF is not yet configured.')
return
if entry['mgmtVrfEnabled'] == "false" :
click.echo('Cannot set SNMP Agent ip address using when management VRF is not yet enabled.')
return

config_db.set_entry('SNMP_AGENT_ADDRESS_CONFIG', (agentip, vrf), {})
cmd="systemctl restart snmp"
os.system (cmd)

@snmpagentaddress.command('del')
@click.argument('agentip', metavar='<SNMP AGENT IP Address>', required=True)
@click.argument('vrf', metavar='<VRF name mgmt/default/data>', required=False)
@click.pass_context

def del_snmp_agent_address(ctx, agentip, vrf):
"""Del the SNMP agent IP and VRF configuration"""

config_db = ConfigDBConnector()
config_db.connect()
if vrf:
entry = config_db.get_entry('MGMT_VRF_CONFIG', "vrf_global")
if entry == {}:
click.echo('Cannot set SNMP Agent ip address using when management VRF is not yet configured.')
return
if entry['mgmtVrfEnabled'] == "false" :
click.echo('Cannot set SNMP Agent ip address using when management VRF is not yet enabled.')
return

config_db.get_entry('SNMP_AGENT_ADDRESS_CONFIG', (agentip, vrf))
config_db.set_entry('SNMP_AGENT_ADDRESS_CONFIG', (agentip, vrf), None)
cmd="systemctl restart snmp"
os.system (cmd)

@config.group()
def snmptrap():
"""SNMP Traps Related Task"""
pass

@snmptrap.command('modify')
@click.argument('ver', metavar='<SNMP Version>', type=click.Choice(['1', '2', '3']), required=True)
@click.argument('serverip', metavar='<SNMP TRAP SERVER IP Address>', required=True)
@click.argument('vrf', metavar='<VRF name mgmt/default/data>', required=False)
@click.argument('trapport', metavar='<SNMP Trap Server port, default 162>', required=False)
@click.pass_context
def modify_snmptrap_server(ctx, ver, serverip, vrf, trapport):
"""Add the SNMP Trap server configuration"""
if not trapport:
trapport = 162

config_db = ConfigDBConnector()
config_db.connect()
if vrf and vrf == "mgmt":
entry = config_db.get_entry('MGMT_VRF_CONFIG', "vrf_global")
if entry == {}:
click.echo('Cannot set SNMPTrap server using --use-mgmt-vrf when management VRF is not yet configured.')
return
if entry['mgmtVrfEnabled'] == "false" :
click.echo('Cannot set SNMPTrap server using --use-mgmt-vrf when management VRF is not yet enabled.')
return

if ver == "1":
config_db.mod_entry('SNMP_TRAP_CONFIG',"v1TrapDest",{"DestIp": serverip, "DestPort": trapport, "vrf": vrf})
elif ver == "2":
config_db.mod_entry('SNMP_TRAP_CONFIG',"v2TrapDest",{"DestIp": serverip, "DestPort": trapport, "vrf": vrf})
else:
config_db.mod_entry('SNMP_TRAP_CONFIG',"v3TrapDest",{"DestIp": serverip, "DestPort": trapport, "vrf": vrf})
cmd="systemctl restart snmp"
os.system (cmd)

#
# 'bgp' group ('config bgp ...')
#
Expand Down Expand Up @@ -925,6 +1095,14 @@ def speed(ctx, interface_name, interface_speed, 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()


#
# 'ip' subgroup ('config interface ip ...')
#
Expand All @@ -942,8 +1120,9 @@ def ip(ctx):
@ip.command()
@click.argument('interface_name', metavar='<interface_name>', required=True)
@click.argument("ip_addr", metavar="<ip_addr>", required=True)
@click.argument('gw', metavar='<default gateway IP address>', required=False)
@click.pass_context
def add(ctx, interface_name, ip_addr):
def add(ctx, interface_name, ip_addr, gw):
"""Add an IP address towards the interface"""
config_db = ctx.obj["config_db"]
if get_interface_naming_mode() == "alias":
Expand All @@ -956,6 +1135,32 @@ def add(ctx, interface_name, ip_addr):
if interface_name.startswith("Ethernet"):
config_db.set_entry("INTERFACE", (interface_name, ip_addr), {"NULL": "NULL"})
config_db.set_entry("INTERFACE", interface_name, {"NULL": "NULL"})
elif interface_name == 'eth0':

# 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 gw:
config_db.set_entry("MGMT_INTERFACE", (interface_name, ip_addr), {"NULL": "NULL"})
else:
config_db.set_entry("MGMT_INTERFACE", (interface_name, ip_addr), {"gwaddr": gw})
cmd="systemctl restart interfaces-config"
os.system (cmd)
cmd="systemctl restart ntp-config"
os.system (cmd)

elif interface_name.startswith("PortChannel"):
config_db.set_entry("PORTCHANNEL_INTERFACE", (interface_name, ip_addr), {"NULL": "NULL"})
config_db.set_entry("PORTCHANNEL_INTERFACE", interface_name, {"NULL": "NULL"})
Expand Down Expand Up @@ -991,6 +1196,12 @@ def remove(ctx, interface_name, ip_addr):
if interface_name.startswith("Ethernet"):
config_db.set_entry("INTERFACE", (interface_name, ip_addr), None)
if_table = "INTERFACE"
elif interface_name == 'eth0':
config_db.set_entry("MGMT_INTERFACE", (interface_name, ip_addr), None)
cmd="systemctl restart interfaces-config"
os.system (cmd)
cmd="systemctl restart ntp-config"
os.system (cmd)
elif interface_name.startswith("PortChannel"):
config_db.set_entry("PORTCHANNEL_INTERFACE", (interface_name, ip_addr), None)
if_table = "PORTCHANNEL_INTERFACE"
Expand All @@ -1003,7 +1214,7 @@ def remove(ctx, interface_name, ip_addr):
ctx.fail("'interface_name' is not valid. Valid names [Ethernet/PortChannel/Vlan/Loopback]")
except ValueError:
ctx.fail("'ip_addr' is not valid.")

exists = False
if if_table:
interfaces = config_db.get_table(if_table)
Expand Down
98 changes: 96 additions & 2 deletions show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,93 @@ 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("\nManagement VRF in Linux:")
cmd = "sudo ip link show type vrf"
run_command(cmd)

@mgmt_vrf.command('interfaces')
def mgmt_vrf_interfaces ():
"""Show management VRF attributes"""

click.echo("\neth0 Interfaces in Management VRF:")
cmd = "sudo ifconfig eth0"
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 route show table 1001"
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 address show mgmt"
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 ...")
#
Expand Down Expand Up @@ -1443,8 +1530,15 @@ def bgp(verbose):
@click.option('--verbose', is_flag=True, help="Enable verbose output")
def ntp(verbose):
"""Show NTP information"""
cmd = "ntpq -p -n"
run_command(cmd, display_cmd=verbose)
config_db = ConfigDBConnector()
config_db.connect()
entry = config_db.get_entry('MGMT_VRF_CONFIG', "vrf_global")
if entry and entry['mgmtVrfEnabled'] == 'true' :
cmd = "mgmtvrf_ntpq -p -n"
run_command(cmd, display_cmd=verbose)
else:
cmd = "ntpq -p -n"
run_command(cmd, display_cmd=verbose)


#
Expand Down

0 comments on commit b5647b9

Please sign in to comment.