Hello community, here is the log from the commit of package python-ncclient for openSUSE:Factory checked in at 2020-08-10 15:05:21 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-ncclient (Old) and /work/SRC/openSUSE:Factory/.python-ncclient.new.3399 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-ncclient" Mon Aug 10 15:05:21 2020 rev:12 rq:825318 version:0.6.9 Changes: -------- --- /work/SRC/openSUSE:Factory/python-ncclient/python-ncclient.changes 2020-02-19 12:41:31.311737996 +0100 +++ /work/SRC/openSUSE:Factory/.python-ncclient.new.3399/python-ncclient.changes 2020-08-10 15:06:00.600254285 +0200 @@ -1,0 +2,12 @@ +Sun Aug 9 14:03:46 UTC 2020 - Martin Hauke <mar...@gmx.de> + +- Update to version 0.6.9 + * Fix for breaking API change +- Update to version 0.6.8 + * Support for namespace prefixes for XPath queries + * edit-config parameter validation + * Support for multiple RPC errors + * API to get supported device types + * Support for subtree filters with multiple top-level tags + +------------------------------------------------------------------- Old: ---- ncclient-0.6.7.tar.gz New: ---- ncclient-0.6.9.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-ncclient.spec ++++++ --- /var/tmp/diff_new_pack.Z0w8Bd/_old 2020-08-10 15:06:01.608254818 +0200 +++ /var/tmp/diff_new_pack.Z0w8Bd/_new 2020-08-10 15:06:01.608254818 +0200 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-ncclient -Version: 0.6.7 +Version: 0.6.9 Release: 0 Summary: Python library for NETCONF clients License: Apache-2.0 @@ -83,6 +83,6 @@ %{python_sitelib}/* %files -n python-ncclient-doc -%doc README README.rst examples docs/build/html +%doc README.md README.rst examples docs/build/html %changelog ++++++ ncclient-0.6.7.tar.gz -> ncclient-0.6.9.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/Changelog new/ncclient-0.6.9/Changelog --- old/ncclient-0.6.7/Changelog 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/Changelog 2020-08-09 00:09:16.000000000 +0200 @@ -1,3 +1,74 @@ +version 0.6.9 +------------- + +* Resiolved breaking API change + +version 0.6.8 +------------- + +* Variety of small updates and bugfixes, but of note: + - Support for namespace prefixes for XPath queries + - `edit-config` parameter validation + - Support for multiple RPC errors + - API to get supported device types + - Support for subtree filters with multiple top-level tags +* Thanks to all contributors! +* Pulled due to avccidental breaking API change + +version 0.6.7 +------------- + +* Variety of bugfixes from a variety of contributors since 0.6.6 (see commit history) + + +version 0.6.6 +------------- + +* Read ssh timeout from config file if not specified in method call +* Tox support +* Huge XML tree parser support +* Adding optional bind address to connect + + +version 0.6.5 +------------- + +* Updated README for 0.6.5 release + + +version 0.6.4 +------------- + +* Pin selectors2 to Python versions <= 3.4 +* Fix config examples to actually use the nc namespace +* Fix: correctly set port for paramiko when using ssh_config file +* Test: add test to check ProxyCommand uses correct port +* Update commits for py3 +* Enhance Alcatel-Lucent-support +* Juniper RPC: allow specifying format in CompareConfiguration +* Parsing of NETCONF 1.1 frames no longer decodes each chunk of bytes +* Fix filter in create_subscription +* Validate 'with-defaults' mode based on supported modes advertised in capability URI + + +version 0.6.3 +------------- + +* Fix homepage link registered with PyPi +* SSH Host Key checking +* Updated junos.py to resolve RestrictedUser error +* Close the channel when closing SSH session +* Invoke self.parse() to ensure errors, if any, have been detected before check in ok() + + +version 0.6.2 +------------- + +* Migration to user selectors instead of select, allowing higher scale operations +* Improved netconf:base:1.1 parsing +* Graceful exit on session close + + version 0.6.0 ------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/README new/ncclient-0.6.9/README --- old/ncclient-0.6.7/README 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/README 1970-01-01 01:00:00.000000000 +0100 @@ -1,90 +0,0 @@ -[![Build Status](https://travis-ci.org/einarnn/ncclient.svg?branch=master)](https://travis-ci.org/einarnn/ncclient) - -ncclient: Python library for NETCONF clients --------------------------------------------- - -ncclient is a Python library that facilitates client-side scripting -and application development around the NETCONF protocol. `ncclient` was -developed by [Shikar Bhushan](http://schmizz.net). It is now maintained -by [Leonidas Poulopoulos (@leopoul)](http://ncclient.org/ncclient/) - -**Docs**: [http://ncclient.readthedocs.org](http://ncclient.readthedocs.org) - -**PyPI**: [https://pypi.python.org/pypi/ncclient](https://pypi.python.org/pypi/ncclient) - -#### Requirements: -* Python 2.7 or Python 3.4+ -* setuptools 0.6+ -* Paramiko 1.7+ -* lxml 3.3.0+ -* libxml2 -* libxslt - -If you are on Debian/Ubuntu install the following libs (via aptitude or apt-get): -* libxml2-dev -* libxslt1-dev - -#### Installation: - - [ncclient] $ sudo python setup.py install - -or via pip: - - pip install ncclient - -#### Examples: - - [ncclient] $ python examples/juniper/*.py - -### Usage -####Get device running config -Use either an interactive Python console (ipython) -or integrate the following in your code: - - from ncclient import manager - - with manager.connect(host=host, port=830, username=user, hostkey_verify=False) as m: - c = m.get_config(source='running').data_xml - with open("%s.xml" % host, 'w') as f: - f.write(c) - -As of 0.4.1 ncclient integrates Juniper's and Cisco's forks, lots of new concepts -have been introduced that ease management of Juniper and Cisco devices respectively. -The biggest change is the introduction of device handlers in connection paramms. -For example to invoke Juniper's functions annd params one has to re-write the above with ***device_params={'name':'junos'}***: - - from ncclient import manager - - with manager.connect(host=host, port=830, username=user, hostkey_verify=False, device_params={'name':'junos'}) as m: - c = m.get_config(source='running').data_xml - with open("%s.xml" % host, 'w') as f: - f.write(c) - -Device handlers are easy to implement and prove to be futureproof. - -####Supported device handlers - -* Juniper: device_params={'name':'junos'} -* Cisco CSR: device_params={'name':'csr'} -* Cisco Nexus: device_params={'name':'nexus'} -* Huawei: device_params={'name':'huawei'} -* Alcatel Lucent: device_params={'name':'alu'} -* H3C: device_params={'name':'h3c'} -* HP Comware: device_params={'name':'hpcomware'} - - -### Changes | brief - v0.5.3 - -* Add notifications support -* Add support for ecdsa keys -* Various bug fixes - -### Contributors -* v0.5.3: [Justin Wilcox](https://github.com/jwwilcox), [Stacy W. Smith](https://github.com/stacywsmith), [Mircea Ulinic](https://github.com/mirceaulinic), [Ebben Aries](https://github.com/earies), [Einar Nilsen-Nygaard](https://github.com/einarnn), [QijunPan](https://github.com/QijunPan) -* v0.5.2: [Nitin Kumar](https://github.com/vnitinv), [Kristian Larsson](https://github.com/plajjan), [palashgupta](https://github.com/palashgupta), [Jonathan Provost](https://github.com/JoProvost), [Jainpriyal](https://github.com/Jainpriyal), [sharang](https://github.com/sharang), [pseguel](https://github.com/pseguel), [nnakamot](https://github.com/nnakamot), [Алексей Пастухов](https://github.com/p-alik), [Christian Giese](https://github.com/GIC-de), [Peipei Guo](https://github.com/peipeiguo), [Time Warner Cable Openstack Team](https://github.com/twc-openstack) -* v0.4.7: [Einar Nilsen-Nygaard](https://github.com/einarnn), [Vaibhav Bajpai](https://github.com/vbajpai), Norio Nakamoto -* v0.4.6: [Nitin Kumar](https://github.com/vnitinv), [Carl Moberg](https://github.com/cmoberg), [Stavros Kroustouris](https://github.com/kroustou) -* v0.4.5: [Sebastian Wiesinger](https://github.com/sebastianw), [Vincent Bernat](https://github.com/vincentbernat), [Matthew Stone](https://github.com/bigmstone), [Nitin Kumar](https://github.com/vnitinv) -* v0.4.3: [Jeremy Schulman](https://github.com/jeremyschulman), [Ray Solomon](https://github.com/rsolomo), [Rick Sherman](https://github.com/shermdog), [subhak186](https://github.com/subhak186) -* v0.4.2: [katharh](https://github.com/katharh), [Francis Luong (Franco)](https://github.com/francisluong), [Vincent Bernat](https://github.com/vincentbernat), [Juergen Brendel](https://github.com/juergenbrendel), [Quentin Loos](https://github.com/Kent1), [Ray Solomon](https://github.com/rsolomo), [Sebastian Wiesinger](https://github.com/sebastianw), [Ebben Aries](https://github.com/earies) -* v0.4.1: [Jeremy Schulman](https://github.com/jeremyschulman), [Ebben Aries](https://github.com/earies), Juergen Brendel diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/README.md new/ncclient-0.6.9/README.md --- old/ncclient-0.6.7/README.md 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/README.md 2020-08-09 00:09:16.000000000 +0200 @@ -19,6 +19,8 @@ | Date | Release | Description | | :----: | :-----: | :---------- | +| 08/08/20 | `0.6.9` | See [release page](https://github.com/ncclient/ncclient/releases/tag/v0.6.9) | +| 08/01/20 | `0.6.8` | Pulled due to accidental breaking API change | | 12/21/19 | `0.6.7` | See [release page](https://github.com/ncclient/ncclient/releases/tag/v0.6.7) | | 05/27/19 | `0.6.6` | See [release page](https://github.com/ncclient/ncclient/releases/tag/v0.6.6) | | 05/27/19 | `0.6.5` | Pulled due to bug in PyPi upload | @@ -186,6 +188,7 @@ ## Contributors +* v0.6.8: [Fred Gan](https://github.com/fredgan), @vnitinv, @kbijakowski, @iwanb, @badguy99, @liuyong, Andrew Mallory, William Lvory * v0.6.7: @vnitinv, @chaitu-tk, @sidhujasminder, @crutcha, @markgoddard, @ganeshrn, @songxl, @doesitblend, @psikala, @xuxiaowei0512, @muffizone * v0.6.6: @sstancu, @hemna, @ishayansheikh * v0.6.4: @davidhankins, @mzagozen, @knobix, @markafarrell, @psikala, @moepman, @apt-itude, @yuekyang diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/README.rst new/ncclient-0.6.9/README.rst --- old/ncclient-0.6.7/README.rst 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/README.rst 2020-08-09 00:09:16.000000000 +0200 @@ -3,8 +3,9 @@ ncclient is a Python library that facilitates client-side scripting and application development around the NETCONF protocol. ``ncclient`` was -developed by `Shikar Bhushan <http://schmizz.net>`_. It is now -maintained by `Leonidas Poulopoulos (@leopoul) <http://ncclient.org>`_ +developed by `Shikar Bhushan <http://schmizz.net>`. It is now +maintained by `Leonidas Poulopoulos (@leopoul) <http://ncclient.org>` +and `Einar Nilsen-Nygaard (@einarnn)`. Docs: `http://ncclient.readthedocs.org <http://ncclient.readthedocs.org>`_ @@ -86,17 +87,80 @@ Supported device handlers ''''''''''''''''''''''''' -* Juniper: device_params={'name':'junos'} -* Cisco CSR: device_params={'name':'csr'} -* Cisco Nexus: device_params={'name':'nexus'} -* Huawei: device_params={'name':'huawei'} -* Alcatel Lucent: device_params={'name':'alu'} -* H3C: device_params={'name':'h3c'} -* HP Comware: device_params={'name':'hpcomware'} +* Juniper: `device_params={'name':'junos'}` +* Cisco: + - CSR: `device_params={'name':'csr'}` + - Nexus: `device_params={'name':'nexus'}` + - IOS XR: `device_params={'name':'iosxr'}` + - IOS XE: `device_params={'name':'iosxe'}` +* Huawei: + - `device_params={'name':'huawei'}` + - `device_params={'name':'huaweiyang'}` +* Alcatel Lucent: `device_params={'name':'alu'}` +* H3C: `device_params={'name':'h3c'}` +* HP Comware: `device_params={'name':'hpcomware'}` +* Server or anything not in above: `device_params={'name':'default'}` Changes \| brief ~~~~~~~~~~~~~~~~ +**v0.6.9** + +* Fix for breaking API change + +**v0.6.8** + +* Pulled due to accidental breaking API change +* Variety of small updates and bugfixes, but of note: + - Support for namespace prefixes for XPath queries + - `edit-config` parameter validation + - Support for multiple RPC errors + - API to get supported device types + - Support for subtree filters with multiple top-level tags +* Thanks to all contributors! + +**v0.6.7** + +- Variety of bugfixes from a variety of contributors since 0.6.6 (see commit history) + +**v0.6.6** + +- Read ssh timeout from config file if not specified in method call +- Tox support +- Huge XML tree parser support +- Adding optional bind address to connect + +**v0.6.5** + +- Updated README for 0.6.5 release + +**v0.6.4** + +- Pin selectors2 to Python versions <= 3.4 +- Fix config examples to actually use the nc namespace +- Fix: correctly set port for paramiko when using ssh_config file +- Test: add test to check ProxyCommand uses correct port +- Update commits for py3 +- Enhance Alcatel-Lucent-support +- Juniper RPC: allow specifying format in CompareConfiguration +- Parsing of NETCONF 1.1 frames no longer decodes each chunk of bytes +- Fix filter in create_subscription +- Validate 'with-defaults' mode based on supported modes advertised in capability URI + +**v0.6.3** + +- Fix homepage link registered with PyPi +- SSH Host Key checking +- Updated junos.py to resolve RestrictedUser error +- Close the channel when closing SSH session +- Invoke self.parse() to ensure errors, if any, have been detected before check in ok() + +**v0.6.2** + +- Migration to user selectors instead of select, allowing higher scale operations +- Improved netconf:base:1.1 parsing +- Graceful exit on session close + **v0.6.0** - Fix use of new Python 3.7 keyword, async @@ -180,7 +244,13 @@ Acknowledgements ~~~~~~~~~~~~~~~~ - +- v0.6.9: [Fred Gan](https://github.com/fredgan) +- v0.6.8: [Fred Gan](https://github.com/fredgan), @vnitinv, @kbijakowski, @iwanb, @badguy99, @liuyong, Andrew Mallory, William Lvory +- v0.6.7: @vnitinv, @chaitu-tk, @sidhujasminder, @crutcha, @markgoddard, @ganeshrn, @songxl, @doesitblend, @psikala, @xuxiaowei0512, @muffizone +- v0.6.6: @sstancu, @hemna, @ishayansheikh +- v0.6.4: @davidhankins, @mzagozen, @knobix, @markafarrell, @psikala, @moepman, @apt-itude, @yuekyang +- v0.6.3: @rdkls, @Anthony25, @rsmekala, @vnitinv, @siming85 +- v0.6.2: @einarnn, @glennmatthews, @bryan-stripe, @nickylba - v0.6.0: `Einar Nilsen-Nygaard`_ - v0.5.4: Various - v0.5.3: `Justin Wilcox`_, `Stacy W. Smith`_, `Mircea Ulinic`_, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/docs/requirements.txt new/ncclient-0.6.9/docs/requirements.txt --- old/ncclient-0.6.7/docs/requirements.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/ncclient-0.6.9/docs/requirements.txt 2020-08-09 00:09:16.000000000 +0200 @@ -0,0 +1,2 @@ +Sphinx==3.2.0 + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/docs/source/conf.py new/ncclient-0.6.9/docs/source/conf.py --- old/ncclient-0.6.7/docs/source/conf.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/docs/source/conf.py 2020-08-09 00:09:16.000000000 +0200 @@ -46,9 +46,9 @@ # built documents. # # The short X.Y version. -version = '0.5' +version = '0.6' # The full version, including alpha/beta/rc tags. -release = '0.5.3' +release = '0.6.9' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/docs/source/index.rst new/ncclient-0.6.9/docs/source/index.rst --- old/ncclient-0.6.7/docs/source/index.rst 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/docs/source/index.rst 2020-08-09 00:09:16.000000000 +0200 @@ -3,7 +3,7 @@ `ncclient` is a Python library for NETCONF clients. It aims to offer an intuitive API that sensibly maps the XML-encoded nature of NETCONF to Python constructs and idioms, and make writing network-management scripts easier. Other key features are: -* Supports all operations and capabilities defined in :rfc:`4741`. +* Supports all operations and capabilities defined in :rfc:`6241`. * Request pipelining. * Asynchronous RPC requests. * Keeping XML out of the way unless really needed. @@ -39,10 +39,19 @@ Supported device handlers ------------------------- -* Juniper: device_params={'name':'junos'} -* Cisco CSR: device_params={'name':'csr'} -* Cisco Nexus: device_params={'name':'nexus'} -* Huawei: device_params={'name':'huawei'} +* Juniper: `device_params={'name':'junos'}` +* Cisco: + - CSR: `device_params={'name':'csr'}` + - Nexus: `device_params={'name':'nexus'}` + - IOS XR: `device_params={'name':'iosxr'}` + - IOS XE: `device_params={'name':'iosxe'}` +* Huawei: + - `device_params={'name':'huawei'}` + - `device_params={'name':'huaweiyang'}` +* Alcatel Lucent: `device_params={'name':'alu'}` +* H3C: `device_params={'name':'h3c'}` +* HP Comware: `device_params={'name':'hpcomware'}` +* Server or anything not in above: `device_params={'name':'default'}` Contents: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/docs/source/manager.rst new/ncclient-0.6.9/docs/source/manager.rst --- old/ncclient-0.6.7/docs/source/manager.rst 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/docs/source/manager.rst 2020-08-09 00:09:16.000000000 +0200 @@ -40,37 +40,79 @@ .. autoattribute:: HUGE_TREE_DEFAULT - .. automethod:: get_config(source, filter=None) + .. method:: get_config(source, filter=None, with_defaults=None) - .. automethod:: edit_config(target, config, default_operation=None, test_option=None, error_option=None) + `get_config` is mapped to :class:`~ncclient.operations.GetConfig` - .. automethod:: copy_config(source, target) + .. method:: get_schema(identifier, version=None, format=None) - .. automethod:: delete_config(target) + `get_schema` is mapped to :class:`~ncclient.operations.GetSchema` - .. automethod:: dispatch(rpc_command, source=None, filter=None) + .. method:: edit_config(config, format='xml', target='candidate', default_operation=None, test_option=None, error_option=None) - .. automethod:: lock(target) + `edit_config` is mapped to :class:`~ncclient.operations.EditConfig` - .. automethod:: unlock(target) + .. method:: copy_config(source, target) - .. automethod:: locked(target) + `copy_config` is mapped to :class:`~ncclient.operations.CopyConfig` + + .. method:: delete_config(target) + + `delete_config` is mapped to :class:`~ncclient.operations.DeleteConfig` + + .. method:: dispatch(rpc_command, source=None, filter=None) + + `dispatch` is mapped to :class:`~ncclient.operations.Dispatch` + + .. method:: lock(target="candidate") + + `lock` is mapped to :class:`~ncclient.operations.Lock` + + .. method:: unlock(target="candidate") + + `unlock` is mapped to :class:`~ncclient.operations.Unlock` + + .. method:: get(filter=None, with_defaults=None) + + `get` is mapped to :class:`~ncclient.operations.Get` + + .. method:: close_session() + + `close_session` is mapped to :class:`~ncclient.operations.CloseSession` - .. automethod:: get() + .. method:: kill_session(session_id) - .. automethod:: close_session() + `kill_session` is mapped to :class:`~ncclient.operations.KillSession` - .. automethod:: kill_session(session_id) + .. method:: commit(confirmed=False, timeout=None, persist=None, persist_id=None) - .. automethod:: commit(confirmed=False, timeout=None, persist=None) + `commit` is mapped to :class:`~ncclient.operations.Commit` - .. automethod:: cancel_commit(persist_id=None) + .. method:: cancel_commit(persist_id=None) - .. automethod:: discard_changes() + `cancel_commit` is mapped to :class:`~ncclient.operations.CancelCommit` - .. automethod:: validate(source) + .. method:: discard_changes() - .. automethod:: create_subscription() + `discard_changes` is mapped to :class:`~ncclient.operations.DiscardChanges` + + .. method:: validate(source="candidate") + + `validate` is mapped to :class:`~ncclient.operations.Validate` + + .. method:: create_subscription(filter=None, stream_name=None, start_time=None, stop_time=None) + + `create_subscription` is mapped to :class:`~ncclient.operations.CreateSubscription` + + .. method:: reboot_machine() + + `reboot_machine` is mapped to :class:`~ncclient.operations.RebootMachine` + + .. method:: poweroff_machine() + + `poweroff_machine` is mapped to :class:`~ncclient.operations.PoweroffMachine` + + .. automethod:: locked(target) .. automethod:: take_notification(block=True, timeout=None) @@ -115,7 +157,13 @@ Here *type* has to be one of `"xpath"` or `"subtree"`. - * For `"xpath"` the *criteria* should be a string containing the XPath expression. + * For `"xpath"` the *criteria* should be a string containing the XPath expression or a tuple containing a dict of namespace mapping and the XPath expression. * For `"subtree"` the *criteria* should be an XML string or an :class:`~xml.etree.ElementTree.Element` object containing the criteria. +* A list of *spec* + + Here *type* has to be `"subtree"`. + + * the *spec* should be a list containing multiple XML string or multiple :class:`~xml.etree.ElementTree.Element` objects. + * A `<filter>` element as an XML string or an :class:`~xml.etree.ElementTree.Element` object. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/docs/source/operations.rst new/ncclient-0.6.9/docs/source/operations.rst --- old/ncclient-0.6.7/docs/source/operations.rst 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/docs/source/operations.rst 2020-08-09 00:09:16.000000000 +0200 @@ -48,6 +48,12 @@ .. autoattribute:: REPLY_CLS +.. autoclass:: GetSchema + :members: request + :show-inheritance: + + .. autoattribute:: REPLY_CLS + Editing ........ @@ -79,6 +85,17 @@ :members: request :show-inheritance: +Flowmon +........ + +.. autoclass:: PoweroffMachine + :members: request + :show-inheritance: + +.. autoclass:: RebootMachine + :members: request + :show-inheritance: + Locking ........ @@ -101,6 +118,13 @@ :members: request :show-inheritance: +Subscribing +............ + +.. autoclass:: CreateSubscription + :members: request + :show-inheritance: + Exceptions ---------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/__init__.py new/ncclient-0.6.9/ncclient/__init__.py --- old/ncclient-0.6.7/ncclient/__init__.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/__init__.py 2020-08-09 00:09:16.000000000 +0200 @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = (0,5,3) +__version__ = (0,6,9) import sys diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/_version.py new/ncclient-0.6.9/ncclient/_version.py --- old/ncclient-0.6.7/ncclient/_version.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/_version.py 2020-08-09 00:09:16.000000000 +0200 @@ -23,9 +23,9 @@ # setup.py/versioneer.py will grep for the variable names, so they must # each be defined on a line of their own. _version.py will just call # get_keywords(). - git_refnames = " (tag: v0.6.7)" - git_full = "8d4cc7195a8160a1e35b4307b8b08c5de3dec97f" - git_date = "2019-12-21 19:16:07 +0000" + git_refnames = " (HEAD -> master, tag: v0.6.9)" + git_full = "3380f1140791f4a8de5d303f919f1e4cc9532f32" + git_date = "2020-08-08 23:09:16 +0100" keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} return keywords diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/capabilities.py new/ncclient-0.6.9/ncclient/capabilities.py --- old/ncclient-0.6.7/ncclient/capabilities.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/capabilities.py 2020-08-09 00:09:16.000000000 +0200 @@ -13,7 +13,6 @@ # limitations under the License. import logging -import sys import six diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/devices/__init__.py new/ncclient-0.6.9/ncclient/devices/__init__.py --- old/ncclient-0.6.7/ncclient/devices/__init__.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/devices/__init__.py 2020-08-09 00:09:16.000000000 +0200 @@ -0,0 +1,19 @@ +# supported devices config, add new device (eg: 'device name':'device lable'). +supported_devices_cfg = {'junos':'Juniper', + 'csr':'Cisco CSR1000v', + 'nexus':'Cisco Nexus', + 'iosxr':'Cisco IOS XR', + 'iosxe':'Cisco IOS XE', + 'huawei':'Huawei', + 'huaweiyang':'Huawei', + 'alu':'Alcatel Lucent', + 'h3c':'H3C', + 'hpcomware':'HP Comware', + 'default':'Server or anything not in above'} + +def get_supported_devices(): + return tuple(supported_devices_cfg.keys()) + +def get_supported_device_labels(): + return supported_devices_cfg + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/devices/csr.py new/ncclient-0.6.9/ncclient/devices/csr.py --- old/ncclient-0.6.7/ncclient/devices/csr.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/devices/csr.py 2020-08-09 00:09:16.000000000 +0200 @@ -28,6 +28,4 @@ super(CsrDeviceHandler, self).__init__(device_params) def add_additional_ssh_connect_params(self, kwargs): - kwargs['allow_agent'] = False - kwargs['look_for_keys'] = False kwargs['unknown_host_cb'] = csr_unknown_host_cb diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/devices/h3c.py new/ncclient-0.6.9/ncclient/devices/h3c.py --- old/ncclient-0.6.7/ncclient/devices/h3c.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/devices/h3c.py 2020-08-09 00:09:16.000000000 +0200 @@ -1,19 +1,17 @@ """ -Handler for Huawei device specific information. +Handler for H3c device specific information. Note that for proper import, the classname has to be: "<Devicename>DeviceHandler" -...where <Devicename> is something like "Default", "Huawei", etc. +...where <Devicename> is something like "Default", "H3c", etc. All device-specific handlers derive from the DefaultDeviceHandler, which implements the generic information needed for interaction with a Netconf server. """ -from ncclient.xml_ import BASE_NS_1_0 - from .default import DefaultDeviceHandler from ncclient.operations.third_party.h3c.rpc import * diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/devices/iosxe.py new/ncclient-0.6.9/ncclient/devices/iosxe.py --- old/ncclient-0.6.7/ncclient/devices/iosxe.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/devices/iosxe.py 2020-08-09 00:09:16.000000000 +0200 @@ -35,6 +35,4 @@ return dict def add_additional_ssh_connect_params(self, kwargs): - kwargs['allow_agent'] = False - kwargs['look_for_keys'] = False kwargs['unknown_host_cb'] = iosxe_unknown_host_cb diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/devices/iosxr.py new/ncclient-0.6.9/ncclient/devices/iosxr.py --- old/ncclient-0.6.7/ncclient/devices/iosxr.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/devices/iosxr.py 2020-08-09 00:09:16.000000000 +0200 @@ -28,9 +28,6 @@ super(IosxrDeviceHandler, self).__init__(device_params) def add_additional_ssh_connect_params(self, kwargs): - kwargs['allow_agent'] = False - kwargs['look_for_keys'] = False - kwargs['hostkey_verify'] = False kwargs['unknown_host_cb'] = iosxr_unknown_host_cb def perform_qualify_check(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/manager.py new/ncclient-0.6.9/ncclient/manager.py --- old/ncclient-0.6.7/ncclient/manager.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/manager.py 2020-08-09 00:09:16.000000000 +0200 @@ -18,10 +18,8 @@ It exposes all core functionality. """ -from ncclient import capabilities from ncclient import operations from ncclient import transport -import six import logging import functools @@ -57,8 +55,6 @@ operations to the :class:`Manager` API. """ -VENDOR_OPERATIONS = {} - def make_device_handler(device_params): """ @@ -131,8 +127,6 @@ device_handler = make_device_handler(device_params) device_handler.add_additional_ssh_connect_params(kwds) - global VENDOR_OPERATIONS - VENDOR_OPERATIONS.update(device_handler.add_additional_operations()) session = transport.SSHSession(device_handler) if "hostkey_verify" not in kwds or kwds["hostkey_verify"]: session.load_known_hosts() @@ -157,8 +151,6 @@ device_handler = make_device_handler(device_params) - global VENDOR_OPERATIONS - VENDOR_OPERATIONS.update(device_handler.add_additional_operations()) session = third_party_import.IOProc(device_handler) session.connect() @@ -180,7 +172,7 @@ """ For details on the expected behavior of the operations and their - parameters refer to :rfc:`4741`. + parameters refer to :rfc:`6241`. Manager instances are also context managers so you can use it like this:: @@ -209,6 +201,9 @@ self._raise_mode = operations.RaiseMode.ALL self._huge_tree = self.HUGE_TREE_DEFAULT self._device_handler = device_handler + self._vendor_operations = {} + if device_handler: + self._vendor_operations.update(device_handler.add_additional_operations()) def __enter__(self): return self @@ -259,8 +254,8 @@ raise NotImplementedError def __getattr__(self, method): - if method in VENDOR_OPERATIONS: - return functools.partial(self.execute, VENDOR_OPERATIONS[method]) + if method in self._vendor_operations: + return functools.partial(self.execute, self._vendor_operations[method]) elif method in OPERATIONS: return functools.partial(self.execute, OPERATIONS[method]) else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/operations/edit.py new/ncclient-0.6.9/ncclient/operations/edit.py --- old/ncclient-0.6.7/ncclient/operations/edit.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/operations/edit.py 2020-08-09 00:09:16.000000000 +0200 @@ -39,7 +39,7 @@ *default_operation* if specified must be one of { `"merge"`, `"replace"`, or `"none"` } - *test_option* if specified must be one of { `"test_then_set"`, `"set"` } + *test_option* if specified must be one of { `"test-then-set"`, `"set"`, `"test-only"` } *error_option* if specified must be one of { `"stop-on-error"`, `"continue-on-error"`, `"rollback-on-error"` } @@ -47,25 +47,25 @@ """ node = new_ele("edit-config") node.append(util.datastore_or_url("target", target, self._assert)) - if default_operation is not None: - # TODO: check if it is a valid default-operation + if (default_operation is not None + and util.validate_args('default_operation', default_operation, ["merge", "replace", "none"]) is True): sub_ele(node, "default-operation").text = default_operation - if test_option is not None: + if (test_option is not None + and util.validate_args('test_option', test_option, ["test-then-set", "set", "test-only"]) is True): self._assert(':validate') + if test_option == 'test-only': + self._assert(':validate:1.1') sub_ele(node, "test-option").text = test_option - if error_option is not None: + if (error_option is not None + and util.validate_args('error_option', error_option, ["stop-on-error", "continue-on-error", "rollback-on-error"]) is True): if error_option == "rollback-on-error": self._assert(":rollback-on-error") sub_ele(node, "error-option").text = error_option -# <<<<<<< HEAD -# node.append(validated_element(config, ("config", qualify("config")))) -# ======= if format == 'xml': node.append(validated_element(config, ("config", qualify("config")))) if format == 'text': config_text = sub_ele(node, "config-text") sub_ele(config_text, "configuration-text").text = config -# >>>>>>> juniper return self._request(node) @@ -149,8 +149,8 @@ *persist_id* value must be equal to the value given in the <persist> parameter to the original <commit> operation. """ node = new_ele("commit") - if (confirmed or persist) and persist_id: - raise OperationError("Invalid operation as confirmed or persist cannot be present with persist-id") + if persist and persist_id: + raise OperationError("Invalid operation as persist cannot be present with persist-id") if confirmed: self._assert(":confirmed-commit") sub_ele(node, "confirmed") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/operations/retrieve.py new/ncclient-0.6.9/ncclient/operations/retrieve.py --- old/ncclient-0.6.7/ncclient/operations/retrieve.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/operations/retrieve.py 2020-08-09 00:09:16.000000000 +0200 @@ -74,7 +74,7 @@ *filter* specifies the portion of the configuration to retrieve (by default entire configuration is retrieved) - *with_defaults* defines an explicit method of retrieving default values from the configuration (see RFC 6243) + *with_defaults* defines an explicit method of retrieving default values from the configuration (see :rfc:`6243`) :seealso: :ref:`filter_params` """ @@ -149,7 +149,7 @@ *filter* specifies the portion of the configuration to retrieve (by default entire configuration is retrieved) - *with_defaults* defines an explicit method of retrieving default values from the configuration (see RFC 6243) + *with_defaults* defines an explicit method of retrieving default values from the configuration (see :rfc:`6243`) :seealso: :ref:`filter_params`""" node = new_ele("get-config") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/operations/rpc.py new/ncclient-0.6.9/ncclient/operations/rpc.py --- old/ncclient-0.6.7/ncclient/operations/rpc.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/operations/rpc.py 2020-08-09 00:09:16.000000000 +0200 @@ -14,7 +14,6 @@ from threading import Event, Lock from uuid import uuid4 -import six from ncclient.xml_ import * from ncclient.logging_ import SessionLoggerAdapter @@ -33,6 +32,7 @@ tag_to_attr = { qualify("error-type"): "_type", qualify("error-tag"): "_tag", + qualify("error-app-tag"): "_app_tag", qualify("error-severity"): "_severity", qualify("error-info"): "_info", qualify("error-path"): "_path", @@ -43,6 +43,7 @@ self._raw = raw if errs is None: # Single RPCError + self._errlist = None for attr in six.itervalues(RPCError.tag_to_attr): setattr(self, attr, None) for subele in raw: @@ -55,6 +56,7 @@ OperationError.__init__(self, self.to_dict()) else: # Multiple errors returned. Errors is a list of RPCError objs + self._errlist = errs errlist = [] for err in errs: if err.severity: @@ -96,6 +98,11 @@ return self._tag @property + def app_tag(self): + "The contents of the `error-app-tag` element." + return self._app_tag + + @property def severity(self): "The contents of the `error-severity` element." return self._severity @@ -115,6 +122,11 @@ "XML string or `None`; representing the `error-info` element." return self._info + @property + def errlist(self): + "List of errors if this represents multiple errors, otherwise None." + return self._errlist + class RPCReply(object): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/operations/util.py new/ncclient-0.6.9/ncclient/operations/util.py --- old/ncclient-0.6.7/ncclient/operations/util.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/operations/util.py 2020-08-09 00:09:16.000000000 +0200 @@ -49,13 +49,23 @@ type = None if isinstance(spec, tuple): type, criteria = spec - rep = new_ele("filter", type=type) if type == "xpath": - rep.attrib["select"] = criteria + if isinstance(criteria, tuple): + ns, select = criteria + rep = new_ele_nsmap("filter", ns, type=type) + rep.attrib["select"] = select + else: + rep = new_ele("filter", type=type) + rep.attrib["select"]=criteria elif type == "subtree": + rep = new_ele("filter", type=type) rep.append(to_ele(criteria)) else: raise OperationError("Invalid filter type") + elif isinstance(spec, list): + rep = new_ele("filter", type="subtree") + for cri in spec: + rep.append(to_ele(cri)) else: rep = validated_element(spec, ("filter", qualify("filter"), @@ -67,3 +77,9 @@ if type == "xpath" and capcheck is not None: capcheck(":xpath") return rep + +def validate_args(arg_name, value, args_list): + # this is a common method, which used to check whether a value is in args_list + if value not in args_list: + raise OperationError('Invalid value "%s" in "%s" element' % (value, arg_name)) + return True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/transport/session.py new/ncclient-0.6.9/ncclient/transport/session.py --- old/ncclient-0.6.7/ncclient/transport/session.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/transport/session.py 2020-08-09 00:09:16.000000000 +0200 @@ -14,8 +14,6 @@ # limitations under the License. -import re -import sys import logging from threading import Thread, Lock, Event try: @@ -72,10 +70,12 @@ else: self.logger.error('error parsing dispatch message: %s', e) return + self.logger.debug('dispatching message to different listeners: %s', + raw) with self._lock: listeners = list(self._listeners) for l in listeners: - self.logger.debug('dispatching message to %r: %s', l, raw) + self.logger.debug('dispatching message to listener: %r', l) l.callback(root, raw) # no try-except; fail loudly if you must! def _dispatch_error(self, err): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/transport/ssh.py new/ncclient-0.6.9/ncclient/transport/ssh.py --- old/ncclient-0.6.7/ncclient/transport/ssh.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/transport/ssh.py 2020-08-09 00:09:16.000000000 +0200 @@ -322,7 +322,7 @@ host_port = '[%s]:%s' % (host, port) known_host_keys_for_this_host.update(self._host_keys.lookup(host_port) or {}) if known_host_keys_for_this_host: - self._transport._preferred_keys = [x.key.get_name() for x in known_host_keys_for_this_host._entries] + self._transport._preferred_keys = list(known_host_keys_for_this_host) # Connect try: @@ -349,7 +349,7 @@ is_known_host = any(self._host_keys.check(lookup, server_key_obj) for lookup in known_hosts_lookups) if not is_known_host and not unknown_host_cb(host, fingerprint): - raise SSHUnknownHostError(known_hosts_lookup[0], fingerprint) + raise SSHUnknownHostError(known_hosts_lookups[0], fingerprint) # Authenticating with our private key/identity if key_filename is None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/transport/third_party/junos/ioproc.py new/ncclient-0.6.9/ncclient/transport/third_party/junos/ioproc.py --- old/ncclient-0.6.7/ncclient/transport/third_party/junos/ioproc.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/transport/third_party/junos/ioproc.py 2020-08-09 00:09:16.000000000 +0200 @@ -1,4 +1,3 @@ -import os import sys import re import six @@ -7,7 +6,6 @@ from cStringIO import StringIO else: from io import BytesIO as StringIO -from select import select if sys.version>='2.7': from subprocess import Popen, check_output, PIPE, STDOUT else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/transport/third_party/junos/parser.py new/ncclient-0.6.9/ncclient/transport/third_party/junos/parser.py --- old/ncclient-0.6.7/ncclient/transport/third_party/junos/parser.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/transport/third_party/junos/parser.py 2020-08-09 00:09:16.000000000 +0200 @@ -33,12 +33,10 @@ import logging logger = logging.getLogger("ncclient.transport.third_party.junos.parser") +from ncclient.xml_ import BASE_NS_1_0 -class RPCTags: - RPC_REPLY_END_TAG = "</rpc-reply>" - RPC_REPLY_END_TAG_LEN = len(RPC_REPLY_END_TAG) - RPC_REPLY_START_TAG = "<rpc-reply" - NAMESPACES = {"nc": "urn:ietf:params:xml:ns:netconf:base:1.0"} +RPC_REPLY_END_TAG = "</rpc-reply>" +RFC_RPC_REPLY_END_TAG = "</nc:rpc-reply>" class JunosXMLParser(DefaultXMLParser): @@ -47,7 +45,6 @@ self._session = session self.sax_parser = make_parser() self.sax_parser.setContentHandler(SAXParser(session)) - self.rpc_tags = RPCTags def parse(self, data): try: @@ -82,9 +79,11 @@ # we need to renew parser, as old parser is gone. self.sax_parser = make_parser() self.sax_parser.setContentHandler(SAXParser(self._session)) - elif self.rpc_tags.RPC_REPLY_END_TAG in data: + elif RPC_REPLY_END_TAG in data or RFC_RPC_REPLY_END_TAG in data: + tag = RPC_REPLY_END_TAG if RPC_REPLY_END_TAG in data else \ + RFC_RPC_REPLY_END_TAG logger.warning("Check for rpc reply end tag within data received: %s" % data) - msg, delim, remaining = data.partition(self.rpc_tags.RPC_REPLY_END_TAG) + msg, delim, remaining = data.partition(tag) self._session._buffer.seek(0, os.SEEK_END) self._session._buffer.write(remaining.encode()) else: @@ -94,9 +93,13 @@ # if then, wait for next iteration of data and do a recursive call to # _delimiter_check for MSG_DELIM check buf = self._session._buffer - buf.seek(buf.tell() - self.rpc_tags.RPC_REPLY_END_TAG_LEN - MSG_DELIM_LEN) + buf.seek(buf.tell() - len(RFC_RPC_REPLY_END_TAG) - MSG_DELIM_LEN) rpc_response_last_msg = buf.read().decode('UTF-8').replace('\n', '') - if self.rpc_tags.RPC_REPLY_END_TAG in rpc_response_last_msg: + if RPC_REPLY_END_TAG in rpc_response_last_msg or \ + RFC_RPC_REPLY_END_TAG in rpc_response_last_msg: + tag = RPC_REPLY_END_TAG if RPC_REPLY_END_TAG in \ + rpc_response_last_msg else \ + RFC_RPC_REPLY_END_TAG # rpc_response_last_msg and data can be overlapping match_obj = difflib.SequenceMatcher(None, rpc_response_last_msg, data).get_matching_blocks() @@ -114,9 +117,7 @@ # as first if condition will add full delimiter, so clean # it off clean_up = len(rpc_response_last_msg) - ( - rpc_response_last_msg.find( - self.rpc_tags.RPC_REPLY_END_TAG) + - self.rpc_tags.RPC_REPLY_END_TAG_LEN) + rpc_response_last_msg.find(tag) + len(tag)) self._session._buffer.truncate(buf.tell() - clean_up) self._delimiter_check(data.encode()) else: @@ -193,12 +194,12 @@ self._session = session self._validate_reply_and_sax_tag = False self._lock = Lock() + self.nc_namespace = None def startElement(self, tag, attributes): if tag in ['rpc-reply', 'nc:rpc-reply']: if tag == 'nc:rpc-reply': - RPCTags.RPC_REPLY_END_TAG = "</nc:rpc-reply>" - RPCTags.RPC_REPLY_START_TAG = "<nc:rpc-reply" + self.nc_namespace = BASE_NS_1_0 # in case last rpc called used sax parsing and error'd out # without resetting use_filer in endElement rpc-reply check with self._lock: @@ -222,7 +223,7 @@ if self._cur == self._root and self._cur.tag == tag: node = self._root else: - node = self._cur.find(tag, namespaces=RPCTags.NAMESPACES) + node = self._cur.find(tag, namespaces={"nc": self.nc_namespace}) if self._validate_reply_and_sax_tag: if tag != self._root.tag: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/ncclient/xml_.py new/ncclient-0.6.9/ncclient/xml_.py --- old/ncclient-0.6.7/ncclient/xml_.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/ncclient/xml_.py 2020-08-09 00:09:16.000000000 +0200 @@ -223,11 +223,17 @@ parser=self.__parser) return self.__root +def parent_ns(node): + if node.prefix: + return node.nsmap[node.prefix] + return None + +new_ele_nsmap = lambda tag, nsmap, attrs={}, **extra: etree.Element(qualify(tag), attrs, nsmap, **extra) new_ele = lambda tag, attrs={}, **extra: etree.Element(qualify(tag), attrs, **extra) new_ele_ns = lambda tag, ns, attrs={}, **extra: etree.Element(qualify(tag,ns), attrs, **extra) -sub_ele = lambda parent, tag, attrs={}, **extra: etree.SubElement(parent, qualify(tag), attrs, **extra) +sub_ele = lambda parent, tag, attrs={}, **extra: etree.SubElement(parent, qualify(tag, parent_ns(parent)), attrs, **extra) sub_ele_ns = lambda parent, tag, ns, attrs={}, **extra: etree.SubElement(parent, qualify(tag, ns), attrs, **extra) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/setup.py new/ncclient-0.6.9/setup.py --- old/ncclient-0.6.7/setup.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/setup.py 2020-08-09 00:09:16.000000000 +0200 @@ -49,7 +49,7 @@ author=__author__, author_email=__author_email__, url="https://github.com/ncclient/ncclient", - packages=find_packages('.'), + packages=find_packages(exclude=['test', 'test.*']), install_requires=install_reqs, tests_require=test_reqs, license=__licence__, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/test/unit/devices/test_default.py new/ncclient-0.6.9/test/unit/devices/test_default.py --- old/ncclient-0.6.7/test/unit/devices/test_default.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/test/unit/devices/test_default.py 2020-08-09 00:09:16.000000000 +0200 @@ -37,6 +37,24 @@ def test_handle_connection_exceptions(self): self.assertFalse(self.obj.handle_connection_exceptions(None)) + def test_is_rpc_error_exempt_1(self): + self.assertFalse(self.obj.is_rpc_error_exempt(None)) + + def test_is_rpc_error_exempt_2(self): + self.obj._exempt_errors_exact_match = ["test_exempt"] + self.assertTrue(self.obj.is_rpc_error_exempt(" Test_Exempt")) + + def test_is_rpc_error_exempt_3(self): + self.obj._exempt_errors_startwith_wildcard_match = ["test_exempt"] + self.assertTrue(self.obj.is_rpc_error_exempt("*Test_Exempt")) + + def test_is_rpc_error_exempt_4(self): + self.obj._exempt_errors_endwith_wildcard_match = ["test_exempt"] + self.assertTrue(self.obj.is_rpc_error_exempt("Test_Exempt*")) + + def test_is_rpc_error_exempt_5(self): + self.obj._exempt_errors_full_wildcard_match = ["test_exempt"] + self.assertTrue(self.obj.is_rpc_error_exempt("*Test_Exempt*")) suite = unittest.TestSuite() unittest.TextTestRunner().run(suite) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/test/unit/devices/test_get_supported_devices.py new/ncclient-0.6.9/test/unit/devices/test_get_supported_devices.py --- old/ncclient-0.6.7/test/unit/devices/test_get_supported_devices.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ncclient-0.6.9/test/unit/devices/test_get_supported_devices.py 2020-08-09 00:09:16.000000000 +0200 @@ -0,0 +1,33 @@ +import unittest +from ncclient import devices + +class TestGetSupportedDevices(unittest.TestCase): + + def test_get_supported_devices(self): + supported_devices = devices.get_supported_devices() + self.assertEqual(sorted(supported_devices), sorted(('junos', + 'csr', + 'nexus', + 'iosxr', + 'iosxe', + 'huawei', + 'huaweiyang', + 'alu', + 'h3c', + 'hpcomware', + 'default'))) + + def test_get_supported_device_labels(self): + supported_device_labels = devices.get_supported_device_labels() + self.assertEqual(supported_device_labels, {'junos':'Juniper', + 'csr':'Cisco CSR1000v', + 'nexus':'Cisco Nexus', + 'iosxr':'Cisco IOS XR', + 'iosxe':'Cisco IOS XE', + 'huawei':'Huawei', + 'huaweiyang':'Huawei', + 'alu':'Alcatel Lucent', + 'h3c':'H3C', + 'hpcomware':'HP Comware', + 'default':'Server or anything not in above'}) + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/test/unit/operations/test_edit.py new/ncclient-0.6.9/test/unit/operations/test_edit.py --- old/ncclient-0.6.7/test/unit/operations/test_edit.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/test/unit/operations/test_edit.py 2020-08-09 00:09:16.000000000 +0200 @@ -52,12 +52,16 @@ host-name foo-bar; } """ - obj.request(copy.deepcopy(root), format="text", target="running", error_option="rollback-on-error", - default_operation="default", test_option="test") + obj.request(copy.deepcopy(root), + format="text", + target="running", + error_option="rollback-on-error", + default_operation="merge", + test_option="test-then-set") node = new_ele("edit-config") node.append(util.datastore_or_url("target", "running")) - sub_ele(node, "default-operation").text = "default" - sub_ele(node, "test-option").text = "test" + sub_ele(node, "default-operation").text = "merge" + sub_ele(node, "test-option").text = "test-then-set" sub_ele(node, "error-option").text = "rollback-on-error" config_text = sub_ele(node, "config-text") sub_ele(config_text, "configuration-text").text = root @@ -66,6 +70,63 @@ call = ElementTree.tostring(call) self.assertEqual(call, xml) + def test_edit_config_invalid_arguments_exception(self): + session = ncclient.transport.SSHSession(self.device_handler) + session._server_capabilities = [":rollback-on-error", ":validate", ":validate:1.1"] + obj = EditConfig( + session, + self.device_handler, + raise_mode=RaiseMode.ALL) + root = """ + system { + host-name foo-bar; + } + """ + #invalid argument "default_operation" + self.assertRaises(OperationError, obj.request, + copy.deepcopy(root), + format="xml", + target="running", + error_option="stop-on-error", + default_operation="create", + test_option="test-then-set") + #invalid argument "error_option" + self.assertRaises(OperationError, obj.request, + copy.deepcopy(root), + format="xml", + target="running", + error_option="commit-on-error", + default_operation="merge", + test_option="test-then-set") + #invalid argument "test_option" + self.assertRaises(OperationError, obj.request, + copy.deepcopy(root), + format="xml", + target="running", + error_option="stop-on-error", + default_operation="merge", + test_option="test") + + def test_edit_config_validate_capability_exception(self): + session = ncclient.transport.SSHSession(self.device_handler) + session._server_capabilities = [":validate"] + obj = EditConfig( + session, + self.device_handler, + raise_mode=RaiseMode.ALL) + root = """ + system { + host-name foo-bar; + } + """ + self.assertRaises(MissingCapabilityError, obj.request, + copy.deepcopy(root), + format="xml", + target="running", + error_option="stop-on-error", + default_operation="merge", + test_option="test-only") + @patch('ncclient.operations.RPC._request') def test_delete_config(self, mock_request): session = ncclient.transport.SSHSession(self.device_handler) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/test/unit/operations/test_retrieve.py new/ncclient-0.6.9/test/unit/operations/test_retrieve.py --- old/ncclient-0.6.7/test/unit/operations/test_retrieve.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/test/unit/operations/test_retrieve.py 2020-08-09 00:09:16.000000000 +0200 @@ -220,3 +220,43 @@ call = mock_request.call_args_list[0][0][0] call = ElementTree.tostring(call) self.assertEqual(call, xml) + + @patch('ncclient.operations.retrieve.RPC._request') + def test_get_with_multi_subtree_filters(self, mock_request): + result = ''' + <?xml version="1.0" encoding="UTF-8"?> + <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" + xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <cont1 xmlns="urn:mod1"> + <le1>test_mod1_001</le1> + <le2>this is a test-one example</le2> + </cont1> + <cont2 xmlns="urn:mod2"> + <le1>test_mod2_002</le1> + <le2>this is a test-two example</le2> + <lst> + <le3>a list of mod2</le3> + </lst> + </cont2> + </data>''' + mock_request.return_value = result + session = ncclient.transport.SSHSession(self.device_handler) + obj = Get(session, self.device_handler, raise_mode=RaiseMode.ALL) + + multi_subtree_filters = [ + '<cont1 xmlns="urn:mod1"> \ + <le1/> \ + <le2/> \ + </cont1>', + '<cont2 xmlns="urn:mod2"/>' + ] + + ret = obj.request(copy.deepcopy(multi_subtree_filters)) + node = new_ele("get") + node.append(util.build_filter(multi_subtree_filters)) + xml = ElementTree.tostring(node) + call = mock_request.call_args_list[0][0][0] + call = ElementTree.tostring(call) + self.assertEqual(call, xml) + self.assertEqual(ret, result) + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/test/unit/operations/test_rpc.py new/ncclient-0.6.9/test/unit/operations/test_rpc.py --- old/ncclient-0.6.7/test/unit/operations/test_rpc.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/test/unit/operations/test_rpc.py 2020-08-09 00:09:16.000000000 +0200 @@ -89,6 +89,31 @@ <configuration-text xmlns="http://xml.juniper.net/xnm/1.1/xnm">%s</configuration-text> </rpc-reply>""" % escape(huge_configuration_text) +xml6 = """<rpc-error xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> + <error-type>application</error-type> + <error-tag>invalid-value</error-tag> + <error-severity>error</error-severity> + <error-path>path/to/node</error-path> + <error-info> + <bad-element>system1</bad-element> + </error-info> + <error-app-tag>app-tag1</error-app-tag> + <error-message>syntax error</error-message> + </rpc-error> +""" + +xml7 = """<rpc-error xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> + <error-type>protocol</error-type> + <error-tag>missing-element</error-tag> + <error-severity>error</error-severity> + <error-path>path/to/different/node</error-path> + <error-info> + <bad-element>system2</bad-element> + </error-info> + <error-app-tag>app-tag2</error-app-tag> + <error-message>missing element error</error-message> + </rpc-error> +""" class TestRPC(unittest.TestCase): @@ -180,6 +205,50 @@ obj.deliver_error(err) self.assertRaises(RPCError, obj._request, node) + def test_rpc_rpcerror_tag_to_attr(self): + '''All elements in <rpc-error> extracted.''' + err = RPCError(to_ele(xml6)) + + self.assertEqual(None, err.errlist) + + self.assertEqual("application", err.type) + self.assertEqual("invalid-value", err.tag) + self.assertEqual("error", err.severity) + self.assertEqual("path/to/node", err.path) + self.assertEqual("app-tag1", err.app_tag) + self.assertEqual("syntax error", err.message) + self.assertIn("<bad-element>system1</bad-element>", err.info) + + def test_rpc_rpcerror_multiple_errors(self): + '''Multiple errors in <rpc-reply> extracted correctly''' + errlist = [RPCError(to_ele(xml6)), RPCError(to_ele(xml7))] + + multiple_xml = ( + '<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">' + + xml6 + xml7 + + "</rpc-reply>") + + multiple_err = RPCError(to_ele(multiple_xml), errs=errlist) + errs = multiple_err.errlist + + self.assertEqual(2, len(errs)) + + self.assertEqual("application", errs[0].type) + self.assertEqual("invalid-value", errs[0].tag) + self.assertEqual("error", errs[0].severity) + self.assertEqual("path/to/node", errs[0].path) + self.assertEqual("app-tag1", errs[0].app_tag) + self.assertEqual("syntax error", errs[0].message) + self.assertIn("<bad-element>system1</bad-element>", errs[0].info) + + self.assertEqual("protocol", errs[1].type) + self.assertEqual("missing-element", errs[1].tag) + self.assertEqual("error", errs[1].severity) + self.assertEqual("path/to/different/node", errs[1].path) + self.assertEqual("app-tag2", errs[1].app_tag) + self.assertEqual("missing element error", errs[1].message) + self.assertIn("<bad-element>system2</bad-element>", errs[1].info) + @patch('ncclient.transport.Session.send') @patch(patch_str) def test_rpc_capability_error(self, mock_thread, mock_send): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/test/unit/operations/test_utils.py new/ncclient-0.6.9/test/unit/operations/test_utils.py --- old/ncclient-0.6.7/test/unit/operations/test_utils.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/test/unit/operations/test_utils.py 2020-08-09 00:09:16.000000000 +0200 @@ -57,7 +57,7 @@ call = ElementTree.tostring(reply) self.assertEqual(call, ElementTree.tostring(to_ele(xml))) - def test_build_filter_2(self): + def test_build_filter_xpath(self): criteria = "configuration/system" filter = ("xpath", criteria) reply = build_filter(filter) @@ -66,7 +66,18 @@ node.attrib["select"] = criteria self.assertEqual(call, ElementTree.tostring(node)) - def test_build_filter_3(self): + def test_build_filter_xpath_ns(self): + select = "configuration/system" + ns = {"ns0": "http://www.xxx.org"} + criteria = (ns, select) + filter = ("xpath", criteria) + reply = build_filter(filter) + call = ElementTree.tostring(reply) + node = new_ele_nsmap("filter", ns, type="xpath") + node.attrib["select"] = select + self.assertEqual(call, ElementTree.tostring(node)) + + def test_build_filter_subtree(self): criteria = """<configuration> <system> <services/> @@ -79,7 +90,7 @@ node.append(to_ele(criteria)) self.assertEqual(call, ElementTree.tostring(node)) - def test_build_filter_4(self): + def test_build_filter_other(self): criteria = """<configuration> <system> <services/> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/test/unit/test_manager.py new/ncclient-0.6.9/test/unit/test_manager.py --- old/ncclient-0.6.7/test/unit/test_manager.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/test/unit/test_manager.py 2020-08-09 00:09:16.000000000 +0200 @@ -26,6 +26,60 @@ manager.connect(host='host') mock_ssh.assert_called_once_with(host='host') + @patch('ncclient.transport.SSHSession.load_known_hosts') + @patch('ncclient.transport.SSHSession.connect') + def test_connect_ssh1(self, mock_ssh, mock_load_known_hosts): + manager.connect(host='host') + mock_ssh.assert_called_once_with(host='host') + mock_load_known_hosts.assert_called_once_with() + + @patch('ncclient.transport.SSHSession.connect') + @patch('ncclient.transport.SSHSession.transport') + @patch('ncclient.transport.SSHSession.close') + def test_connect_exception(self, mock_close, mock_transport, mock_ssh): + mock_ssh.side_effect = Exception + try: + manager.connect(host='host') + except Exception: + Exception("connect occured exception") + mock_ssh.assert_called_once_with(host='host') + + @patch('ncclient.transport.SSHSession.connect') + @patch('ncclient.transport.SSHSession.take_notification') + def test_manager_take_notification(self, mock_take_notification, mock_ssh): + mock_take_notification.return_value = "test_take_notification" + conn = self._mock_manager() + ret = conn.take_notification() + mock_take_notification.assert_called_once_with(True, None) + self.assertEqual(ret, "test_take_notification") + + @patch('ncclient.transport.SSHSession.connect') + @patch('ncclient.operations.retrieve.GetConfig._request') + def test_manager_getattr(self, mock_request, mock_ssh): + conn = self._mock_manager() + conn.get_config("running") + mock_ssh.assert_called_once_with(host='10.10.10.10', + port=22, + username='user', + password='password', + timeout=3, + hostkey_verify=False, + allow_agent=False) + + @patch('ncclient.transport.SSHSession.connect') + @patch('ncclient.operations.third_party.juniper.rpc.GetConfiguration._request') + @patch('ncclient.operations.third_party.juniper.rpc.ExecuteRpc._request') + def test_manager_getattr2(self, mock_rpc, mock_request, mock_ssh): + conn = self._mock_manager() + conn.get_edit('config') + mock_ssh.assert_called_once_with(host='10.10.10.10', + port=22, + username='user', + password='password', + timeout=3, + hostkey_verify=False, + allow_agent=False) + @patch('ncclient.manager.connect_ssh') def test_connect_ssh_with_hostkey_ed25519(self, mock_ssh): hostkey = 'AAAAC3NzaC1lZDI1NTE5AAAAIIiHpGSf8fla6tCwLpwshvMGmUK+B/0v5CsRu+5v4uT7' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/test/unit/test_xml_.py new/ncclient-0.6.9/test/unit/test_xml_.py --- old/ncclient-0.6.7/test/unit/test_xml_.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/test/unit/test_xml_.py 2020-08-09 00:09:16.000000000 +0200 @@ -176,3 +176,16 @@ result_xml = result.data_xml self.assertRaises(XMLError, validated_element, result_xml, tags=["rpc"]) + + def test_sub_ele_inherit_parent_namespace(self): + device_params = {'name': 'junos'} + device_handler = manager.make_device_handler(device_params) + transform_reply = device_handler.transform_reply() + result = NCElement(self.reply, transform_reply) + ele = new_ele_ns(result.find("./cli").tag, "http://www.xxx.org") + child = sub_ele(ele, "child") + sibling = sub_ele(ele, "sibling") + grandchild = sub_ele(child, "grandchild") + self.assertEqual(child.tag, "{http://www.xxx.org}child") + self.assertEqual(sibling.tag, "{http://www.xxx.org}sibling") + self.assertEqual(grandchild.tag, "{http://www.xxx.org}grandchild") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/test/unit/transport/test_session.py new/ncclient-0.6.9/test/unit/transport/test_session.py --- old/ncclient-0.6.7/test/unit/transport/test_session.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/test/unit/transport/test_session.py 2020-08-09 00:09:16.000000000 +0200 @@ -76,6 +76,26 @@ self.assertNotEqual( mock_log.call_args_list[0][0][0].find("error parsing dispatch message"), -1) + @patch('ncclient.transport.session.parse_root') + @patch('ncclient.devices.junos.JunosDeviceHandler.handle_raw_dispatch') + @patch('ncclient.transport.session.HelloHandler.errback') + @patch('ncclient.logging_.SessionLoggerAdapter.debug') + def test_dispatch_message_error2(self, mock_log, mock_errback, + mock_handle_raw_dispatch, mock_parse_root): + mock_parse_root.side_effect = Exception + mock_handle_raw_dispatch.return_value = Exception() + mock_errback.side_effect = Exception + cap = [':candidate'] + obj = Session(cap) + device_handler = JunosDeviceHandler({'name': 'junos'}) + obj._device_handler = device_handler + listener = HelloHandler(None, None) + obj._listeners.add(listener) + obj._dispatch_message(rpc_reply) + mock_handle_raw_dispatch.assert_called_once_with(rpc_reply) + self.assertEqual( + mock_log.call_args_list[0][0][0].find("error dispatching to"), -1) + @patch('ncclient.transport.session.HelloHandler.errback') def test_dispatch_error(self, mock_handler): cap = [':candidate'] @@ -105,6 +125,26 @@ log_args[0].find("server_capabilities="), -1) self.assertEqual(log_args[2], [':candidate']) + @patch('ncclient.logging_.SessionLoggerAdapter.info') + @patch('ncclient.transport.session.Thread.start') + @patch('ncclient.transport.session.Event') + def test_post_connect2(self, mock_lock, mock_handler, mock_log): + cap = ['urn:ietf:params:netconf:base:1.1'] + obj = Session(cap) + device_handler = JunosDeviceHandler({'name': 'junos'}) + obj._device_handler = device_handler + obj._connected = True + obj._id = 100 + obj._server_capabilities = cap + obj._post_connect() + log_args = mock_log.call_args_list[0][0] + self.assertNotEqual(log_args[0].find("initialized"), -1) + self.assertNotEqual(log_args[0].find("session-id="), -1) + self.assertEqual(log_args[1], 100) + self.assertNotEqual( + log_args[0].find("server_capabilities="), -1) + self.assertEqual(log_args[2], ['urn:ietf:params:netconf:base:1.1']) + def test_add_listener(self): cap = [':candidate'] obj = Session(cap) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.7/test/unit/transport/test_ssh.py new/ncclient-0.6.9/test/unit/transport/test_ssh.py --- old/ncclient-0.6.7/test/unit/transport/test_ssh.py 2019-12-21 20:16:07.000000000 +0100 +++ new/ncclient-0.6.9/test/unit/transport/test_ssh.py 2020-08-09 00:09:16.000000000 +0200 @@ -267,6 +267,16 @@ obj.load_known_hosts() mock_load.assert_called_once_with("file_name") + @patch('os.path.expanduser') + @patch('paramiko.hostkeys.HostKeys.load') + def test_load_host_key_IOError(self, mock_load, mock_os): + mock_os.return_value = "file_name" + mock_load.side_effect = IOError + device_handler = JunosDeviceHandler({'name': 'junos'}) + obj = SSHSession(device_handler) + obj.load_known_hosts() + mock_load.assert_called_with("file_name") + @unittest.skipIf(sys.version_info.major == 2, "test not supported < Python3") @patch('ncclient.transport.ssh.SSHSession.close') @patch('paramiko.channel.Channel.recv')