[Python-modules-commits] [junos-eznc] 01/03: Import junos-eznc_2.0.1.orig.tar.gz

Vincent Bernat bernat at moszumanska.debian.org
Tue Nov 1 19:13:52 UTC 2016


This is an automated email from the git hooks/post-receive script.

bernat pushed a commit to branch master
in repository junos-eznc.

commit daf35fa8937003a2038596f68cd2fb5fd16d68dd
Author: Vincent Bernat <bernat at debian.org>
Date:   Tue Nov 1 20:10:17 2016 +0100

    Import junos-eznc_2.0.1.orig.tar.gz
---
 PKG-INFO                                  |   5 +-
 lib/jnpr/junos/__init__.py                |  12 +
 lib/jnpr/junos/console.py                 | 244 ++++++++
 lib/jnpr/junos/device.py                  | 994 +++++++++++++++++-------------
 lib/jnpr/junos/exception.py               |   7 +-
 lib/jnpr/junos/factory/cfgtable.py        | 498 ++++++++++++++-
 lib/jnpr/junos/factory/factory_cls.py     |   6 +-
 lib/jnpr/junos/factory/factory_loader.py  |  16 +-
 lib/jnpr/junos/factory/optable.py         |   3 +-
 lib/jnpr/junos/factory/table.py           |   6 +-
 lib/jnpr/junos/factory/view.py            |  10 +-
 lib/jnpr/junos/jxml.py                    |   4 +-
 lib/jnpr/junos/op/ethport.yml             |   2 +-
 lib/jnpr/junos/op/intopticdiag.yml        |   2 +-
 lib/jnpr/junos/op/ospf.yml                |  12 +
 lib/jnpr/junos/op/phyport.yml             |   6 +-
 lib/jnpr/junos/op/vlan.yml                |   1 +
 lib/jnpr/junos/resources/__init__.py      |   0
 lib/jnpr/junos/resources/autosys.py       |   7 +
 lib/jnpr/junos/resources/autosys.yml      |  12 +
 lib/jnpr/junos/resources/bgp.py           |   7 +
 lib/jnpr/junos/resources/bgp.yml          |  19 +
 lib/jnpr/junos/resources/interface.py     |   7 +
 lib/jnpr/junos/resources/interface.yml    |  22 +
 lib/jnpr/junos/resources/staticroutes.py  |   7 +
 lib/jnpr/junos/resources/staticroutes.yml |  14 +
 lib/jnpr/junos/resources/syslog.py        |   7 +
 lib/jnpr/junos/resources/syslog.yml       |  19 +
 lib/jnpr/junos/resources/user.py          |   7 +
 lib/jnpr/junos/resources/user.yml         |  18 +
 lib/jnpr/junos/rpcmeta.py                 |  21 +-
 lib/jnpr/junos/transport/__init__.py      |   0
 lib/jnpr/junos/transport/tty.py           | 269 ++++++++
 lib/jnpr/junos/transport/tty_netconf.py   | 177 ++++++
 lib/jnpr/junos/transport/tty_serial.py    |  96 +++
 lib/jnpr/junos/transport/tty_telnet.py    | 113 ++++
 lib/jnpr/junos/utils/config.py            |  37 +-
 lib/jnpr/junos/utils/fs.py                |   6 +-
 lib/jnpr/junos/utils/ftp.py               | 113 ++++
 lib/jnpr/junos/utils/scp.py               |  21 +-
 lib/jnpr/junos/utils/start_shell.py       |  64 +-
 lib/jnpr/junos/utils/sw.py                |  20 +-
 lib/jnpr/junos/version.py                 |   4 +-
 lib/junos_eznc.egg-info/PKG-INFO          |   5 +-
 lib/junos_eznc.egg-info/SOURCES.txt       |  21 +
 lib/junos_eznc.egg-info/requires.txt      |   2 +
 requirements.txt                          |   2 +
 setup.cfg                                 |   3 +
 setup.py                                  |  11 +-
 49 files changed, 2417 insertions(+), 542 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index 8404bdd..f68c966 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,9 +1,9 @@
 Metadata-Version: 1.1
 Name: junos-eznc
-Version: 1.3.1
+Version: 2.0.1
 Summary: Junos 'EZ' automation for non-programmers
 Home-page: http://www.github.com/Juniper/py-junos-eznc
-Author: Jeremy Schulman
+Author: Jeremy Schulman, Nitin Kumar
 Author-email: jnpr-community-netdev at juniper.net
 License: Apache 2.0
 Description: UNKNOWN
