From 7b579c1a39936826cb943a580c3a1933be475a81 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Kotari Date: Tue, 26 Jul 2022 03:33:19 -0400 Subject: [PATCH 1/6] adding initial code refactor --- netsim_wrapper/json.py | 32 ++++++++++++++++++++ netsim_wrapper/netsim.py | 13 ++++++++ netsim_wrapper/run.py | 0 netsim_wrapper/system.py | 62 +++++++++++++++++++++++++++++++++++++++ netsim_wrapper/utils.py | 51 ++++++++++++++++++++++++++++++++ netsim_wrapper/wrapper.py | 0 netsim_wrapper/yaml.py | 43 +++++++++++++++++++++++++++ 7 files changed, 201 insertions(+) create mode 100644 netsim_wrapper/json.py create mode 100644 netsim_wrapper/netsim.py create mode 100644 netsim_wrapper/run.py create mode 100644 netsim_wrapper/system.py create mode 100644 netsim_wrapper/utils.py create mode 100644 netsim_wrapper/wrapper.py create mode 100644 netsim_wrapper/yaml.py diff --git a/netsim_wrapper/json.py b/netsim_wrapper/json.py new file mode 100644 index 0000000..d915d2f --- /dev/null +++ b/netsim_wrapper/json.py @@ -0,0 +1,32 @@ +from logging import INFO +from json import load, dump + +from .system import System + + +class Json(System): + def __init__(self, level=INFO) -> None: + super(Json, self).__init__(level=level) + self.log.debug(f"base class: {__class__.__name__} initialize") + + def load(self, fpath): + data = None + try: + self.log.debug(f"base class: {__class__.__name__}.{__class__.__module__}") + with open(fpath) as f: + data = load(f) + except EnvironmentError as e: + self.log.error("error while loading the file: {}".format(fpath)) + self.log.error(e) + return data + + def dump(self, fpath, template): + try: + self.log.debug(f"base class: {__class__.__name__}.{__class__.__module__}") + with open(fpath, 'w') as f: + dump(template, f, indent=2) + self.log.info("please, find the file in: {}".format(fpath)) + except EnvironmentError as e: + self.log.error("error while createing of template") + self.log.error(e) + diff --git a/netsim_wrapper/netsim.py b/netsim_wrapper/netsim.py new file mode 100644 index 0000000..acb50a1 --- /dev/null +++ b/netsim_wrapper/netsim.py @@ -0,0 +1,13 @@ +from .utils import Utils +from logging import INFO + +class Netsim(Utils): + name = 'ncs-netsim' + command = ['ncs-netsim'] + netsim_options = [] + netsim_dir = 'netsim' + + def __init__(self, path='.', level=INFO) -> None: + super().__init__(path, level) + + \ No newline at end of file diff --git a/netsim_wrapper/run.py b/netsim_wrapper/run.py new file mode 100644 index 0000000..e69de29 diff --git a/netsim_wrapper/system.py b/netsim_wrapper/system.py new file mode 100644 index 0000000..d31ede6 --- /dev/null +++ b/netsim_wrapper/system.py @@ -0,0 +1,62 @@ +""" +The module is for system operations exit, unlink and setting logger +""" + +import logging + +from pathlib import Path +from sys import exit as sys_exit +import subprocess + +format = '%(levelname)-8s | %(asctime)s | %(module)-8s:%(lineno)-4d | %(message)s' + + +def get_log(level): + logging.basicConfig(level=level, format=format) + return logging.getLogger() + + +class System: + def __init__(self, path='.', level=logging.INFO) -> None: + super(System, self).__init__() + self.log = get_log(level) + self.log.debug(f"base class: {__class__.__name__} initialize") + self.path = Path(path).absolute() + + @property + def exit(self): + sys_exit() + + def delete(self, fpath): + path = Path(fpath).absolute() + if path.exists(): + path.unlink() + + def is_file(self, fpath): + path = Path(fpath).absolute() + return path.is_file() + + def is_dir(self, fpath): + path = Path(fpath).absolute() + return path.is_dir() + + def call(self, cmd): + try: + subprocess.call(cmd, shell=True) + except EnvironmentError as e: + self.log.error("failed to run command: {}".format(cmd)) + self.log.error(e) + + def run(self, cmd, error=True): + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + out, err = out.decode('utf-8'), err.decode('utf-8') + if err == '' or 'env.sh' in err: + self.log.debug("`{}` ran successfully".format(' '.join(cmd))) + return out + if error: + self.log.error("an error occured while running command `{}`".format(' '.join(cmd))) + self.log.error('message: {}'.format(err)) + if 'command not found' in err or 'Unknown command' in err: + raise ValueError("command not found.") + return False diff --git a/netsim_wrapper/utils.py b/netsim_wrapper/utils.py new file mode 100644 index 0000000..cc5b3b1 --- /dev/null +++ b/netsim_wrapper/utils.py @@ -0,0 +1,51 @@ +""" +The module is for system operations (subprocess) and setting logger +""" + +from logging import INFO +from pathlib import Path + +from .system import System +from .json import Json + +class Xml(System): + def __init__(self, level=INFO) -> None: + super().__init__(level=level) + self.log.debug(f"base class: {__class__.__name__} initialize") + + def dump(self, fpath, xml_data): + try: + self.log.debug(f"base class: {__class__.__name__}.{__class__.__module__}") + with open(fpath, 'w') as fp: + fp.write(xml_data) + self.log.info("please, find the file in: {}".format(fpath)) + except EnvironmentError as e: + self.log.error("error on createing xml file") + self.log.error(e) + + +class Utils(Json, System): + def __init__(self, path='.', level=INFO) -> None: + super(Utils, self).__init__(path, level) + self.log.debug(f"base class: {__class__.__name__} initialize") + + def create(self, fpath): + if not self.is_file(fpath): + self.dump(fpath, {}) + + def rstrip_digits(self, given_string): + return given_string.rstrip('1234567890') + + def get_index(self, given_list, element): + try: + return given_list.index(element) + except ValueError: + return None + + def get_path(self): + path = None + # if len(cmd_lst) > index+1: + # path = cmd_lst[index+1] + # else: + # path = '{}/{}.{}'.format(self.path, filename, filetype) + return path diff --git a/netsim_wrapper/wrapper.py b/netsim_wrapper/wrapper.py new file mode 100644 index 0000000..e69de29 diff --git a/netsim_wrapper/yaml.py b/netsim_wrapper/yaml.py new file mode 100644 index 0000000..c30a4d7 --- /dev/null +++ b/netsim_wrapper/yaml.py @@ -0,0 +1,43 @@ +from logging import INFO +from collections import OrderedDict +from yaml import add_representer, dump, load, FullLoader + +from .system import System + + +class Yaml(System): + def __init__(self, level=INFO) -> None: + super(Yaml, self).__init__(level=level) + self.log.debug(f"base class: {__class__.__name__} initialize") + self._setup_yaml + + @property + def _setup_yaml(self): + represent_dict_order = lambda self, data: \ + self.represent_mapping( + 'tag:yaml.org,2002:map', + data.items() + ) + add_representer(OrderedDict, represent_dict_order) + + def load(self, fpath): + data = None + try: + self.log.debug(f"base class: {__class__.__name__}.{__class__.__module__}") + with open(fpath) as f: + data = load(f, Loader=FullLoader) + except EnvironmentError as e: + self.log.error("error while loading the file {}".format(fpath)) + self.log.error(e) + return data + + def dump(self, fpath, template): + try: + self.log.debug(f"base class: {__class__.__name__}.{__class__.__module__}") + with open(fpath, 'w') as f: + dump(template, f, sort_keys=False) + self.log.info("please, find the file in: {}".format(fpath)) + except EnvironmentError as e: + self.log.error("error while createing of template") + self.log.error(e) + From 8e38f29b59d886daa36697295a4975680a944313 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Kotari Date: Wed, 27 Jul 2022 10:52:14 -0400 Subject: [PATCH 2/6] initial refactor --- .version | 2 +- README.md | 4 + .../create_device_from.json | 0 .../create_device_from.yaml | 0 .../create_network_from.json | 0 .../create_network_from.yaml | 0 netsim_wrapper/common/__init__.py | 0 netsim_wrapper/common/log.py | 28 +++ netsim_wrapper/common/singleton.py | 15 ++ netsim_wrapper/{ => common}/system.py | 38 ++-- netsim_wrapper/common/utils.py | 41 ++++ netsim_wrapper/json.py | 32 --- netsim_wrapper/netsim.py | 134 +++++++++++- netsim_wrapper/netsim2.py | 204 ++---------------- netsim_wrapper/templates/__init__.py | 0 netsim_wrapper/templates/convert.py | 31 +++ netsim_wrapper/templates/json.py | 40 ++++ netsim_wrapper/templates/template.py | 76 +++++++ netsim_wrapper/templates/xml.py | 37 ++++ netsim_wrapper/templates/yaml.py | 49 +++++ netsim_wrapper/utils.py | 51 ----- netsim_wrapper/yaml.py | 43 ---- 22 files changed, 494 insertions(+), 331 deletions(-) rename {templates => examples-templates}/create_device_from.json (100%) rename {templates => examples-templates}/create_device_from.yaml (100%) rename {templates => examples-templates}/create_network_from.json (100%) rename {templates => examples-templates}/create_network_from.yaml (100%) create mode 100644 netsim_wrapper/common/__init__.py create mode 100644 netsim_wrapper/common/log.py create mode 100644 netsim_wrapper/common/singleton.py rename netsim_wrapper/{ => common}/system.py (64%) create mode 100644 netsim_wrapper/common/utils.py delete mode 100644 netsim_wrapper/json.py create mode 100644 netsim_wrapper/templates/__init__.py create mode 100644 netsim_wrapper/templates/convert.py create mode 100644 netsim_wrapper/templates/json.py create mode 100644 netsim_wrapper/templates/template.py create mode 100644 netsim_wrapper/templates/xml.py create mode 100644 netsim_wrapper/templates/yaml.py delete mode 100644 netsim_wrapper/utils.py delete mode 100644 netsim_wrapper/yaml.py diff --git a/.version b/.version index ef538c2..389f774 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -3.1.2 +4.0 \ No newline at end of file diff --git a/README.md b/README.md index b80871a..13f0c08 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Netsim Wrapper +## TODO: need to update badges + [![License: Apache 2.0](https://img.shields.io/badge/License-Apache2-yellow.svg)](https://opensource.org/licenses/Apache-2.0) [![Version: 3.1.2](https://img.shields.io/badge/Version-3.1.2-parrotgreen.svg)](https://github.com/NSO-developer/netsim-wrapper) [![Downloads](https://pepy.tech/badge/netsim-wrapper)](https://pepy.tech/project/netsim-wrapper) @@ -7,6 +9,8 @@ ncs-netsim is a great tool, but it lack of following features which are developed as part of netsim-wrapper +## TODO: changing options + - netsim-wrapper features - delete-devices \ - create-network-from [ yaml | json ] \ diff --git a/templates/create_device_from.json b/examples-templates/create_device_from.json similarity index 100% rename from templates/create_device_from.json rename to examples-templates/create_device_from.json diff --git a/templates/create_device_from.yaml b/examples-templates/create_device_from.yaml similarity index 100% rename from templates/create_device_from.yaml rename to examples-templates/create_device_from.yaml diff --git a/templates/create_network_from.json b/examples-templates/create_network_from.json similarity index 100% rename from templates/create_network_from.json rename to examples-templates/create_network_from.json diff --git a/templates/create_network_from.yaml b/examples-templates/create_network_from.yaml similarity index 100% rename from templates/create_network_from.yaml rename to examples-templates/create_network_from.yaml diff --git a/netsim_wrapper/common/__init__.py b/netsim_wrapper/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/netsim_wrapper/common/log.py b/netsim_wrapper/common/log.py new file mode 100644 index 0000000..73f834f --- /dev/null +++ b/netsim_wrapper/common/log.py @@ -0,0 +1,28 @@ +""" +Application logging operations +""" + +import logging + +from .singleton import SingletonMeta + + +class Logger(metaclas=SingletonMeta): + """ + Logger with singleton metaclass + """ + format = '%(levelname)-8s | %(asctime)s | %(module)-8s:%(lineno)-4d | %(message)s' + log = logging.getLogger() + + def __init__(self, level=logging.INFO) -> None: + self.level = level + self.log = self.get_log + + @property + def get_log(self): + """ + Fetching logger with logging level + """ + logging.basicConfig(level=self.level, format=format) + return logging.getLogger() + diff --git a/netsim_wrapper/common/singleton.py b/netsim_wrapper/common/singleton.py new file mode 100644 index 0000000..88af635 --- /dev/null +++ b/netsim_wrapper/common/singleton.py @@ -0,0 +1,15 @@ +""" +Singleton Metaclass +""" + +class SingletonMeta(type): + """ + Singleton Metaclass + """ + + _instance = None + + def __call__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = super().__call__(*args, **kwargs) + return cls._instance diff --git a/netsim_wrapper/system.py b/netsim_wrapper/common/system.py similarity index 64% rename from netsim_wrapper/system.py rename to netsim_wrapper/common/system.py index d31ede6..65dfb13 100644 --- a/netsim_wrapper/system.py +++ b/netsim_wrapper/common/system.py @@ -1,58 +1,70 @@ """ -The module is for system operations exit, unlink and setting logger +The module is for system operations exit, unlink """ -import logging - from pathlib import Path from sys import exit as sys_exit import subprocess -format = '%(levelname)-8s | %(asctime)s | %(module)-8s:%(lineno)-4d | %(message)s' - - -def get_log(level): - logging.basicConfig(level=level, format=format) - return logging.getLogger() +from .log import Logger class System: - def __init__(self, path='.', level=logging.INFO) -> None: + def __init__(self) -> None: super(System, self).__init__() - self.log = get_log(level) - self.log.debug(f"base class: {__class__.__name__} initialize") - self.path = Path(path).absolute() + self.log = Logger().log + self.log.debug("class: {} initialize".format(__class__.__name__)) @property def exit(self): + self.log.debug('exit method called') sys_exit() def delete(self, fpath): + """ + Delete file if exist + """ path = Path(fpath).absolute() if path.exists(): path.unlink() + self.log.debug('deleted file: {}'.format(fpath)) def is_file(self, fpath): + """ + Given path is it a file + """ path = Path(fpath).absolute() return path.is_file() def is_dir(self, fpath): + """ + Given path is it a folder + """ path = Path(fpath).absolute() return path.is_dir() def call(self, cmd): + """ + System subprocess call + """ try: + self.log.debug("call cmd: {}".format(' '.join(cmd))) subprocess.call(cmd, shell=True) except EnvironmentError as e: self.log.error("failed to run command: {}".format(cmd)) self.log.error(e) def run(self, cmd, error=True): + """ + System subprocess open and communicate + """ + self.log.debug("popen cmd: {}".format(' '.join(cmd))) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() out, err = out.decode('utf-8'), err.decode('utf-8') if err == '' or 'env.sh' in err: self.log.debug("`{}` ran successfully".format(' '.join(cmd))) + self.log.debug("out: {}".format(out)) return out if error: self.log.error("an error occured while running command `{}`".format(' '.join(cmd))) diff --git a/netsim_wrapper/common/utils.py b/netsim_wrapper/common/utils.py new file mode 100644 index 0000000..b0ab378 --- /dev/null +++ b/netsim_wrapper/common/utils.py @@ -0,0 +1,41 @@ +""" +The module is for system operations (subprocess) and setting logger +""" + +from logging import INFO +from pathlib import Path + +from .system import System +from ..templates.json import Json +from ..templates.yaml import Yaml +from ..templates.xml import Xml + + +class Utils(System): + def __init__(self) -> None: + super(Utils, self).__init__() + self.log.debug("class: {} initialize".format(__class__.__name__)) + # self.json = Json(level=level) + # self.yaml = Yaml(level=level) + # self.xml = Xml(level=level) + + # def create(self, fpath): + # if not self.is_file(fpath): + # self.dump(fpath, {}) + + def rstrip_digits(self, given_string): + return given_string.rstrip('1234567890') + + def get_index(self, given_list, element): + try: + return given_list.index(element) + except ValueError: + return None + + def get_path(self): + path = None + # if len(cmd_lst) > index+1: + # path = cmd_lst[index+1] + # else: + # path = '{}/{}.{}'.format(self.path, filename, filetype) + return path diff --git a/netsim_wrapper/json.py b/netsim_wrapper/json.py deleted file mode 100644 index d915d2f..0000000 --- a/netsim_wrapper/json.py +++ /dev/null @@ -1,32 +0,0 @@ -from logging import INFO -from json import load, dump - -from .system import System - - -class Json(System): - def __init__(self, level=INFO) -> None: - super(Json, self).__init__(level=level) - self.log.debug(f"base class: {__class__.__name__} initialize") - - def load(self, fpath): - data = None - try: - self.log.debug(f"base class: {__class__.__name__}.{__class__.__module__}") - with open(fpath) as f: - data = load(f) - except EnvironmentError as e: - self.log.error("error while loading the file: {}".format(fpath)) - self.log.error(e) - return data - - def dump(self, fpath, template): - try: - self.log.debug(f"base class: {__class__.__name__}.{__class__.__module__}") - with open(fpath, 'w') as f: - dump(template, f, indent=2) - self.log.info("please, find the file in: {}".format(fpath)) - except EnvironmentError as e: - self.log.error("error while createing of template") - self.log.error(e) - diff --git a/netsim_wrapper/netsim.py b/netsim_wrapper/netsim.py index acb50a1..5ec73e4 100644 --- a/netsim_wrapper/netsim.py +++ b/netsim_wrapper/netsim.py @@ -1,13 +1,139 @@ -from .utils import Utils +from re import compile +from typing import Any from logging import INFO +from .common.utils import Utils + +class Netsim(Utils): + name = 'ncs-netsim' + command = ['ncs-netsim'] + options = [] + netsim_dir = 'netsim' + help = None + + def __init__(self) -> None: + super(Netsim, self).__init__() + self.help() + self.options() + + def ncs_netsim(self, cmd, print_result=True, throw_err=True) -> Any: + try: + output = self.run(cmd=self.command+cmd, error=throw_err) + if not output: + raise ValueError("something went wrong, try netsim-wrapper --help") + except ValueError as e: + if throw_err: + self.log.error(e) + self.exit + raise ValueError(e) + if print_result: + print(output.rsplit('\n')) + return output + + def help(self): + if self.help: + return + try: + self.help = self.run(self.command + ['--help']) + except ValueError as e: + self.log.error(e) + self.exit + except FileNotFoundError as e: + self.log.error('ncs-netsim command not found. please source ncsrc file') + self.exit + + def options(self): + if len(self.netsim_options): + return self.options + rgx_cmd = compile(r'^\s+([a-z-]+)') + rgx_opt = compile(r'^\s+\[(\S+)\s+\|\s+([a-z-]+).*?\]\s+([a-z]+)') + for line in self.help.split('\n'): + res = rgx_cmd.match(line) + if res: + self.options += list(res.groups()) + res = rgx_opt.match(line) + if res: + self.options += list(res.groups()) + self.options += ['cli', 'cli-c', 'cli-i', '--dir'] + + + + + class Netsim(Utils): name = 'ncs-netsim' command = ['ncs-netsim'] netsim_options = [] netsim_dir = 'netsim' - def __init__(self, path='.', level=INFO) -> None: - super().__init__(path, level) + _instance = None + _ncs_netsim_help = None + + __stdout = subprocess.PIPE + __stderr = subprocess.PIPE + + _split = '#######' + + def __new__(cls, log_level=logging.INFO, log_format=None): + if cls._instance is None: + cls._instance = object.__new__(cls) + return cls._instance + + def __init__(self, log_level=logging.INFO, log_format=None, *args, **kwargs): + Utils.__init__(self, log_level, log_format) + + + @property + def __netsim_devices_created_by(self): + self._netsim_devices_created_by = {} + data = self.run_ncs_netsim__command(['list'], print_output=False).split('\n') + result = list(filter(lambda x: 'netconf' in x, data)) + for each in result: + each = each.split('/') + dev_name = each[-1].strip() + if dev_name == each[-2]: + self._netsim_devices_created_by[dev_name] = ['add-device', each[-2]] + else: + self._netsim_devices_created_by[dev_name] = ['add-to-network', each[-2]] + + + def _netsim_device_mapper(self, data): + _netsim_mapper = collections.OrderedDict() + for each_device in data: + device = each_device.split('=') + if len(device) > 1: + device = device[1].split('\n')[0] + _netsim_mapper[device] = self._netsim_device_keypair_mapper(device, each_device) + return _netsim_mapper + + def _netsim_device_keypair_mapper(self, device, data): + _mapper = {} + for each_line in data.split('\n'): + if len(each_line.split('[')) > 1: + key = (each_line.split('[')[0]).strip(' ') + value = each_line.split('=')[1] + _mapper[key] = value + _mapper['created_by'] = self._netsim_devices_created_by.get(device)[0] + _mapper['parent'] = self._netsim_devices_created_by.get(device)[1] + return _mapper + + def _dump_netsim_mapper(self, path, netsim_mapper): + fp = open(path, 'w') + fp.write('\n') + index = 0 + for device_name, device_dict in netsim_mapper.items(): + fp.write('## device {}\n'.format(device_name)) + for key, value in device_dict.items(): + if key in ['created_by', 'parent']: + continue + fp.write('{}[{}]={}\n'.format(key, index, value)) + fp.write('#######\n\n') + index += 1 + fp.close() + + - \ No newline at end of file + def read_netsim(self, path): + data = open(path).read().split(self._split) + self.__netsim_devices_created_by + return self._netsim_device_mapper(data) diff --git a/netsim_wrapper/netsim2.py b/netsim_wrapper/netsim2.py index 05afe0b..e59c322 100644 --- a/netsim_wrapper/netsim2.py +++ b/netsim_wrapper/netsim2.py @@ -10,131 +10,6 @@ from operator import methodcaller -class Utils: - name = 'utils' - - _instance = None - def __new__(cls, log_level=logging.INFO, log_format=None): - if cls._instance is None: - cls._instance = object.__new__(cls) - return cls._instance - - def __init__(self, log_level=logging.INFO, log_format=None, *args, **kwargs): - self.__format = log_format - self.current_path = os.path.abspath('.') - self.logger = self.__set_logger_level(log_level) - self._setup_yaml - - def __set_logger_level(self, log_level): - if self.__format is None: - self.__format = '[ %(levelname)s ] :: [ %(name)s ] :: %(message)s' - logging.basicConfig(stream=sys.stdout, level=log_level, - format=self.__format, datefmt=None) - logger = logging.getLogger(self.name) - logger.setLevel(log_level) - return logger - - @property - def _setup_yaml(self): - represent_dict_order = lambda self, data: \ - self.represent_mapping( - 'tag:yaml.org,2002:map', - data.items() - ) - yaml.add_representer(collections.OrderedDict, represent_dict_order) - - def __del__(self): - self._instance = None - - @property - def _exit(self): - sys.exit() - - def _dump_yaml(self, filename, template): - try: - with open(filename, 'w') as f: - yaml.dump(template, f, sort_keys=False) - self.logger.info("please, find the {} file in current directory".format(filename)) - self.logger.info("update based on your requirement") - except EnvironmentError as e: - self.logger.error("error on createing of template..") - self.logger.error(e) - - def _dump_json(self, filename, template): - try: - with open(filename, 'w') as f: - json.dump(template, f, indent=2) - self.logger.info("please, find the {} file in current directory".format(filename)) - self.logger.info("update based on your requirement") - except EnvironmentError as e: - self.logger.error("error on createing of template..") - self.logger.error(e) - - def _load_yaml(self, path): - data = None - try: - with open(path) as f: - data = yaml.load(f, Loader=yaml.FullLoader) - except EnvironmentError as e: - self.logger.error("error while loading the {} file..".format(path)) - self.logger.error(e) - return data - - def _load_json(self, path): - data = None - try: - with open(path) as f: - data = json.load(f) - except EnvironmentError as e: - self.logger.error("error while loading the {} file..".format(path)) - self.logger.error(e) - return data - - def _create_file(self, path): - if not os.path.exists(path): - with open(path, "w") as fp: - json.dump({}, fp) - - def _delete_file(self, path): - if os.path.exists(path): - os.remove(path) - - def _rstrip_digits(self, given_string): - return given_string.rstrip('1234567890') - - def get_index(self, given_list, element): - try: - return given_list.index(element) - except ValueError: - return None - - def _load_path(self, cmd_lst, index, filename, filetype='yaml'): - if len(cmd_lst) > index+1: - path = cmd_lst[index+1] - else: - path = '{}/{}.{}'.format(self.current_path, filename, filetype) - return path - - def _dump_xml(self, filename, xml_data): - try: - with open(filename, 'w') as fp: - fp.write(xml_data) - except EnvironmentError as e: - self.logger.error("error on createing xml file") - self.logger.error(e) - - def _run_bash_commands(self, cmd): - try: - subprocess.call(cmd, shell=True) - except EnvironmentError as e: - self.logger.error("failed to run command: {}".format(cmd)) - self.logger.error(e) - - def _is_file(self, fname): - return os.path.isfile(fname) - - def _is_folder(self, fname): - return os.path.isdir(fname) class Netsim(Utils): name = 'ncs-netsim' @@ -211,68 +86,23 @@ def __netsim_devices_created_by(self): else: self._netsim_devices_created_by[dev_name] = ['add-to-network', each[-2]] - @property - def _build_network_template(self): - template = collections.OrderedDict() - template['nso-packages-path'] = '' # String - template['compile-neds'] = True # True/False - template['start-devices'] = True # True/False - template['add-to-nso'] = True # True/False - template['add-authgroup-to-nso'] = True # True/False - template['authgroup'] = collections.OrderedDict() # Dict - template['authgroup']['type'] = 'custom' # local/system/custom - template['authgroup']['path'] = '' - template['device-mode'] = collections.OrderedDict() - template['device-mode']['prefix-based'] = collections.OrderedDict() - template['device-mode']['prefix-based'][''] = collections.OrderedDict() - template['device-mode']['prefix-based']['']['count'] = 2 - template['device-mode']['prefix-based']['']['prefix'] = '' - template['load-day0-config'] = True - template['config-path'] = '' # config path - template['config-files'] = [] - template['config-files'].append('') # each file path - template['config-files'].append('') - return template - @property - def _build_device_template(self): - template = collections.OrderedDict() - template['nso-packages-path'] = '' # String - template['compile-neds'] = True # True/False - template['start-devices'] = True # True/False - template['add-to-nso'] = True # True/False - template['add-authgroup-to-nso'] = True # True/False - template['authgroup'] = collections.OrderedDict() - template['authgroup']['type'] = 'custom' # local/system/custom - template['authgroup']['path'] = '' - template['device-mode'] = collections.OrderedDict() - template['device-mode']['name-based'] = collections.OrderedDict() - template['device-mode']['name-based'][''] = [] - template['device-mode']['name-based'][''].append('device1') - template['device-mode']['name-based'][''].append('device2') - template['load-day0-config'] = True # True/False - template['config-path'] = '' # config path - template['config-files'] = [] - template['config-files'].append('') # each file path - template['config-files'].append('') - return template - - def _run_command(self, command, throw_err=True): - self.logger.debug("command `{}` running on ncs-netsim".format(' '.join(command))) - p = subprocess.Popen(command, stdout=self.__stdout, - stderr=self.__stderr) - out, err = p.communicate() - out, err = out.decode('utf-8'), err.decode('utf-8') - if err == '' or 'env.sh' in err: - self.logger.debug("`{}` ran successfully".format(' '.join(command))) - return out - if throw_err: - self.logger.error("an error occured while running command `{}`".format(' '.join(command))) - self.logger.error('message: {}'.format(err)) - if 'command not found' in err or 'Unknown command' in err: - raise ValueError("command not found.") - raise ValueError("try netsim-wrapper --help") - raise ValueError("{}\ntry netsim-wrapper --help".format(err)) + # def _run_command(self, command, throw_err=True): + # self.logger.debug("command `{}` running on ncs-netsim".format(' '.join(command))) + # p = subprocess.Popen(command, stdout=self.__stdout, + # stderr=self.__stderr) + # out, err = p.communicate() + # out, err = out.decode('utf-8'), err.decode('utf-8') + # if err == '' or 'env.sh' in err: + # self.logger.debug("`{}` ran successfully".format(' '.join(command))) + # return out + # if throw_err: + # self.logger.error("an error occured while running command `{}`".format(' '.join(command))) + # self.logger.error('message: {}'.format(err)) + # if 'command not found' in err or 'Unknown command' in err: + # raise ValueError("command not found.") + # raise ValueError("try netsim-wrapper --help") + # raise ValueError("{}\ntry netsim-wrapper --help".format(err)) def _netsim_device_mapper(self, data): _netsim_mapper = collections.OrderedDict() @@ -330,7 +160,7 @@ def read_netsim(self, path): class NetsimWrapper(Netsim): name = 'netsim-wrapper' options = [] - version = '3.1.2' + version = '3.1.2' # 4.0 _instance = None _netsim_wrapper_help = None diff --git a/netsim_wrapper/templates/__init__.py b/netsim_wrapper/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/netsim_wrapper/templates/convert.py b/netsim_wrapper/templates/convert.py new file mode 100644 index 0000000..bb97c75 --- /dev/null +++ b/netsim_wrapper/templates/convert.py @@ -0,0 +1,31 @@ +from copy import copy +from typing import Union + + +def object2xml(data: Union[dict, bool], root='object'): + xml = f'<{root}>' + if isinstance(data, dict): + for key, value in data.items(): + xml += object2xml(value, key) + + elif isinstance(data, (list, tuple, set)): + for item in data: + xml += object2xml(item, 'item') + + else: + xml += str(data) + + xml += f'' + return xml + +def xml2dict(r,root=True): + if root: + return {r.tag : xml2dict(r, False)} + d=copy(r.attrib) + if r.text: + d["_text"]=r.text + for x in r.findall("./*"): + if x.tag not in d: + d[x.tag]=[] + d[x.tag].append(xml2dict(x,False)) + return d \ No newline at end of file diff --git a/netsim_wrapper/templates/json.py b/netsim_wrapper/templates/json.py new file mode 100644 index 0000000..95c2c04 --- /dev/null +++ b/netsim_wrapper/templates/json.py @@ -0,0 +1,40 @@ +""" +Custom JSON class from Template Abstrat Factory +""" + +from json import load, dump + +from .template import TemplateAbstractFactory + + +class Json(TemplateAbstractFactory): + """ + Custom JSON class + """ + def __init__(self) -> None: + super(Json, self).__init__() + self.log.debug("class: {} initialize".format(__class__.__name__)) + + def load(self, fpath): + data = None + try: + self.log.debug("method: {}.{}".format(__class__.__name__, __class__.__module__)) + with open(fpath) as f: + data = load(f) + except EnvironmentError as e: + self.log.error("error while loading json file: {}".format(fpath)) + self.log.error(e) + return data + + def dump(self, fpath, template): + try: + self.log.debug("method: {}.{}".format(__class__.__name__, __class__.__module__)) + with open(fpath, "w") as f: + dump(template, f, indent=2) + self.log.info("please, find the file in: {}".format(fpath)) + except EnvironmentError as e: + self.log.error("error while creating json template") + self.log.error(e) + + def template(self, type): + return super().template(type) diff --git a/netsim_wrapper/templates/template.py b/netsim_wrapper/templates/template.py new file mode 100644 index 0000000..d597fe0 --- /dev/null +++ b/netsim_wrapper/templates/template.py @@ -0,0 +1,76 @@ +from collections import OrderedDict +from ..common.log import Logger +from abc import abstractmethod, ABC +from logging import INFO + + +class BaseTemplate: + def __init__(self) -> None: + super(BaseTemplate, self).__init__() + self.log = Logger().log + self.log.debug("class: {} initialize".format(__class__.__name__)) + @property + def network_template(self): + template = OrderedDict() + template['nso-packages-path'] = '' # String + template['compile-neds'] = True # True/False + template['start-devices'] = True # True/False + template['add-to-nso'] = True # True/False + template['add-authgroup-to-nso'] = True # True/False + template['authgroup'] = OrderedDict() # Dict + template['authgroup']['type'] = 'custom' # local/system/custom + template['authgroup']['path'] = '' + template['device-mode'] = OrderedDict() + template['device-mode']['prefix-based'] = OrderedDict() + template['device-mode']['prefix-based'][''] = OrderedDict() + template['device-mode']['prefix-based']['']['count'] = 2 + template['device-mode']['prefix-based']['']['prefix'] = '' + template['load-day0-config'] = True # True/False + template['config-path'] = '' # String + template['config-files'] = [] + template['config-files'].append('') # each file path + template['config-files'].append('') + return template + + @property + def device_template(self): + template = OrderedDict() + template['nso-packages-path'] = '' # String + template['compile-neds'] = True # True/False + template['start-devices'] = True # True/False + template['add-to-nso'] = True # True/False + template['add-authgroup-to-nso'] = True # True/False + template['authgroup'] = OrderedDict() + template['authgroup']['type'] = 'custom' # local/system/custom + template['authgroup']['path'] = '' + template['device-mode'] = OrderedDict() + template['device-mode']['name-based'] = OrderedDict() + template['device-mode']['name-based'][''] = [] + template['device-mode']['name-based'][''].append('device1') + template['device-mode']['name-based'][''].append('device2') + template['load-day0-config'] = True # True/False + template['config-path'] = '' # config path + template['config-files'] = [] + template['config-files'].append('') # each file path + template['config-files'].append('') + return template + + +class TemplateAbstractFactory(ABC, BaseTemplate): + def __init__(self) -> None: + super(TemplateAbstractFactory, self).__init__() + self.log.debug("class: {} initialize".format(__class__.__name__)) + + @abstractmethod + def load(self, fpath): + pass + + @abstractmethod + def dump(self, fpath, template): + pass + + def template(self, type): + if type == 'network': + return self.network_template + if type == 'device': + return self.device_template diff --git a/netsim_wrapper/templates/xml.py b/netsim_wrapper/templates/xml.py new file mode 100644 index 0000000..8e80495 --- /dev/null +++ b/netsim_wrapper/templates/xml.py @@ -0,0 +1,37 @@ +import xml.etree.ElementTree as ET + +from .convert import object2xml, xml2dict +from .template import TemplateAbstractFactory + + +class Xml(TemplateAbstractFactory): + def __init__(self) -> None: + super(Xml, self).__init__() + self.log.debug("class: {} initialize".format(__class__.__name__)) + + + def load(self, fpath): + data = None + try: + self.log.debug("method: {}.{}".format(__class__.__name__, __class__.__module__)) + with ET.parse(fpath) as tree: + data = tree.getroot() + data = xml2dict(data) + except EnvironmentError as e: + self.log.error("error while loading xml file: {}".format(fpath)) + self.log.error(e) + return data + + def dump(self, fpath, template): + try: + self.log.debug("method: {}.{}".format(__class__.__name__, __class__.__module__)) + with open(fpath, 'w') as fp: + fp.write(template) + self.log.info("please, find the file in: {}".format(fpath)) + except EnvironmentError as e: + self.log.error("error on createing xml file") + self.log.error(e) + + def template(self, type): + temp = super().template(type) + return object2xml(temp) diff --git a/netsim_wrapper/templates/yaml.py b/netsim_wrapper/templates/yaml.py new file mode 100644 index 0000000..17fb1a5 --- /dev/null +++ b/netsim_wrapper/templates/yaml.py @@ -0,0 +1,49 @@ +""" +Custom YAML class from Template Abstrat Factory +""" + +from collections import OrderedDict +from yaml import add_representer, dump, load, FullLoader + +from .template import TemplateAbstractFactory + + +class Yaml(TemplateAbstractFactory): + """ + Custom YAML class + """ + def __init__(self) -> None: + super(Yaml, self).__init__() + self.log.debug("class: {} initialize".format(__class__.__name__)) + self._setup_yaml + + @property + def _setup_yaml(self): + represent_dict_order = lambda self, data: self.represent_mapping( + "tag:yaml.org,2002:map", data.items() + ) + add_representer(OrderedDict, represent_dict_order) + + def load(self, fpath): + data = None + try: + self.log.debug("method: {}.{}".format(__class__.__name__, __class__.__module__)) + with open(fpath) as f: + data = load(f, Loader=FullLoader) + except EnvironmentError as e: + self.log.error("error while loading yaml file {}".format(fpath)) + self.log.error(e) + return data + + def dump(self, fpath, template): + try: + self.log.debug("method: {}.{}".format(__class__.__name__, __class__.__module__)) + with open(fpath, "w") as f: + dump(template, f, sort_keys=False) + self.log.info("please, find the file in: {}".format(fpath)) + except EnvironmentError as e: + self.log.error("error while createing yaml template") + self.log.error(e) + + def template(self, type): + return super().template(type) diff --git a/netsim_wrapper/utils.py b/netsim_wrapper/utils.py deleted file mode 100644 index cc5b3b1..0000000 --- a/netsim_wrapper/utils.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -The module is for system operations (subprocess) and setting logger -""" - -from logging import INFO -from pathlib import Path - -from .system import System -from .json import Json - -class Xml(System): - def __init__(self, level=INFO) -> None: - super().__init__(level=level) - self.log.debug(f"base class: {__class__.__name__} initialize") - - def dump(self, fpath, xml_data): - try: - self.log.debug(f"base class: {__class__.__name__}.{__class__.__module__}") - with open(fpath, 'w') as fp: - fp.write(xml_data) - self.log.info("please, find the file in: {}".format(fpath)) - except EnvironmentError as e: - self.log.error("error on createing xml file") - self.log.error(e) - - -class Utils(Json, System): - def __init__(self, path='.', level=INFO) -> None: - super(Utils, self).__init__(path, level) - self.log.debug(f"base class: {__class__.__name__} initialize") - - def create(self, fpath): - if not self.is_file(fpath): - self.dump(fpath, {}) - - def rstrip_digits(self, given_string): - return given_string.rstrip('1234567890') - - def get_index(self, given_list, element): - try: - return given_list.index(element) - except ValueError: - return None - - def get_path(self): - path = None - # if len(cmd_lst) > index+1: - # path = cmd_lst[index+1] - # else: - # path = '{}/{}.{}'.format(self.path, filename, filetype) - return path diff --git a/netsim_wrapper/yaml.py b/netsim_wrapper/yaml.py deleted file mode 100644 index c30a4d7..0000000 --- a/netsim_wrapper/yaml.py +++ /dev/null @@ -1,43 +0,0 @@ -from logging import INFO -from collections import OrderedDict -from yaml import add_representer, dump, load, FullLoader - -from .system import System - - -class Yaml(System): - def __init__(self, level=INFO) -> None: - super(Yaml, self).__init__(level=level) - self.log.debug(f"base class: {__class__.__name__} initialize") - self._setup_yaml - - @property - def _setup_yaml(self): - represent_dict_order = lambda self, data: \ - self.represent_mapping( - 'tag:yaml.org,2002:map', - data.items() - ) - add_representer(OrderedDict, represent_dict_order) - - def load(self, fpath): - data = None - try: - self.log.debug(f"base class: {__class__.__name__}.{__class__.__module__}") - with open(fpath) as f: - data = load(f, Loader=FullLoader) - except EnvironmentError as e: - self.log.error("error while loading the file {}".format(fpath)) - self.log.error(e) - return data - - def dump(self, fpath, template): - try: - self.log.debug(f"base class: {__class__.__name__}.{__class__.__module__}") - with open(fpath, 'w') as f: - dump(template, f, sort_keys=False) - self.log.info("please, find the file in: {}".format(fpath)) - except EnvironmentError as e: - self.log.error("error while createing of template") - self.log.error(e) - From 821b2c0cea78fcd3adc9e29697d532740ebe7192 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Kotari Date: Wed, 27 Jul 2022 11:18:06 -0400 Subject: [PATCH 3/6] updated singletonMeta class with lru_cache --- netsim_wrapper/common/singleton.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/netsim_wrapper/common/singleton.py b/netsim_wrapper/common/singleton.py index 88af635..169d6b9 100644 --- a/netsim_wrapper/common/singleton.py +++ b/netsim_wrapper/common/singleton.py @@ -2,14 +2,9 @@ Singleton Metaclass """ -class SingletonMeta(type): - """ - Singleton Metaclass - """ - _instance = None +from functools import lru_cache + - def __call__(cls, *args, **kwargs): - if not cls._instance: - cls._instance = super().__call__(*args, **kwargs) - return cls._instance +class SingletonMeta(type): + __call__ = lru_cache(type.__call__) From 447a7f27afc031523c719988eb6f478ca579eca6 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Kotari Date: Wed, 27 Jul 2022 11:31:26 -0400 Subject: [PATCH 4/6] updated log, abstract methods --- netsim_wrapper/common/log.py | 2 +- netsim_wrapper/common/singleton.py | 1 - netsim_wrapper/templates/template.py | 6 ++---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/netsim_wrapper/common/log.py b/netsim_wrapper/common/log.py index 73f834f..5cb0f49 100644 --- a/netsim_wrapper/common/log.py +++ b/netsim_wrapper/common/log.py @@ -12,7 +12,7 @@ class Logger(metaclas=SingletonMeta): Logger with singleton metaclass """ format = '%(levelname)-8s | %(asctime)s | %(module)-8s:%(lineno)-4d | %(message)s' - log = logging.getLogger() + log = logging.basicConfig(level=logging.INFO, format=format) or logging.getLogger() def __init__(self, level=logging.INFO) -> None: self.level = level diff --git a/netsim_wrapper/common/singleton.py b/netsim_wrapper/common/singleton.py index 169d6b9..d144bed 100644 --- a/netsim_wrapper/common/singleton.py +++ b/netsim_wrapper/common/singleton.py @@ -2,7 +2,6 @@ Singleton Metaclass """ - from functools import lru_cache diff --git a/netsim_wrapper/templates/template.py b/netsim_wrapper/templates/template.py index d597fe0..246aae3 100644 --- a/netsim_wrapper/templates/template.py +++ b/netsim_wrapper/templates/template.py @@ -62,12 +62,10 @@ def __init__(self) -> None: self.log.debug("class: {} initialize".format(__class__.__name__)) @abstractmethod - def load(self, fpath): - pass + def load(self, fpath):... @abstractmethod - def dump(self, fpath, template): - pass + def dump(self, fpath, template):... def template(self, type): if type == 'network': From 14a083ba4adff5696bd45f182cf405a66c495347 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Kotari Date: Wed, 27 Jul 2022 11:37:39 -0400 Subject: [PATCH 5/6] merged log and singleton --- netsim_wrapper/common/log.py | 6 +++++- netsim_wrapper/common/singleton.py | 9 --------- 2 files changed, 5 insertions(+), 10 deletions(-) delete mode 100644 netsim_wrapper/common/singleton.py diff --git a/netsim_wrapper/common/log.py b/netsim_wrapper/common/log.py index 5cb0f49..72e7975 100644 --- a/netsim_wrapper/common/log.py +++ b/netsim_wrapper/common/log.py @@ -4,7 +4,11 @@ import logging -from .singleton import SingletonMeta +from functools import lru_cache + + +class SingletonMeta(type): + __call__ = lru_cache(type.__call__) class Logger(metaclas=SingletonMeta): diff --git a/netsim_wrapper/common/singleton.py b/netsim_wrapper/common/singleton.py deleted file mode 100644 index d144bed..0000000 --- a/netsim_wrapper/common/singleton.py +++ /dev/null @@ -1,9 +0,0 @@ -""" -Singleton Metaclass -""" - -from functools import lru_cache - - -class SingletonMeta(type): - __call__ = lru_cache(type.__call__) From cac02b83d72a5f54b9ac2aeeb21945272320ec4a Mon Sep 17 00:00:00 2001 From: Kiran Kumar Kotari Date: Wed, 27 Jul 2022 12:18:01 -0400 Subject: [PATCH 6/6] minor corrections --- netsim_wrapper/common/log.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netsim_wrapper/common/log.py b/netsim_wrapper/common/log.py index 72e7975..b051a26 100644 --- a/netsim_wrapper/common/log.py +++ b/netsim_wrapper/common/log.py @@ -8,10 +8,10 @@ class SingletonMeta(type): - __call__ = lru_cache(type.__call__) + __call__ = lru_cache(maxsize=None)(type.__call__) -class Logger(metaclas=SingletonMeta): +class Logger(metaclass=SingletonMeta): """ Logger with singleton metaclass """