├── .gitignore ├── .version ├── MANIFEST.in ├── README.md ├── ncs_pycli ├── __init__.py └── ncs_pycli.py ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Custom 2 | netsim/ 3 | backup* 4 | 5 | # OS Files 6 | .DS_Store 7 | *.DS_Store 8 | 9 | # Byte-compiled / optimized / DLL files 10 | __pycache__/ 11 | *.py[cod] 12 | *$py.class 13 | 14 | # C extensions 15 | *.so 16 | 17 | # Distribution / packaging 18 | .Python 19 | build/ 20 | develop-eggs/ 21 | dist/ 22 | downloads/ 23 | eggs/ 24 | .eggs/ 25 | lib/ 26 | lib64/ 27 | parts/ 28 | sdist/ 29 | var/ 30 | wheels/ 31 | pip-wheel-metadata/ 32 | share/python-wheels/ 33 | *.egg-info/ 34 | .installed.cfg 35 | *.egg 36 | MANIFEST 37 | 38 | # PyInstaller 39 | # Usually these files are written by a python script from a template 40 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 41 | *.manifest 42 | *.spec 43 | 44 | # Installer logs 45 | pip-log.txt 46 | pip-delete-this-directory.txt 47 | 48 | # Unit test / coverage reports 49 | htmlcov/ 50 | .tox/ 51 | .nox/ 52 | .coverage 53 | .coverage.* 54 | .cache 55 | nosetests.xml 56 | coverage.xml 57 | *.cover 58 | .hypothesis/ 59 | .pytest_cache/ 60 | 61 | # Translations 62 | *.mo 63 | *.pot 64 | 65 | # Django stuff: 66 | *.log 67 | local_settings.py 68 | db.sqlite3 69 | db.sqlite3-journal 70 | 71 | # Flask stuff: 72 | instance/ 73 | .webassets-cache 74 | 75 | # Scrapy stuff: 76 | .scrapy 77 | 78 | # Sphinx documentation 79 | docs/_build/ 80 | 81 | # PyBuilder 82 | target/ 83 | 84 | # Jupyter Notebook 85 | .ipynb_checkpoints 86 | 87 | # IPython 88 | profile_default/ 89 | ipython_config.py 90 | 91 | # pyenv 92 | .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # celery beat schedule file 102 | celerybeat-schedule 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | -------------------------------------------------------------------------------- /.version: -------------------------------------------------------------------------------- 1 | 1.3.4 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include requirements.txt 3 | include .version -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ncs_pycli (or) ncs-pycli 2 | 3 | Gives you an interactive NSO python shell with tab completion. 4 | 5 | A power tool for quick prototyping 6 | 7 | ## How to Install 8 | 9 | ```bash 10 | pip install ncs-pycli 11 | ``` 12 | 13 | ### Prerequisites 14 | 15 | - Python 16 | - IPython [ipython.org](https://ipython.org) 17 | 18 | If you already have python you can install IPython with 19 | 20 | ```bash 21 | pip install ipython 22 | ``` 23 | 24 | ## Usage 25 | 26 | ```bash 27 | #> ncs_pycli (or) ncs-pycli 28 | Your maagic object 'root -> (root)' is now prepared... go have some fun! 29 | trans.compare() to see your current transaction 30 | trans.apply() to commit 31 | Maapi object can be found at m 32 | 33 | In [1]: for device in root.ncs__devices.device: 34 | ...: print(device.name) 35 | ...: 36 | ce0 37 | ce1 38 | ce2 39 | ce3 40 | ce4 41 | ce5 42 | pe0 43 | pe1 44 | pe2 45 | 46 | In [2]: device = root.ncs__devices.device['ce0'] 47 | 48 | In [3]: type(device) 49 | Out[3]: ncs.maagic.ListElement 50 | 51 | In [4]: device 52 | Out[4]: ListElement name=device tag=617911018 keys={ce0} 53 | 54 | In [5]: help(device) 55 | 56 | In [6]: device. 57 | device.active_settings device.connect_timeout device.netconf_notifications device.snmp_notification_address 58 | device.address device.delete_config device.no_lsa device.source 59 | device.al__alarm_summary device.description device.out_of_sync_commit_behaviour device.ssh 60 | device.apply_template device.device_profile device.ping device.ssh_keep_alive 61 | device.authgroup device.device_type device.platform device.state 62 | device.capability device.disconnect device.port device.sync_from 63 | device.check_sync device.instantiate_from_other_device device.read_timeout device.sync_to 64 | device.check_yang_modules device.live_status device.remote_node device.trace 65 | device.choice_lsa device.live_status_protocol device.rpc device.use_lsa 66 | device.commit_queue device.location device.scp_from device.write_timeout 67 | device.compare_config device.module device.scp_to 68 | device.config device.name device.service_list 69 | device.connect device.ned_settings device.session_pool 70 | 71 | In [6]: device.config.io 72 | device.config.ios__aaa device.config.ios__ethernet device.config.ios__multilink device.config.ios__snmp_server 73 | device.config.ios__access_list device.config.ios__event device.config.ios__no device.config.ios__spanning_tree 74 | device.config.ios__alarm_contact device.config.ios__fabric device.config.ios__ntp device.config.ios__system 75 | device.config.ios__archive device.config.ios__gatekeeper device.config.ios__parameter_map device.config.ios__table_map 76 | device.config.ios__authentication device.config.ios__hostname device.config.ios__platform device.config.ios__tacacs_server 77 | device.config.ios__banner device.config.ios__interface device.config.ios__policer device.config.ios__tftp_server 78 | device.config.ios__bba_group device.config.ios__ip device.config.ios__policy_map device.config.ios__transceiver 79 | device.config.ios__card device.config.ios__ipv6 device.config.ios__port_channel device.config.ios__upgrade 80 | device.config.ios__class_map device.config.ios__l2 device.config.ios__power device.config.ios__username 81 | device.config.ios__clock device.config.ios__l2protocol_tunnel device.config.ios__privilege device.config.ios__version 82 | device.config.ios__config_register device.config.ios__license device.config.ios__radius device.config.ios__vlan 83 | device.config.ios__control_plane device.config.ios__line device.config.ios__radius_server device.config.ios__vmps 84 | device.config.ios__controller device.config.ios__logging device.config.ios__redundancy device.config.ios__voice_card 85 | device.config.ios__crypto device.config.ios__mac device.config.ios__rep device.config.ios__vpdn 86 | device.config.ios__disable_eadi device.config.ios__memory_size device.config.ios__route_map device.config.ios__vrf 87 | device.config.ios__dot11 device.config.ios__mgcp device.config.ios__router device.config.ios__vtp 88 | device.config.ios__dot1x device.config.ios__mls device.config.ios__scheduler device.config.ios__xconnect 89 | device.config.ios__enable device.config.ios__monitor device.config.ios__service device.config.ios__zone 90 | device.config.ios__errdisable device.config.ios__mpls device.config.ios__snmp device.config.ios__zone_pair 91 | 92 | In [6]: device.config.ios__hostname='CE0' 93 | 94 | In [7]: trans.compare() 95 | Diff set: 96 | kp=/ncs:devices/device{ce0}, op=MOP_MODIFIED, oldv=None, newv=None 97 | kp=/ncs:devices/device{ce0}/config/ios:hostname, op=MOP_VALUE_SET, oldv=None, newv=CE0 98 | 99 | In [8]: trans.apply() 100 | 101 | In [9]: %hist 102 | for device in root.ncs__devices.device: 103 | print(device.name) 104 | device = root.ncs__devices.device['ce0'] 105 | help(device) 106 | type(device) 107 | device 108 | device.config.ios__hostname='CE0' 109 | trans.compare() 110 | trans.apply() 111 | ``` 112 | 113 | ## Contact 114 | 115 | Contact Hakan Niska with any suggestions or comments. If you find any bugs please fix them and send me a pull request. 116 | -------------------------------------------------------------------------------- /ncs_pycli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NSO-developer/ncs_pycli/1a2415136c3f2d2495d7f28b06a2a36831592a67/ncs_pycli/__init__.py -------------------------------------------------------------------------------- /ncs_pycli/ncs_pycli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ipython 2 | import os 3 | import sys 4 | import logging 5 | import subprocess 6 | 7 | 8 | OPER = { 9 | 1: 'MOP_CREATED', 10 | 2: 'MOP_DELETED', 11 | 3: 'MOP_MODIFIED', 12 | 4: 'MOP_VALUE_SET', 13 | 5: 'MOP_MOVED_AFTER', 14 | 6: 'MOP_ATTR_SET' 15 | } 16 | 17 | class DiffIterator(object): 18 | def __init__(self): 19 | self.count = 0 20 | def __call__(self, kp, op, oldv, newv): 21 | print('kp={0}, op={1}, oldv={2}, newv={3}'.format( 22 | str(kp), OPER[op], str(oldv), str(newv))) 23 | self.count += 1 24 | return ncs.ITER_RECURSE 25 | 26 | def compare(self): 27 | print("Diff set:") 28 | self.diff_iterate(DiffIterator(), ncs.ITER_WANT_ATTR) 29 | 30 | class Utils: 31 | name = 'Utils' 32 | 33 | _instance = None 34 | 35 | __stdout = subprocess.PIPE 36 | __stderr = subprocess.PIPE 37 | 38 | def __new__(cls, log_level=logging.INFO, log_format=None): 39 | if cls._instance is None: 40 | cls._instance = object.__new__(cls) 41 | return cls._instance 42 | 43 | def __init__(self, log_level=logging.INFO, log_format=None, *args, **kwargs): 44 | # logger setup 45 | self.__format = log_format 46 | self.logger = self.__set_logger_level(log_level) 47 | 48 | def __set_logger_level(self, log_level): 49 | if self.__format is None: 50 | self.__format = '[ %(levelname)s ] :: [ %(name)s ] :: %(message)s' 51 | logging.basicConfig(stream=sys.stdout, level=log_level, 52 | format=self.__format, datefmt=None) 53 | logger = logging.getLogger(self.name) 54 | logger.setLevel(log_level) 55 | return logger 56 | 57 | def _run_command(self, command): 58 | self.logger.debug("command `{}` running on terminal".format(' '.join(command))) 59 | p = subprocess.Popen(command, stdout=self.__stdout, stderr=self.__stderr) 60 | out, err = p.communicate() 61 | out, err = out.decode('utf-8'), err.decode('utf-8') 62 | if err == '': 63 | self.logger.debug("`{}` ran successfully".format(' '.join(command))) 64 | return out 65 | self.logger.error("command issue: {}".format(err)) 66 | self._exit 67 | 68 | @property 69 | def _exit(self): 70 | sys.exit() 71 | 72 | class NcsPycli(Utils): 73 | name = 'ncs_pycli' 74 | version = '1.3.4' 75 | command = ['ipython'] 76 | options = [] 77 | 78 | _instance = None 79 | 80 | def __init__(self, log_level=logging.INFO, log_format=None, *args, **kwargs): 81 | Utils.__init__(self, log_level, log_format) 82 | 83 | def _create_profile(self): 84 | self.command += ['profile', 'create'] 85 | self.logger.info('creating ipython profile') 86 | out = self._run_command(self.command) 87 | if out is None or out == '': 88 | self.logger.info("couldn't able to create an ipython instance.") 89 | self._exit 90 | 91 | def _get_shell(self): 92 | if ipy.get_ipython() is None: 93 | shell = ipy_shell.instance() 94 | if shell is None: 95 | self._create_profile() 96 | self._get_shell() 97 | shell.user_ns['shell'] = shell 98 | return ipy.get_ipython() 99 | 100 | def initialize(self, cmd_lst): 101 | if '--version' in cmd_lst or '-v' in cmd_lst: 102 | print('ncs_pycli version {}'.format(self.version)) 103 | self._exit 104 | ncs.maapi.Transaction.compare = compare 105 | shell = self._get_shell() 106 | shell.define_macro('new_trans', """trans=m.start_write_trans() 107 | root = ncs.maagic.get_root(trans) 108 | print("new transaction created") 109 | """) 110 | path = os.path.abspath('.') 111 | # os.environ['PYTHONPATH'] += ':'+path 112 | sys.path.insert(0, path) 113 | try: 114 | import _ncs 115 | m = ncs.maapi.Maapi() 116 | m.start_user_session('admin', 'system', []) 117 | trans = m.start_write_trans() 118 | root = ncs.maagic.get_root(trans) 119 | new_trans = shell.user_ns['new_trans'] 120 | except _ncs.error.Error as e: 121 | if 'Failed to connect to ConfD' in e.args[0]: 122 | self.logger.error("cloudn't able to find ncs running..!") 123 | self.logger.error(e.args[0]) 124 | exit(-1) 125 | 126 | print("Your maagic object 'root -> %s' is now prepared... go have some fun!\ntrans.compare() to see your current transaction\ntrans.apply() to commit\ntrans.revert() to revert changes\nMaapi object can be found at m" % (str(root))) 127 | print("""You can restart the transaction and create a fresh root object by invoking new_trans: 128 | In [1]: new_trans 129 | new transaction created""") 130 | ipy.embed(display_banner=False, config=shell.config, using=False) 131 | 132 | 133 | obj = NcsPycli() 134 | try: 135 | import ncs 136 | except ModuleNotFoundError: 137 | obj.logger.error('ncs module not found..!') 138 | obj.logger.info('source ncsrc or add /src/ncs/pyapi path to $PYTHONPATH') 139 | 140 | try: 141 | import IPython as ipy 142 | from IPython.terminal.interactiveshell import TerminalInteractiveShell as ipy_shell 143 | except ModuleNotFoundError: 144 | obj.logger.error('no module ipython found..!') 145 | obj.logger.info('you can install via pip "pip install ipython"') 146 | 147 | 148 | def run(): 149 | obj = NcsPycli() 150 | obj.initialize(sys.argv) 151 | 152 | 153 | if __name__ == '__main__': 154 | run() 155 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ipython -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | from os import path 3 | from io import open 4 | 5 | here = path.abspath(path.dirname(__file__)) 6 | reqs = [] 7 | 8 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 9 | long_description = f.read() 10 | 11 | with open(path.join(here, '.version'), encoding='utf-8') as f: 12 | version = f.read() 13 | 14 | with open(path.join(here, 'requirements.txt'), encoding='utf-8') as f: 15 | read_lines = f.readlines() 16 | reqs = [each.strip() for each in read_lines] 17 | 18 | setup( 19 | name = 'ncs_pycli', 20 | version = version, 21 | description = "Gives you an interactive NSO python shell with tab completion.", 22 | long_description = long_description, 23 | long_description_content_type = 'text/markdown', 24 | url = 'https://github.com/NSO-developer/ncs_pycli.git', 25 | author = 'Hakan Niska, Kiran Kumar Kotari', 26 | author_email = 'hniska@cisco.com, kkotari@cisco.com', 27 | entry_points={ 28 | 'console_scripts': [ 29 | 'ncs_pycli=ncs_pycli.ncs_pycli:run', 30 | 'ncs-pycli=ncs_pycli.ncs_pycli:run', 31 | ], 32 | }, 33 | install_requires=reqs, 34 | classifiers = [ 35 | 'Development Status :: 3 - Alpha', 36 | 'Intended Audience :: Developers', 37 | 'Topic :: Software Development :: Build Tools', 38 | 'License :: OSI Approved :: MIT License', 39 | 'Operating System :: OS Independent', 40 | 'Programming Language :: Python :: 2.7', 41 | 'Programming Language :: Python :: 3.5', 42 | 'Programming Language :: Python :: 3.6', 43 | 'Programming Language :: Python :: 3.7', 44 | ], 45 | keywords = 'ncs-pycli ncs_pycli', 46 | packages = find_packages(where='.', exclude=['tests']), 47 | include_package_data=True, 48 | ) 49 | 50 | --------------------------------------------------------------------------------