@@ -20,6 +20,7 @@ Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.4
 Classifier: Topic :: Software Development :: Libraries
 Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/lib/jnpr/junos/__init__.py b/lib/jnpr/junos/__init__.py
index 5764bbb..40b27c8 100644
--- a/lib/jnpr/junos/__init__.py
+++ b/lib/jnpr/junos/__init__.py
@@ -1,4 +1,5 @@
 from jnpr.junos.device import Device
+from jnpr.junos.console import Console
 from jnpr.junos.factory.to_json import PyEzJSONEncoder
 from jnpr.junos.facts.swver import version_info, version_yaml_representer
 from . import jxml
@@ -10,6 +11,17 @@ import json
 import yaml
 import logging
 
+import sys
+import warnings
+
+if sys.version_info[:2] == (2, 6):
+    warnings.warn(
+        "Python 2.6 is no longer supported by the Python core team, please "
+        "upgrade your Python. A future version of PyEZ will drop "
+        "support for Python 2.6",
+        DeprecationWarning
+    )
+
 __version__ = version.VERSION
 __date__ = version.DATE
 
diff --git a/lib/jnpr/junos/console.py b/lib/jnpr/junos/console.py
new file mode 100644
index 0000000..bacb5f1
--- /dev/null
+++ b/lib/jnpr/junos/console.py
@@ -0,0 +1,244 @@
+"""
+This file defines the 'netconifyCmdo' class.
+Used by the 'netconify' shell utility.
+"""
+import traceback
+import sys
+import logging
+import warnings
+
+# 3rd-party packages
+import ncclient.transport.errors as NcErrors
+import ncclient.operations.errors as NcOpErrors
+from ncclient.devices.junos import JunosDeviceHandler
+from lxml import etree
+from jnpr.junos.transport.tty_telnet import Telnet
+from jnpr.junos.transport.tty_serial import Serial
+from ncclient.operations.rpc import RPCReply, RPCError
+from ncclient.xml_ import NCElement
+from jnpr.junos.device import _Connection
+
+# local modules
+from jnpr.junos.rpcmeta import _RpcMetaExec
+from jnpr.junos import exception as EzErrors
+from jnpr.junos.facts import *
+from jnpr.junos import jxml as JXML
+from jnpr.junos.decorators import timeoutDecorator, normalizeDecorator
+
+QFX_MODEL_LIST = ['QFX3500', 'QFX3600', 'VIRTUAL CHASSIS']
+QFX_MODE_NODE = 'NODE'
+QFX_MODE_SWITCH = 'SWITCH'
+
+logger = logging.getLogger("jnpr.junos.console")
+
+
+class Console(_Connection):
+
+    def __init__(self, **kvargs):
+        """
+        NoobDevice object constructor.
+
+        :param str host:
+            **REQUIRED** host-name or ipaddress of target device
+
+        :param str user:
+            *OPTIONAL* login user-name, uses root if not provided
+
+        :param str passwd:
+            *OPTIONAL* in console connection for device at zeroized state
+            password is not required
+
+        :param int port:
+            *OPTIONAL*  port, defaults to '23' for telnet mode and
+            '/dev/ttyUSB0' for serial.
+
+        :param int baud:
+            *OPTIONAL*  baud, default baud rate is 9600
+
+        :param str mode:
+            *OPTIONAL*  mode, mode of connection (telnet/serial)
+            default is telnet
+
+        :param int timeout:
+            *OPTIONAL*  timeout, default is 0.5
+
+        :param int attempts:
+            *OPTIONAL*  attempts, default is 10
+
+        :param str ssh_config:
+            *OPTIONAL* The path to the SSH configuration file.
+            This can be used to load SSH information from a configuration file.
+            By default ~/.ssh/config is queried it will be used by SCP class.
+            So its assumed ssh is enabled by the time we use SCP functionality.
+
+        :param bool gather_facts:
+            *OPTIONAL* default is ``False``.  If ``False`` then the
+            facts are not gathered on call to :meth:`open`
+
+        """
+
+        # ----------------------------------------
+        # setup instance connection/open variables
+        # ----------------------------------------
+
+        self._tty = None
+        self._facts = {}
+        self.connected = False
+        self._skip_logout = False
+        self.results = dict(changed=False, failed=False, errmsg=None)
+
+        # hostname is not required in serial mode connection
+        self._hostname = kvargs.get('host')
+        self._auth_user = kvargs.get('user', 'root')
+        self._auth_password = kvargs.get(
+            'password',
+            '') or kvargs.get(
+            'passwd',
+            '')
+        self._port = kvargs.get('port', '23')
+        self._baud = kvargs.get('baud', '9600')
+        self._mode = kvargs.get('mode', 'telnet')
+        self._timeout = kvargs.get('timeout', '0.5')
+        self._normalize = kvargs.get('normalize', False)
+        self._norm_transform = lambda: JXML.normalize_xslt.encode('UTF-8')
+        self.transform = self._norm_transform
+        # self.timeout needed by PyEZ utils
+        #self.timeout = self._timeout
+        self._attempts = kvargs.get('attempts', 10)
+        self.gather_facts = kvargs.get('gather_facts', False)
+        self.rpc = _RpcMetaExec(self)
+        self._ssh_config = kvargs.get('ssh_config')
+        self._manages = []
+        self.junos_dev_handler = JunosDeviceHandler(device_params=
+                                                    {'name': 'junos',
+                                                     'local': False})
+
+    @property
+    def timeout(self):
+        """
+        :returns: current console connection timeout value (int) in seconds.
+        """
+        return self._timeout
+
+    @timeout.setter
+    def timeout(self, value):
+        """
+        Used to change the console connection timeout value (default=0.5 sec).
+
+        :param int value:
+            New timeout value in seconds
+        """
+        self._timeout = value
+
+    def open(self):
+        """
+        open the connection to the device
+        """
+
+        # ---------------------------------------------------------------
+        # validate device hostname or IP address
+        # ---------------------------------------------------------------
+        if self._mode.upper() == 'TELNET' and self._hostname is None:
+            self.results['failed'] = True
+            self.results[
+                'errmsg'] = 'ERROR: Device hostname/IP not specified !!!'
+            return self.results
+
+        # --------------------
+        # login to the CONSOLE
+        # --------------------
+        try:
+            self._tty_login()
+        except RuntimeError as err:
+            logger.error("ERROR:  {0}:{1}\n".format('login', str(err)))
+            logger.error(
+                "\nComplete traceback message: {0}".format(
+                    traceback.format_exc()))
+            raise err
+        except Exception as ex:
+            logger.error("Exception occurred: {0}:{1}\n".format('login', str(ex)))
+            raise ex
+        self.connected = True
+        if self.gather_facts is True:
+            logger.info('facts: retrieving device facts...')
+            self.facts_refresh()
+            self.results['facts'] = self._facts
+        return self
+
+    def close(self, skip_logout=False):
+        """
+        Closes the connection to the device.
+        """
+        if skip_logout is False and self.connected is True:
+            try:
+                self._tty_logout()
+            except Exception as err:
+                logger.error("ERROR {0}:{1}\n".format('logout', str(err)))
+                raise err
+            self.connected = False
+        elif self.connected is True:
+            try:
+                self._tty._tty_close()
+            except Exception as err:
+                logger.error("ERROR {0}:{1}\n".format('close', str(err)))
+                logger.error(
+                    "\nComplete traceback message: {0}".format(
+                        traceback.format_exc()))
+                raise err
+            self.connected = False
+
+    def _rpc_reply(self, rpc_cmd_e):
+        encode = None if sys.version < '3' else 'unicode'
+        rpc_cmd = etree.tostring(rpc_cmd_e, encoding=encode) if \
+                isinstance(rpc_cmd_e, etree._Element) else rpc_cmd_e
+        reply = self._tty.nc.rpc(rpc_cmd)
+        rpc_rsp_e = NCElement(reply, self.junos_dev_handler.transform_reply())._NCElement__doc
+        return rpc_rsp_e
+
+    # -------------------------------------------------------------------------
+    # LOGIN/LOGOUT
+    # -------------------------------------------------------------------------
+
+    def _tty_login(self):
+        tty_args = dict()
+        tty_args['user'] = self._auth_user
+        tty_args['passwd'] = self._auth_password
+        tty_args['timeout'] = float(self._timeout)
+        tty_args['attempts'] = int(self._attempts)
+        tty_args['baud'] = self._baud
+        if self._mode.upper() == 'TELNET':
+            tty_args['host'] = self._hostname
+            tty_args['port'] = self._port
+            self.console = ('telnet', self._hostname, self.port)
+            self._tty = Telnet(**tty_args)
+        elif self._mode.upper() == 'SERIAL':
+            tty_args['port'] = self._port
+            self.console = ('serial', self._port)
+            self._tty = Serial(**tty_args)
+        else:
+            logger.error('Mode should be either telnet or serial')
+            raise AttributeError('Mode to be telnet/serial')
+
+        self._tty.login()
+
+    def _tty_logout(self):
+        self._tty.logout()
+
+    def zeroize(self):
+        """ perform device ZEROIZE actions """
+        logger.info("zeroize : ZEROIZE device, rebooting")
+        self._tty.nc.zeroize()
+        self._skip_logout = True
+        self.results['changed'] = True
+
+    # -----------------------------------------------------------------------
+    # Context Manager
+    # -----------------------------------------------------------------------
+
+    def __enter__(self):
+        self._conn = self.open()
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        if self.connected:
+            self.close()
diff --git a/lib/jnpr/junos/device.py b/lib/jnpr/junos/device.py
index 483f043..b89d630 100644
--- a/lib/jnpr/junos/device.py
+++ b/lib/jnpr/junos/device.py
@@ -9,7 +9,9 @@ import traceback
 import socket
 import datetime
 import time
