Skip to content

Commit

Permalink
CLI GEN-2 merged
Browse files Browse the repository at this point in the history
Signed-off-by: Vivek Reddy Karri <vkarri@nvidia.com>
  • Loading branch information
vivekrnv committed Aug 11, 2021
1 parent 7be9ee4 commit 4fdf805
Show file tree
Hide file tree
Showing 15 changed files with 786 additions and 342 deletions.
109 changes: 90 additions & 19 deletions config/config_mgmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
config_mgmt.py provides classes for configuration validation and for Dynamic
Port Breakout.
'''

import os
import re
import syslog
import yang as ly
from json import load
from sys import flags
from time import sleep as tsleep
Expand Down Expand Up @@ -46,34 +49,38 @@ def __init__(self, source="configDB", debug=False, allowTablesWithoutYang=True):
try:
self.configdbJsonIn = None
self.configdbJsonOut = None
self.source = source
self.allowTablesWithoutYang = allowTablesWithoutYang

# logging vars
self.SYSLOG_IDENTIFIER = "ConfigMgmt"
self.DEBUG = debug

self.sy = sonic_yang.SonicYang(YANG_DIR, debug=debug)
# load yang models
self.sy.loadYangModel()
# load jIn from config DB or from config DB json file.
if source.lower() == 'configdb':
self.readConfigDB()
# treat any other source as file input
else:
self.readConfigDBJson(source)
# this will crop config, xlate and load.
self.sy.loadData(self.configdbJsonIn)

# Raise if tables without YANG models are not allowed but exist.
if not allowTablesWithoutYang and len(self.sy.tablesWithOutYang):
raise Exception('Config has tables without YANG models')
self.__init_sonic_yang()

except Exception as e:
self.sysLog(doPrint=True, logLevel=syslog.LOG_ERR, msg=str(e))
raise Exception('ConfigMgmt Class creation failed')

return

def __init_sonic_yang(self):
self.sy = sonic_yang.SonicYang(YANG_DIR, debug=self.DEBUG)
# load yang models
self.sy.loadYangModel()
# load jIn from config DB or from config DB json file.
if self.source.lower() == 'configdb':
self.readConfigDB()
# treat any other source as file input
else:
self.readConfigDBJson(self.source)
# this will crop config, xlate and load.
self.sy.loadData(self.configdbJsonIn)

# Raise if tables without YANG models are not allowed but exist.
if not self.allowTablesWithoutYang and len(self.sy.tablesWithOutYang):
raise Exception('Config has tables without YANG models')

def __del__(self):
pass

Expand Down Expand Up @@ -213,6 +220,70 @@ def writeConfigDB(self, jDiff):

return

def add_module(self, yang_module_str, replace_if_exists=False):
"""
Validate and add new YANG module to the system.
Parameters:
yang_module_str (str): YANG module in string representation.
Returns:
None
"""

module_name = self.get_module_name(yang_module_str)
module_path = os.path.join(YANG_DIR, '{}.yang'.format(module_name))
if os.path.exists(module_path) and not replace_if_exists:
raise Exception('{} already exists'.format(module_name))
with open(module_path, 'w') as module_file:
module_file.write(yang_module_str)
try:
self.__init_sonic_yang()
except Exception:
os.remove(module_path)
raise

def remove_module(self, module_name):
"""
Remove YANG module on the system and validate.
Parameters:
module_name (str): YANG module name.
Returns:
None
"""

module_path = os.path.join(YANG_DIR, '{}.yang'.format(module_name))
if not os.path.exists(module_path):
return
with open(module_path, 'r') as module_file:
yang_module_str = module_file.read()
try:
os.remove(module_path)
self.__init_sonic_yang()
except Exception:
self.add_module(yang_module_str)
raise

@staticmethod
def get_module_name(yang_module_str):
"""
Read yangs module name from yang_module_str
Parameters:
yang_module_str(str): YANG module string.
Returns:
str: Module name
"""

# Instantiate new context since parse_module_mem() loads the module into context.
sy = sonic_yang.SonicYang(YANG_DIR)
module = sy.ctx.parse_module_mem(yang_module_str, ly.LYS_IN_YANG)
return module.name()


# End of Class ConfigMgmt

class ConfigMgmtDPB(ConfigMgmt):
Expand Down Expand Up @@ -417,8 +488,8 @@ def _deletePorts(self, ports=list(), force=False):
deps.extend(dep)

# No further action with no force and deps exist
if force == False and deps:
return configToLoad, deps, False;
if not force and deps:
return configToLoad, deps, False

# delets all deps, No topological sort is needed as of now, if deletion
# of deps fails, return immediately
Expand All @@ -436,8 +507,8 @@ def _deletePorts(self, ports=list(), force=False):
self.sy.deleteNode(str(xPathPort))

# Let`s Validate the tree now
if self.validateConfigData()==False:
return configToLoad, deps, False;
if not self.validateConfigData():
return configToLoad, deps, False

# All great if we are here, Lets get the diff
self.configdbJsonOut = self.sy.getData()
Expand Down
2 changes: 1 addition & 1 deletion sonic_package_manager/constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def parse(constraints: Dict) -> 'ComponentConstraints':
"""

components = {component: VersionConstraint.parse(version)
for component, version in constraints.items()}
for component, version in constraints.items()}
return ComponentConstraints(components)

def deparse(self) -> Dict[str, str]:
Expand Down
9 changes: 9 additions & 0 deletions sonic_package_manager/dockerapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,15 @@ def rm(self, container: str, **kwargs):
self.client.containers.get(container).remove(**kwargs)
log.debug(f'removed container {container}')

def rm_by_ancestor(self, image_id: str, **kwargs):
""" Docker 'rm' command for running containers instantiated
from image passed to this function. """

# Clean containers based on the old image
containers = self.ps(filters={'ancestor': image_id}, all=True)
for container in containers:
self.rm(container.id, **kwargs)

def ps(self, **kwargs):
""" Docker 'ps' command. """

Expand Down
1 change: 0 additions & 1 deletion sonic_package_manager/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,3 @@ class PackageComponentConflictError(PackageInstallationError):
def __str__(self):
return (f'Package {self.name} conflicts with {self.component} {self.constraint} '
f'in package {self.dependency} but version {self.installed_ver} is installed')

20 changes: 13 additions & 7 deletions sonic_package_manager/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ def install(ctx,

package_source = package_expr or from_repository or from_tarball
if not package_source:
exit_cli(f'Package source is not specified', fg='red')
exit_cli('Package source is not specified', fg='red')

if not yes and not force:
click.confirm(f'{package_source} is going to be installed, '
Expand All @@ -386,7 +386,7 @@ def install(ctx,
except Exception as err:
exit_cli(f'Failed to install {package_source}: {err}', fg='red')
except KeyboardInterrupt:
exit_cli(f'Operation canceled by user', fg='red')
exit_cli('Operation canceled by user', fg='red')


@cli.command()
Expand All @@ -409,15 +409,16 @@ def reset(ctx, name, force, yes, skip_host_plugins):
except Exception as err:
exit_cli(f'Failed to reset package {name}: {err}', fg='red')
except KeyboardInterrupt:
exit_cli(f'Operation canceled by user', fg='red')
exit_cli('Operation canceled by user', fg='red')


@cli.command()
@add_options(PACKAGE_COMMON_OPERATION_OPTIONS)
@click.option('--keep-config', is_flag=True, help='Keep features configuration in CONFIG DB.')
@click.argument('name')
@click.pass_context
@root_privileges_required
def uninstall(ctx, name, force, yes):
def uninstall(ctx, name, force, yes, keep_config):
""" Uninstall package. """

manager: PackageManager = ctx.obj
Expand All @@ -426,12 +427,17 @@ def uninstall(ctx, name, force, yes):
click.confirm(f'Package {name} is going to be uninstalled, '
f'continue?', abort=True, show_default=True)

uninstall_opts = {
'force': force,
'keep_config': keep_config,
}

try:
manager.uninstall(name, force)
manager.uninstall(name, **uninstall_opts)
except Exception as err:
exit_cli(f'Failed to uninstall package {name}: {err}', fg='red')
except KeyboardInterrupt:
exit_cli(f'Operation canceled by user', fg='red')
exit_cli('Operation canceled by user', fg='red')


@cli.command()
Expand All @@ -453,7 +459,7 @@ def migrate(ctx, database, force, yes, dockerd_socket):
except Exception as err:
exit_cli(f'Failed to migrate packages {err}', fg='red')
except KeyboardInterrupt:
exit_cli(f'Operation canceled by user', fg='red')
exit_cli('Operation canceled by user', fg='red')


if __name__ == "__main__":
Expand Down
Loading

0 comments on commit 4fdf805

Please sign in to comment.