[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