+import sys
 import json
+import re
 
 # 3rd-party packages
 from lxml import etree
@@ -23,11 +25,11 @@ import jinja2
 # local modules
 from jnpr.junos.rpcmeta import _RpcMetaExec
 from jnpr.junos import exception as EzErrors
-from jnpr.junos.cfg import Resource
 from jnpr.junos.facts import *
 from jnpr.junos import jxml as JXML
 from jnpr.junos.decorators import timeoutDecorator, normalizeDecorator
 
+
 _MODULEPATH = os.path.dirname(__file__)
 
 
@@ -43,53 +45,28 @@ class _MyTemplateLoader(jinja2.BaseLoader):
         self.paths = ['.', os.path.join(_MODULEPATH, 'templates')]
 
     def get_source(self, environment, template):
-
         def _in_path(dir):
             return os.path.exists(os.path.join(dir, template))
 
-        path = filter(_in_path, self.paths)
+        path = list(filter(_in_path, self.paths))
         if not path:
             raise jinja2.TemplateNotFound(template)
 
         path = os.path.join(path[0], template)
         mtime = os.path.getmtime(path)
-        with file(path) as f:
-            source = f.read().decode('utf-8')
+        with open(path) as f:
+            # You are trying to decode an object that is already decoded.
+            # You have a str, there is no need to decode from UTF-8 anymore.
+            # open already decodes to Unicode in Python 3 if you open in text mode.
+            # If you want to open it as bytes, so that you can then decode,
+            # you need to open with mode 'rb'.
+            source = f.read()
         return source, path, lambda: mtime == os.path.getmtime(path)
 
 _Jinja2ldr = jinja2.Environment(loader=_MyTemplateLoader())
 
 
