Skip to content

Commit

Permalink
[show/config]: combine feature and container feature cli (#1015)
Browse files Browse the repository at this point in the history
merge container feature cli into feature cli

config command:
```
admin@vlab-01:~$ sudo config feature
Usage: config feature [OPTIONS] COMMAND [ARGS]...

  Modify configuration of features

Options:
  -?, -h, --help  Show this message and exit.

Commands:
  autorestart  Configure autorestart status for a feature
  state        Configure status of feature
```

show command:
```
admin@vlab-01:~$ show feature
Usage: show feature [OPTIONS] COMMAND [ARGS]...

  Show feature status

Options:
  -?, -h, --help  Show this message and exit.

Commands:
  autorestart  Show auto-restart status for a feature
  status       Show feature status
```

output:

```
admin@vlab-01:~$ show feature status
Feature     Status    AutoRestart
----------  --------  -------------
lldp        enabled   enabled
pmon        enabled   enabled
sflow       disabled  enabled
database    enabled   disabled
restapi     disabled  enabled
telemetry   enabled   enabled
snmp        enabled   enabled
bgp         enabled   enabled
radv        enabled   enabled
dhcp_relay  enabled   enabled
nat         enabled   enabled
teamd       enabled   enabled
syncd       enabled   enabled
swss        enabled   enabled
admin@vlab-01:~$ show feature autorestart
Feature     AutoRestart
----------  -------------
lldp        enabled
pmon        enabled
sflow       enabled
database    disabled
restapi     enabled
telemetry   enabled
snmp        enabled
bgp         enabled
radv        enabled
dhcp_relay  enabled
nat         enabled
teamd       enabled
syncd       enabled
swss        enabled
```

Signed-off-by: Guohan Lu <lguohan@gmail.com>
  • Loading branch information
lguohan authored and abdosi committed Aug 9, 2020
1 parent 098f8e9 commit dad3da5
Show file tree
Hide file tree
Showing 4 changed files with 347 additions and 99 deletions.
126 changes: 67 additions & 59 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
SYSTEMCTL_ACTION_RESET_FAILED="reset-failed"

DEFAULT_NAMESPACE = ''

asic_type = None
config_db = None

# ========================== Syslog wrappers ==========================

def log_debug(msg):
Expand Down Expand Up @@ -387,7 +391,7 @@ def _is_neighbor_ipaddress(config_db, ipaddress):

def _get_all_neighbor_ipaddresses(config_db, ignore_local_hosts=False):
"""Returns list of strings containing IP addresses of all BGP neighbors
if the flag ignore_local_hosts is set to True, additional check to see if
if the flag ignore_local_hosts is set to True, additional check to see if
if the BGP neighbor AS number is same as local BGP AS number, if so ignore that neigbor.
"""
addrs = []
Expand Down Expand Up @@ -527,12 +531,12 @@ def _get_disabled_services_list():
log_warning("Feature is None")
continue

status = feature_table[feature_name]['status']
if not status:
log_warning("Status of feature '{}' is None".format(feature_name))
state = feature_table[feature_name]['state']
if not state:
log_warning("Enable state of feature '{}' is None".format(feature_name))
continue

if status == "disabled":
if state == "disabled":
disabled_services_list.append(feature_name)
else:
log_warning("Unable to retreive FEATURE table")
Expand Down Expand Up @@ -632,13 +636,31 @@ def is_ipaddress(val):
@click.group(cls=AbbreviationGroup, context_settings=CONTEXT_SETTINGS)
def config():
"""SONiC command line - 'config' command"""
#
# Load asic_type for further use
#
global asic_type

try:
version_info = sonic_device_util.get_sonic_version_info()
asic_type = version_info['asic_type']
except KeyError, TypeError:
raise click.Abort()

if asic_type == 'mellanox':
platform.add_command(mlnx.mlnx)

if os.geteuid() != 0:
exit("Root privileges are required for this operation")

# Load the global config file database_global.json once.
SonicDBConfig.load_sonic_global_db_config()

global config_db

config_db = ConfigDBConnector()
config_db.connect()

config.add_command(aaa.aaa)
config.add_command(aaa.tacacs)
# === Add NAT Configuration ==========
Expand Down Expand Up @@ -745,7 +767,7 @@ def load(filename, yes):
# if any of the config files in linux host OR namespace is not present, return
if not os.path.isfile(file):
click.echo("The config_db file {} doesn't exist".format(file))
return
return

if namespace is None:
command = "{} -j {} --write-to-db".format(SONIC_CFGGEN_PATH, file)
Expand Down Expand Up @@ -961,7 +983,7 @@ def load_minigraph(no_service_restart):

if os.path.isfile('/etc/sonic/acl.json'):
run_command("acl-loader update full /etc/sonic/acl.json", display_cmd=True)

# Write latest db version string into db
db_migrator='/usr/bin/db_migrator.py'
if os.path.isfile(db_migrator) and os.access(db_migrator, os.X_OK):
Expand All @@ -971,7 +993,7 @@ def load_minigraph(no_service_restart):
else:
cfggen_namespace_option = " -n {}".format(namespace)
run_command(db_migrator + ' -o set_version' + cfggen_namespace_option)

# We first run "systemctl reset-failed" to remove the "failed"
# status from all services before we attempt to restart them
if not no_service_restart:
Expand Down Expand Up @@ -1512,7 +1534,7 @@ def add_snmp_agent_address(ctx, agentip, port, vrf):
#Construct SNMP_AGENT_ADDRESS_CONFIG table key in the format ip|<port>|<vrf>
key = agentip+'|'
if port:
key = key+port
key = key+port
key = key+'|'
if vrf:
key = key+vrf
Expand All @@ -1533,7 +1555,7 @@ def del_snmp_agent_address(ctx, agentip, port, vrf):

key = agentip+'|'
if port:
key = key+port
key = key+port
key = key+'|'
if vrf:
key = key+vrf
Expand Down Expand Up @@ -1752,7 +1774,7 @@ def all(verbose):
config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace)
config_db.connect()
bgp_neighbor_ip_list = _get_all_neighbor_ipaddresses(config_db, ignore_local_hosts)
for ipaddress in bgp_neighbor_ip_list:
for ipaddress in bgp_neighbor_ip_list:
_change_bgp_session_status_by_addr(config_db, ipaddress, 'up', verbose)

