Skip to content

Commit

Permalink
[cli/feature]: split feature command into a separate file (sonic-net#…
Browse files Browse the repository at this point in the history
…1034)

create new feature.py for both config and show, move the code into feature.py
move common code into sonic-utiltities.common.cli.py

Signed-off-by: Guohan Lu <[email protected]>
  • Loading branch information
lguohan authored Aug 8, 2020
1 parent 621aad0 commit e722cb0
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 139 deletions.
48 changes: 48 additions & 0 deletions config/feature.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import click

from utilities_common.cli import AbbreviationGroup, pass_db

#
# 'feature' group ('config feature ...')
#
@click.group(cls=AbbreviationGroup, name='feature', invoke_without_command=False)
def feature():
"""Configure features"""
pass

#
# 'state' command ('config feature state ...')
#
@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"]))
@pass_db
def feature_state(db, name, state):
"""Enable/disable a feature"""
state_data = db.cfgdb.get_entry('FEATURE', name)

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

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

#
# 'autorestart' command ('config feature autorestart ...')
#
@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"]))
@pass_db
def feature_autorestart(db, name, autorestart):
"""Enable/disable autorestart of a feature"""
feature_table = db.cfgdb.get_table('FEATURE')
if not feature_table:
click.echo("Unable to retrieve feature table from Config DB.")
sys.exit(1)

if not feature_table.has_key(name):
click.echo("Unable to retrieve feature '{}'".format(name))
sys.exit(1)

db.cfgdb.mod_entry('FEATURE', name, {'auto_restart': autorestart})
90 changes: 3 additions & 87 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
from swsssdk import ConfigDBConnector, SonicV2Connector, SonicDBConfig
from utilities_common.db import Db
from utilities_common.intf_filter import parse_interface_in_filter
from utilities_common.cli import AbbreviationGroup, pass_db

import aaa
import mlnx
import nat
import feature
from config_mgmt import ConfigMgmtDPB

CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help', '-?'])
Expand Down Expand Up @@ -54,46 +56,6 @@

asic_type = None


class AbbreviationGroup(click.Group):
"""This subclass of click.Group supports abbreviated subgroup/subcommand names
"""

def get_command(self, ctx, cmd_name):
# Try to get builtin commands as normal
rv = click.Group.get_command(self, ctx, cmd_name)
if rv is not None:
return rv

# Allow automatic abbreviation of the command. "status" for
# instance will match "st". We only allow that however if
# there is only one command.
# If there are multiple matches and the shortest one is the common prefix of all the matches, return
# the shortest one
matches = []
shortest = None
for x in self.list_commands(ctx):
if x.lower().startswith(cmd_name.lower()):
matches.append(x)
if not shortest:
shortest = x
elif len(shortest) > len(x):
shortest = x

if not matches:
return None
elif len(matches) == 1:
return click.Group.get_command(self, ctx, matches[0])
else:
for x in matches:
if not x.startswith(shortest):
break
else:
return click.Group.get_command(self, ctx, shortest)

ctx.fail('Too many matches: %s' % ', '.join(sorted(matches)))


#
# Load breakout config file for Dynamic Port Breakout
#
Expand Down Expand Up @@ -922,10 +884,9 @@ def config(ctx):

ctx.obj = Db()

pass_db = click.make_pass_decorator(Db, ensure=True)

config.add_command(aaa.aaa)
config.add_command(aaa.tacacs)
config.add_command(feature.feature)
# === Add NAT Configuration ==========
config.add_command(nat.nat)

Expand Down Expand Up @@ -3838,50 +3799,5 @@ def delete(ctx):
sflow_tbl['global'].pop('agent_id')
config_db.set_entry('SFLOW', 'global', sflow_tbl['global'])

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

#
# 'state' command ('config feature state ...')
#
@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"]))
@pass_db
def feature_state(db, name, state):
"""Enable/disable a feature"""
state_data = db.cfgdb.get_entry('FEATURE', name)

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

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

#
# 'autorestart' command ('config feature autorestart ...')
#
@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"]))
@pass_db
def feature_autorestart(db, name, autorestart):
"""Enable/disable autorestart of a feature"""
feature_table = db.cfgdb.get_table('FEATURE')
if not feature_table:
click.echo("Unable to retrieve feature table from Config DB.")
sys.exit(1)