-class Device(object):
-    """
-    Junos Device class.
-
-    :attr:`ON_JUNOS`:
-        **READ-ONLY** -
-        Auto-set to ``True`` when this code is running on a Junos device,
-        vs. running on a local-server remotely connecting to a device.
-
-    :attr:`auto_probe`:
-        When non-zero the call to :meth:`open` will probe for NETCONF
-        reachability before proceeding with the NETCONF session establishment.
-        If you want to enable this behavior by default, you could do the
-        following in your code::
-
-            from jnpr.junos import Device
-
-            # set all device open to auto-probe with timeout of 10 sec
-            Device.auto_probe = 10
-
-            dev = Device( ... )
-            dev.open()   # this will probe before attempting NETCONF connect
-
-    """
-    ON_JUNOS = platform.system().upper() == 'JUNOS'
-    auto_probe = 0          # default is no auto-probe
-
-    # -------------------------------------------------------------------------
-    # PROPERTIES
-    # -------------------------------------------------------------------------
+class _Connection(object):
 
     # ------------------------------------------------------------------------
     # property: hostname
@@ -164,8 +141,13 @@ class Device(object):
             self._logfile = False
             return rc
 
-        if not isinstance(value, file):
-            raise ValueError("value must be a file object")
+        if sys.version < '3':
+            if not isinstance(value, file):
+                raise ValueError("value must be a file object")
+        else:
+            import io
+            if not isinstance(value, io.TextIOWrapper):
+                raise ValueError("value must be a file object")
 
         self._logfile = value
         return self._logfile
@@ -208,49 +190,15 @@ class Device(object):
         raise RuntimeError("facts is read-only!")
 
     # ------------------------------------------------------------------------
-    # property: manages
-    # ------------------------------------------------------------------------
-
-    @property
-    def manages(self):
-        """
-        :returns:
-            ``list`` of Resource Managers/Utilities attached to this
-            instance using the :meth:`bind` method.
-        """
-        return self._manages
-
-    # ------------------------------------------------------------------------
-    # property: transform
+    # property: port
     # ------------------------------------------------------------------------
 
     @property