# 'neighbor' subcommand
Expand Down Expand Up @@ -1907,7 +1929,7 @@ def speed(ctx, interface_name, interface_speed, verbose):
run_command(command, display_cmd=verbose)

def _get_all_mgmtinterface_keys():
"""Returns list of strings containing mgmt interface keys
"""Returns list of strings containing mgmt interface keys
"""
config_db = ConfigDBConnector()
config_db.connect()
Expand Down Expand Up @@ -2622,9 +2644,6 @@ def asymmetric(ctx, interface_name, status):
def platform():
"""Platform-related configuration tasks"""

if asic_type == 'mellanox':
platform.add_command(mlnx.mlnx)

# 'firmware' subgroup ("config platform firmware ...")
@platform.group(cls=AbbreviationGroup)
def firmware():
Expand Down Expand Up @@ -2820,7 +2839,7 @@ def add_ntp_server(ctx, ntp_ip_address):
if ntp_ip_address in ntp_servers:
click.echo("NTP server {} is already configured".format(ntp_ip_address))
return
else:
else:
db.set_entry('NTP_SERVER', ntp_ip_address, {'NULL': 'NULL'})
click.echo("NTP server {} added to configuration".format(ntp_ip_address))
try:
Expand All @@ -2841,7 +2860,7 @@ def del_ntp_server(ctx, ntp_ip_address):
if ntp_ip_address in ntp_servers:
db.set_entry('NTP_SERVER', '{}'.format(ntp_ip_address), None)
click.echo("NTP server {} removed from configuration".format(ntp_ip_address))
else:
else:
ctx.fail("NTP server {} is not configured.".format(ntp_ip_address))
try:
click.echo("Restarting ntp-config service...")
Expand Down Expand Up @@ -3129,58 +3148,47 @@ def delete(ctx):
config_db.set_entry('SFLOW', 'global', sflow_tbl['global'])

#
# 'feature' command ('config feature name state')
#
@config.command('feature')
@click.argument('name', metavar='<feature-name>', required=True)
@click.argument('state', metavar='<feature-state>', required=True, type=click.Choice(["enabled", "disabled"]))
def feature_status(name, state):
""" Configure status of feature"""
config_db = ConfigDBConnector()
config_db.connect()
status_data = config_db.get_entry('FEATURE', name)

if not status_data:
click.echo(" Feature '{}' doesn't exist".format(name))
return

config_db.mod_entry('FEATURE', name, {'status': state})

#
# 'container' group ('config container ...')
# 'feature' group ('config feature ...')
#
@config.group(cls=AbbreviationGroup, name='container', invoke_without_command=False)
def container():
"""Modify configuration of containers"""
@config.group(cls=AbbreviationGroup, name='feature', invoke_without_command=False)
def feature():
"""Modify configuration of features"""
pass

#
# 'feature' group ('config container feature ...')
# 'state' command ('config feature state ...')
#
@container.group(cls=AbbreviationGroup, name='feature', invoke_without_command=False)
def feature():
"""Modify configuration of container features"""
pass
@feature.command('state', short_help="Enable/disable a feature")
@click.argument('name', metavar='<feature-name>', required=True)
@click.argument('state', metavar='<state>', required=True, type=click.Choice(["enabled", "disabled"]))
def feature_state(name, state):
"""Enable/disable a feature"""
state_data = config_db.get_entry('FEATURE', name)

if not state_data:
click.echo("Feature '{}' doesn't exist".format(name))
sys.exit(1)

config_db.mod_entry('FEATURE', name, {'state': state})

#
# 'autorestart' subcommand ('config container feature autorestart ...')
# 'autorestart' command ('config feature autorestart ...')
#
@feature.command(name='autorestart', short_help="Configure the status of autorestart feature for specific container")
@click.argument('container_name', metavar='<container_name>', required=True)
@click.argument('autorestart_status', metavar='<autorestart_status>', required=True, type=click.Choice(["enabled", "disabled"]))
def autorestart(container_name, autorestart_status):
config_db = ConfigDBConnector()
config_db.connect()
container_feature_table = config_db.get_table('CONTAINER_FEATURE')
if not container_feature_table:
click.echo("Unable to retrieve container feature table from Config DB.")
return
@feature.command(name='autorestart', short_help="Enable/disable autosrestart of a feature")
@click.argument('name', metavar='<feature-name>', required=True)
@click.argument('autorestart', metavar='<autorestart>', required=True, type=click.Choice(["enabled", "disabled"]))
def feature_autorestart(name, autorestart):
"""Enable/disable autorestart of a feature"""
feature_table = config_db.get_table('FEATURE')
if not feature_table:
click.echo("Unable to retrieve feature table from Config DB.")
sys.exit(1)

if not container_feature_table.has_key(container_name):
click.echo("Unable to retrieve features for container '{}'".format(container_name))
return
if not feature_table.has_key(name):
click.echo("Unable to retrieve feature '{}'".format(name))
sys.exit(1)

config_db.mod_entry('CONTAINER_FEATURE', container_name, {'auto_restart': autorestart_status})
config_db.mod_entry('FEATURE', name, {'auto_restart': autorestart})

if __name__ == '__main__':
config()
82 changes: 43 additions & 39 deletions show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

VLAN_SUB_INTERFACE_SEPARATOR = '.'

config_db = None

try:
# noinspection PyPep8Naming
import ConfigParser as configparser
Expand Down Expand Up @@ -548,7 +550,10 @@ def get_bgp_neighbor_ip_to_name(ip, static_neighbors, dynamic_neighbors):
@click.group(cls=AliasedGroup, context_settings=CONTEXT_SETTINGS)
def cli():
"""SONiC command line - 'show' command"""
pass
global config_db

config_db = ConfigDBConnector()
config_db.connect()

#
# 'vrf' command ("show vrf")
Expand Down Expand Up @@ -1570,7 +1575,7 @@ def protocol(verbose):
from .bgp_quagga_v6 import bgp
ipv6.add_command(bgp)
elif routing_stack == "frr":
from .bgp_frr_v4 import bgp
from .bgp_frr_v4 import bgp
ip.add_command(bgp)
from .bgp_frr_v6 import bgp
ipv6.add_command(bgp)
Expand Down Expand Up @@ -2765,53 +2770,52 @@ def ztp(status, verbose):
run_command(cmd, display_cmd=verbose)

#
# show features
#
@cli.command('features')
def features():
"""Show status of optional features"""
config_db = ConfigDBConnector()
config_db.connect()
header = ['Feature', 'Status']
body = []
status_data = config_db.get_table('FEATURE')
for key in status_data.keys():
body.append([key, status_data[key]['status']])
click.echo(tabulate(body, header))

#
# 'container' group (show container ...)
# 'feature' group (show feature ...)
#
@cli.group(name='container', invoke_without_command=False)
def container():
"""Show container"""
@cli.group(name='feature', invoke_without_command=False)
def feature():
"""Show feature status"""
pass

#
# 'feature' group (show container feature ...)
# 'state' subcommand (show feature status)
#
@container.group(name='feature', invoke_without_command=False)
def feature():
"""Show container feature"""
pass
@feature.command('status', short_help="Show feature state")
@click.argument('feature_name', required=False)
def autorestart(feature_name):
header = ['Feature', 'State', 'AutoRestart']
body = []
feature_table = config_db.get_table('FEATURE')
if feature_name:
if feature_table and feature_table.has_key(feature_name):
body.append([feature_name, feature_table[feature_name]['state'], \
feature_table[feature_name]['auto_restart']])
else:
click.echo("Can not find feature {}".format(feature_name))
sys.exit(1)
else:
for key in natsorted(feature_table.keys()):
body.append([key, feature_table[key]['state'], feature_table[key]['auto_restart']])
click.echo(tabulate(body, header))

#
# 'autorestart' subcommand (show container feature autorestart)
# 'autorestart' subcommand (show feature autorestart)
#
@feature.command('autorestart', short_help="Show whether the auto-restart feature for container(s) is enabled or disabled")
@click.argument('container_name', required=False)
def autorestart(container_name):
config_db = ConfigDBConnector()
config_db.connect()
header = ['Container Name', 'Status']
@feature.command('autorestart', short_help="Show auto-restart state for a feature")
@click.argument('feature_name', required=False)
def autorestart(feature_name):
header = ['Feature', 'AutoRestart']
body = []
container_feature_table = config_db.get_table('CONTAINER_FEATURE')
if container_name:
if container_feature_table and container_feature_table.has_key(container_name):
body.append([container_name, container_feature_table[container_name]['auto_restart']])
feature_table = config_db.get_table('FEATURE')
if feature_name:
if feature_table and feature_table.has_key(feature_name):
body.append([feature_name, feature_table[feature_name]['auto_restart']])
else:
click.echo("Can not find feature {}".format(feature_name))
sys.exit(1)
else:
for name in container_feature_table.keys():
body.append([name, container_feature_table[name]['auto_restart']])
for name in natsorted(feature_table.keys()):
body.append([name, feature_table[name]['auto_restart']])
click.echo(tabulate(body, header))

#
Expand Down
Loading

0 comments on commit dad3da5

Please sign in to comment.