Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.1.2
4.0
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# 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)
[![Downloads](https://pepy.tech/badge/netsim-wrapper/week)](https://pepy.tech/project/netsim-wrapper/week)

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 \<device-names>
- create-network-from [ yaml | json ] \<filename>
Expand Down
Empty file.
32 changes: 32 additions & 0 deletions netsim_wrapper/common/log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
Application logging operations
"""

import logging

from functools import lru_cache


class SingletonMeta(type):
__call__ = lru_cache(maxsize=None)(type.__call__)


class Logger(metaclass=SingletonMeta):
"""
Logger with singleton metaclass
"""
format = '%(levelname)-8s | %(asctime)s | %(module)-8s:%(lineno)-4d | %(message)s'
log = logging.basicConfig(level=logging.INFO, format=format) or 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()

74 changes: 74 additions & 0 deletions netsim_wrapper/common/system.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""
The module is for system operations exit, unlink
"""

from pathlib import Path
from sys import exit as sys_exit
import subprocess

from .log import Logger


class System:
def __init__(self) -> None:
super(System, self).__init__()
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)))
self.log.error('message: {}'.format(err))
if 'command not found' in err or 'Unknown command' in err:
raise ValueError("command not found.")
return False
41 changes: 41 additions & 0 deletions netsim_wrapper/common/utils.py
Original file line number Diff line number Diff line change
@@ -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
139 changes: 139 additions & 0 deletions netsim_wrapper/netsim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
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'

_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()



def read_netsim(self, path):
data = open(path).read().split(self._split)
self.__netsim_devices_created_by
return self._netsim_device_mapper(data)
Loading