-    def transform(self):
-        """
-        :returns: the current RPC XML Transformation.
+    def port(self):
         """
-        return self._conn._device_handler.transform_reply
-
-    @transform.setter
-    def transform(self, func):
-        """
-        Used to change the RPC XML Transformation.
-
-        :param lambda value:
-            New transform lambda
+        :returns: the port (str) to connect to the Junos device
         """
-        self._conn._device_handler.transform_reply = func
-
-    # -----------------------------------------------------------------------
-    # OVERLOADS
-    # -----------------------------------------------------------------------
-
-    def __repr__(self):
-        return "Device(%s)" % self.hostname
-
-    # -----------------------------------------------------------------------
-    # CONSTRUCTOR
-    # -----------------------------------------------------------------------
+        return self._port
 
     def _sshconf_lkup(self):
         if self._ssh_config:
@@ -264,245 +212,254 @@ class Device(object):
             return None
         else:
             sshconf = paramiko.SSHConfig()
-            sshconf.parse(open(sshconf_path, 'r'))
-            found = sshconf.lookup(self._hostname)
-            self._hostname = found.get('hostname', self._hostname)
-            self._port = found.get('port', self._port)
-            self._conf_auth_user = found.get('user')
-            self._conf_ssh_private_key_file = found.get('identityfile')
+            with open(sshconf_path, 'r') as fp:
+                sshconf.parse(fp)
+                found = sshconf.lookup(self._hostname)
+                self._hostname = found.get('hostname', self._hostname)
+                self._port = found.get('port', self._port)
+                self._conf_auth_user = found.get('user')
+                self._conf_ssh_private_key_file = found.get('identityfile')
             return sshconf_path
 
-    def __init__(self, *vargs, **kvargs):
+    def display_xml_rpc(self, command, format='xml'):
         """
-        Device object constructor.
-
-        :param str vargs[0]: host-name or ipaddress.  This is an
-                             alternative for **host**
-
-        :param str host:
-            **REQUIRED** host-name or ipaddress of target device
-
-        :param str user:
-            *OPTIONAL* login user-name, uses $USER if not provided
+        Executes the CLI command and returns the CLI xml object by default.
 
-        :param str passwd:
-            *OPTIONAL* if not provided, assumed ssh-keys are enforced
+        For example::
+          print dev.display_xml_rpc('show version').tag
+          or
+          print dev.display_xml_rpc('show version', format='text')
 
-        :param int port:
-            *OPTIONAL* NETCONF port (defaults to 830)
+        :param str command:
+          The CLI command to retrieve XML RPC for, e.g. "show version"
 
-        :param bool gather_facts:
-            *OPTIONAL* default is ``True``.  If ``False`` then the
-            facts are not gathered on call to :meth:`open`
+        :param str format:
+          The return format, by default is XML.  You can optionally select
+          "text" to return the XML structure as a string.
+        """
+        try:
+            command = command + '| display xml rpc'
+            rsp = self.rpc.cli(command)
+            if format == 'text':
+                encode = None if sys.version < '3' else 'unicode'
+                return etree.tostring(rsp[0], encoding=encode)
+            return rsp[0]
+        except:
+            return "invalid command: " + command
 
-        :param bool auto_probe:
-            *OPTIONAL*  if non-zero then this enables auto_probe at time of
-            :meth:`open` and defines the amount of time(sec) for the
-            probe timeout
+    # ------------------------------------------------------------------------
+    # Template: retrieves a Jinja2 template
+    # ------------------------------------------------------------------------
 
-        :param str ssh_private_key_file:
-            *OPTIONAL* The path to the SSH private key file.
-            This can be used if you need to provide a private key rather than
-            loading the key into the ssh-key-ring/environment.  if your
-            ssh-key requires a password, then you must provide it via
-            **passwd**
+    def Template(self, filename, parent=None, gvars=None):
+        """
+        Used to return a Jinja2 :class:`Template`.
 
-        :param str ssh_config:
-            *OPTIONAL* The path to the SSH configuration file.
-            This can be used to load SSH information from a configuration file.
-            By default ~/.ssh/config is queried.
+        :param str filename:
+            file-path to Jinja2 template file on local device
 
-        :param bool normalize:
-            *OPTIONAL* default is ``False``.  If ``True`` then the
-            XML returned by :meth:`execute` will have whitespace normalized
+        :returns: Jinja2 :class:`Template` give **filename**.
         """
 
-        # ----------------------------------------
-        # setup instance connection/open variables
-        # ----------------------------------------
+        return self._j2ldr.get_template(filename, parent, gvars)
 
