Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-ncclient for openSUSE:Factory checked in at 2024-11-11 14:58:22 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-ncclient (Old) and /work/SRC/openSUSE:Factory/.python-ncclient.new.2017 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-ncclient" Mon Nov 11 14:58:22 2024 rev:23 rq:1223349 version:0.6.16 Changes: -------- --- /work/SRC/openSUSE:Factory/python-ncclient/python-ncclient.changes 2024-10-27 11:26:19.465014436 +0100 +++ /work/SRC/openSUSE:Factory/.python-ncclient.new.2017/python-ncclient.changes 2024-11-11 14:58:25.567166739 +0100 @@ -1,0 +2,11 @@ +Sun Nov 10 19:50:09 UTC 2024 - Martin Hauke <mar...@gmx.de> + +- Update to version 0.6.16 + * Add Ciena driver. + * Update session.py. + * call_home: timeout as argument. + * Introduce Unix Socket Transport. + * feat: Add support for Nokia SR OS private candidate mode. + * Fix support of .ssh/id_ed25519 keys. + +------------------------------------------------------------------- Old: ---- ncclient-0.6.15.tar.gz New: ---- ncclient-0.6.16.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-ncclient.spec ++++++ --- /var/tmp/diff_new_pack.5sh7Zx/_old 2024-11-11 14:58:27.323240211 +0100 +++ /var/tmp/diff_new_pack.5sh7Zx/_new 2024-11-11 14:58:27.351241382 +0100 @@ -17,7 +17,7 @@ Name: python-ncclient -Version: 0.6.15 +Version: 0.6.16 Release: 0 Summary: Python library for NETCONF clients License: Apache-2.0 @@ -75,7 +75,6 @@ %install %python_install -%python_expand rm -rf %{buildroot}%{$python_sitelib}/test %fdupes %{buildroot} %check ++++++ ncclient-0.6.15.tar.gz -> ncclient-0.6.16.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/README.md new/ncclient-0.6.16/README.md --- old/ncclient-0.6.15/README.md 2023-10-16 17:46:13.000000000 +0200 +++ new/ncclient-0.6.16/README.md 2024-10-09 10:17:14.000000000 +0200 @@ -19,6 +19,7 @@ | Date | Release | Description | | :----: | :-----: | :---------- | +| 10/18/23 | `0.6.15` | See [release page](https://github.com/ncclient/ncclient/releases/tag/v0.6.15)| | 04/10/22 | `0.6.13` | See [release page](https://github.com/ncclient/ncclient/releases/tag/v0.6.13)| | 05/29/21 | `0.6.12` | See [release page](https://github.com/ncclient/ncclient/releases/tag/v0.6.12)| | 05/27/21 | `0.6.11` | See [release page](https://github.com/ncclient/ncclient/releases/tag/v0.6.11)| @@ -96,18 +97,19 @@ When instantiating a connection to a known type of NETCONF server: -* Juniper: `device_params={'name':'junos'}` +* Alcatel Lucent: `device_params={'name':'alu'}` +* Ciena: `device_params={'name':'ciena'}` * Cisco: - CSR: `device_params={'name':'csr'}` - Nexus: `device_params={'name':'nexus'}` - IOS XR: `device_params={'name':'iosxr'}` - IOS XE: `device_params={'name':'iosxe'}` +* H3C: `device_params={'name':'h3c'}` +* HP Comware: `device_params={'name':'hpcomware'}` * Huawei: - `device_params={'name':'huawei'}` - `device_params={'name':'huaweiyang'}` -* Nokia SR OS: `device_params={'name':'sros'}` -* H3C: `device_params={'name':'h3c'}` -* HP Comware: `device_params={'name':'hpcomware'}` +* Juniper: `device_params={'name':'junos'}` * Server or anything not in above: `device_params={'name':'default'}` @@ -132,13 +134,18 @@ 1. Install testing dependencies: ``` - pip install nose rednose coverage coveralls mock + pip install pytest coverage coveralls mock ``` 1. Finally, run the tests: ``` - nosetests test --rednose --verbosity=3 + pytest test --verbosity=3 + ``` + + For coverage, showing missing lines do: + ``` + coverage run -m pytest test --verbosity=3 && coverage report -m ``` ### Making a Release diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/README.rst new/ncclient-0.6.16/README.rst --- old/ncclient-0.6.15/README.rst 2023-10-16 17:46:13.000000000 +0200 +++ new/ncclient-0.6.16/README.rst 2024-10-09 10:17:14.000000000 +0200 @@ -87,18 +87,19 @@ Supported device handlers ''''''''''''''''''''''''' -* Juniper: `device_params={'name':'junos'}` +* Alcatel Lucent: `device_params={'name':'alu'}` +* Ciena: `device_params={'name':'ciena'}` * Cisco: - CSR: `device_params={'name':'csr'}` - Nexus: `device_params={'name':'nexus'}` - IOS XR: `device_params={'name':'iosxr'}` - IOS XE: `device_params={'name':'iosxe'}` +* H3C: `device_params={'name':'h3c'}` +* HP Comware: `device_params={'name':'hpcomware'}` * Huawei: - `device_params={'name':'huawei'}` - `device_params={'name':'huaweiyang'}` -* Nokia SR OS: `device_params={'name':'sros'}` -* H3C: `device_params={'name':'h3c'}` -* HP Comware: `device_params={'name':'hpcomware'}` +* Juniper: `device_params={'name':'junos'}` * Server or anything not in above: `device_params={'name':'default'}` Changes \| brief diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/docs/source/index.rst new/ncclient-0.6.16/docs/source/index.rst --- old/ncclient-0.6.15/docs/source/index.rst 2023-10-16 17:46:13.000000000 +0200 +++ new/ncclient-0.6.16/docs/source/index.rst 2024-10-09 10:17:14.000000000 +0200 @@ -39,18 +39,19 @@ Supported device handlers ------------------------- -* Juniper: `device_params={'name':'junos'}` +* Alcatel Lucent: `device_params={'name':'alu'}` +* Ciena: `device_params={'name':'ciena'}` * Cisco: - CSR: `device_params={'name':'csr'}` - Nexus: `device_params={'name':'nexus'}` - IOS XR: `device_params={'name':'iosxr'}` - IOS XE: `device_params={'name':'iosxe'}` +* H3C: `device_params={'name':'h3c'}` +* HP Comware: `device_params={'name':'hpcomware'}` * 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'}` +* Juniper: `device_params={'name':'junos'}` * Server or anything not in above: `device_params={'name':'default'}` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/examples/base/nc10.py new/ncclient-0.6.16/examples/base/nc10.py --- old/ncclient-0.6.15/examples/base/nc10.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ncclient-0.6.16/examples/base/nc10.py 2024-10-09 10:17:14.000000000 +0200 @@ -0,0 +1,24 @@ +#! /usr/bin/env python +# +# Connect to NETCONF server using unix socket passed on the command line. +# Then display the servers capabilities. After connecting you can also use +# all other operations supported by ncclient, example: get(), get_config()... +# +# To test against a example NETCONF server with unix socket support check out: +# https://github.com/CESNET/libnetconf2 +# It contains an example server which can listen for connections using a unix +# socket. +# +# $ ./nc10.py "/path/to/socket" + +import sys, warnings +warnings.simplefilter("ignore", DeprecationWarning) +from ncclient import manager + +def demo(path): + with manager.connect_uds(path) as m: + for c in m.server_capabilities: + print(c) + +if __name__ == '__main__': + demo(sys.argv[1]) \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/examples/vendor/nokia/sros/config_mode.py new/ncclient-0.6.16/examples/vendor/nokia/sros/config_mode.py --- old/ncclient-0.6.15/examples/vendor/nokia/sros/config_mode.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ncclient-0.6.16/examples/vendor/nokia/sros/config_mode.py 2024-10-09 10:17:14.000000000 +0200 @@ -0,0 +1,118 @@ +import logging +import sys +import threading +import time +from ncclient import manager + +# Device connection details +DEVICE_HOST = '62.40.111.58' +DEVICE_PORT = 3830 +DEVICE_USER = 'admin' +DEVICE_PASS = 'admin' + +# Configuration payload template for SNMP community string +EDIT_CONFIG_PAYLOAD = """ +<config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> + <configure xmlns="urn:nokia.com:sros:ns:yang:sr:conf"> + <system> + <security> + <snmp> + <community> + <community-string>TestCommunity-{}</community-string> + <access-permissions>r</access-permissions> + <version>v2c</version> + </community> + </snmp> + </security> + </system> + </configure> +</config> +""" + + +def create_ncclient_session(host, port, username, password, config_mode=None): + """ + Creates and returns an ncclient manager session for connecting to the Nokia SR OS device. + + :param host: Hostname or IP of the Nokia device + :param port: NETCONF port + :param username: Username for device authentication + :param password: Password for device authentication + :param config_mode: 'private' to use private candidate mode, None for default mode + :return: NETCONF manager session + """ + device_params = {'name': 'sros'} + if config_mode: + device_params['config_mode'] = config_mode + + return manager.connect( + host=host, + port=port, + username=username, + password=password, + hostkey_verify=False, + device_params=device_params + ) + + +def configure_and_commit(session_id, config_mode=None): + """ + Creates a NETCONF session, edits the configuration, and commits the changes. + + :param session_id: Unique session identifier for logging + :param config_mode: 'private' for private candidate mode, None for default mode + """ + try: + with create_ncclient_session(DEVICE_HOST, DEVICE_PORT, DEVICE_USER, DEVICE_PASS, config_mode=config_mode) as nc_session: + logging.info(f"Session {session_id} started with config_mode: {config_mode or 'default'}") + + # Edit the device configuration using the payload + edit_payload = EDIT_CONFIG_PAYLOAD.format(session_id) + nc_session.edit_config(target='candidate', config=edit_payload) + logging.info(f"Session {session_id} successfully edited the configuration.") + + # Commit the changes to the device + nc_session.commit(comment=f"Session {session_id} commit in {config_mode or 'default'} mode") + logging.info(f"Session {session_id} committed successfully in {config_mode or 'default'} mode.") + + except Exception as e: + logging.error(f"Session {session_id} encountered an error: {e}") + + +def run_concurrent_sessions(session_ids, config_mode=None): + """ + Runs multiple NETCONF sessions concurrently. + + :param session_ids: List of session IDs to identify different threads + :param config_mode: 'private' to use private candidate mode, None for default mode + """ + threads = [] + for session_id in session_ids: + thread = threading.Thread(target=configure_and_commit, args=(session_id, config_mode)) + threads.append(thread) + thread.start() + + # Ensure all threads finish execution + for thread in threads: + thread.join() + + +def main(): + LOG_FORMAT = '%(asctime)s %(levelname)s %(filename)s:%(lineno)d %(message)s' + logging.basicConfig(stream=sys.stdout, level=logging.INFO, format=LOG_FORMAT) + + logging.info("Starting test with concurrent sessions in default mode (expecting conflicts)...") + + # Run test with concurrent sessions in default mode + run_concurrent_sessions([1, 2, 3, 4, 5]) + + logging.info("Now testing with concurrent sessions in private mode (no conflicts expected)...") + + # Run test with concurrent sessions in private candidate mode + run_concurrent_sessions([6, 7, 8, 9], config_mode='private') + + logging.info("Test completed.") + + +if __name__ == '__main__': + main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/ncclient/_version.py new/ncclient-0.6.16/ncclient/_version.py --- old/ncclient-0.6.15/ncclient/_version.py 2023-10-16 17:46:13.000000000 +0200 +++ new/ncclient-0.6.16/ncclient/_version.py 2024-10-09 10:17:14.000000000 +0200 @@ -26,9 +26,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.15)" - git_full = "f0f4d95ed8613f256f0972491d6c47f53246da0b" - git_date = "2023-10-16 16:46:13 +0100" + git_refnames = " (tag: v0.6.16)" + git_full = "652ef67b266df0ee02bbbbc4cc6eb09ca01f4878" + git_date = "2024-10-09 09:17:14 +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.15/ncclient/devices/__init__.py new/ncclient-0.6.16/ncclient/devices/__init__.py --- old/ncclient-0.6.15/ncclient/devices/__init__.py 2023-10-16 17:46:13.000000000 +0200 +++ new/ncclient-0.6.16/ncclient/devices/__init__.py 2024-10-09 10:17:14.000000000 +0200 @@ -1,20 +1,24 @@ # supported devices config, add new device (eg: 'device name':'device label'). -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', - 'sros':'Nokia SR OS', - 'default':'Server or anything not in above'} +supported_devices_cfg = { + "alu": "Alcatel Lucent", + "ciena": "Ciena", + "csr": "Cisco CSR1000v", + "h3c": "H3C", + "hpcomware": "HP Comware", + "huawei": "Huawei", + "huaweiyang": "Huawei", + "iosxe": "Cisco IOS XE", + "iosxr": "Cisco IOS XR", + "junos": "Juniper", + "nexus": "Cisco Nexus", + "sros": "Nokia SR OS", + "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.15/ncclient/devices/ciena.py new/ncclient-0.6.16/ncclient/devices/ciena.py --- old/ncclient-0.6.15/ncclient/devices/ciena.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ncclient-0.6.16/ncclient/devices/ciena.py 2024-10-09 10:17:14.000000000 +0200 @@ -0,0 +1,19 @@ +from .default import DefaultDeviceHandler +from ncclient.xml_ import BASE_NS_1_0 + + +class CienaDeviceHandler(DefaultDeviceHandler): + """ + Ciena handler for device specific information. + """ + + def __init__(self, device_params): + super(CienaDeviceHandler, self).__init__(device_params) + + def get_xml_base_namespace_dict(self): + return {None: BASE_NS_1_0} + + def get_xml_extra_prefix_kwargs(self): + d = {} + d.update(self.get_xml_base_namespace_dict()) + return {"nsmap": d} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/ncclient/devices/sros.py new/ncclient-0.6.16/ncclient/devices/sros.py --- old/ncclient-0.6.15/ncclient/devices/sros.py 2023-10-16 17:46:13.000000000 +0200 +++ new/ncclient-0.6.16/ncclient/devices/sros.py 2024-10-09 10:17:14.000000000 +0200 @@ -1,10 +1,11 @@ -from lxml import etree - from .default import DefaultDeviceHandler from ncclient.operations.third_party.sros.rpc import MdCliRawCommand, Commit from ncclient.xml_ import BASE_NS_1_0 +class ConfigMode: + PRIVATE = 'private' + def passthrough(xml): return xml @@ -30,7 +31,11 @@ 'urn:ietf:params:xml:ns:netconf:base:1.0', 'urn:ietf:params:xml:ns:yang:1', 'urn:ietf:params:netconf:capability:confirmed-commit:1.1', - 'urn:ietf:params:netconf:capability:validate:1.1'] + 'urn:ietf:params:netconf:capability:validate:1.1', + ] + if self.device_params.get('config_mode') == ConfigMode.PRIVATE: + additional.append('urn:nokia.com:nc:pc') + return base + additional def get_xml_base_namespace_dict(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/ncclient/manager.py new/ncclient-0.6.16/ncclient/manager.py --- old/ncclient-0.6.15/ncclient/manager.py 2023-10-16 17:46:13.000000000 +0200 +++ new/ncclient-0.6.16/ncclient/manager.py 2024-10-09 10:17:14.000000000 +0200 @@ -158,6 +158,19 @@ return Manager(session, device_handler, **manager_params) +def connect_uds(*args, **kwargs): + """Initialize a :class:`Manager` over the Unix Socket transport.""" + device_params = _extract_device_params(kwargs) + manager_params = _extract_manager_params(kwargs) + nc_params = _extract_nc_params(kwargs) + + device_handler = make_device_handler(device_params) + device_handler.add_additional_netconf_params(nc_params) + session = transport.UnixSocketSession(device_handler) + + session.connect(*args, **kwargs) + + return Manager(session, device_handler, **manager_params) def connect_ioproc(*args, **kwds): device_params = _extract_device_params(kwds) @@ -191,7 +204,7 @@ port = kwds.get("port",4334) srv_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv_socket.bind((host, port)) - srv_socket.settimeout(10) + srv_socket.settimeout(kwds.get("timeout", 10)) srv_socket.listen() sock, remote_host = srv_socket.accept() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/ncclient/transport/__init__.py new/ncclient-0.6.16/ncclient/transport/__init__.py --- old/ncclient-0.6.15/ncclient/transport/__init__.py 2023-10-16 17:46:13.000000000 +0200 +++ new/ncclient-0.6.16/ncclient/transport/__init__.py 2024-10-09 10:17:14.000000000 +0200 @@ -17,6 +17,7 @@ from ncclient.transport.session import Session, SessionListener, NetconfBase from ncclient.transport.ssh import SSHSession from ncclient.transport.tls import TLSSession +from ncclient.transport.unixSocket import UnixSocketSession from ncclient.transport.errors import * __all__ = [ @@ -24,6 +25,7 @@ 'SessionListener', 'SSHSession', 'TLSSession', + 'UnixSocketSession', 'TransportError', 'AuthenticationError', 'SessionCloseError', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/ncclient/transport/errors.py new/ncclient-0.6.16/ncclient/transport/errors.py --- old/ncclient-0.6.15/ncclient/transport/errors.py 2023-10-16 17:46:13.000000000 +0200 +++ new/ncclient-0.6.16/ncclient/transport/errors.py 2024-10-09 10:17:14.000000000 +0200 @@ -53,3 +53,6 @@ class TLSError(TransportError): pass + +class UnixSocketError (TransportError): + pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/ncclient/transport/session.py new/ncclient-0.6.16/ncclient/transport/session.py --- old/ncclient-0.6.15/ncclient/transport/session.py 2023-10-16 17:46:13.000000000 +0200 +++ new/ncclient-0.6.16/ncclient/transport/session.py 2024-10-09 10:17:14.000000000 +0200 @@ -218,6 +218,21 @@ self._transport_register(s, selectors.EVENT_READ) self.logger.debug('selector type = %s', s.__class__.__name__) while True: + + if not q.empty() and self._send_ready(): + self.logger.debug("Sending message") + data = q.get().encode() + if self._base == NetconfBase.BASE_11: + data = b"%s%s%s" % (start_delim(len(data)), data, END_DELIM) + else: + data = b"%s%s" % (data, MSG_DELIM) + self.logger.info("Sending:\n%s", data) + while data: + n = self._transport_write(data) + if n <= 0: + raise SessionCloseError(self._buffer.getvalue(), data) + data = data[n:] + events = s.select(timeout=TICK) if events: data = self._transport_read() @@ -234,19 +249,6 @@ else: # End of session, unexpected raise SessionCloseError(self._buffer.getvalue()) - if not q.empty() and self._send_ready(): - self.logger.debug("Sending message") - data = q.get().encode() - if self._base == NetconfBase.BASE_11: - data = b"%s%s%s" % (start_delim(len(data)), data, END_DELIM) - else: - data = b"%s%s" % (data, MSG_DELIM) - self.logger.info("Sending:\n%s", data) - while data: - n = self._transport_write(data) - if n <= 0: - raise SessionCloseError(self._buffer.getvalue(), data) - data = data[n:] except Exception as e: self.logger.debug("Broke out of main loop, error=%r", e) self._dispatch_error(e) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/ncclient/transport/ssh.py new/ncclient-0.6.16/ncclient/transport/ssh.py --- old/ncclient-0.6.15/ncclient/transport/ssh.py 2023-10-16 17:46:13.000000000 +0200 +++ new/ncclient-0.6.16/ncclient/transport/ssh.py 2024-10-09 10:17:14.000000000 +0200 @@ -453,22 +453,28 @@ rsa_key = os.path.expanduser("~/.ssh/id_rsa") dsa_key = os.path.expanduser("~/.ssh/id_dsa") ecdsa_key = os.path.expanduser("~/.ssh/id_ecdsa") + ed25519_key = os.path.expanduser("~/.ssh/id_ed25519") if os.path.isfile(rsa_key): keyfiles.append((paramiko.RSAKey, rsa_key)) if os.path.isfile(dsa_key): keyfiles.append((paramiko.DSSKey, dsa_key)) if os.path.isfile(ecdsa_key): keyfiles.append((paramiko.ECDSAKey, ecdsa_key)) + if os.path.isfile(ed25519_key): + keyfiles.append((paramiko.Ed25519Key, ed25519_key)) # look in ~/ssh/ for windows users: rsa_key = os.path.expanduser("~/ssh/id_rsa") dsa_key = os.path.expanduser("~/ssh/id_dsa") ecdsa_key = os.path.expanduser("~/ssh/id_ecdsa") + ed25519_key = os.path.expanduser("~/ssh/id_ed25519") if os.path.isfile(rsa_key): keyfiles.append((paramiko.RSAKey, rsa_key)) if os.path.isfile(dsa_key): keyfiles.append((paramiko.DSSKey, dsa_key)) if os.path.isfile(ecdsa_key): keyfiles.append((paramiko.ECDSAKey, ecdsa_key)) + if os.path.isfile(ed25519_key): + keyfiles.append((paramiko.Ed25519Key, ed25519_key)) for cls, filename in keyfiles: try: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/ncclient/transport/unixSocket.py new/ncclient-0.6.16/ncclient/transport/unixSocket.py --- old/ncclient-0.6.15/ncclient/transport/unixSocket.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ncclient-0.6.16/ncclient/transport/unixSocket.py 2024-10-09 10:17:14.000000000 +0200 @@ -0,0 +1,83 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import socket +import threading +from io import BytesIO as StringIO + +from socket import AF_UNIX, SOCK_STREAM + +from ncclient.capabilities import Capabilities +from ncclient.logging_ import SessionLoggerAdapter +from ncclient.transport.errors import UnixSocketError +from ncclient.transport.session import Session +from ncclient.transport.parser import DefaultXMLParser + +logger = logging.getLogger("ncclient.transport.unix") + +DEFAULT_TIMEOUT = 120 + +BUF_SIZE = 4096 + +class UnixSocketSession(Session): + + "Implements a NETCONF Session over Unix Socket on local machine." + + def __init__(self, device_handler): + capabilities = Capabilities(device_handler.get_capabilities()) + Session.__init__(self, capabilities) + self._connected = False + self._socket = None + self._buffer = StringIO() + self._device_handler = device_handler + self._message_list = [] + self._closing = threading.Event() + self.parser = DefaultXMLParser(self) + self.logger = SessionLoggerAdapter(logger, {'session': self}) + + def _dispatch_message(self, raw): + self.logger.info("Received message from host") + self.logger.debug("Received:\n%s", raw) + return super(UnixSocketSession, self)._dispatch_message(raw) + + def close(self): + self._closing.set() + self._socket.close() + self._connected = False + + def connect(self, path=None, timeout=DEFAULT_TIMEOUT): + sock = socket.socket(AF_UNIX, SOCK_STREAM) + sock.settimeout(timeout) + + try: + sock.connect(path) + except Exception: + raise UnixSocketError("Could not connect to %s" % path) + + self._socket = sock + self._connected = True + self._post_connect() + + def _transport_read(self): + return self._socket.recv(BUF_SIZE) + + def _transport_write(self, data): + return self._socket.send(data) + + def _transport_register(self, selector, event): + selector.register(self._socket, event) + + def _send_ready(self): + # In contrast to Paramiko's `Channel`, pure python sockets do not + # expose `send_ready()` function. + return True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/test/unit/devices/test_ciena.py new/ncclient-0.6.16/test/unit/devices/test_ciena.py --- old/ncclient-0.6.15/test/unit/devices/test_ciena.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ncclient-0.6.16/test/unit/devices/test_ciena.py 2024-10-09 10:17:14.000000000 +0200 @@ -0,0 +1,18 @@ +import unittest +from ncclient.devices.ciena import * +from ncclient.xml_ import * + + +class TestCienaDevice(unittest.TestCase): + + def setUp(self): + self.obj = CienaDeviceHandler({'name': 'ciena'}) + + def test_get_xml_base_namespace_dict(self): + expected = {None: BASE_NS_1_0} + self.assertDictEqual(expected, self.obj.get_xml_base_namespace_dict()) + + def test_get_xml_extra_prefix_kwargs(self): + expected = dict() + expected["nsmap"] = self.obj.get_xml_base_namespace_dict() + self.assertDictEqual(expected, self.obj.get_xml_extra_prefix_kwargs()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/test/unit/devices/test_get_supported_devices.py new/ncclient-0.6.16/test/unit/devices/test_get_supported_devices.py --- old/ncclient-0.6.15/test/unit/devices/test_get_supported_devices.py 2023-10-16 17:46:13.000000000 +0200 +++ new/ncclient-0.6.16/test/unit/devices/test_get_supported_devices.py 2024-10-09 10:17:14.000000000 +0200 @@ -7,6 +7,7 @@ supported_devices = devices.get_supported_devices() self.assertEqual(sorted(supported_devices), sorted(('junos', 'csr', + 'ciena', 'nexus', 'iosxr', 'iosxe', @@ -22,6 +23,7 @@ supported_device_labels = devices.get_supported_device_labels() self.assertEqual(supported_device_labels, {'junos':'Juniper', 'csr':'Cisco CSR1000v', + 'ciena': 'Ciena', 'nexus':'Cisco Nexus', 'iosxr':'Cisco IOS XR', 'iosxe':'Cisco IOS XE', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/test/unit/devices/test_sros.py new/ncclient-0.6.16/test/unit/devices/test_sros.py --- old/ncclient-0.6.15/test/unit/devices/test_sros.py 2023-10-16 17:46:13.000000000 +0200 +++ new/ncclient-0.6.16/test/unit/devices/test_sros.py 2024-10-09 10:17:14.000000000 +0200 @@ -1,7 +1,8 @@ import unittest -from ncclient.devices.sros import * -from ncclient.xml_ import * +from ncclient.devices.sros import SrosDeviceHandler, ConfigMode +from ncclient.xml_ import to_ele, to_xml, BASE_NS_1_0 +from ncclient.operations.third_party.sros.rpc import MdCliRawCommand, Commit capabilities = ['urn:ietf:params:netconf:base:1.0', 'urn:ietf:params:netconf:base:1.1', @@ -36,32 +37,39 @@ class TestSrosDevice(unittest.TestCase): def setUp(self): - self.obj = SrosDeviceHandler({'name': 'sros'}) + self.device_handler = SrosDeviceHandler({'name': 'sros'}) def test_add_additional_operations(self): expected = { 'md_cli_raw_command': MdCliRawCommand, 'commit': Commit, } - self.assertDictEqual(expected, self.obj.add_additional_operations()) + self.assertDictEqual(expected, self.device_handler.add_additional_operations()) def test_transform_reply(self): expected = xml - actual = self.obj.transform_reply() + actual = self.device_handler.transform_reply() ele = to_ele(xml) self.assertEqual(expected, to_xml(actual(ele))) - def test_get_capabilities(self): - self.assertListEqual(capabilities, self.obj.get_capabilities()) + def test_get_capabilities_without_config_mode(self): + """Test capabilities without 'config_mode' set""" + self.assertListEqual(capabilities, self.device_handler.get_capabilities()) + + def test_get_capabilities_with_config_mode_private(self): + """Test capabilities when 'config_mode' is set to 'private'""" + device_handler = SrosDeviceHandler({'name': 'sros', 'config_mode': ConfigMode.PRIVATE}) + expected_capabilities = capabilities + ['urn:nokia.com:nc:pc'] + self.assertListEqual(expected_capabilities, device_handler.get_capabilities()) def test_get_xml_base_namespace_dict(self): expected = {None: BASE_NS_1_0} - self.assertDictEqual(expected, self.obj.get_xml_base_namespace_dict()) + self.assertDictEqual(expected, self.device_handler.get_xml_base_namespace_dict()) def test_get_xml_extra_prefix_kwargs(self): expected = dict() - expected['nsmap'] = self.obj.get_xml_base_namespace_dict() - self.assertDictEqual(expected, self.obj.get_xml_extra_prefix_kwargs()) + expected['nsmap'] = self.device_handler.get_xml_base_namespace_dict() + self.assertDictEqual(expected, self.device_handler.get_xml_extra_prefix_kwargs()) def test_perform_qualify_check(self): - self.assertFalse(self.obj.perform_qualify_check()) + self.assertFalse(self.device_handler.perform_qualify_check()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ncclient-0.6.15/test/unit/transport/test_UnixSocket.py new/ncclient-0.6.16/test/unit/transport/test_UnixSocket.py --- old/ncclient-0.6.15/test/unit/transport/test_UnixSocket.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ncclient-0.6.16/test/unit/transport/test_UnixSocket.py 2024-10-09 10:17:14.000000000 +0200 @@ -0,0 +1,33 @@ + +import sys +import unittest +import socket +from ncclient.devices.junos import JunosDeviceHandler + +try: + from unittest.mock import MagicMock, patch, call +except ImportError: + from mock import MagicMock, patch, call + +from ncclient.transport.errors import UnixSocketError +from ncclient.transport.unixSocket import UnixSocketSession + +PATH = '/tmp/test_socket.sock' + +class TestUnixSocket(unittest.TestCase): + + @patch('socket.socket.close') + def test_close_UnixSocket(self, mock_sock_close_fn): + session = UnixSocketSession(MagicMock()) + session._socket = socket.socket() + session._connected = True + session.close() + mock_sock_close_fn.assert_called_once_with() + self.assertFalse(session.connected) + + @patch('socket.socket') + @patch('ncclient.transport.Session._post_connect') + def test_connect_UnixSocket(self, mock_post_connect, mock_socket): + session = UnixSocketSession(MagicMock()) + session.connect(path=PATH) + self.assertTrue(session.connected)