if not feature_table.has_key(name):
click.echo("Unable to retrieve feature '{}'".format(name))
sys.exit(1)

db.cfgdb.mod_entry('FEATURE', name, {'auto_restart': autorestart})

if __name__ == '__main__':
config()
56 changes: 56 additions & 0 deletions show/feature.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import click
from natsort import natsorted
from tabulate import tabulate

from utilities_common.cli import AbbreviationGroup, pass_db

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

#
# 'status' subcommand (show feature status)
#
@feature.command('status', short_help="Show feature state")
@click.argument('feature_name', required=False)
@pass_db
def feature_status(db, feature_name):
header = ['Feature', 'State', 'AutoRestart']
body = []
feature_table = db.cfgdb.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 feature autorestart)
#
@feature.command('autorestart', short_help="Show auto-restart state for a feature")
@click.argument('feature_name', required=False)
@pass_db
def feature_autorestart(db, feature_name):
header = ['Feature', 'AutoRestart']
body = []
feature_table = db.cfgdb.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 natsorted(feature_table.keys()):
body.append([name, feature_table[name]['auto_restart']])
click.echo(tabulate(body, header))
54 changes: 2 additions & 52 deletions show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from tabulate import tabulate
from utilities_common.db import Db

import feature
import mlnx

# Global Variables
Expand Down Expand Up @@ -559,7 +560,7 @@ def cli(ctx):

ctx.obj = Db()

pass_db = click.make_pass_decorator(Db, ensure=True)
cli.add_command(feature.feature)

#
# 'vrf' command ("show vrf")
Expand Down Expand Up @@ -3040,57 +3041,6 @@ def ztp(status, verbose):
cmd = cmd + " --verbose"
run_command(cmd, display_cmd=verbose)

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

#
# 'status' subcommand (show feature status)
#
@feature.command('status', short_help="Show feature state")
@click.argument('feature_name', required=False)
@pass_db
def feature_status(db, feature_name):
header = ['Feature', 'State', 'AutoRestart']
body = []
feature_table = db.cfgdb.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 feature autorestart)
#
@feature.command('autorestart', short_help="Show auto-restart state for a feature")
@click.argument('feature_name', required=False)
@pass_db
def feature_autorestart(db, feature_name):
header = ['Feature', 'AutoRestart']
body = []
feature_table = db.cfgdb.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 natsorted(feature_table.keys()):
body.append([name, feature_table[name]['auto_restart']])
click.echo(tabulate(body, header))

#
# 'vnet' command ("show vnet")
#
Expand Down
9 changes: 9 additions & 0 deletions tests/feature_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ def test_show_feature_status(self, get_cmd_module):
assert result.exit_code == 0
assert result.output == show_feature_status_output

def test_show_feature_status_abbrev_cmd(self, get_cmd_module):
(config, show) = get_cmd_module
runner = CliRunner()
result = runner.invoke(show.cli.commands["feature"], ["st"])
print(result.exit_code)
print(result.output)
assert result.exit_code == 0
assert result.output == show_feature_status_output

def test_show_bgp_feature_status(self, get_cmd_module):
(config, show) = get_cmd_module
runner = CliRunner()
Expand Down
43 changes: 43 additions & 0 deletions utilities_common/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import click

from utilities_common.db import Db

pass_db = click.make_pass_decorator(Db, ensure=True)

class AbbreviationGroup(click.Group):
"""This subclass of click.Group supports abbreviated subgroup/subcommand names
"""

def get_command(self, ctx, cmd_name):
# Try to get builtin commands as normal
rv = click.Group.get_command(self, ctx, cmd_name)
if rv is not None:
return rv

# Allow automatic abbreviation of the command. "status" for
# instance will match "st". We only allow that however if
# there is only one command.
# If there are multiple matches and the shortest one is the common prefix of all the matches, return
# the shortest one
matches = []
shortest = None
for x in self.list_commands(ctx):
if x.lower().startswith(cmd_name.lower()):
matches.append(x)
if not shortest:
shortest = x
elif len(shortest) > len(x):
shortest = x

if not matches:
return None
elif len(matches) == 1:
return click.Group.get_command(self, ctx, matches[0])
else:
for x in matches:
if not x.startswith(shortest):
break
else:
return click.Group.get_command(self, ctx, shortest)

ctx.fail('Too many matches: %s' % ', '.join(sorted(matches)))

0 comments on commit e722cb0

Please sign in to comment.