-        hostname = vargs[0] if len(vargs) else kvargs.get('host')
+    # ------------------------------------------------------------------------
+    # property: manages
+    # ------------------------------------------------------------------------
 
-        self._port = kvargs.get('port', 830)
-        self._gather_facts = kvargs.get('gather_facts', True)
-        self._normalize = kvargs.get('normalize', False)
-        self._auto_probe = kvargs.get('auto_probe', self.__class__.auto_probe)
+    @property
+    def manages(self):
+        """
+        :returns:
+            ``list`` of Resource Managers/Utilities attached to this
+            instance using the :meth:`bind` method.
+        """
+        return self._manages
 
-        if self.__class__.ON_JUNOS is True and hostname is None:
-            # ---------------------------------
-            # running on a Junos device locally
-            # ---------------------------------
-            self._auth_user = None
-            self._auth_password = None
-            self._hostname = 'localhost'
-            self._ssh_private_key_file = None
-            self._ssh_config = None
-        else:
-            # --------------------------
-            # making a remote connection
-            # --------------------------
-            if hostname is None:
-                raise ValueError("You must provide the 'host' value")
-            self._hostname = hostname
-            # user will default to $USER
-            self._auth_user = os.getenv('USER')
-            self._conf_auth_user = None
-            self._conf_ssh_private_key_file = None
-            # user can get updated by ssh_config
-            self._ssh_config = kvargs.get('ssh_config')
-            self._sshconf_path = self._sshconf_lkup()
-            # but if user or private key is explit from call, then use it.
-            self._auth_user = kvargs.get('user') or self._conf_auth_user or self._auth_user
-            self._ssh_private_key_file = kvargs.get('ssh_private_key_file') or self._conf_ssh_private_key_file
-            self._auth_password = kvargs.get('password') or kvargs.get('passwd')
+    # ------------------------------------------------------------------------
+    # dealing with bind aspects
+    # ------------------------------------------------------------------------
 
-        # -----------------------------
-        # initialize instance variables
-        # ------------------------------
+    def bind(self, *vargs, **kvargs):
+        """
+        Used to attach things to this Device instance and make them a
+        property of the :class:Device instance.  The most common use
+        for bind is attaching Utility instances to a :class:Device.
+        For example::
 
-        self._conn = None
-        self._j2ldr = _Jinja2ldr
-        self._manages = []
-        self._facts = {}
+            from jnpr.junos.utils.config import Config
 
-        # public attributes
+            dev.bind( cu=Config )
+            dev.cu.lock()
+            # ... load some changes
+            dev.cu.commit()
+            dev.cu.unlock()
 
-        self.connected = False
-        self.rpc = _RpcMetaExec(self)
+        :param list vargs:
+          A list of functions that will get bound as instance methods to
+          this Device instance.
 
-    # -----------------------------------------------------------------------
-    # Basic device methods
-    # -----------------------------------------------------------------------
+          .. warning:: Experimental.
 
-    def open(self, *vargs, **kvargs):
+        :param new_property:
+          name/class pairs that will create resource-managers bound as
+          instance attributes to this Device instance.  See code example above
         """
-        Opens a connection to the device using existing login/auth
-        information.
+        if len(vargs):
+            for fn in vargs:
+                # check for name clashes before binding
+                if hasattr(self, fn.__name__):
+                    raise ValueError(
+                        "request attribute name %s already exists" %
+                        fn.__name__)
+            for fn in vargs:
+                # bind as instance method, majik.
+                if sys.version < '3':
+                    self.__dict__[
+                        fn.__name__] = types.MethodType(
+                        fn,
+                        self,
+                        self.__class__)
+                else:
+                    self.__dict__[
+                        fn.__name__] = types.MethodType(
+                        fn,
+                        self.__class__)
+            return
 
-        :param bool gather_facts:
-            If set to ``True``/``False`` will override the device
-            instance value for only this open process
+        # first verify that the names do not conflict with
+        # existing object attribute names
 
-        :param bool auto_probe:
-            If non-zero then this enables auto_probe and defines the amount
-            of time/seconds for the probe timeout
+        for name in kvargs.keys():
+            # check for name-clashes before binding
+            if hasattr(self, name):
+                raise ValueError(
+                    "requested attribute name %s already exists" %
+                    name)
 
-        :param bool normalize:
-            If set to ``True``/``False`` will override the device
-            instance value for only this open process
+        # now instantiate items and bind to this :Device:
+        for name, thing in kvargs.items():
+            new_inst = thing(self)
+            self.__dict__[name] = new_inst
+            self._manages.append(name)
 
-        :returns Device: Device instance (*self*).
+    @property
+    def _sshconf_path(self):
+        return self._sshconf_lkup()
 
-        :raises ProbeError:
-            When **auto_probe** is ``True`` and the probe activity
-            exceeds the timeout
+    # ------------------------------------------------------------------------
+    # probe
+    # ------------------------------------------------------------------------
 
-        :raises ConnectAuthError:
-            When provided authentication credentials fail to login
+    def probe(self, timeout=5, intvtimeout=1):
+        """
+        Probe the device to determine if the Device can accept a remote
+        connection.
+        This method is meant to be called *prior* to :open():
+        This method will not work with ssh-jumphost environments.
 
-        :raises ConnectRefusedError:
-            When the device does not have NETCONF enabled
+        :param int timeout:
+          The probe will report ``True``/``False`` if the device report
+          connectivity within this timeout (seconds)
 
-        :raises ConnectTimeoutError:
-            When the the :meth:`Device.timeout` value is exceeded
-            during the attempt to connect to the remote device
+        :param int intvtimeout:
+          Timeout interval on the socket connection. Generally you should not
+          change this value, but you can if you want to twiddle the frequency
+          of the socket attempts on the connection
 
-        :raises ConnectError:
-            When an error, other than the above, occurs.  The
-            originating ``Exception`` is assigned as ``err._orig``
-            and re-raised to the caller.
+        :returns: ``True`` if probe is successful, ``False`` otherwise
         """
+        start = datetime.datetime.now()
+        end = start + datetime.timedelta(seconds=timeout)
+        probe_ok = True
 
-        auto_probe = kvargs.get('auto_probe', self._auto_probe)
-        if auto_probe is not 0:
-            if not self.probe(auto_probe):
-                raise EzErrors.ProbeError(self)
+        while datetime.datetime.now() < end:
+            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            s.settimeout(intvtimeout)
+            try:
+                s.connect((self.hostname, int(self._port)))
+                s.shutdown(socket.SHUT_RDWR)
+                s.close()
+                break
+            except:
+                time.sleep(1)
+                pass
+        else:
+            elapsed = datetime.datetime.now() - start
+            probe_ok = False
 
-        try:
-            ts_start = datetime.datetime.now()
+        return probe_ok
 
-            # we want to enable the ssh-agent if-and-only-if we are
-            # not given a password or an ssh key file.
-            # in this condition it means we want to query the agent
-            # for available ssh keys
+    # ------------------------------------------------------------------------
+    # cli - for cheating commands :-)
+    # ------------------------------------------------------------------------
 
-            allow_agent = bool((self._auth_password is None) and
-                               (self._ssh_private_key_file is None))
+    def cli(self, command, format='text', warning=True):
+        """
+        Executes the CLI command and returns the CLI text output by default.
 
-            # open connection using ncclient transport
-            self._conn = netconf_ssh.connect(
-                host=self._hostname,
-                port=self._port,
-                username=self._auth_user,
-                password=self._auth_password,
-                hostkey_verify=False,
-                key_filename=self._ssh_private_key_file,
-                allow_agent=allow_agent,
-                ssh_config=self._sshconf_lkup(),
-                device_params={'name': 'junos'})
+        :param str command:
+          The CLI command to execute, e.g. "show version"
 
-        except NcErrors.AuthenticationError as err:
-            # bad authentication credentials
-            raise EzErrors.ConnectAuthError(self)
+        :param str format:
+          The return format, by default is text.  You can optionally select
+          "xml" to return the XML structure.
 
-        except NcErrors.SSHError as err:
-            # this is a bit of a hack for now, since we want to
-            # know if the connection was refused or we simply could
-            # not open a connection due to reachability.  so using
-            # a timestamp to differentiate the two conditions for now
-            # if the diff is < 3 sec, then assume the host is
-            # reachable, but NETCONF connection is refushed.
+        .. note::
+            You can also use this method to obtain the XML RPC command for a
+            given CLI command by using the pipe filter ``| display xml rpc``.
+            When you do this, the return value is the XML RPC command. For 
+            example if you provide as the command ``show version | display xml rpc``,
+            you will get back the XML Element ``<get-software-information>``.
 
-            ts_err = datetime.datetime.now()
-            diff_ts = ts_err - ts_start
-            if diff_ts.seconds < 3:
-                raise EzErrors.ConnectRefusedError(self)
+        .. warning::
+            This function is provided for **DEBUG** purposes only!
+            **DO NOT** use this method for general automation purposes as
+            that puts you in the realm of "screen-scraping the CLI".  The purpose of
+            the PyEZ framework is to migrate away from that tooling pattern.
+            Interaction with the device should be done via the RPC function.
 
-            # at this point, we assume that the connection
-            # has timeed out due to ip-reachability issues
+        .. warning::
+            You cannot use "pipe" filters with **command** such as ``| match``
+            or ``| count``, etc.  The only value use of the "pipe" is for the
+            ``| display xml rpc`` as noted above.
+        """
+        if 'display xml rpc' not in command and warning is True:
+            warnings.simplefilter("always")
+            warnings.warn("CLI command is for debug use only!", RuntimeWarning)
+            warnings.resetwarnings()
 
-            if str(err).find('not open') > 0:
-                raise EzErrors.ConnectTimeoutError(self)
+        try:
+            rsp = self.rpc.cli(command, format)
+            if isinstance(rsp, dict) and format.lower() == 'json':
+                return rsp
+            # rsp returned True means <rpc-reply> is empty, hence return
+            # empty str as would be the case on cli
+            # ex:
+            # <rpc-reply message-id="urn:uuid:281f624f-022b-11e6-bfa8">
+            # </rpc-reply>
+            if rsp is True:
+                return ''
+            if rsp.tag in ['output', 'rpc-reply']:
+                encode = None if sys.version < '3' else 'unicode'
+                return etree.tostring(rsp, method="text", with_tail=False,
+                                      encoding=encode)
+            if rsp.tag == 'configuration-information':
+                return rsp.findtext('configuration-output')
+            if rsp.tag == 'rpc':
+                return rsp[0]
+            return rsp
+        except EzErrors.RpcError as ex:
+            if str(ex) is not '':
+                return "%s: %s" % (str(ex), command)
             else:
-                # otherwise raise a generic connection
-                # error for now.  tag the new exception
-                # with the original for debug
-                cnx = EzErrors.ConnectError(self)
-                cnx._orig = err
-                raise cnx
-
-        except socket.gaierror:
-            # invalid DNS name, so unreachable
-            raise EzErrors.ConnectUnknownHostError(self)
-
-        except Exception as err:
-            # anything else, we will re-raise as a
-            # generic ConnectError
-            cnx_err = EzErrors.ConnectError(self)
-            cnx_err._orig = err
-            raise cnx_err
-
-        self.connected = True
-
-        self._nc_transform = self.transform
-        self._norm_transform = lambda: JXML.normalize_xslt
-
-        normalize = kvargs.get('normalize', self._normalize)
-        if normalize is True:
-            self.transform = self._norm_transform
-
-        gather_facts = kvargs.get('gather_facts', self._gather_facts)
-        if gather_facts is True:
-            self.facts_refresh()
-
-        return self
+                return "invalid command: " + command
+        except Exception as ex:
+            return "invalid command: " + command
 
-    def close(self):
-        """
-        Closes the connection to the device.
-        """
-        self._conn.close_session()
-        self.connected = False
+    # ------------------------------------------------------------------------
+    # execute
+    # ------------------------------------------------------------------------
 
     @normalizeDecorator
     @timeoutDecorator
@@ -515,7 +472,7 @@ class Device(object):
           the command starts with the specific command element, i.e., not the
           <rpc> element itself
 
-        :param func to_py':
+        :param func to_py:
           Is a caller provided function that takes the response and
           will convert the results to native python types.  all kvargs
           will be passed to this function as well in the form::
@@ -556,7 +513,7 @@ class Device(object):
         # @@@ need to trap this and re-raise accordingly.
 
         try:
-            rpc_rsp_e = self._conn.rpc(rpc_cmd_e)._NCElement__doc
+            rpc_rsp_e = self._rpc_reply(rpc_cmd_e)
         except NcOpErrors.TimeoutExpiredError:
             # err is a TimeoutExpiredError from ncclient,
             # which has no such attribute as xml.
@@ -566,11 +523,14 @@ class Device(object):
         except RPCError as err:
             rsp = JXML.remove_namespaces(err.xml)
             # see if this is a permission error
-            e = EzErrors.PermissionError if rsp.findtext('error-message') == 'permission denied' else EzErrors.RpcError
+            e = EzErrors.PermissionError if rsp.findtext('error-message') == \
+                'permission denied' \
+                else EzErrors.RpcError
             raise e(cmd=rpc_cmd_e, rsp=rsp, errs=err)
         # Something unexpected happened - raise it up
         except Exception as err:
-            warnings.warn("An unknown exception occured - please report.", RuntimeWarning)
+            warnings.warn("An unknown exception occured - please report.",
... 3113 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/junos-eznc.git



More information about the Python-modules-commits mailing list