netmiko-2.4.2/0000775000127700012770000000000013534772673014406 5ustar gitusergituser00000000000000netmiko-2.4.2/MANIFEST.in0000664000127700012770000000006713265436437016143 0ustar gitusergituser00000000000000include *.md include requirements.txt include LICENSE netmiko-2.4.2/PLATFORMS.md0000664000127700012770000000206313511742056016263 0ustar gitusergituser00000000000000# Supported Platforms ###### Regularly tested - Arista vEOS - Cisco ASA - Cisco IOS - Cisco IOS-XE - Cisco IOS-XR - Cisco NX-OS - Cisco SG300 - HP ProCurve - Juniper Junos - Linux ###### Limited testing - Alcatel AOS6/AOS8 - Apresia Systems AEOS - Calix B6 - Cisco AireOS (Wireless LAN Controllers) - CloudGenix ION - Dell OS9 (Force10) - Dell OS10 - Dell PowerConnect - Extreme ERS (Avaya) - Extreme VSP (Avaya) - Extreme VDX (Brocade) - Extreme MLX/NetIron (Brocade/Foundry) - HPE Comware7 - Huawei - IP Infusion OcNOS - Mellanox - MikroTik RouterOS - MikroTik SwitchOS - NetApp cDOT - OneAccess - Palo Alto PAN-OS - Pluribus - Ruckus ICX/FastIron - Ubiquiti EdgeSwitch - Vyatta VyOS ###### Experimental - A10 - Accedian - Aruba - Ciena SAOS - Citrix Netscaler - Cisco Telepresence - Check Point GAiA - Coriant - Dell OS6 - Dell EMC Isilon - Eltex - Enterasys - Endace - Extreme EXOS - Extreme Wing - Extreme SLX (Brocade) - F5 TMSH - F5 Linux - Fortinet - MRV Communications OptiSwitch - MRV LX - Nokia/Alcatel SR-OS - QuantaMesh - Rad ETX - Versa Networks FlexVNF netmiko-2.4.2/setup.cfg0000664000127700012770000000041513534772673016227 0ustar gitusergituser00000000000000[bdist_wheel] universal = 1 [metadata] license_file = LICENSE [pylama] linters = mccabe,pep8,pyflakes ignore = D203,C901 skip = tests/*,build/*,.tox/*,netmiko/_textfsm/*,examples/use_cases/* [pylama:pep8] max_line_length = 100 [egg_info] tag_build = tag_date = 0 netmiko-2.4.2/TESTING.md0000664000127700012770000000174513265436437016050 0ustar gitusergituser00000000000000# Testing This document covers the test suite for Netmiko. ## The simple version ``` cd ./netmiko/tests/etc cp test_devices.yml.example test_devices.yml cp responses.yml.example responses.yml cp commands.yml.example commands.yml ``` #### edit test_devices.yml Pick the device_types you want to test against; update: * ip * username * password * secret (optional) #### edit responses.yml For the device_types that you are testing against, update the following to match the test device(s): * the base_prompt * router_prompt * enable_prompt * interface_ip #### Execute the test ``` cd ./netmiko/tests ``` Note, the test_device is the name of the device from test_devices.yml and responses.yml: ``` py.test -v test_netmiko_show.py --test_device cisco881 py.test -v test_netmiko_config.py --test_device cisco881 ``` There are three tests available: * test_netmiko_show.py * test_netmiko_config.py * test_netmiko_commit.py # currently only for Juniper and IOS-XR netmiko-2.4.2/requirements.txt0000664000127700012770000000006213342300534017644 0ustar gitusergituser00000000000000--index-url https://pypi.python.org/simple/ -e . netmiko-2.4.2/VENDOR.md0000664000127700012770000000437413265436437015731 0ustar gitusergituser00000000000000Steps for adding a new vendor ======= Create a new vendor directory underneath netmiko/netmiko: ``` $ cd netmiko/netmiko $ mkdir arista ``` Make the directory a Python package: ``` $ cd arista $ touch __init__.py ``` Create a new module for the vendor: ``` $ vi arista_ssh.py from netmiko.cisco_base_connection import CiscoSSHConnection class AristaSSH(CiscoSSHConnection): pass ``` Inherit from the CiscoSSHConnection class. Note, the netmiko package will need to be in your PYTHONPATH Update \_\_init__.py to export the new class: ``` $ vi __init__.py from netmiko.arista.arista_ssh import AristaSSH __all__ = ['AristaSSH'] ``` Update the dispatcher adding the new class: ``` $ cd netmiko/netmiko $ vi ssh_dispatcher.py from netmiko.cisco import CiscoIosSSH from netmiko.cisco import CiscoAsaSSH from netmiko.arista import AristaSSH # Add here CLASS_MAPPER = { 'cisco_ios' : CiscoIosSSH, 'cisco_asa' : CiscoAsaSSH, 'arista_eos' : AristaSSH, # Add Here (device_type to class mapping) } ``` Your class will need to support the following methods: ``` def __init__(self, ip, username, password, secret='', port=22, device_type='', verbose=True): def establish_connection(self, sleep_time=3, verbose=True): def disable_paging(self): def find_prompt(self): def clear_buffer(self): def send_command(self, command_string, delay_factor=1, max_loops=30): def strip_prompt(self, a_string): def strip_command(self, command_string, output): def normalize_linefeeds(self, a_string): def enable(self): def config_mode(self): def exit_config_mode(self): def send_config_set(self, config_commands=None): def cleanup(self): def disconnect(self): ``` As much as possible, you should re-use the inherited methods from CiscoSSHConnection and BaseConnection (i.e. only re-write what you have to). BaseConnection is intended to be generic (i.e. irrespective of the vendor) CiscoSSHConnection is Cisco-IOS specific (because lots of vendors imitate Cisco IOS). You should also write unit tests and verify the functionality of your new class. See netmiko/tests/test_cisco_ios.py and netmiko/tests/test_cisco_ios_enable.py. Note, you will also need to update the 'packages' section of 'setup.py' file if you are adding a completely new package. netmiko-2.4.2/setup.py0000664000127700012770000000364113516415527016114 0ustar gitusergituser00000000000000from setuptools import setup from setuptools import find_packages import os import re with open("README.md", "r") as fs: long_description = fs.read() def find_version(*file_paths): """ This pattern was modeled on a method from the Python Packaging User Guide: https://packaging.python.org/en/latest/single_source_version.html We read instead of importing so we don't get import errors if our code imports from dependencies listed in install_requires. """ base_module_file = os.path.join(*file_paths) with open(base_module_file) as f: base_module_data = f.read() version_match = re.search( r"^__version__ = ['\"]([^'\"]*)['\"]", base_module_data, re.M ) if version_match: return version_match.group(1) raise RuntimeError("Unable to find version string.") setup( name="netmiko", version=find_version("netmiko", "__init__.py"), description="Multi-vendor library to simplify Paramiko SSH connections to network devices", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/ktbyers/netmiko", author="Kirk Byers", author_email="ktbyers@twb-tech.com", license="MIT", classifiers=[ "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", ], packages=find_packages(exclude=("test*",)), install_requires=[ "setuptools>=38.4.0", "paramiko>=2.4.3", "scp>=0.13.2", "pyserial", "textfsm", 'enum34; python_version == "2.7"', 'ipaddress; python_version == "2.7"', ], extras_require={"test": ["pyyaml==5.1", "pytest>=4.6.3"]}, ) netmiko-2.4.2/LICENSE0000664000127700012770000000206613265436437015413 0ustar gitusergituser00000000000000The MIT License (MIT) Copyright (c) 2016 Kirk Byers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. netmiko-2.4.2/netmiko.egg-info/0000775000127700012770000000000013534772673017546 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko.egg-info/dependency_links.txt0000664000127700012770000000000113534772673023614 0ustar gitusergituser00000000000000 netmiko-2.4.2/netmiko.egg-info/requires.txt0000664000127700012770000000021713534772673022146 0ustar gitusergituser00000000000000setuptools>=38.4.0 paramiko>=2.4.3 scp>=0.13.2 pyserial textfsm [:python_version == "2.7"] enum34 ipaddress [test] pyyaml==5.1 pytest>=4.6.3 netmiko-2.4.2/netmiko.egg-info/top_level.txt0000664000127700012770000000001013534772673022267 0ustar gitusergituser00000000000000netmiko netmiko-2.4.2/netmiko.egg-info/SOURCES.txt0000664000127700012770000000724713534772673021444 0ustar gitusergituser00000000000000COMMON_ISSUES.md LICENSE MANIFEST.in PLATFORMS.md README.md TESTING.md VENDOR.md requirements.txt setup.cfg setup.py netmiko/__init__.py netmiko/base_connection.py netmiko/cisco_base_connection.py netmiko/netmiko_globals.py netmiko/py23_compat.py netmiko/scp_functions.py netmiko/scp_handler.py netmiko/snmp_autodetect.py netmiko/ssh_autodetect.py netmiko/ssh_dispatcher.py netmiko/ssh_exception.py netmiko/utilities.py netmiko.egg-info/PKG-INFO netmiko.egg-info/SOURCES.txt netmiko.egg-info/dependency_links.txt netmiko.egg-info/requires.txt netmiko.egg-info/top_level.txt netmiko/_textfsm/__init__.py netmiko/_textfsm/_clitable.py netmiko/_textfsm/_terminal.py netmiko/_textfsm/_texttable.py netmiko/a10/__init__.py netmiko/a10/a10_ssh.py netmiko/accedian/__init__.py netmiko/accedian/accedian_ssh.py netmiko/alcatel/__init__.py netmiko/alcatel/alcatel_aos_ssh.py netmiko/alcatel/alcatel_sros_ssh.py netmiko/apresia/__init__.py netmiko/apresia/apresia_aeos.py netmiko/arista/__init__.py netmiko/arista/arista.py netmiko/aruba/__init__.py netmiko/aruba/aruba_ssh.py netmiko/calix/__init__.py netmiko/calix/calix_b6.py netmiko/checkpoint/__init__.py netmiko/checkpoint/checkpoint_gaia_ssh.py netmiko/ciena/__init__.py netmiko/ciena/ciena_saos_ssh.py netmiko/cisco/__init__.py netmiko/cisco/cisco_asa_ssh.py netmiko/cisco/cisco_ios.py netmiko/cisco/cisco_nxos_ssh.py netmiko/cisco/cisco_s300.py netmiko/cisco/cisco_tp_tcce.py netmiko/cisco/cisco_wlc_ssh.py netmiko/cisco/cisco_xr.py netmiko/citrix/__init__.py netmiko/citrix/netscaler_ssh.py netmiko/cloudgenix/__init__.py netmiko/cloudgenix/cloudgenix_ion.py netmiko/coriant/__init__.py netmiko/coriant/coriant_ssh.py netmiko/dell/__init__.py netmiko/dell/dell_dnos6.py netmiko/dell/dell_force10_ssh.py netmiko/dell/dell_isilon_ssh.py netmiko/dell/dell_os10_ssh.py netmiko/dell/dell_powerconnect.py netmiko/eltex/__init__.py netmiko/eltex/eltex_ssh.py netmiko/endace/__init__.py netmiko/endace/endace_ssh.py netmiko/enterasys/__init__.py netmiko/enterasys/enterasys_ssh.py netmiko/extreme/__init__.py netmiko/extreme/extreme_ers_ssh.py netmiko/extreme/extreme_exos.py netmiko/extreme/extreme_netiron.py netmiko/extreme/extreme_nos_ssh.py netmiko/extreme/extreme_slx_ssh.py netmiko/extreme/extreme_vsp_ssh.py netmiko/extreme/extreme_wing_ssh.py netmiko/f5/__init__.py netmiko/f5/f5_linux_ssh.py netmiko/f5/f5_tmsh_ssh.py netmiko/flexvnf/__init__.py netmiko/flexvnf/flexvnf_ssh.py netmiko/fortinet/__init__.py netmiko/fortinet/fortinet_ssh.py netmiko/hp/__init__.py netmiko/hp/hp_comware.py netmiko/hp/hp_procurve.py netmiko/huawei/__init__.py netmiko/huawei/huawei.py netmiko/ipinfusion/__init__.py netmiko/ipinfusion/ipinfusion_ocnos.py netmiko/juniper/__init__.py netmiko/juniper/juniper.py netmiko/keymile/__init__.py netmiko/keymile/keymile_nos_ssh.py netmiko/keymile/keymile_ssh.py netmiko/linux/__init__.py netmiko/linux/linux_ssh.py netmiko/mellanox/__init__.py netmiko/mellanox/mellanox_mlnxos_ssh.py netmiko/mikrotik/__init__.py netmiko/mikrotik/mikrotik_ssh.py netmiko/mrv/__init__.py netmiko/mrv/mrv_lx.py netmiko/mrv/mrv_ssh.py netmiko/netapp/__init__.py netmiko/netapp/netapp_cdot_ssh.py netmiko/oneaccess/__init__.py netmiko/oneaccess/oneaccess_oneos.py netmiko/ovs/__init__.py netmiko/ovs/ovs_linux_ssh.py netmiko/paloalto/__init__.py netmiko/paloalto/paloalto_panos.py netmiko/pluribus/__init__.py netmiko/pluribus/pluribus_ssh.py netmiko/quanta/__init__.py netmiko/quanta/quanta_mesh_ssh.py netmiko/rad/__init__.py netmiko/rad/rad_etx.py netmiko/ruckus/__init__.py netmiko/ruckus/ruckus_fastiron.py netmiko/terminal_server/__init__.py netmiko/terminal_server/terminal_server.py netmiko/ubiquiti/__init__.py netmiko/ubiquiti/edge_ssh.py netmiko/vyos/__init__.py netmiko/vyos/vyos_ssh.pynetmiko-2.4.2/netmiko.egg-info/PKG-INFO0000664000127700012770000002526213534772673020652 0ustar gitusergituser00000000000000Metadata-Version: 2.1 Name: netmiko Version: 2.4.2 Summary: Multi-vendor library to simplify Paramiko SSH connections to network devices Home-page: https://github.com/ktbyers/netmiko Author: Kirk Byers Author-email: ktbyers@twb-tech.com License: MIT Description: ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/netmiko.svg) [![PyPI](https://img.shields.io/pypi/v/netmiko.svg)](https://pypi.python.org/pypi/netmiko) [![Downloads](https://pepy.tech/badge/netmiko)](https://pepy.tech/project/netmiko) ![GitHub contributors](https://img.shields.io/github/contributors/ktbyers/netmiko.svg) [![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) Netmiko ======= Multi-vendor library to simplify Paramiko SSH connections to network devices ## Quick Links - [Supported Platforms](https://ktbyers.github.io/netmiko/#supported-platforms) - [Installation](https://ktbyers.github.io/netmiko/#installation) - [Tutorials/Examples/Getting Started](https://ktbyers.github.io/netmiko/#tutorialsexamplesgetting-started) - [Common Issues/FAQ](https://ktbyers.github.io/netmiko/#common-issuesfaq) - [API-Documentation](https://ktbyers.github.io/netmiko/#api-documentation) - [TextFSM Integration](https://ktbyers.github.io/netmiko/#textfsm-integration) - [Contributing](https://ktbyers.github.io/netmiko/#contributing) - [Questions/Discussion](https://ktbyers.github.io/netmiko/#questionsdiscussion) ## Supported Platforms Netmiko supports a wide range of devices. These devices fall into three categories: - Regularly Tested - Limited Testing - Experimental Regularly tested means we try to run our full test suite against that set of devices prior to each Netmiko release. Limited testing means the config and show operation system tests passed against a test on that platform at one point in time so we are reasonably comfortable the driver should generally work. Experimental means that we reviewed the PR and the driver seems reasonable, but we don't have good data on whether the driver fully passes the unit tests or how reliably it works. Click [PLATFORMS](PLATFORMS.md) for a list of all supported platforms. ## Installation To install netmiko, simply us pip: ``` $ pip install netmiko ``` Netmiko has the following requirements (which pip will install for you) - Paramiko >= 2.4.3 - scp >= 0.13.2 - pyserial - textfsm ## Tutorials/Examples/Getting Started ### Tutorials: - [Getting Started](https://pynet.twb-tech.com/blog/automation/netmiko.html) - [Secure Copy](https://pynet.twb-tech.com/blog/automation/netmiko-scp.html) - [Netmiko through SSH Proxy](https://pynet.twb-tech.com/blog/automation/netmiko-proxy.html) - [Netmiko and TextFSM](https://pynet.twb-tech.com/blog/automation/netmiko-textfsm.html) - [Netmiko and what constitutes done](https://pynet.twb-tech.com/blog/automation/netmiko-what-is-done.html) ### Example Scripts: See the following directory for [example scripts](https://github.com/ktbyers/netmiko/tree/develop/examples/use_cases), including examples of: - [Simple Connection](https://github.com/ktbyers/netmiko/blob/develop/examples/use_cases/case1_simple_conn/simple_conn.py) - [Sending Show Commands](https://github.com/ktbyers/netmiko/tree/develop/examples/use_cases/case4_show_commands) - [Sending Configuration Commands](https://github.com/ktbyers/netmiko/tree/develop/examples/use_cases/case6_config_change) - [Handling Additional Prompting](https://github.com/ktbyers/netmiko/blob/develop/examples/use_cases/case5_prompting/send_command_prompting.py) - [Connecting with SSH Keys](https://github.com/ktbyers/netmiko/blob/develop/examples/use_cases/case9_ssh_keys/conn_ssh_keys.py) - [Cisco Genie Integration](https://github.com/ktbyers/netmiko/blob/develop/examples/use_cases/case18_structured_data_genie) ### Getting Started: #### Create a dictionary representing the device. Supported device_types can be found in [ssh_dispatcher.py](https://github.com/ktbyers/netmiko/blob/master/netmiko/ssh_dispatcher.py), see CLASS_MAPPER keys. ```py from netmiko import ConnectHandler cisco_881 = { 'device_type': 'cisco_ios', 'host': '10.10.10.10', 'username': 'test', 'password': 'password', 'port' : 8022, # optional, defaults to 22 'secret': 'secret', # optional, defaults to '' } ``` #### Establish an SSH connection to the device by passing in the device dictionary. ```py net_connect = ConnectHandler(**cisco_881) ``` #### Execute show commands. ```py output = net_connect.send_command('show ip int brief') print(output) ``` ``` Interface IP-Address OK? Method Status Protocol FastEthernet0 unassigned YES unset down down FastEthernet1 unassigned YES unset down down FastEthernet2 unassigned YES unset down down FastEthernet3 unassigned YES unset down down FastEthernet4 10.10.10.10 YES manual up up Vlan1 unassigned YES unset down down ``` #### Execute configuration change commands (will automatically enter into config mode) ```py config_commands = [ 'logging buffered 20000', 'logging buffered 20010', 'no logging console' ] output = net_connect.send_config_set(config_commands) print(output) ``` ``` pynet-rtr1#config term Enter configuration commands, one per line. End with CNTL/Z. pynet-rtr1(config)#logging buffered 20000 pynet-rtr1(config)#logging buffered 20010 pynet-rtr1(config)#no logging console pynet-rtr1(config)#end pynet-rtr1# ``` ## Common Issues/FAQ Answers to some [common questions](COMMON_ISSUES.md) Topics covered in above document: - Handling commands that prompt for additional input - Enabling logging of all reads/writes of the communication channel - Redispatch -- or connecting through a terminal server ## API-Documentation API Documentation Below are some of the particularly handy Classes/functions for easy reference: - [Base Connection Object](https://ktbyers.github.io/netmiko/docs/netmiko/base_connection.html) - [SSH Autodetect](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.SSHDetect) - [SSH Dispatcher](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.ssh_dispatcher) - [Redispatch](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.redispatch) ## TextFSM Integration Netmiko has been configured to automatically look in `~/ntc-template/templates/index` for the ntc-templates index file. Alternatively, you can explicitly tell Netmiko where to look for the TextFSM template directory by setting the `NET_TEXTFSM` environment variable (note, there must be an index file in this directory): ``` export NET_TEXTFSM=/path/to/ntc-templates/templates/ ``` [More info on TextFSM and Netmiko](https://pynet.twb-tech.com/blog/automation/netmiko-textfsm.html). ## Contributing Contributors are always welcome! You can contribute to Netmiko in a variety of ways: spreading the word about Netmiko, answering questions on Slack (see below in Quests/Discussions), responding to issues, adding to the common issues, reporting/fixing bugs, or even adding your own device type. Before contributing a new vendor/platform device type, remember that any code added needs to be supported in some fashion (much more so for the "regularly tested" devices and the core of Netmiko)! To add a vendor/platform you can follow the outline [here](VENDOR.md). Once you've worked on your first pass of your driver and have it functional, you'll need to include test data in order for it to be merged into develop, you can see the general flow of how to do that [here](TESTING.md). For all code contributions, please ensure that you have ran `black` against the code or your code will fail the Travis CI build. ## Questions/Discussion If you find an issue with Netmiko, then you can open an issue on this projects issue page here: [https://github.com/ktbyers/netmiko/issues](https://github.com/ktbyers/netmiko/issues). Please make sure you've read through the common issues and examples prior to opening an issue. Please only open issues for bugs, feature requests, or other topics related to development of Netmiko. If you simply have a question, join us on Slack... If you have questions or would like to discuss Netmiko, a #netmiko channel exists in [this Slack](https://pynet.slack.com) workspace. To join, use [this invitation](https://join.slack.com/t/pynet/shared_invite/enQtNTA2MDI3NjU0MTM0LTQ5MjExNGNlNWIzMmRhOTZmNmZkNDA2Nzk4Y2Q1Y2RkMWNhZGEzM2Y5MjI0NDYxODkzM2M0ODIwYzFkMzVmZGY). Once you have entered the workspace, then you can join the #netmiko channel. --- Kirk Byers Python for Network Engineers https://pynet.twb-tech.com Platform: UNKNOWN Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Description-Content-Type: text/markdown Provides-Extra: test netmiko-2.4.2/README.md0000664000127700012770000002101013516455041015642 0ustar gitusergituser00000000000000![PyPI - Python Version](https://img.shields.io/pypi/pyversions/netmiko.svg) [![PyPI](https://img.shields.io/pypi/v/netmiko.svg)](https://pypi.python.org/pypi/netmiko) [![Downloads](https://pepy.tech/badge/netmiko)](https://pepy.tech/project/netmiko) ![GitHub contributors](https://img.shields.io/github/contributors/ktbyers/netmiko.svg) [![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) Netmiko ======= Multi-vendor library to simplify Paramiko SSH connections to network devices ## Quick Links - [Supported Platforms](https://ktbyers.github.io/netmiko/#supported-platforms) - [Installation](https://ktbyers.github.io/netmiko/#installation) - [Tutorials/Examples/Getting Started](https://ktbyers.github.io/netmiko/#tutorialsexamplesgetting-started) - [Common Issues/FAQ](https://ktbyers.github.io/netmiko/#common-issuesfaq) - [API-Documentation](https://ktbyers.github.io/netmiko/#api-documentation) - [TextFSM Integration](https://ktbyers.github.io/netmiko/#textfsm-integration) - [Contributing](https://ktbyers.github.io/netmiko/#contributing) - [Questions/Discussion](https://ktbyers.github.io/netmiko/#questionsdiscussion) ## Supported Platforms Netmiko supports a wide range of devices. These devices fall into three categories: - Regularly Tested - Limited Testing - Experimental Regularly tested means we try to run our full test suite against that set of devices prior to each Netmiko release. Limited testing means the config and show operation system tests passed against a test on that platform at one point in time so we are reasonably comfortable the driver should generally work. Experimental means that we reviewed the PR and the driver seems reasonable, but we don't have good data on whether the driver fully passes the unit tests or how reliably it works. Click [PLATFORMS](PLATFORMS.md) for a list of all supported platforms. ## Installation To install netmiko, simply us pip: ``` $ pip install netmiko ``` Netmiko has the following requirements (which pip will install for you) - Paramiko >= 2.4.3 - scp >= 0.13.2 - pyserial - textfsm ## Tutorials/Examples/Getting Started ### Tutorials: - [Getting Started](https://pynet.twb-tech.com/blog/automation/netmiko.html) - [Secure Copy](https://pynet.twb-tech.com/blog/automation/netmiko-scp.html) - [Netmiko through SSH Proxy](https://pynet.twb-tech.com/blog/automation/netmiko-proxy.html) - [Netmiko and TextFSM](https://pynet.twb-tech.com/blog/automation/netmiko-textfsm.html) - [Netmiko and what constitutes done](https://pynet.twb-tech.com/blog/automation/netmiko-what-is-done.html) ### Example Scripts: See the following directory for [example scripts](https://github.com/ktbyers/netmiko/tree/develop/examples/use_cases), including examples of: - [Simple Connection](https://github.com/ktbyers/netmiko/blob/develop/examples/use_cases/case1_simple_conn/simple_conn.py) - [Sending Show Commands](https://github.com/ktbyers/netmiko/tree/develop/examples/use_cases/case4_show_commands) - [Sending Configuration Commands](https://github.com/ktbyers/netmiko/tree/develop/examples/use_cases/case6_config_change) - [Handling Additional Prompting](https://github.com/ktbyers/netmiko/blob/develop/examples/use_cases/case5_prompting/send_command_prompting.py) - [Connecting with SSH Keys](https://github.com/ktbyers/netmiko/blob/develop/examples/use_cases/case9_ssh_keys/conn_ssh_keys.py) - [Cisco Genie Integration](https://github.com/ktbyers/netmiko/blob/develop/examples/use_cases/case18_structured_data_genie) ### Getting Started: #### Create a dictionary representing the device. Supported device_types can be found in [ssh_dispatcher.py](https://github.com/ktbyers/netmiko/blob/master/netmiko/ssh_dispatcher.py), see CLASS_MAPPER keys. ```py from netmiko import ConnectHandler cisco_881 = { 'device_type': 'cisco_ios', 'host': '10.10.10.10', 'username': 'test', 'password': 'password', 'port' : 8022, # optional, defaults to 22 'secret': 'secret', # optional, defaults to '' } ``` #### Establish an SSH connection to the device by passing in the device dictionary. ```py net_connect = ConnectHandler(**cisco_881) ``` #### Execute show commands. ```py output = net_connect.send_command('show ip int brief') print(output) ``` ``` Interface IP-Address OK? Method Status Protocol FastEthernet0 unassigned YES unset down down FastEthernet1 unassigned YES unset down down FastEthernet2 unassigned YES unset down down FastEthernet3 unassigned YES unset down down FastEthernet4 10.10.10.10 YES manual up up Vlan1 unassigned YES unset down down ``` #### Execute configuration change commands (will automatically enter into config mode) ```py config_commands = [ 'logging buffered 20000', 'logging buffered 20010', 'no logging console' ] output = net_connect.send_config_set(config_commands) print(output) ``` ``` pynet-rtr1#config term Enter configuration commands, one per line. End with CNTL/Z. pynet-rtr1(config)#logging buffered 20000 pynet-rtr1(config)#logging buffered 20010 pynet-rtr1(config)#no logging console pynet-rtr1(config)#end pynet-rtr1# ``` ## Common Issues/FAQ Answers to some [common questions](COMMON_ISSUES.md) Topics covered in above document: - Handling commands that prompt for additional input - Enabling logging of all reads/writes of the communication channel - Redispatch -- or connecting through a terminal server ## API-Documentation API Documentation Below are some of the particularly handy Classes/functions for easy reference: - [Base Connection Object](https://ktbyers.github.io/netmiko/docs/netmiko/base_connection.html) - [SSH Autodetect](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.SSHDetect) - [SSH Dispatcher](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.ssh_dispatcher) - [Redispatch](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.redispatch) ## TextFSM Integration Netmiko has been configured to automatically look in `~/ntc-template/templates/index` for the ntc-templates index file. Alternatively, you can explicitly tell Netmiko where to look for the TextFSM template directory by setting the `NET_TEXTFSM` environment variable (note, there must be an index file in this directory): ``` export NET_TEXTFSM=/path/to/ntc-templates/templates/ ``` [More info on TextFSM and Netmiko](https://pynet.twb-tech.com/blog/automation/netmiko-textfsm.html). ## Contributing Contributors are always welcome! You can contribute to Netmiko in a variety of ways: spreading the word about Netmiko, answering questions on Slack (see below in Quests/Discussions), responding to issues, adding to the common issues, reporting/fixing bugs, or even adding your own device type. Before contributing a new vendor/platform device type, remember that any code added needs to be supported in some fashion (much more so for the "regularly tested" devices and the core of Netmiko)! To add a vendor/platform you can follow the outline [here](VENDOR.md). Once you've worked on your first pass of your driver and have it functional, you'll need to include test data in order for it to be merged into develop, you can see the general flow of how to do that [here](TESTING.md). For all code contributions, please ensure that you have ran `black` against the code or your code will fail the Travis CI build. ## Questions/Discussion If you find an issue with Netmiko, then you can open an issue on this projects issue page here: [https://github.com/ktbyers/netmiko/issues](https://github.com/ktbyers/netmiko/issues). Please make sure you've read through the common issues and examples prior to opening an issue. Please only open issues for bugs, feature requests, or other topics related to development of Netmiko. If you simply have a question, join us on Slack... If you have questions or would like to discuss Netmiko, a #netmiko channel exists in [this Slack](https://pynet.slack.com) workspace. To join, use [this invitation](https://join.slack.com/t/pynet/shared_invite/enQtNTA2MDI3NjU0MTM0LTQ5MjExNGNlNWIzMmRhOTZmNmZkNDA2Nzk4Y2Q1Y2RkMWNhZGEzM2Y5MjI0NDYxODkzM2M0ODIwYzFkMzVmZGY). Once you have entered the workspace, then you can join the #netmiko channel. --- Kirk Byers Python for Network Engineers https://pynet.twb-tech.com netmiko-2.4.2/COMMON_ISSUES.md0000664000127700012770000000561513265436437017016 0ustar gitusergituser00000000000000### Show commands that prompt for more information In these cases, I generally use the send_command_timing() method (which doesn't look for prompts) For example, a CLI interaction that looks like this: ``` pynet-rtr1#copy running-config flash:test1.txt Destination filename [test1.txt]? 5587 bytes copied in 1.316 secs (4245 bytes/sec) pynet-rtr1# ``` ```python cmd1 = "copy running-config flash:test1.txt output = net_connect.send_command_timing(cmd1) if 'Destination filename' in output: output += net_connect.send_command_timing("\n") print(output) ``` ### Enable Netmiko logging of all reads and writes of the communications channel This will create a file named 'test.log' in the current working directory. ```python import logging logging.basicConfig(filename='test.log', level=logging.DEBUG) logger = logging.getLogger("netmiko") ``` ### Does Netmiko support connecting via a terminal server There is a 'terminal_server' device_type that basically does nothing post SSH connect. This means you have to manually handle the interaction with the terminal server to connect to the end device. After you are fully connected to the end network device, you can then 'redispatch' and Netmiko will behave normally ```python from __future__ import unicode_literals, print_function import time from netmiko import ConnectHandler, redispatch net_connect = ConnectHandler( device_type='terminal_server', # Notice 'terminal_server' here ip='10.10.10.10', username='admin', password='admin123', secret='secret123') # Manually handle interaction in the Terminal Server # (fictional example, but hopefully you see the pattern) # Send Enter a Couple of Times net_connect.write_channel("\r\n") time.sleep(1) net_connect.write_channel("\r\n") time.sleep(1) output = net_connect.read_channel() print(output) # Should hopefully see the terminal server prompt # Login to end device from terminal server net_connect.write_channel("connect 1\r\n") time.sleep(1) # Manually handle the Username and Password max_loops = 10 i = 1 while i <= max_loops: output = net_connect.read_channel() if 'Username' in output: net_connect.write_channel(net_connect.username + '\r\n') time.sleep(1) output = net_connect.read_channel() # Search for password pattern / send password if 'Password' in output: net_connect.write_channel(net_connect.password + '\r\n') time.sleep(.5) output = net_connect.read_channel() # Did we successfully login if '>' in output or '#' in output: break net_connect.write_channel('\r\n') time.sleep(.5) i += 1 # We are now logged into the end device # Dynamically reset the class back to the proper Netmiko class redispatch(net_connect, device_type='cisco_ios') # Now just do your normal Netmiko operations new_output = net_connect.send_command("show ip int brief") ``` netmiko-2.4.2/PKG-INFO0000664000127700012770000002526213534772673015512 0ustar gitusergituser00000000000000Metadata-Version: 2.1 Name: netmiko Version: 2.4.2 Summary: Multi-vendor library to simplify Paramiko SSH connections to network devices Home-page: https://github.com/ktbyers/netmiko Author: Kirk Byers Author-email: ktbyers@twb-tech.com License: MIT Description: ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/netmiko.svg) [![PyPI](https://img.shields.io/pypi/v/netmiko.svg)](https://pypi.python.org/pypi/netmiko) [![Downloads](https://pepy.tech/badge/netmiko)](https://pepy.tech/project/netmiko) ![GitHub contributors](https://img.shields.io/github/contributors/ktbyers/netmiko.svg) [![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) Netmiko ======= Multi-vendor library to simplify Paramiko SSH connections to network devices ## Quick Links - [Supported Platforms](https://ktbyers.github.io/netmiko/#supported-platforms) - [Installation](https://ktbyers.github.io/netmiko/#installation) - [Tutorials/Examples/Getting Started](https://ktbyers.github.io/netmiko/#tutorialsexamplesgetting-started) - [Common Issues/FAQ](https://ktbyers.github.io/netmiko/#common-issuesfaq) - [API-Documentation](https://ktbyers.github.io/netmiko/#api-documentation) - [TextFSM Integration](https://ktbyers.github.io/netmiko/#textfsm-integration) - [Contributing](https://ktbyers.github.io/netmiko/#contributing) - [Questions/Discussion](https://ktbyers.github.io/netmiko/#questionsdiscussion) ## Supported Platforms Netmiko supports a wide range of devices. These devices fall into three categories: - Regularly Tested - Limited Testing - Experimental Regularly tested means we try to run our full test suite against that set of devices prior to each Netmiko release. Limited testing means the config and show operation system tests passed against a test on that platform at one point in time so we are reasonably comfortable the driver should generally work. Experimental means that we reviewed the PR and the driver seems reasonable, but we don't have good data on whether the driver fully passes the unit tests or how reliably it works. Click [PLATFORMS](PLATFORMS.md) for a list of all supported platforms. ## Installation To install netmiko, simply us pip: ``` $ pip install netmiko ``` Netmiko has the following requirements (which pip will install for you) - Paramiko >= 2.4.3 - scp >= 0.13.2 - pyserial - textfsm ## Tutorials/Examples/Getting Started ### Tutorials: - [Getting Started](https://pynet.twb-tech.com/blog/automation/netmiko.html) - [Secure Copy](https://pynet.twb-tech.com/blog/automation/netmiko-scp.html) - [Netmiko through SSH Proxy](https://pynet.twb-tech.com/blog/automation/netmiko-proxy.html) - [Netmiko and TextFSM](https://pynet.twb-tech.com/blog/automation/netmiko-textfsm.html) - [Netmiko and what constitutes done](https://pynet.twb-tech.com/blog/automation/netmiko-what-is-done.html) ### Example Scripts: See the following directory for [example scripts](https://github.com/ktbyers/netmiko/tree/develop/examples/use_cases), including examples of: - [Simple Connection](https://github.com/ktbyers/netmiko/blob/develop/examples/use_cases/case1_simple_conn/simple_conn.py) - [Sending Show Commands](https://github.com/ktbyers/netmiko/tree/develop/examples/use_cases/case4_show_commands) - [Sending Configuration Commands](https://github.com/ktbyers/netmiko/tree/develop/examples/use_cases/case6_config_change) - [Handling Additional Prompting](https://github.com/ktbyers/netmiko/blob/develop/examples/use_cases/case5_prompting/send_command_prompting.py) - [Connecting with SSH Keys](https://github.com/ktbyers/netmiko/blob/develop/examples/use_cases/case9_ssh_keys/conn_ssh_keys.py) - [Cisco Genie Integration](https://github.com/ktbyers/netmiko/blob/develop/examples/use_cases/case18_structured_data_genie) ### Getting Started: #### Create a dictionary representing the device. Supported device_types can be found in [ssh_dispatcher.py](https://github.com/ktbyers/netmiko/blob/master/netmiko/ssh_dispatcher.py), see CLASS_MAPPER keys. ```py from netmiko import ConnectHandler cisco_881 = { 'device_type': 'cisco_ios', 'host': '10.10.10.10', 'username': 'test', 'password': 'password', 'port' : 8022, # optional, defaults to 22 'secret': 'secret', # optional, defaults to '' } ``` #### Establish an SSH connection to the device by passing in the device dictionary. ```py net_connect = ConnectHandler(**cisco_881) ``` #### Execute show commands. ```py output = net_connect.send_command('show ip int brief') print(output) ``` ``` Interface IP-Address OK? Method Status Protocol FastEthernet0 unassigned YES unset down down FastEthernet1 unassigned YES unset down down FastEthernet2 unassigned YES unset down down FastEthernet3 unassigned YES unset down down FastEthernet4 10.10.10.10 YES manual up up Vlan1 unassigned YES unset down down ``` #### Execute configuration change commands (will automatically enter into config mode) ```py config_commands = [ 'logging buffered 20000', 'logging buffered 20010', 'no logging console' ] output = net_connect.send_config_set(config_commands) print(output) ``` ``` pynet-rtr1#config term Enter configuration commands, one per line. End with CNTL/Z. pynet-rtr1(config)#logging buffered 20000 pynet-rtr1(config)#logging buffered 20010 pynet-rtr1(config)#no logging console pynet-rtr1(config)#end pynet-rtr1# ``` ## Common Issues/FAQ Answers to some [common questions](COMMON_ISSUES.md) Topics covered in above document: - Handling commands that prompt for additional input - Enabling logging of all reads/writes of the communication channel - Redispatch -- or connecting through a terminal server ## API-Documentation API Documentation Below are some of the particularly handy Classes/functions for easy reference: - [Base Connection Object](https://ktbyers.github.io/netmiko/docs/netmiko/base_connection.html) - [SSH Autodetect](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.SSHDetect) - [SSH Dispatcher](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.ssh_dispatcher) - [Redispatch](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.redispatch) ## TextFSM Integration Netmiko has been configured to automatically look in `~/ntc-template/templates/index` for the ntc-templates index file. Alternatively, you can explicitly tell Netmiko where to look for the TextFSM template directory by setting the `NET_TEXTFSM` environment variable (note, there must be an index file in this directory): ``` export NET_TEXTFSM=/path/to/ntc-templates/templates/ ``` [More info on TextFSM and Netmiko](https://pynet.twb-tech.com/blog/automation/netmiko-textfsm.html). ## Contributing Contributors are always welcome! You can contribute to Netmiko in a variety of ways: spreading the word about Netmiko, answering questions on Slack (see below in Quests/Discussions), responding to issues, adding to the common issues, reporting/fixing bugs, or even adding your own device type. Before contributing a new vendor/platform device type, remember that any code added needs to be supported in some fashion (much more so for the "regularly tested" devices and the core of Netmiko)! To add a vendor/platform you can follow the outline [here](VENDOR.md). Once you've worked on your first pass of your driver and have it functional, you'll need to include test data in order for it to be merged into develop, you can see the general flow of how to do that [here](TESTING.md). For all code contributions, please ensure that you have ran `black` against the code or your code will fail the Travis CI build. ## Questions/Discussion If you find an issue with Netmiko, then you can open an issue on this projects issue page here: [https://github.com/ktbyers/netmiko/issues](https://github.com/ktbyers/netmiko/issues). Please make sure you've read through the common issues and examples prior to opening an issue. Please only open issues for bugs, feature requests, or other topics related to development of Netmiko. If you simply have a question, join us on Slack... If you have questions or would like to discuss Netmiko, a #netmiko channel exists in [this Slack](https://pynet.slack.com) workspace. To join, use [this invitation](https://join.slack.com/t/pynet/shared_invite/enQtNTA2MDI3NjU0MTM0LTQ5MjExNGNlNWIzMmRhOTZmNmZkNDA2Nzk4Y2Q1Y2RkMWNhZGEzM2Y5MjI0NDYxODkzM2M0ODIwYzFkMzVmZGY). Once you have entered the workspace, then you can join the #netmiko channel. --- Kirk Byers Python for Network Engineers https://pynet.twb-tech.com Platform: UNKNOWN Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Description-Content-Type: text/markdown Provides-Extra: test netmiko-2.4.2/netmiko/0000775000127700012770000000000013534772673016054 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/vyos/0000775000127700012770000000000013534772673017054 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/vyos/vyos_ssh.py0000664000127700012770000001036713510465674021304 0ustar gitusergituser00000000000000from __future__ import print_function from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class VyOSSSH(CiscoSSHConnection): """Implement methods for interacting with VyOS network devices.""" def session_preparation(self): """Prepare the session after the connection has been established.""" self._test_channel_read() self.set_base_prompt() self.disable_paging(command="set terminal length 0") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def check_enable_mode(self, *args, **kwargs): """No enable mode on VyOS.""" pass def enable(self, *args, **kwargs): """No enable mode on VyOS.""" pass def exit_enable_mode(self, *args, **kwargs): """No enable mode on VyOS.""" pass def check_config_mode(self, check_string="#"): """Checks if the device is in configuration mode""" return super(VyOSSSH, self).check_config_mode(check_string=check_string) def config_mode(self, config_command="configure", pattern=r"[edit]"): """Enter configuration mode.""" return super(VyOSSSH, self).config_mode( config_command=config_command, pattern=pattern ) def exit_config_mode(self, exit_config="exit", pattern=r"exit"): """Exit configuration mode""" output = "" if self.check_config_mode(): output = self.send_command_timing( exit_config, strip_prompt=False, strip_command=False ) if "Cannot exit: configuration modified" in output: output += self.send_command_timing( "exit discard", strip_prompt=False, strip_command=False ) if self.check_config_mode(): raise ValueError("Failed to exit configuration mode") return output def commit(self, comment="", delay_factor=0.1): """ Commit the candidate configuration. Commit the entered configuration. Raise an error and return the failure if the commit fails. default: command_string = commit comment: command_string = commit comment """ delay_factor = self.select_delay_factor(delay_factor) error_marker = ["Failed to generate committed config", "Commit failed"] command_string = "commit" if comment: command_string += ' comment "{}"'.format(comment) output = self.config_mode() output += self.send_command_expect( command_string, strip_prompt=False, strip_command=False, delay_factor=delay_factor, ) if any(x in output for x in error_marker): raise ValueError( "Commit failed with following errors:\n\n{}".format(output) ) return output def set_base_prompt( self, pri_prompt_terminator="$", alt_prompt_terminator="#", delay_factor=1 ): """Sets self.base_prompt: used as delimiter for stripping of trailing prompt in output.""" prompt = super(VyOSSSH, self).set_base_prompt( pri_prompt_terminator=pri_prompt_terminator, alt_prompt_terminator=alt_prompt_terminator, delay_factor=delay_factor, ) # Set prompt to user@hostname (remove two additional characters) self.base_prompt = prompt[:-2].strip() return self.base_prompt def send_config_set( self, config_commands=None, exit_config_mode=False, delay_factor=1, max_loops=150, strip_prompt=False, strip_command=False, config_mode_command=None, ): """Remain in configuration mode.""" return super(VyOSSSH, self).send_config_set( config_commands=config_commands, exit_config_mode=exit_config_mode, delay_factor=delay_factor, max_loops=max_loops, strip_prompt=strip_prompt, strip_command=strip_command, config_mode_command=config_mode_command, ) def save_config(self, *args, **kwargs): """Not Implemented""" raise NotImplementedError netmiko-2.4.2/netmiko/vyos/__init__.py0000664000127700012770000000015113440357406021147 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.vyos.vyos_ssh import VyOSSSH __all__ = ["VyOSSSH"] netmiko-2.4.2/netmiko/py23_compat.py0000664000127700012770000000075213440357406020557 0ustar gitusergituser00000000000000"""Simplify Python3 compatibility. Modeled after six behavior for small set of things""" from __future__ import print_function from __future__ import unicode_literals import io import sys PY2 = sys.version_info.major == 2 PY3 = sys.version_info.major == 3 if PY3: string_types = (str,) text_type = str bufferedio_types = io.BufferedIOBase else: string_types = (basestring,) # noqa text_type = unicode # noqa bufferedio_types = (io.BufferedIOBase, file) # noqa netmiko-2.4.2/netmiko/eltex/0000775000127700012770000000000013534772673017175 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/eltex/eltex_ssh.py0000664000127700012770000000125213510465674021537 0ustar gitusergituser00000000000000from __future__ import print_function from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class EltexSSH(CiscoSSHConnection): def session_preparation(self): """Prepare the session after the connection has been established.""" self.ansi_escape_codes = True self._test_channel_read() self.set_base_prompt() self.disable_paging(command="terminal datadump") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def save_config(self, *args, **kwargs): """Not Implemented""" raise NotImplementedError netmiko-2.4.2/netmiko/eltex/__init__.py0000664000127700012770000000015513440357405021273 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.eltex.eltex_ssh import EltexSSH __all__ = ["EltexSSH"] netmiko-2.4.2/netmiko/ipinfusion/0000775000127700012770000000000013534772673020237 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/ipinfusion/__init__.py0000664000127700012770000000031213440357405022330 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.ipinfusion.ipinfusion_ocnos import ( IpInfusionOcNOSSSH, IpInfusionOcNOSTelnet, ) __all__ = ["IpInfusionOcNOSSSH", "IpInfusionOcNOSTelnet"] netmiko-2.4.2/netmiko/ipinfusion/ipinfusion_ocnos.py0000664000127700012770000000505713440357405024170 0ustar gitusergituser00000000000000from __future__ import unicode_literals import time from telnetlib import IAC, DO, DONT, WILL, WONT, SB, SE, TTYPE from netmiko.cisco_base_connection import CiscoBaseConnection class IpInfusionOcNOSBase(CiscoBaseConnection): """Common Methods for IP Infusion OcNOS support.""" def __init__(self, *args, **kwargs): if kwargs.get("default_enter") is None: kwargs["default_enter"] = "\r" return super(IpInfusionOcNOSBase, self).__init__(**kwargs) def session_preparation(self): self._test_channel_read() self.set_base_prompt() self.disable_paging(command="terminal length 0") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def save_config(self, cmd="write", confirm=False, confirm_response=""): """Saves Config Using write command""" return super(IpInfusionOcNOSBase, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) class IpInfusionOcNOSSSH(IpInfusionOcNOSBase): """IP Infusion OcNOS SSH driver.""" pass class IpInfusionOcNOSTelnet(IpInfusionOcNOSBase): """IP Infusion OcNOS Telnet driver.""" def _process_option(self, tsocket, command, option): """ For all telnet options, re-implement the default telnetlib behaviour and refuse to handle any options. If the server expresses interest in 'terminal type' option, then reply back with 'xterm' terminal type. """ if command == DO and option == TTYPE: tsocket.sendall(IAC + WILL + TTYPE) tsocket.sendall(IAC + SB + TTYPE + b"\0" + b"xterm" + IAC + SE) elif command in (DO, DONT): tsocket.sendall(IAC + WONT + option) elif command in (WILL, WONT): tsocket.sendall(IAC + DONT + option) def telnet_login( self, pri_prompt_terminator="#", alt_prompt_terminator=">", username_pattern=r"(?:user:|sername|login|user name)", pwd_pattern=r"assword:", delay_factor=1, max_loops=20, ): # set callback function to handle telnet options. self.remote_conn.set_option_negotiation_callback(self._process_option) return super(IpInfusionOcNOSTelnet, self).telnet_login( pri_prompt_terminator=pri_prompt_terminator, alt_prompt_terminator=alt_prompt_terminator, username_pattern=username_pattern, pwd_pattern=pwd_pattern, delay_factor=delay_factor, max_loops=max_loops, ) netmiko-2.4.2/netmiko/quanta/0000775000127700012770000000000013534772673017345 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/quanta/quanta_mesh_ssh.py0000664000127700012770000000144313510465674023075 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.cisco_base_connection import CiscoSSHConnection class QuantaMeshSSH(CiscoSSHConnection): def disable_paging(self, command="no pager", delay_factor=1): """Disable paging""" return super(QuantaMeshSSH, self).disable_paging(command=command) def config_mode(self, config_command="configure"): """Enter configuration mode.""" return super(QuantaMeshSSH, self).config_mode(config_command=config_command) def save_config( self, cmd="copy running-config startup-config", confirm=False, confirm_response="", ): """Saves Config""" return super(QuantaMeshSSH, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) netmiko-2.4.2/netmiko/quanta/__init__.py0000664000127700012770000000017613440357406021447 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.quanta.quanta_mesh_ssh import QuantaMeshSSH __all__ = ["QuantaMeshSSH"] netmiko-2.4.2/netmiko/terminal_server/0000775000127700012770000000000013534772673021255 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/terminal_server/__init__.py0000664000127700012770000000036013440357406023352 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.terminal_server.terminal_server import TerminalServerSSH from netmiko.terminal_server.terminal_server import TerminalServerTelnet __all__ = ["TerminalServerSSH", "TerminalServerTelnet"] netmiko-2.4.2/netmiko/terminal_server/terminal_server.py0000664000127700012770000000163713440357406025024 0ustar gitusergituser00000000000000"""Generic Terminal Server driver.""" from __future__ import unicode_literals from netmiko.base_connection import BaseConnection class TerminalServer(BaseConnection): """Generic Terminal Server driver. Allow direct write_channel / read_channel operations without session_preparation causing an exception. """ def session_preparation(self): """Do nothing here; base_prompt is not set; paging is not disabled.""" pass class TerminalServerSSH(TerminalServer): """Generic Terminal Server driver SSH.""" pass class TerminalServerTelnet(TerminalServer): """Generic Terminal Server driver telnet.""" def telnet_login(self, *args, **kwargs): # Disable automatic handling of username and password when using terminal server driver pass def std_login(self, *args, **kwargs): return super(TerminalServerTelnet, self).telnet_login(*args, **kwargs) netmiko-2.4.2/netmiko/fortinet/0000775000127700012770000000000013534772673017706 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/fortinet/__init__.py0000664000127700012770000000017113440357405022002 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.fortinet.fortinet_ssh import FortinetSSH __all__ = ["FortinetSSH"] netmiko-2.4.2/netmiko/fortinet/fortinet_ssh.py0000664000127700012770000000723113510465674022764 0ustar gitusergituser00000000000000from __future__ import unicode_literals import paramiko import time import re from netmiko.cisco_base_connection import CiscoSSHConnection class FortinetSSH(CiscoSSHConnection): def _modify_connection_params(self): """Modify connection parameters prior to SSH connection.""" paramiko.Transport._preferred_kex = ( "diffie-hellman-group14-sha1", "diffie-hellman-group-exchange-sha1", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group1-sha1", ) def session_preparation(self): """Prepare the session after the connection has been established.""" self._test_channel_read() self.set_base_prompt(alt_prompt_terminator="$") self.disable_paging() # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def disable_paging(self, delay_factor=1): """Disable paging is only available with specific roles so it may fail.""" check_command = "get system status | grep Virtual" output = self.send_command_timing(check_command) self.allow_disable_global = True self.vdoms = False self._output_mode = "more" if "Virtual domain configuration: enable" in output: self.vdoms = True vdom_additional_command = "config global" output = self.send_command_timing(vdom_additional_command, delay_factor=2) if "Command fail" in output: self.allow_disable_global = False self.remote_conn.close() self.establish_connection(width=100, height=1000) new_output = "" if self.allow_disable_global: self._retrieve_output_mode() disable_paging_commands = [ "config system console", "set output standard", "end", ] # There is an extra 'end' required if in multi-vdoms are enabled if self.vdoms: disable_paging_commands.append("end") outputlist = [ self.send_command_timing(command, delay_factor=2) for command in disable_paging_commands ] # Should test output is valid new_output = self.RETURN.join(outputlist) return output + new_output def _retrieve_output_mode(self): """Save the state of the output mode so it can be reset at the end of the session.""" reg_mode = re.compile(r"output\s+:\s+(?P.*)\s+\n") output = self.send_command("get system console") result_mode_re = reg_mode.search(output) if result_mode_re: result_mode = result_mode_re.group("mode").strip() if result_mode in ["more", "standard"]: self._output_mode = result_mode def cleanup(self): """Re-enable paging globally.""" if self.allow_disable_global: # Return paging state output_mode_cmd = "set output {}".format(self._output_mode) enable_paging_commands = ["config system console", output_mode_cmd, "end"] if self.vdoms: enable_paging_commands.insert(0, "config global") # Should test output is valid for command in enable_paging_commands: self.send_command_timing(command) def config_mode(self, config_command=""): """No config mode for Fortinet devices.""" return "" def exit_config_mode(self, exit_config=""): """No config mode for Fortinet devices.""" return "" def save_config(self, *args, **kwargs): """Not Implemented""" raise NotImplementedError netmiko-2.4.2/netmiko/base_connection.py0000664000127700012770000020176513534772563021570 0ustar gitusergituser00000000000000""" Base connection class for netmiko Handles SSH connection and methods that are generically applicable to different platforms (Cisco and non-Cisco). Also defines methods that should generally be supported by child classes """ from __future__ import print_function from __future__ import unicode_literals import io import re import socket import telnetlib import time from collections import deque from os import path from threading import Lock import paramiko import serial from netmiko import log from netmiko.netmiko_globals import MAX_BUFFER, BACKSPACE_CHAR from netmiko.py23_compat import string_types, bufferedio_types, text_type from netmiko.ssh_exception import ( NetMikoTimeoutException, NetMikoAuthenticationException, ) from netmiko.utilities import ( write_bytes, check_serial_port, get_structured_data, get_structured_data_genie, ) class BaseConnection(object): """ Defines vendor independent methods. Otherwise method left as a stub method. """ def __init__( self, ip="", host="", username="", password=None, secret="", port=None, device_type="", verbose=False, global_delay_factor=1, use_keys=False, key_file=None, pkey=None, passphrase=None, allow_agent=False, ssh_strict=False, system_host_keys=False, alt_host_keys=False, alt_key_file="", ssh_config_file=None, timeout=100, session_timeout=60, auth_timeout=None, blocking_timeout=8, banner_timeout=5, keepalive=0, default_enter=None, response_return=None, serial_settings=None, fast_cli=False, session_log=None, session_log_record_writes=False, session_log_file_mode="write", allow_auto_change=False, encoding="ascii", ): """ Initialize attributes for establishing connection to target device. :param ip: IP address of target device. Not required if `host` is provided. :type ip: str :param host: Hostname of target device. Not required if `ip` is provided. :type host: str :param username: Username to authenticate against target device if required. :type username: str :param password: Password to authenticate against target device if required. :type password: str :param secret: The enable password if target device requires one. :type secret: str :param port: The destination port used to connect to the target device. :type port: int or None :param device_type: Class selection based on device type. :type device_type: str :param verbose: Enable additional messages to standard output. :type verbose: bool :param global_delay_factor: Multiplication factor affecting Netmiko delays (default: 1). :type global_delay_factor: int :param use_keys: Connect to target device using SSH keys. :type use_keys: bool :param key_file: Filename path of the SSH key file to use. :type key_file: str :param pkey: SSH key object to use. :type pkey: paramiko.PKey :param passphrase: Passphrase to use for encrypted key; password will be used for key decryption if not specified. :type passphrase: str :param allow_agent: Enable use of SSH key-agent. :type allow_agent: bool :param ssh_strict: Automatically reject unknown SSH host keys (default: False, which means unknown SSH host keys will be accepted). :type ssh_strict: bool :param system_host_keys: Load host keys from the users known_hosts file. :type system_host_keys: bool :param alt_host_keys: If `True` host keys will be loaded from the file specified in alt_key_file. :type alt_host_keys: bool :param alt_key_file: SSH host key file to use (if alt_host_keys=True). :type alt_key_file: str :param ssh_config_file: File name of OpenSSH configuration file. :type ssh_config_file: str :param timeout: Connection timeout. :type timeout: float :param session_timeout: Set a timeout for parallel requests. :type session_timeout: float :param auth_timeout: Set a timeout (in seconds) to wait for an authentication response. :type auth_timeout: float :param banner_timeout: Set a timeout to wait for the SSH banner (pass to Paramiko). :type banner_timeout: float :param keepalive: Send SSH keepalive packets at a specific interval, in seconds. Currently defaults to 0, for backwards compatibility (it will not attempt to keep the connection alive). :type keepalive: int :param default_enter: Character(s) to send to correspond to enter key (default: \n). :type default_enter: str :param response_return: Character(s) to use in normalized return data to represent enter key (default: \n) :type response_return: str :param fast_cli: Provide a way to optimize for performance. Converts select_delay_factor to select smallest of global and specific. Sets default global_delay_factor to .1 (default: False) :type fast_cli: boolean :param session_log: File path or BufferedIOBase subclass object to write the session log to. :type session_log: str :param session_log_record_writes: The session log generally only records channel reads due to eliminate command duplication due to command echo. You can enable this if you want to record both channel reads and channel writes in the log (default: False). :type session_log_record_writes: boolean :param session_log_file_mode: "write" or "append" for session_log file mode (default: "write") :type session_log_file_mode: str :param allow_auto_change: Allow automatic configuration changes for terminal settings. (default: False) :type allow_auto_change: bool :param encoding: Encoding to be used when writing bytes to the output channel. (default: ascii) :type encoding: str """ self.remote_conn = None self.TELNET_RETURN = "\r\n" if default_enter is None: if "telnet" not in device_type: self.RETURN = "\n" else: self.RETURN = self.TELNET_RETURN else: self.RETURN = default_enter # Line Separator in response lines self.RESPONSE_RETURN = "\n" if response_return is None else response_return if ip: self.host = ip.strip() elif host: self.host = host.strip() if not ip and not host and "serial" not in device_type: raise ValueError("Either ip or host must be set") if port is None: if "telnet" in device_type: port = 23 else: port = 22 self.port = int(port) self.username = username self.password = password self.secret = secret self.device_type = device_type self.ansi_escape_codes = False self.verbose = verbose self.timeout = timeout self.auth_timeout = auth_timeout self.banner_timeout = banner_timeout self.session_timeout = session_timeout self.blocking_timeout = blocking_timeout self.keepalive = keepalive self.allow_auto_change = allow_auto_change self.encoding = encoding # Netmiko will close the session_log if we open the file self.session_log = None self.session_log_record_writes = session_log_record_writes self._session_log_close = False # Ensures last write operations prior to disconnect are recorded. self._session_log_fin = False if session_log is not None: if isinstance(session_log, string_types): # If session_log is a string, open a file corresponding to string name. self.open_session_log(filename=session_log, mode=session_log_file_mode) elif isinstance(session_log, bufferedio_types): # In-memory buffer or an already open file handle self.session_log = session_log else: raise ValueError( "session_log must be a path to a file, a file handle, " "or a BufferedIOBase subclass." ) # Default values self.serial_settings = { "port": "COM1", "baudrate": 9600, "bytesize": serial.EIGHTBITS, "parity": serial.PARITY_NONE, "stopbits": serial.STOPBITS_ONE, } if serial_settings is None: serial_settings = {} self.serial_settings.update(serial_settings) if "serial" in device_type: self.host = "serial" comm_port = self.serial_settings.pop("port") # Get the proper comm port reference if a name was enterred comm_port = check_serial_port(comm_port) self.serial_settings.update({"port": comm_port}) self.fast_cli = fast_cli self.global_delay_factor = global_delay_factor if self.fast_cli and self.global_delay_factor == 1: self.global_delay_factor = 0.1 # set in set_base_prompt method self.base_prompt = "" self._session_locker = Lock() # determine if telnet or SSH if "_telnet" in device_type: self.protocol = "telnet" self.password = password or "" elif "_serial" in device_type: self.protocol = "serial" self.password = password or "" else: self.protocol = "ssh" if not ssh_strict: self.key_policy = paramiko.AutoAddPolicy() else: self.key_policy = paramiko.RejectPolicy() # Options for SSH host_keys self.use_keys = use_keys self.key_file = key_file self.pkey = pkey self.passphrase = passphrase self.allow_agent = allow_agent self.system_host_keys = system_host_keys self.alt_host_keys = alt_host_keys self.alt_key_file = alt_key_file # For SSH proxy support self.ssh_config_file = ssh_config_file # Establish the remote connection self._open() def _open(self): """Decouple connection creation from __init__ for mocking.""" self._modify_connection_params() self.establish_connection() self._try_session_preparation() def __enter__(self): """Establish a session using a Context Manager.""" return self def __exit__(self, exc_type, exc_value, traceback): """Gracefully close connection on Context Manager exit.""" self.disconnect() def _modify_connection_params(self): """Modify connection parameters prior to SSH connection.""" pass def _timeout_exceeded(self, start, msg="Timeout exceeded!"): """Raise NetMikoTimeoutException if waiting too much in the serving queue. :param start: Initial start time to see if session lock timeout has been exceeded :type start: float (from time.time() call i.e. epoch time) :param msg: Exception message if timeout was exceeded :type msg: str """ if not start: # Must provide a comparison time return False if time.time() - start > self.session_timeout: # session_timeout exceeded raise NetMikoTimeoutException(msg) return False def _lock_netmiko_session(self, start=None): """Try to acquire the Netmiko session lock. If not available, wait in the queue until the channel is available again. :param start: Initial start time to measure the session timeout :type start: float (from time.time() call i.e. epoch time) """ if not start: start = time.time() # Wait here until the SSH channel lock is acquired or until session_timeout exceeded while not self._session_locker.acquire(False) and not self._timeout_exceeded( start, "The netmiko channel is not available!" ): time.sleep(0.1) return True def _unlock_netmiko_session(self): """ Release the channel at the end of the task. """ if self._session_locker.locked(): self._session_locker.release() def _write_channel(self, out_data): """Generic handler that will write to both SSH and telnet channel. :param out_data: data to be written to the channel :type out_data: str (can be either unicode/byte string) """ if self.protocol == "ssh": self.remote_conn.sendall(write_bytes(out_data, encoding=self.encoding)) elif self.protocol == "telnet": self.remote_conn.write(write_bytes(out_data, encoding=self.encoding)) elif self.protocol == "serial": self.remote_conn.write(write_bytes(out_data, encoding=self.encoding)) self.remote_conn.flush() else: raise ValueError("Invalid protocol specified") try: log.debug( "write_channel: {}".format( write_bytes(out_data, encoding=self.encoding) ) ) if self._session_log_fin or self.session_log_record_writes: self._write_session_log(out_data) except UnicodeDecodeError: # Don't log non-ASCII characters; this is null characters and telnet IAC (PY2) pass def _write_session_log(self, data): if self.session_log is not None and len(data) > 0: # Hide the password and secret in the session_log if self.password: data = data.replace(self.password, "********") if self.secret: data = data.replace(self.secret, "********") self.session_log.write(write_bytes(data, encoding=self.encoding)) self.session_log.flush() def write_channel(self, out_data): """Generic handler that will write to both SSH and telnet channel. :param out_data: data to be written to the channel :type out_data: str (can be either unicode/byte string) """ self._lock_netmiko_session() try: self._write_channel(out_data) finally: # Always unlock the SSH channel, even on exception. self._unlock_netmiko_session() def is_alive(self): """Returns a boolean flag with the state of the connection.""" null = chr(0) if self.remote_conn is None: log.error("Connection is not initialised, is_alive returns False") return False if self.protocol == "telnet": try: # Try sending IAC + NOP (IAC is telnet way of sending command) # IAC = Interpret as Command; it comes before the NOP. log.debug("Sending IAC + NOP") # Need to send multiple times to test connection self.remote_conn.sock.sendall(telnetlib.IAC + telnetlib.NOP) self.remote_conn.sock.sendall(telnetlib.IAC + telnetlib.NOP) self.remote_conn.sock.sendall(telnetlib.IAC + telnetlib.NOP) return True except AttributeError: return False else: # SSH try: # Try sending ASCII null byte to maintain the connection alive log.debug("Sending the NULL byte") self.write_channel(null) return self.remote_conn.transport.is_active() except (socket.error, EOFError): log.error("Unable to send", exc_info=True) # If unable to send, we can tell for sure that the connection is unusable return False return False def _read_channel(self): """Generic handler that will read all the data from an SSH or telnet channel.""" if self.protocol == "ssh": output = "" while True: if self.remote_conn.recv_ready(): outbuf = self.remote_conn.recv(MAX_BUFFER) if len(outbuf) == 0: raise EOFError("Channel stream closed by remote device.") output += outbuf.decode("utf-8", "ignore") else: break elif self.protocol == "telnet": output = self.remote_conn.read_very_eager().decode("utf-8", "ignore") elif self.protocol == "serial": output = "" while self.remote_conn.in_waiting > 0: output += self.remote_conn.read(self.remote_conn.in_waiting).decode( "utf-8", "ignore" ) log.debug("read_channel: {}".format(output)) self._write_session_log(output) return output def read_channel(self): """Generic handler that will read all the data from an SSH or telnet channel.""" output = "" self._lock_netmiko_session() try: output = self._read_channel() finally: # Always unlock the SSH channel, even on exception. self._unlock_netmiko_session() return output def _read_channel_expect(self, pattern="", re_flags=0, max_loops=150): """Function that reads channel until pattern is detected. pattern takes a regular expression. By default pattern will be self.base_prompt Note: this currently reads beyond pattern. In the case of SSH it reads MAX_BUFFER. In the case of telnet it reads all non-blocking data. There are dependencies here like determining whether in config_mode that are actually depending on reading beyond pattern. :param pattern: Regular expression pattern used to identify the command is done \ (defaults to self.base_prompt) :type pattern: str (regular expression) :param re_flags: regex flags used in conjunction with pattern to search for prompt \ (defaults to no flags) :type re_flags: int :param max_loops: max number of iterations to read the channel before raising exception. Will default to be based upon self.timeout. :type max_loops: int """ output = "" if not pattern: pattern = re.escape(self.base_prompt) log.debug("Pattern is: {}".format(pattern)) i = 1 loop_delay = 0.1 # Default to making loop time be roughly equivalent to self.timeout (support old max_loops # argument for backwards compatibility). if max_loops == 150: max_loops = int(self.timeout / loop_delay) while i < max_loops: if self.protocol == "ssh": try: # If no data available will wait timeout seconds trying to read self._lock_netmiko_session() new_data = self.remote_conn.recv(MAX_BUFFER) if len(new_data) == 0: raise EOFError("Channel stream closed by remote device.") new_data = new_data.decode("utf-8", "ignore") log.debug("_read_channel_expect read_data: {}".format(new_data)) output += new_data self._write_session_log(new_data) except socket.timeout: raise NetMikoTimeoutException( "Timed-out reading channel, data not available." ) finally: self._unlock_netmiko_session() elif self.protocol == "telnet" or "serial": output += self.read_channel() if re.search(pattern, output, flags=re_flags): log.debug("Pattern found: {} {}".format(pattern, output)) return output time.sleep(loop_delay * self.global_delay_factor) i += 1 raise NetMikoTimeoutException( "Timed-out reading channel, pattern not found in output: {}".format(pattern) ) def _read_channel_timing(self, delay_factor=1, max_loops=150): """Read data on the channel based on timing delays. Attempt to read channel max_loops number of times. If no data this will cause a 15 second delay. Once data is encountered read channel for another two seconds (2 * delay_factor) to make sure reading of channel is complete. :param delay_factor: multiplicative factor to adjust delay when reading channel (delays get multiplied by this factor) :type delay_factor: int or float :param max_loops: maximum number of loops to iterate through before returning channel data. Will default to be based upon self.timeout. :type max_loops: int """ # Time to delay in each read loop loop_delay = 0.1 final_delay = 2 # Default to making loop time be roughly equivalent to self.timeout (support old max_loops # and delay_factor arguments for backwards compatibility). delay_factor = self.select_delay_factor(delay_factor) if delay_factor == 1 and max_loops == 150: max_loops = int(self.timeout / loop_delay) channel_data = "" i = 0 while i <= max_loops: time.sleep(loop_delay * delay_factor) new_data = self.read_channel() if new_data: channel_data += new_data else: # Safeguard to make sure really done time.sleep(final_delay * delay_factor) new_data = self.read_channel() if not new_data: break else: channel_data += new_data i += 1 return channel_data def read_until_prompt(self, *args, **kwargs): """Read channel until self.base_prompt detected. Return ALL data available.""" return self._read_channel_expect(*args, **kwargs) def read_until_pattern(self, *args, **kwargs): """Read channel until pattern detected. Return ALL data available.""" return self._read_channel_expect(*args, **kwargs) def read_until_prompt_or_pattern(self, pattern="", re_flags=0): """Read until either self.base_prompt or pattern is detected. :param pattern: the pattern used to identify that the output is complete (i.e. stop \ reading when pattern is detected). pattern will be combined with self.base_prompt to \ terminate output reading when the first of self.base_prompt or pattern is detected. :type pattern: regular expression string :param re_flags: regex flags used in conjunction with pattern to search for prompt \ (defaults to no flags) :type re_flags: int """ combined_pattern = re.escape(self.base_prompt) if pattern: combined_pattern = r"({}|{})".format(combined_pattern, pattern) return self._read_channel_expect(combined_pattern, re_flags=re_flags) def serial_login( self, pri_prompt_terminator=r"#\s*$", alt_prompt_terminator=r">\s*$", username_pattern=r"(?:[Uu]ser:|sername|ogin)", pwd_pattern=r"assword", delay_factor=1, max_loops=20, ): self.telnet_login( pri_prompt_terminator, alt_prompt_terminator, username_pattern, pwd_pattern, delay_factor, max_loops, ) def telnet_login( self, pri_prompt_terminator=r"#\s*$", alt_prompt_terminator=r">\s*$", username_pattern=r"(?:user:|username|login|user name)", pwd_pattern=r"assword", delay_factor=1, max_loops=20, ): """Telnet login. Can be username/password or just password. :param pri_prompt_terminator: Primary trailing delimiter for identifying a device prompt :type pri_prompt_terminator: str :param alt_prompt_terminator: Alternate trailing delimiter for identifying a device prompt :type alt_prompt_terminator: str :param username_pattern: Pattern used to identify the username prompt :type username_pattern: str :param delay_factor: See __init__: global_delay_factor :type delay_factor: int :param max_loops: Controls the wait time in conjunction with the delay_factor (default: 20) """ delay_factor = self.select_delay_factor(delay_factor) time.sleep(1 * delay_factor) output = "" return_msg = "" i = 1 while i <= max_loops: try: output = self.read_channel() return_msg += output # Search for username pattern / send username if re.search(username_pattern, output, flags=re.I): self.write_channel(self.username + self.TELNET_RETURN) time.sleep(1 * delay_factor) output = self.read_channel() return_msg += output # Search for password pattern / send password if re.search(pwd_pattern, output, flags=re.I): self.write_channel(self.password + self.TELNET_RETURN) time.sleep(0.5 * delay_factor) output = self.read_channel() return_msg += output if re.search( pri_prompt_terminator, output, flags=re.M ) or re.search(alt_prompt_terminator, output, flags=re.M): return return_msg # Check if proper data received if re.search(pri_prompt_terminator, output, flags=re.M) or re.search( alt_prompt_terminator, output, flags=re.M ): return return_msg self.write_channel(self.TELNET_RETURN) time.sleep(0.5 * delay_factor) i += 1 except EOFError: self.remote_conn.close() msg = "Login failed: {}".format(self.host) raise NetMikoAuthenticationException(msg) # Last try to see if we already logged in self.write_channel(self.TELNET_RETURN) time.sleep(0.5 * delay_factor) output = self.read_channel() return_msg += output if re.search(pri_prompt_terminator, output, flags=re.M) or re.search( alt_prompt_terminator, output, flags=re.M ): return return_msg msg = "Login failed: {}".format(self.host) self.remote_conn.close() raise NetMikoAuthenticationException(msg) def _try_session_preparation(self): """ In case of an exception happening during `session_preparation()` Netmiko should gracefully clean-up after itself. This might be challenging for library users to do since they do not have a reference to the object. This is possibly related to threads used in Paramiko. """ try: self.session_preparation() except Exception: self.disconnect() raise def session_preparation(self): """ Prepare the session after the connection has been established This method handles some differences that occur between various devices early on in the session. In general, it should include: self._test_channel_read() self.set_base_prompt() self.disable_paging() self.set_terminal_width() self.clear_buffer() """ self._test_channel_read() self.set_base_prompt() self.disable_paging() self.set_terminal_width() # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def _use_ssh_config(self, dict_arg): """Update SSH connection parameters based on contents of SSH config file. :param dict_arg: Dictionary of SSH connection parameters :type dict_arg: dict """ connect_dict = dict_arg.copy() # Use SSHConfig to generate source content. full_path = path.abspath(path.expanduser(self.ssh_config_file)) if path.exists(full_path): ssh_config_instance = paramiko.SSHConfig() with io.open(full_path, "rt", encoding="utf-8") as f: ssh_config_instance.parse(f) source = ssh_config_instance.lookup(self.host) else: source = {} # Keys get normalized to lower-case if "proxycommand" in source: proxy = paramiko.ProxyCommand(source["proxycommand"]) elif "proxyjump" in source: hops = list(reversed(source["proxyjump"].split(","))) if len(hops) > 1: raise ValueError( "ProxyJump with more than one proxy server is not supported." ) port = source.get("port", self.port) host = source.get("hostname", self.host) # -F {full_path} forces the continued use of the same SSH config file cmd = "ssh -F {} -W {}:{} {}".format(full_path, host, port, hops[0]) proxy = paramiko.ProxyCommand(cmd) else: proxy = None # Only update 'hostname', 'sock', 'port', and 'username' # For 'port' and 'username' only update if using object defaults if connect_dict["port"] == 22: connect_dict["port"] = int(source.get("port", self.port)) if connect_dict["username"] == "": connect_dict["username"] = source.get("user", self.username) if proxy: connect_dict["sock"] = proxy connect_dict["hostname"] = source.get("hostname", self.host) return connect_dict def _connect_params_dict(self): """Generate dictionary of Paramiko connection parameters.""" conn_dict = { "hostname": self.host, "port": self.port, "username": self.username, "password": self.password, "look_for_keys": self.use_keys, "allow_agent": self.allow_agent, "key_filename": self.key_file, "pkey": self.pkey, "passphrase": self.passphrase, "timeout": self.timeout, "auth_timeout": self.auth_timeout, "banner_timeout": self.banner_timeout, } # Check if using SSH 'config' file mainly for SSH proxy support if self.ssh_config_file: conn_dict = self._use_ssh_config(conn_dict) return conn_dict def _sanitize_output( self, output, strip_command=False, command_string=None, strip_prompt=False ): """Strip out command echo, trailing router prompt and ANSI escape codes. :param output: Output from a remote network device :type output: unicode string :param strip_command: :type strip_command: """ if self.ansi_escape_codes: output = self.strip_ansi_escape_codes(output) output = self.normalize_linefeeds(output) if strip_command and command_string: command_string = self.normalize_linefeeds(command_string) output = self.strip_command(command_string, output) if strip_prompt: output = self.strip_prompt(output) return output def establish_connection(self, width=None, height=None): """Establish SSH connection to the network device Timeout will generate a NetMikoTimeoutException Authentication failure will generate a NetMikoAuthenticationException width and height are needed for Fortinet paging setting. :param width: Specified width of the VT100 terminal window :type width: int :param height: Specified height of the VT100 terminal window :type height: int """ if self.protocol == "telnet": self.remote_conn = telnetlib.Telnet( self.host, port=self.port, timeout=self.timeout ) self.telnet_login() elif self.protocol == "serial": self.remote_conn = serial.Serial(**self.serial_settings) self.serial_login() elif self.protocol == "ssh": ssh_connect_params = self._connect_params_dict() self.remote_conn_pre = self._build_ssh_client() # initiate SSH connection try: self.remote_conn_pre.connect(**ssh_connect_params) except socket.error: self.paramiko_cleanup() msg = "Connection to device timed-out: {device_type} {ip}:{port}".format( device_type=self.device_type, ip=self.host, port=self.port ) raise NetMikoTimeoutException(msg) except paramiko.ssh_exception.AuthenticationException as auth_err: self.paramiko_cleanup() msg = "Authentication failure: unable to connect {device_type} {ip}:{port}".format( device_type=self.device_type, ip=self.host, port=self.port ) msg += self.RETURN + text_type(auth_err) raise NetMikoAuthenticationException(msg) if self.verbose: print( "SSH connection established to {}:{}".format(self.host, self.port) ) # Use invoke_shell to establish an 'interactive session' if width and height: self.remote_conn = self.remote_conn_pre.invoke_shell( term="vt100", width=width, height=height ) else: self.remote_conn = self.remote_conn_pre.invoke_shell() self.remote_conn.settimeout(self.blocking_timeout) if self.keepalive: self.remote_conn.transport.set_keepalive(self.keepalive) self.special_login_handler() if self.verbose: print("Interactive SSH session established") return "" def _test_channel_read(self, count=40, pattern=""): """Try to read the channel (generally post login) verify you receive data back. :param count: the number of times to check the channel for data :type count: int :param pattern: Regular expression pattern used to determine end of channel read :type pattern: str """ def _increment_delay(main_delay, increment=1.1, maximum=8): """Increment sleep time to a maximum value.""" main_delay = main_delay * increment if main_delay >= maximum: main_delay = maximum return main_delay i = 0 delay_factor = self.select_delay_factor(delay_factor=0) main_delay = delay_factor * 0.1 time.sleep(main_delay * 10) new_data = "" while i <= count: new_data += self._read_channel_timing() if new_data and pattern: if re.search(pattern, new_data): break elif new_data: break else: self.write_channel(self.RETURN) main_delay = _increment_delay(main_delay) time.sleep(main_delay) i += 1 # check if data was ever present if new_data: return new_data else: raise NetMikoTimeoutException("Timed out waiting for data") def _build_ssh_client(self): """Prepare for Paramiko SSH connection.""" # Create instance of SSHClient object remote_conn_pre = paramiko.SSHClient() # Load host_keys for better SSH security if self.system_host_keys: remote_conn_pre.load_system_host_keys() if self.alt_host_keys and path.isfile(self.alt_key_file): remote_conn_pre.load_host_keys(self.alt_key_file) # Default is to automatically add untrusted hosts (make sure appropriate for your env) remote_conn_pre.set_missing_host_key_policy(self.key_policy) return remote_conn_pre def select_delay_factor(self, delay_factor): """ Choose the greater of delay_factor or self.global_delay_factor (default). In fast_cli choose the lesser of delay_factor of self.global_delay_factor. :param delay_factor: See __init__: global_delay_factor :type delay_factor: int """ if self.fast_cli: if delay_factor <= self.global_delay_factor: return delay_factor else: return self.global_delay_factor else: if delay_factor >= self.global_delay_factor: return delay_factor else: return self.global_delay_factor def special_login_handler(self, delay_factor=1): """Handler for devices like WLC, Extreme ERS that throw up characters prior to login.""" pass def disable_paging(self, command="terminal length 0", delay_factor=1): """Disable paging default to a Cisco CLI method. :param command: Device command to disable pagination of output :type command: str :param delay_factor: See __init__: global_delay_factor :type delay_factor: int """ delay_factor = self.select_delay_factor(delay_factor) time.sleep(delay_factor * 0.1) self.clear_buffer() command = self.normalize_cmd(command) log.debug("In disable_paging") log.debug("Command: {0}".format(command)) self.write_channel(command) output = self.read_until_prompt() if self.ansi_escape_codes: output = self.strip_ansi_escape_codes(output) log.debug("{0}".format(output)) log.debug("Exiting disable_paging") return output def set_terminal_width(self, command="", delay_factor=1): """CLI terminals try to automatically adjust the line based on the width of the terminal. This causes the output to get distorted when accessed programmatically. Set terminal width to 511 which works on a broad set of devices. :param command: Command string to send to the device :type command: str :param delay_factor: See __init__: global_delay_factor :type delay_factor: int """ if not command: return "" delay_factor = self.select_delay_factor(delay_factor) command = self.normalize_cmd(command) self.write_channel(command) output = self.read_until_prompt() if self.ansi_escape_codes: output = self.strip_ansi_escape_codes(output) return output def set_base_prompt( self, pri_prompt_terminator="#", alt_prompt_terminator=">", delay_factor=1 ): """Sets self.base_prompt Used as delimiter for stripping of trailing prompt in output. Should be set to something that is general and applies in multiple contexts. For Cisco devices this will be set to router hostname (i.e. prompt without > or #). This will be set on entering user exec or privileged exec on Cisco, but not when entering/exiting config mode. :param pri_prompt_terminator: Primary trailing delimiter for identifying a device prompt :type pri_prompt_terminator: str :param alt_prompt_terminator: Alternate trailing delimiter for identifying a device prompt :type alt_prompt_terminator: str :param delay_factor: See __init__: global_delay_factor :type delay_factor: int """ prompt = self.find_prompt(delay_factor=delay_factor) if not prompt[-1] in (pri_prompt_terminator, alt_prompt_terminator): raise ValueError("Router prompt not found: {0}".format(repr(prompt))) # Strip off trailing terminator self.base_prompt = prompt[:-1] return self.base_prompt def find_prompt(self, delay_factor=1): """Finds the current network device prompt, last line only. :param delay_factor: See __init__: global_delay_factor :type delay_factor: int """ delay_factor = self.select_delay_factor(delay_factor) self.clear_buffer() self.write_channel(self.RETURN) time.sleep(delay_factor * 0.1) # Initial attempt to get prompt prompt = self.read_channel() if self.ansi_escape_codes: prompt = self.strip_ansi_escape_codes(prompt) # Check if the only thing you received was a newline count = 0 prompt = prompt.strip() while count <= 10 and not prompt: prompt = self.read_channel().strip() if prompt: if self.ansi_escape_codes: prompt = self.strip_ansi_escape_codes(prompt).strip() else: self.write_channel(self.RETURN) time.sleep(delay_factor * 0.1) count += 1 # If multiple lines in the output take the last line prompt = self.normalize_linefeeds(prompt) prompt = prompt.split(self.RESPONSE_RETURN)[-1] prompt = prompt.strip() if not prompt: raise ValueError("Unable to find prompt: {}".format(prompt)) time.sleep(delay_factor * 0.1) self.clear_buffer() return prompt def clear_buffer(self): """Read any data available in the channel.""" self.read_channel() def send_command_timing( self, command_string, delay_factor=1, max_loops=150, strip_prompt=True, strip_command=True, normalize=True, use_textfsm=False, use_genie=False, ): """Execute command_string on the SSH channel using a delay-based mechanism. Generally used for show commands. :param command_string: The command to be executed on the remote device. :type command_string: str :param delay_factor: Multiplying factor used to adjust delays (default: 1). :type delay_factor: int or float :param max_loops: Controls wait time in conjunction with delay_factor. Will default to be based upon self.timeout. :type max_loops: int :param strip_prompt: Remove the trailing router prompt from the output (default: True). :type strip_prompt: bool :param strip_command: Remove the echo of the command from the output (default: True). :type strip_command: bool :param normalize: Ensure the proper enter is sent at end of command (default: True). :type normalize: bool :param use_textfsm: Process command output through TextFSM template (default: False). :type normalize: bool :param use_genie: Process command output through PyATS/Genie parser (default: False). :type normalize: bool """ output = "" delay_factor = self.select_delay_factor(delay_factor) self.clear_buffer() if normalize: command_string = self.normalize_cmd(command_string) self.write_channel(command_string) output = self._read_channel_timing( delay_factor=delay_factor, max_loops=max_loops ) output = self._sanitize_output( output, strip_command=strip_command, command_string=command_string, strip_prompt=strip_prompt, ) # If both TextFSM and Genie are set, try TextFSM then Genie for parser_flag, parser_func in ( (use_textfsm, get_structured_data), (use_genie, get_structured_data_genie), ): if parser_flag: structured_output = parser_func( output, platform=self.device_type, command=command_string.strip() ) # If we have structured data; return it. if not isinstance(structured_output, string_types): return structured_output return output def strip_prompt(self, a_string): """Strip the trailing router prompt from the output. :param a_string: Returned string from device :type a_string: str """ response_list = a_string.split(self.RESPONSE_RETURN) last_line = response_list[-1] if self.base_prompt in last_line: return self.RESPONSE_RETURN.join(response_list[:-1]) else: return a_string def _first_line_handler(self, data, search_pattern): """ In certain situations the first line will get repainted which causes a false match on the terminating pattern. Filter this out. returns a tuple of (data, first_line_processed) Where data is the original data potentially with the first line modified and the first_line_processed is a flag indicating that we have handled the first line. """ try: # First line is the echo line containing the command. In certain situations # it gets repainted and needs filtered lines = data.split(self.RETURN) first_line = lines[0] if BACKSPACE_CHAR in first_line: pattern = search_pattern + r".*$" first_line = re.sub(pattern, repl="", string=first_line) lines[0] = first_line data = self.RETURN.join(lines) return (data, True) except IndexError: return (data, False) def send_command( self, command_string, expect_string=None, delay_factor=1, max_loops=500, auto_find_prompt=True, strip_prompt=True, strip_command=True, normalize=True, use_textfsm=False, use_genie=False, ): """Execute command_string on the SSH channel using a pattern-based mechanism. Generally used for show commands. By default this method will keep waiting to receive data until the network device prompt is detected. The current network device prompt will be determined automatically. :param command_string: The command to be executed on the remote device. :type command_string: str :param expect_string: Regular expression pattern to use for determining end of output. If left blank will default to being based on router prompt. :type expect_string: str :param delay_factor: Multiplying factor used to adjust delays (default: 1). :type delay_factor: int :param max_loops: Controls wait time in conjunction with delay_factor. Will default to be based upon self.timeout. :type max_loops: int :param strip_prompt: Remove the trailing router prompt from the output (default: True). :type strip_prompt: bool :param strip_command: Remove the echo of the command from the output (default: True). :type strip_command: bool :param normalize: Ensure the proper enter is sent at end of command (default: True). :type normalize: bool :param use_textfsm: Process command output through TextFSM template (default: False). :type normalize: bool :param use_genie: Process command output through PyATS/Genie parser (default: False). :type normalize: bool """ # Time to delay in each read loop loop_delay = 0.2 # Default to making loop time be roughly equivalent to self.timeout (support old max_loops # and delay_factor arguments for backwards compatibility). delay_factor = self.select_delay_factor(delay_factor) if delay_factor == 1 and max_loops == 500: # Default arguments are being used; use self.timeout instead max_loops = int(self.timeout / loop_delay) # Find the current router prompt if expect_string is None: if auto_find_prompt: try: prompt = self.find_prompt(delay_factor=delay_factor) except ValueError: prompt = self.base_prompt else: prompt = self.base_prompt search_pattern = re.escape(prompt.strip()) else: search_pattern = expect_string if normalize: command_string = self.normalize_cmd(command_string) time.sleep(delay_factor * loop_delay) self.clear_buffer() self.write_channel(command_string) i = 1 output = "" past_three_reads = deque(maxlen=3) first_line_processed = False # Keep reading data until search_pattern is found or until max_loops is reached. while i <= max_loops: new_data = self.read_channel() if new_data: if self.ansi_escape_codes: new_data = self.strip_ansi_escape_codes(new_data) output += new_data past_three_reads.append(new_data) # Case where we haven't processed the first_line yet (there is a potential issue # in the first line (in cases where the line is repainted). if not first_line_processed: output, first_line_processed = self._first_line_handler( output, search_pattern ) # Check if we have already found our pattern if re.search(search_pattern, output): break else: # Check if pattern is in the past three reads if re.search(search_pattern, "".join(past_three_reads)): break time.sleep(delay_factor * loop_delay) i += 1 else: # nobreak raise IOError( "Search pattern never detected in send_command_expect: {}".format( search_pattern ) ) output = self._sanitize_output( output, strip_command=strip_command, command_string=command_string, strip_prompt=strip_prompt, ) # If both TextFSM and Genie are set, try TextFSM then Genie for parser_flag, parser_func in ( (use_textfsm, get_structured_data), (use_genie, get_structured_data_genie), ): if parser_flag: structured_output = parser_func( output, platform=self.device_type, command=command_string.strip() ) # If we have structured data; return it. if not isinstance(structured_output, string_types): return structured_output return output def send_command_expect(self, *args, **kwargs): """Support previous name of send_command method. :param args: Positional arguments to send to send_command() :type args: list :param kwargs: Keyword arguments to send to send_command() :type kwargs: dict """ return self.send_command(*args, **kwargs) @staticmethod def strip_backspaces(output): """Strip any backspace characters out of the output. :param output: Output obtained from a remote network device. :type output: str """ backspace_char = "\x08" return output.replace(backspace_char, "") def strip_command(self, command_string, output): """ Strip command_string from output string Cisco IOS adds backspaces into output for long commands (i.e. for commands that line wrap) :param command_string: The command string sent to the device :type command_string: str :param output: The returned output as a result of the command string sent to the device :type output: str """ backspace_char = "\x08" # Check for line wrap (remove backspaces) if backspace_char in output: output = output.replace(backspace_char, "") output_lines = output.split(self.RESPONSE_RETURN) new_output = output_lines[1:] return self.RESPONSE_RETURN.join(new_output) else: command_length = len(command_string) return output[command_length:] def normalize_linefeeds(self, a_string): """Convert `\r\r\n`,`\r\n`, `\n\r` to `\n.` :param a_string: A string that may have non-normalized line feeds i.e. output returned from device, or a device prompt :type a_string: str """ newline = re.compile("(\r\r\r\n|\r\r\n|\r\n|\n\r)") a_string = newline.sub(self.RESPONSE_RETURN, a_string) if self.RESPONSE_RETURN == "\n": # Convert any remaining \r to \n return re.sub("\r", self.RESPONSE_RETURN, a_string) else: return a_string def normalize_cmd(self, command): """Normalize CLI commands to have a single trailing newline. :param command: Command that may require line feed to be normalized :type command: str """ command = command.rstrip() command += self.RETURN return command def check_enable_mode(self, check_string=""): """Check if in enable mode. Return boolean. :param check_string: Identification of privilege mode from device :type check_string: str """ self.write_channel(self.RETURN) output = self.read_until_prompt() return check_string in output def enable(self, cmd="", pattern="ssword", re_flags=re.IGNORECASE): """Enter enable mode. :param cmd: Device command to enter enable mode :type cmd: str :param pattern: pattern to search for indicating device is waiting for password :type pattern: str :param re_flags: Regular expression flags used in conjunction with pattern :type re_flags: int """ output = "" msg = ( "Failed to enter enable mode. Please ensure you pass " "the 'secret' argument to ConnectHandler." ) if not self.check_enable_mode(): self.write_channel(self.normalize_cmd(cmd)) try: output += self.read_until_prompt_or_pattern( pattern=pattern, re_flags=re_flags ) self.write_channel(self.normalize_cmd(self.secret)) output += self.read_until_prompt() except NetMikoTimeoutException: raise ValueError(msg) if not self.check_enable_mode(): raise ValueError(msg) return output def exit_enable_mode(self, exit_command=""): """Exit enable mode. :param exit_command: Command that exits the session from privileged mode :type exit_command: str """ output = "" if self.check_enable_mode(): self.write_channel(self.normalize_cmd(exit_command)) output += self.read_until_prompt() if self.check_enable_mode(): raise ValueError("Failed to exit enable mode.") return output def check_config_mode(self, check_string="", pattern=""): """Checks if the device is in configuration mode or not. :param check_string: Identification of configuration mode from the device :type check_string: str :param pattern: Pattern to terminate reading of channel :type pattern: str """ self.write_channel(self.RETURN) # You can encounter an issue here (on router name changes) prefer delay-based solution if not pattern: output = self._read_channel_timing() else: output = self.read_until_pattern(pattern=pattern) return check_string in output def config_mode(self, config_command="", pattern=""): """Enter into config_mode. :param config_command: Configuration command to send to the device :type config_command: str :param pattern: Pattern to terminate reading of channel :type pattern: str """ output = "" if not self.check_config_mode(): self.write_channel(self.normalize_cmd(config_command)) output = self.read_until_pattern(pattern=pattern) if not self.check_config_mode(): raise ValueError("Failed to enter configuration mode.") return output def exit_config_mode(self, exit_config="", pattern=""): """Exit from configuration mode. :param exit_config: Command to exit configuration mode :type exit_config: str :param pattern: Pattern to terminate reading of channel :type pattern: str """ output = "" if self.check_config_mode(): self.write_channel(self.normalize_cmd(exit_config)) output = self.read_until_pattern(pattern=pattern) if self.check_config_mode(): raise ValueError("Failed to exit configuration mode") log.debug("exit_config_mode: {}".format(output)) return output def send_config_from_file(self, config_file=None, **kwargs): """ Send configuration commands down the SSH channel from a file. The file is processed line-by-line and each command is sent down the SSH channel. **kwargs are passed to send_config_set method. :param config_file: Path to configuration file to be sent to the device :type config_file: str :param kwargs: params to be sent to send_config_set method :type kwargs: dict """ with io.open(config_file, "rt", encoding="utf-8") as cfg_file: return self.send_config_set(cfg_file, **kwargs) def send_config_set( self, config_commands=None, exit_config_mode=True, delay_factor=1, max_loops=150, strip_prompt=False, strip_command=False, config_mode_command=None, ): """ Send configuration commands down the SSH channel. config_commands is an iterable containing all of the configuration commands. The commands will be executed one after the other. Automatically exits/enters configuration mode. :param config_commands: Multiple configuration commands to be sent to the device :type config_commands: list or string :param exit_config_mode: Determines whether or not to exit config mode after complete :type exit_config_mode: bool :param delay_factor: Factor to adjust delays :type delay_factor: int :param max_loops: Controls wait time in conjunction with delay_factor (default: 150) :type max_loops: int :param strip_prompt: Determines whether or not to strip the prompt :type strip_prompt: bool :param strip_command: Determines whether or not to strip the command :type strip_command: bool :param config_mode_command: The command to enter into config mode :type config_mode_command: str """ delay_factor = self.select_delay_factor(delay_factor) if config_commands is None: return "" elif isinstance(config_commands, string_types): config_commands = (config_commands,) if not hasattr(config_commands, "__iter__"): raise ValueError("Invalid argument passed into send_config_set") # Send config commands cfg_mode_args = (config_mode_command,) if config_mode_command else tuple() output = self.config_mode(*cfg_mode_args) for cmd in config_commands: self.write_channel(self.normalize_cmd(cmd)) if self.fast_cli: pass else: time.sleep(delay_factor * 0.05) # Gather output output += self._read_channel_timing( delay_factor=delay_factor, max_loops=max_loops ) if exit_config_mode: output += self.exit_config_mode() output = self._sanitize_output(output) log.debug("{}".format(output)) return output def strip_ansi_escape_codes(self, string_buffer): """ Remove any ANSI (VT100) ESC codes from the output http://en.wikipedia.org/wiki/ANSI_escape_code Note: this does not capture ALL possible ANSI Escape Codes only the ones I have encountered Current codes that are filtered: ESC = '\x1b' or chr(27) ESC = is the escape character [^ in hex ('\x1b') ESC[24;27H Position cursor ESC[?25h Show the cursor ESC[E Next line (HP does ESC-E) ESC[K Erase line from cursor to the end of line ESC[2K Erase entire line ESC[1;24r Enable scrolling from start to row end ESC[?6l Reset mode screen with options 640 x 200 monochrome (graphics) ESC[?7l Disable line wrapping ESC[2J Code erase display ESC[00;32m Color Green (30 to 37 are different colors) more general pattern is ESC[\d\d;\d\dm and ESC[\d\d;\d\d;\d\dm ESC[6n Get cursor position HP ProCurve and Cisco SG300 require this (possible others). :param string_buffer: The string to be processed to remove ANSI escape codes :type string_buffer: str """ # noqa log.debug("In strip_ansi_escape_codes") log.debug("repr = {}".format(repr(string_buffer))) code_position_cursor = chr(27) + r"\[\d+;\d+H" code_show_cursor = chr(27) + r"\[\?25h" code_next_line = chr(27) + r"E" code_erase_line_end = chr(27) + r"\[K" code_erase_line = chr(27) + r"\[2K" code_erase_start_line = chr(27) + r"\[K" code_enable_scroll = chr(27) + r"\[\d+;\d+r" code_form_feed = chr(27) + r"\[1L" code_carriage_return = chr(27) + r"\[1M" code_disable_line_wrapping = chr(27) + r"\[\?7l" code_reset_mode_screen_options = chr(27) + r"\[\?\d+l" code_reset_graphics_mode = chr(27) + r"\[00m" code_erase_display = chr(27) + r"\[2J" code_graphics_mode = chr(27) + r"\[\d\d;\d\dm" code_graphics_mode2 = chr(27) + r"\[\d\d;\d\d;\d\dm" code_graphics_mode3 = chr(27) + r"\[(3|4)\dm" code_graphics_mode4 = chr(27) + r"\[(9|10)[0-7]m" code_get_cursor_position = chr(27) + r"\[6n" code_cursor_position = chr(27) + r"\[m" code_erase_display = chr(27) + r"\[J" code_attrs_off = chr(27) + r"\[0m" code_reverse = chr(27) + r"\[7m" code_set = [ code_position_cursor, code_show_cursor, code_erase_line, code_enable_scroll, code_erase_start_line, code_form_feed, code_carriage_return, code_disable_line_wrapping, code_erase_line_end, code_reset_mode_screen_options, code_reset_graphics_mode, code_erase_display, code_graphics_mode, code_graphics_mode2, code_graphics_mode3, code_graphics_mode4, code_get_cursor_position, code_cursor_position, code_erase_display, code_attrs_off, code_reverse, ] output = string_buffer for ansi_esc_code in code_set: output = re.sub(ansi_esc_code, "", output) # CODE_NEXT_LINE must substitute with return output = re.sub(code_next_line, self.RETURN, output) log.debug("new_output = {0}".format(output)) log.debug("repr = {0}".format(repr(output))) return output def cleanup(self): """Any needed cleanup before closing connection.""" pass def paramiko_cleanup(self): """Cleanup Paramiko to try to gracefully handle SSH session ending.""" self.remote_conn_pre.close() del self.remote_conn_pre def disconnect(self): """Try to gracefully close the SSH connection.""" try: self.cleanup() if self.protocol == "ssh": self.paramiko_cleanup() elif self.protocol == "telnet": self.remote_conn.close() elif self.protocol == "serial": self.remote_conn.close() except Exception: # There have been race conditions observed on disconnect. pass finally: self.remote_conn_pre = None self.remote_conn = None self.close_session_log() def commit(self): """Commit method for platforms that support this.""" raise AttributeError("Network device does not support 'commit()' method") def save_config(self, *args, **kwargs): """Not Implemented""" raise NotImplementedError def open_session_log(self, filename, mode="write"): """Open the session_log file.""" if mode == "append": self.session_log = open(filename, mode="ab") else: self.session_log = open(filename, mode="wb") self._session_log_close = True def close_session_log(self): """Close the session_log file (if it is a file that we opened).""" if self.session_log is not None and self._session_log_close: self.session_log.close() self.session_log = None class TelnetConnection(BaseConnection): pass netmiko-2.4.2/netmiko/enterasys/0000775000127700012770000000000013534772673020071 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/enterasys/enterasys_ssh.py0000664000127700012770000000120613510465674023326 0ustar gitusergituser00000000000000"""Enterasys support.""" from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class EnterasysSSH(CiscoSSHConnection): """Enterasys support.""" def session_preparation(self): """Enterasys requires enable mode to disable paging.""" self._test_channel_read() self.set_base_prompt() self.disable_paging(command="set length 0") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def save_config(self, *args, **kwargs): """Not Implemented""" raise NotImplementedError netmiko-2.4.2/netmiko/enterasys/__init__.py0000664000127700012770000000017513440357405022171 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.enterasys.enterasys_ssh import EnterasysSSH __all__ = ["EnterasysSSH"] netmiko-2.4.2/netmiko/coriant/0000775000127700012770000000000013534772673017513 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/coriant/__init__.py0000664000127700012770000000011513440357405021605 0ustar gitusergituser00000000000000from netmiko.coriant.coriant_ssh import CoriantSSH __all__ = ["CoriantSSH"] netmiko-2.4.2/netmiko/coriant/coriant_ssh.py0000664000127700012770000000273513510465674022402 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.cisco_base_connection import CiscoSSHConnection class CoriantSSH(CiscoSSHConnection): def session_preparation(self): self._test_channel_read() self.set_base_prompt() def check_enable_mode(self, *args, **kwargs): raise AttributeError("Coriant devices do not support enable mode!") def enable(self, *args, **kwargs): raise AttributeError("Coriant devices do not support enable mode!") def exit_enable_mode(self, *args, **kwargs): raise AttributeError("Coriant devices do not support enable mode!") def check_config_mode(self): """Coriant devices do not have a config mode.""" return False def config_mode(self): """Coriant devices do not have a config mode.""" return "" def exit_config_mode(self): """Coriant devices do not have a config mode.""" return "" def set_base_prompt( self, pri_prompt_terminator=":", alt_prompt_terminator=">", delay_factor=2 ): """Sets self.base_prompt: used as delimiter for stripping of trailing prompt in output.""" super(CoriantSSH, self).set_base_prompt( pri_prompt_terminator=pri_prompt_terminator, alt_prompt_terminator=alt_prompt_terminator, delay_factor=delay_factor, ) return self.base_prompt def save_config(self, *args, **kwargs): """Not Implemented""" raise NotImplementedError netmiko-2.4.2/netmiko/ssh_dispatcher.py0000664000127700012770000002413213534772563021431 0ustar gitusergituser00000000000000"""Controls selection of proper class based on the device type.""" from __future__ import unicode_literals from netmiko.a10 import A10SSH from netmiko.accedian import AccedianSSH from netmiko.alcatel import AlcatelAosSSH from netmiko.alcatel import AlcatelSrosSSH from netmiko.arista import AristaSSH, AristaTelnet from netmiko.arista import AristaFileTransfer from netmiko.apresia import ApresiaAeosSSH, ApresiaAeosTelnet from netmiko.aruba import ArubaSSH from netmiko.calix import CalixB6SSH, CalixB6Telnet from netmiko.checkpoint import CheckPointGaiaSSH from netmiko.ciena import CienaSaosSSH from netmiko.cisco import CiscoAsaSSH, CiscoAsaFileTransfer from netmiko.cisco import ( CiscoIosSSH, CiscoIosFileTransfer, CiscoIosTelnet, CiscoIosSerial, ) from netmiko.cisco import CiscoNxosSSH, CiscoNxosFileTransfer from netmiko.cisco import CiscoS300SSH from netmiko.cisco import CiscoTpTcCeSSH from netmiko.cisco import CiscoWlcSSH from netmiko.cisco import CiscoXrSSH, CiscoXrTelnet, CiscoXrFileTransfer from netmiko.citrix import NetscalerSSH from netmiko.cloudgenix import CloudGenixIonSSH from netmiko.coriant import CoriantSSH from netmiko.dell import DellDNOS6SSH from netmiko.dell import DellDNOS6Telnet from netmiko.dell import DellForce10SSH from netmiko.dell import DellOS10SSH, DellOS10FileTransfer from netmiko.dell import DellPowerConnectSSH from netmiko.dell import DellPowerConnectTelnet from netmiko.dell import DellIsilonSSH from netmiko.eltex import EltexSSH from netmiko.endace import EndaceSSH from netmiko.enterasys import EnterasysSSH from netmiko.extreme import ExtremeErsSSH from netmiko.extreme import ExtremeExosSSH from netmiko.extreme import ExtremeExosTelnet from netmiko.extreme import ExtremeNetironSSH from netmiko.extreme import ExtremeNetironTelnet from netmiko.extreme import ExtremeNosSSH from netmiko.extreme import ExtremeSlxSSH from netmiko.extreme import ExtremeVspSSH from netmiko.extreme import ExtremeWingSSH from netmiko.f5 import F5TmshSSH from netmiko.f5 import F5LinuxSSH from netmiko.flexvnf import FlexvnfSSH from netmiko.fortinet import FortinetSSH from netmiko.hp import HPProcurveSSH, HPProcurveTelnet, HPComwareSSH, HPComwareTelnet from netmiko.huawei import HuaweiSSH, HuaweiVrpv8SSH, HuaweiTelnet from netmiko.ipinfusion import IpInfusionOcNOSSSH, IpInfusionOcNOSTelnet from netmiko.juniper import JuniperSSH, JuniperTelnet from netmiko.juniper import JuniperFileTransfer from netmiko.keymile import KeymileSSH, KeymileNOSSSH from netmiko.linux import LinuxSSH, LinuxFileTransfer from netmiko.mikrotik import MikrotikRouterOsSSH from netmiko.mikrotik import MikrotikSwitchOsSSH from netmiko.mellanox import MellanoxMlnxosSSH from netmiko.mrv import MrvLxSSH from netmiko.mrv import MrvOptiswitchSSH from netmiko.netapp import NetAppcDotSSH from netmiko.oneaccess import OneaccessOneOSTelnet, OneaccessOneOSSSH from netmiko.ovs import OvsLinuxSSH from netmiko.paloalto import PaloAltoPanosSSH from netmiko.paloalto import PaloAltoPanosTelnet from netmiko.pluribus import PluribusSSH from netmiko.quanta import QuantaMeshSSH from netmiko.rad import RadETXSSH from netmiko.rad import RadETXTelnet from netmiko.ruckus import RuckusFastironSSH from netmiko.ruckus import RuckusFastironTelnet from netmiko.terminal_server import TerminalServerSSH from netmiko.terminal_server import TerminalServerTelnet from netmiko.ubiquiti import UbiquitiEdgeSSH from netmiko.vyos import VyOSSSH # The keys of this dictionary are the supported device_types CLASS_MAPPER_BASE = { "a10": A10SSH, "accedian": AccedianSSH, "alcatel_aos": AlcatelAosSSH, "alcatel_sros": AlcatelSrosSSH, "apresia_aeos": ApresiaAeosSSH, "arista_eos": AristaSSH, "aruba_os": ArubaSSH, "avaya_ers": ExtremeErsSSH, "avaya_vsp": ExtremeVspSSH, "brocade_fastiron": RuckusFastironSSH, "brocade_netiron": ExtremeNetironSSH, "brocade_nos": ExtremeNosSSH, "brocade_vdx": ExtremeNosSSH, "brocade_vyos": VyOSSSH, "checkpoint_gaia": CheckPointGaiaSSH, "calix_b6": CalixB6SSH, "ciena_saos": CienaSaosSSH, "cisco_asa": CiscoAsaSSH, "cisco_ios": CiscoIosSSH, "cisco_nxos": CiscoNxosSSH, "cisco_s300": CiscoS300SSH, "cisco_tp": CiscoTpTcCeSSH, "cisco_wlc": CiscoWlcSSH, "cisco_xe": CiscoIosSSH, "cisco_xr": CiscoXrSSH, "cloudgenix_ion": CloudGenixIonSSH, "coriant": CoriantSSH, "dell_dnos9": DellForce10SSH, "dell_force10": DellForce10SSH, "dell_os6": DellDNOS6SSH, "dell_os9": DellForce10SSH, "dell_os10": DellOS10SSH, "dell_powerconnect": DellPowerConnectSSH, "dell_isilon": DellIsilonSSH, "endace": EndaceSSH, "eltex": EltexSSH, "enterasys": EnterasysSSH, "extreme": ExtremeExosSSH, "extreme_ers": ExtremeErsSSH, "extreme_exos": ExtremeExosSSH, "extreme_netiron": ExtremeNetironSSH, "extreme_nos": ExtremeNosSSH, "extreme_slx": ExtremeSlxSSH, "extreme_vdx": ExtremeNosSSH, "extreme_vsp": ExtremeVspSSH, "extreme_wing": ExtremeWingSSH, "f5_ltm": F5TmshSSH, "f5_tmsh": F5TmshSSH, "f5_linux": F5LinuxSSH, "flexvnf": FlexvnfSSH, "fortinet": FortinetSSH, "generic_termserver": TerminalServerSSH, "hp_comware": HPComwareSSH, "hp_procurve": HPProcurveSSH, "huawei": HuaweiSSH, "huawei_vrpv8": HuaweiVrpv8SSH, "ipinfusion_ocnos": IpInfusionOcNOSSSH, "juniper": JuniperSSH, "juniper_junos": JuniperSSH, "keymile": KeymileSSH, "keymile_nos": KeymileNOSSSH, "linux": LinuxSSH, "mikrotik_routeros": MikrotikRouterOsSSH, "mikrotik_switchos": MikrotikSwitchOsSSH, "mellanox": MellanoxMlnxosSSH, "mellanox_mlnxos": MellanoxMlnxosSSH, "mrv_lx": MrvLxSSH, "mrv_optiswitch": MrvOptiswitchSSH, "netapp_cdot": NetAppcDotSSH, "netscaler": NetscalerSSH, "oneaccess_oneos": OneaccessOneOSSSH, "ovs_linux": OvsLinuxSSH, "paloalto_panos": PaloAltoPanosSSH, "pluribus": PluribusSSH, "quanta_mesh": QuantaMeshSSH, "rad_etx": RadETXSSH, "ruckus_fastiron": RuckusFastironSSH, "ubiquiti_edge": UbiquitiEdgeSSH, "ubiquiti_edgeswitch": UbiquitiEdgeSSH, "vyatta_vyos": VyOSSSH, "vyos": VyOSSSH, } FILE_TRANSFER_MAP = { "arista_eos": AristaFileTransfer, "cisco_asa": CiscoAsaFileTransfer, "cisco_ios": CiscoIosFileTransfer, "dell_os10": DellOS10FileTransfer, "cisco_nxos": CiscoNxosFileTransfer, "cisco_xe": CiscoIosFileTransfer, "cisco_xr": CiscoXrFileTransfer, "juniper_junos": JuniperFileTransfer, "linux": LinuxFileTransfer, } # Also support keys that end in _ssh new_mapper = {} for k, v in CLASS_MAPPER_BASE.items(): new_mapper[k] = v alt_key = k + "_ssh" new_mapper[alt_key] = v CLASS_MAPPER = new_mapper new_mapper = {} for k, v in FILE_TRANSFER_MAP.items(): new_mapper[k] = v alt_key = k + "_ssh" new_mapper[alt_key] = v FILE_TRANSFER_MAP = new_mapper # Add telnet drivers CLASS_MAPPER["apresia_aeos_telnet"] = ApresiaAeosTelnet CLASS_MAPPER["arista_eos_telnet"] = AristaTelnet CLASS_MAPPER["brocade_fastiron_telnet"] = RuckusFastironTelnet CLASS_MAPPER["brocade_netiron_telnet"] = ExtremeNetironTelnet CLASS_MAPPER["calix_b6_telnet"] = CalixB6Telnet CLASS_MAPPER["cisco_ios_telnet"] = CiscoIosTelnet CLASS_MAPPER["cisco_xr_telnet"] = CiscoXrTelnet CLASS_MAPPER["dell_dnos6_telnet"] = DellDNOS6Telnet CLASS_MAPPER["dell_powerconnect_telnet"] = DellPowerConnectTelnet CLASS_MAPPER["extreme_telnet"] = ExtremeExosTelnet CLASS_MAPPER["extreme_exos_telnet"] = ExtremeExosTelnet CLASS_MAPPER["extreme_netiron_telnet"] = ExtremeNetironTelnet CLASS_MAPPER["generic_termserver_telnet"] = TerminalServerTelnet CLASS_MAPPER["hp_procurve_telnet"] = HPProcurveTelnet CLASS_MAPPER["hp_comware_telnet"] = HPComwareTelnet CLASS_MAPPER["huawei_telnet"] = HuaweiTelnet CLASS_MAPPER["ipinfusion_ocnos_telnet"] = IpInfusionOcNOSTelnet CLASS_MAPPER["juniper_junos_telnet"] = JuniperTelnet CLASS_MAPPER["paloalto_panos_telnet"] = PaloAltoPanosTelnet CLASS_MAPPER["oneaccess_oneos_telnet"] = OneaccessOneOSTelnet CLASS_MAPPER["rad_etx_telnet"] = RadETXTelnet CLASS_MAPPER["ruckus_fastiron_telnet"] = RuckusFastironTelnet # Add serial drivers CLASS_MAPPER["cisco_ios_serial"] = CiscoIosSerial # Add general terminal_server driver and autodetect CLASS_MAPPER["terminal_server"] = TerminalServerSSH CLASS_MAPPER["autodetect"] = TerminalServerSSH platforms = list(CLASS_MAPPER.keys()) platforms.sort() platforms_base = list(CLASS_MAPPER_BASE.keys()) platforms_base.sort() platforms_str = "\n".join(platforms_base) platforms_str = "\n" + platforms_str scp_platforms = list(FILE_TRANSFER_MAP.keys()) scp_platforms.sort() scp_platforms_str = "\n".join(scp_platforms) scp_platforms_str = "\n" + scp_platforms_str def ConnectHandler(*args, **kwargs): """Factory function selects the proper class and creates object based on device_type.""" if kwargs["device_type"] not in platforms: raise ValueError( "Unsupported device_type: " "currently supported platforms are: {}".format(platforms_str) ) ConnectionClass = ssh_dispatcher(kwargs["device_type"]) return ConnectionClass(*args, **kwargs) def ssh_dispatcher(device_type): """Select the class to be instantiated based on vendor/platform.""" return CLASS_MAPPER[device_type] def redispatch(obj, device_type, session_prep=True): """Dynamically change Netmiko object's class to proper class. Generally used with terminal_server device_type when you need to redispatch after interacting with terminal server. """ new_class = ssh_dispatcher(device_type) obj.device_type = device_type obj.__class__ = new_class if session_prep: obj._try_session_preparation() def FileTransfer(*args, **kwargs): """Factory function selects the proper SCP class and creates object based on device_type.""" if len(args) >= 1: device_type = args[0].device_type else: device_type = kwargs["ssh_conn"].device_type if device_type not in scp_platforms: raise ValueError( "Unsupported SCP device_type: " "currently supported platforms are: {}".format(scp_platforms_str) ) FileTransferClass = FILE_TRANSFER_MAP[device_type] return FileTransferClass(*args, **kwargs) netmiko-2.4.2/netmiko/cisco_base_connection.py0000664000127700012770000002100613511742056022721 0ustar gitusergituser00000000000000"""CiscoBaseConnection is netmiko SSH class for Cisco and Cisco-like platforms.""" from __future__ import unicode_literals from netmiko.base_connection import BaseConnection from netmiko.scp_handler import BaseFileTransfer from netmiko.ssh_exception import NetMikoAuthenticationException import re import time class CiscoBaseConnection(BaseConnection): """Base Class for cisco-like behavior.""" def check_enable_mode(self, check_string="#"): """Check if in enable mode. Return boolean.""" return super(CiscoBaseConnection, self).check_enable_mode( check_string=check_string ) def enable(self, cmd="enable", pattern="ssword", re_flags=re.IGNORECASE): """Enter enable mode.""" return super(CiscoBaseConnection, self).enable( cmd=cmd, pattern=pattern, re_flags=re_flags ) def exit_enable_mode(self, exit_command="disable"): """Exits enable (privileged exec) mode.""" return super(CiscoBaseConnection, self).exit_enable_mode( exit_command=exit_command ) def check_config_mode(self, check_string=")#", pattern=""): """ Checks if the device is in configuration mode or not. Cisco IOS devices abbreviate the prompt at 20 chars in config mode """ return super(CiscoBaseConnection, self).check_config_mode( check_string=check_string, pattern=pattern ) def config_mode(self, config_command="config term", pattern=""): """ Enter into configuration mode on remote device. Cisco IOS devices abbreviate the prompt at 20 chars in config mode """ if not pattern: pattern = re.escape(self.base_prompt[:16]) return super(CiscoBaseConnection, self).config_mode( config_command=config_command, pattern=pattern ) def exit_config_mode(self, exit_config="end", pattern="#"): """Exit from configuration mode.""" return super(CiscoBaseConnection, self).exit_config_mode( exit_config=exit_config, pattern=pattern ) def serial_login( self, pri_prompt_terminator=r"#\s*$", alt_prompt_terminator=r">\s*$", username_pattern=r"(?:user:|username|login)", pwd_pattern=r"assword", delay_factor=1, max_loops=20, ): self.write_channel(self.TELNET_RETURN) output = self.read_channel() if re.search(pri_prompt_terminator, output, flags=re.M) or re.search( alt_prompt_terminator, output, flags=re.M ): return output else: return self.telnet_login( pri_prompt_terminator, alt_prompt_terminator, username_pattern, pwd_pattern, delay_factor, max_loops, ) def telnet_login( self, pri_prompt_terminator=r"#\s*$", alt_prompt_terminator=r">\s*$", username_pattern=r"(?:user:|username|login|user name)", pwd_pattern=r"assword", delay_factor=1, max_loops=20, ): """Telnet login. Can be username/password or just password.""" delay_factor = self.select_delay_factor(delay_factor) time.sleep(1 * delay_factor) output = "" return_msg = "" i = 1 while i <= max_loops: try: output = self.read_channel() return_msg += output # Search for username pattern / send username if re.search(username_pattern, output, flags=re.I): self.write_channel(self.username + self.TELNET_RETURN) time.sleep(1 * delay_factor) output = self.read_channel() return_msg += output # Search for password pattern / send password if re.search(pwd_pattern, output, flags=re.I): self.write_channel(self.password + self.TELNET_RETURN) time.sleep(0.5 * delay_factor) output = self.read_channel() return_msg += output if re.search( pri_prompt_terminator, output, flags=re.M ) or re.search(alt_prompt_terminator, output, flags=re.M): return return_msg # Support direct telnet through terminal server if re.search(r"initial configuration dialog\? \[yes/no\]: ", output): self.write_channel("no" + self.TELNET_RETURN) time.sleep(0.5 * delay_factor) count = 0 while count < 15: output = self.read_channel() return_msg += output if re.search(r"ress RETURN to get started", output): output = "" break time.sleep(2 * delay_factor) count += 1 # Check for device with no password configured if re.search(r"assword required, but none set", output): self.remote_conn.close() msg = "Login failed - Password required, but none set: {}".format( self.host ) raise NetMikoAuthenticationException(msg) # Check if proper data received if re.search(pri_prompt_terminator, output, flags=re.M) or re.search( alt_prompt_terminator, output, flags=re.M ): return return_msg self.write_channel(self.TELNET_RETURN) time.sleep(0.5 * delay_factor) i += 1 except EOFError: self.remote_conn.close() msg = "Login failed: {}".format(self.host) raise NetMikoAuthenticationException(msg) # Last try to see if we already logged in self.write_channel(self.TELNET_RETURN) time.sleep(0.5 * delay_factor) output = self.read_channel() return_msg += output if re.search(pri_prompt_terminator, output, flags=re.M) or re.search( alt_prompt_terminator, output, flags=re.M ): return return_msg self.remote_conn.close() msg = "Login failed: {}".format(self.host) raise NetMikoAuthenticationException(msg) def cleanup(self): """Gracefully exit the SSH session.""" try: self.exit_config_mode() except Exception: pass # Always try to send final 'exit' regardless of whether exit_config_mode works or not. self._session_log_fin = True self.write_channel("exit" + self.RETURN) def _autodetect_fs(self, cmd="dir", pattern=r"Directory of (.*)/"): """Autodetect the file system on the remote device. Used by SCP operations.""" if not self.check_enable_mode(): raise ValueError("Must be in enable mode to auto-detect the file-system.") output = self.send_command_expect(cmd) match = re.search(pattern, output) if match: file_system = match.group(1) # Test file_system cmd = "dir {}".format(file_system) output = self.send_command_expect(cmd) if "% Invalid" in output or "%Error:" in output: raise ValueError( "An error occurred in dynamically determining remote file " "system: {} {}".format(cmd, output) ) else: return file_system raise ValueError( "An error occurred in dynamically determining remote file " "system: {} {}".format(cmd, output) ) def save_config( self, cmd="copy running-config startup-config", confirm=False, confirm_response="", ): """Saves Config.""" self.enable() if confirm: output = self.send_command_timing(command_string=cmd) if confirm_response: output += self.send_command_timing(confirm_response) else: # Send enter by default output += self.send_command_timing(self.RETURN) else: # Some devices are slow so match on trailing-prompt if you can output = self.send_command(command_string=cmd) return output class CiscoSSHConnection(CiscoBaseConnection): pass class CiscoFileTransfer(BaseFileTransfer): pass netmiko-2.4.2/netmiko/ovs/0000775000127700012770000000000013534772673016663 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/ovs/ovs_linux_ssh.py0000664000127700012770000000017513265436437022137 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.linux.linux_ssh import LinuxSSH class OvsLinuxSSH(LinuxSSH): pass netmiko-2.4.2/netmiko/ovs/__init__.py0000664000127700012770000000016513440357405020762 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.ovs.ovs_linux_ssh import OvsLinuxSSH __all__ = ["OvsLinuxSSH"] netmiko-2.4.2/netmiko/_textfsm/0000775000127700012770000000000013534772673017705 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/_textfsm/_clitable.py0000664000127700012770000003363013516415527022172 0ustar gitusergituser00000000000000""" Google's clitable.py is inherently integrated to Linux: This is a workaround for that (basically include modified clitable code without anything that is Linux-specific). _clitable.py is identical to Google's as of 2017-12-17 _texttable.py is identical to Google's as of 2017-12-17 _terminal.py is a highly stripped down version of Google's such that clitable.py works https://github.com/google/textfsm/blob/master/clitable.py """ # Some of this code is from Google with the following license: # # Copyright 2012 Google Inc. All Rights Reserved. # # 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 copy import os import re import threading try: # TextFSM >= 1.0 (new package structure) from textfsm import copyable_regex_object except ImportError: # TextFSM <= 0.4.1 import copyable_regex_object import textfsm from netmiko._textfsm import _texttable as texttable class Error(Exception): """Base class for errors.""" class IndexTableError(Error): """General INdexTable error.""" class CliTableError(Error): """General CliTable error.""" class IndexTable(object): """Class that reads and stores comma-separated values as a TextTable. Stores a compiled regexp of the value for efficient matching. Includes functions to preprocess Columns (both compiled and uncompiled). Attributes: index: TextTable, the index file parsed into a texttable. compiled: TextTable, the table but with compiled regexp for each field. """ def __init__(self, preread=None, precompile=None, file_path=None): """Create new IndexTable object. Args: preread: func, Pre-processing, applied to each field as it is read. precompile: func, Pre-compilation, applied to each field before compiling. file_path: String, Location of file to use as input. """ self.index = None self.compiled = None if file_path: self._index_file = file_path self._index_handle = open(self._index_file, "r") self._ParseIndex(preread, precompile) def __del__(self): """Close index handle.""" if hasattr(self, "_index_handle"): self._index_handle.close() def __len__(self): """Returns number of rows in table.""" return self.index.size def __copy__(self): """Returns a copy of an IndexTable object.""" clone = IndexTable() if hasattr(self, "_index_file"): # pylint: disable=protected-access clone._index_file = self._index_file clone._index_handle = self._index_handle clone.index = self.index clone.compiled = self.compiled return clone def __deepcopy__(self, memodict=None): """Returns a deepcopy of an IndexTable object.""" clone = IndexTable() if hasattr(self, "_index_file"): # pylint: disable=protected-access clone._index_file = copy.deepcopy(self._index_file) clone._index_handle = open(clone._index_file, "r") clone.index = copy.deepcopy(self.index) clone.compiled = copy.deepcopy(self.compiled) return clone def _ParseIndex(self, preread, precompile): """Reads index file and stores entries in TextTable. For optimisation reasons, a second table is created with compiled entries. Args: preread: func, Pre-processing, applied to each field as it is read. precompile: func, Pre-compilation, applied to each field before compiling. Raises: IndexTableError: If the column headers has illegal column labels. """ self.index = texttable.TextTable() self.index.CsvToTable(self._index_handle) if preread: for row in self.index: for col in row.header: row[col] = preread(col, row[col]) self.compiled = copy.deepcopy(self.index) for row in self.compiled: for col in row.header: if precompile: row[col] = precompile(col, row[col]) if row[col]: row[col] = copyable_regex_object.CopyableRegexObject(row[col]) def GetRowMatch(self, attributes): """Returns the row number that matches the supplied attributes.""" for row in self.compiled: try: for key in attributes: # Silently skip attributes not present in the index file. # pylint: disable=E1103 if ( key in row.header and row[key] and not row[key].match(attributes[key]) ): # This line does not match, so break and try next row. raise StopIteration() return row.row except StopIteration: pass return 0 class CliTable(texttable.TextTable): """Class that reads CLI output and parses into tabular format. Reads an index file and uses it to map command strings to templates. It then uses TextFSM to parse the command output (raw) into a tabular format. The superkey is the set of columns that contain data that uniquely defines the row, the key is the row number otherwise. This is typically gathered from the templates 'Key' value but is extensible. Attributes: raw: String, Unparsed command string from device/command. index_file: String, file where template/command mappings reside. template_dir: String, directory where index file and templates reside. """ # Parse each template index only once across all instances. # Without this, the regexes are parsed at every call to CliTable(). _lock = threading.Lock() INDEX = {} # pylint: disable=C6409 def synchronised(func): """Synchronisation decorator.""" # pylint: disable=E0213 def Wrapper(main_obj, *args, **kwargs): main_obj._lock.acquire() # pylint: disable=W0212 try: return func(main_obj, *args, **kwargs) # pylint: disable=E1102 finally: main_obj._lock.release() # pylint: disable=W0212 return Wrapper # pylint: enable=C6409 @synchronised def __init__(self, index_file=None, template_dir=None): """Create new CLiTable object. Args: index_file: String, file where template/command mappings reside. template_dir: String, directory where index file and templates reside. """ # pylint: disable=E1002 super(CliTable, self).__init__() self._keys = set() self.raw = None self.index_file = index_file self.template_dir = template_dir if index_file: self.ReadIndex(index_file) def ReadIndex(self, index_file=None): """Reads the IndexTable index file of commands and templates. Args: index_file: String, file where template/command mappings reside. Raises: CliTableError: A template column was not found in the table. """ self.index_file = index_file or self.index_file fullpath = os.path.join(self.template_dir, self.index_file) if self.index_file and fullpath not in self.INDEX: self.index = IndexTable(self._PreParse, self._PreCompile, fullpath) self.INDEX[fullpath] = self.index else: self.index = self.INDEX[fullpath] # Does the IndexTable have the right columns. if "Template" not in self.index.index.header: # pylint: disable=E1103 raise CliTableError("Index file does not have 'Template' column.") def _TemplateNamesToFiles(self, template_str): """Parses a string of templates into a list of file handles.""" template_list = template_str.split(":") template_files = [] try: for tmplt in template_list: template_files.append(open(os.path.join(self.template_dir, tmplt), "r")) except: # noqa for tmplt in template_files: tmplt.close() raise return template_files def ParseCmd(self, cmd_input, attributes=None, templates=None): """Creates a TextTable table of values from cmd_input string. Parses command output with template/s. If more than one template is found subsequent tables are merged if keys match (dropped otherwise). Args: cmd_input: String, Device/command response. attributes: Dict, attribute that further refine matching template. templates: String list of templates to parse with. If None, uses index Raises: CliTableError: A template was not found for the given command. """ # Store raw command data within the object. self.raw = cmd_input if not templates: # Find template in template index. row_idx = self.index.GetRowMatch(attributes) if row_idx: templates = self.index.index[row_idx]["Template"] else: raise CliTableError( 'No template found for attributes: "%s"' % attributes ) template_files = self._TemplateNamesToFiles(templates) try: # Re-initialise the table. self.Reset() self._keys = set() self.table = self._ParseCmdItem(self.raw, template_file=template_files[0]) # Add additional columns from any additional tables. for tmplt in template_files[1:]: self.extend( self._ParseCmdItem(self.raw, template_file=tmplt), set(self._keys) ) finally: for f in template_files: f.close() def _ParseCmdItem(self, cmd_input, template_file=None): """Creates Texttable with output of command. Args: cmd_input: String, Device response. template_file: File object, template to parse with. Returns: TextTable containing command output. Raises: CliTableError: A template was not found for the given command. """ # Build FSM machine from the template. fsm = textfsm.TextFSM(template_file) if not self._keys: self._keys = set(fsm.GetValuesByAttrib("Key")) # Pass raw data through FSM. table = texttable.TextTable() table.header = fsm.header # Fill TextTable from record entries. for record in fsm.ParseText(cmd_input): table.Append(record) return table def _PreParse(self, key, value): """Executed against each field of each row read from index table.""" if key == "Command": return re.sub(r"(\[\[.+?\]\])", self._Completion, value) else: return value def _PreCompile(self, key, value): """Executed against each field of each row before compiling as regexp.""" if key == "Template": return else: return value def _Completion(self, match): # pylint: disable=C6114 r"""Replaces double square brackets with variable length completion. Completion cannot be mixed with regexp matching or '\' characters i.e. '[[(\n)]] would become (\(n)?)?.' Args: match: A regex Match() object. Returns: String of the format '(a(b(c(d)?)?)?)?'. """ # Strip the outer '[[' & ']]' and replace with ()? regexp pattern. word = str(match.group())[2:-2] return "(" + ("(").join(word) + ")?" * len(word) def LabelValueTable(self, keys=None): """Return LabelValue with FSM derived keys.""" keys = keys or self.superkey # pylint: disable=E1002 return super(CliTable, self).LabelValueTable(keys) # pylint: disable=W0622,C6409 def sort(self, cmp=None, key=None, reverse=False): """Overrides sort func to use the KeyValue for the key.""" if not key and self._keys: key = self.KeyValue super(CliTable, self).sort(cmp=cmp, key=key, reverse=reverse) # pylint: enable=W0622 def AddKeys(self, key_list): """Mark additional columns as being part of the superkey. Supplements the Keys already extracted from the FSM template. Useful when adding new columns to existing tables. Note: This will impact attempts to further 'extend' the table as the superkey must be common between tables for successful extension. Args: key_list: list of header entries to be included in the superkey. Raises: KeyError: If any entry in list is not a valid header entry. """ for keyname in key_list: if keyname not in self.header: raise KeyError("'%s'" % keyname) self._keys = self._keys.union(set(key_list)) @property def superkey(self): """Returns a set of column names that together constitute the superkey.""" sorted_list = [] for header in self.header: if header in self._keys: sorted_list.append(header) return sorted_list def KeyValue(self, row=None): """Returns the super key value for the row.""" if not row: if self._iterator: # If we are inside an iterator use current row iteration. row = self[self._iterator] else: row = self.row # If no superkey then use row number. if not self.superkey: return ["%s" % row.row] sorted_list = [] for header in self.header: if header in self.superkey: sorted_list.append(row[header]) return sorted_list netmiko-2.4.2/netmiko/_textfsm/_terminal.py0000664000127700012770000000563513440357405022226 0ustar gitusergituser00000000000000""" Google's clitable.py is inherently integrated to Linux. This is a workaround for that (basically include modified clitable code without anything that is Linux-specific). _clitable.py is identical to Google's as of 2017-12-17 _texttable.py is identical to Google's as of 2017-12-17 _terminal.py is a highly stripped down version of Google's such that clitable.py works https://github.com/google/textfsm/blob/master/clitable.py """ # Some of this code is from Google with the following license: # # Copyright 2012 Google Inc. All Rights Reserved. # # 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. from __future__ import absolute_import from __future__ import division from __future__ import print_function import re __version__ = "0.1.1" # ANSI, ISO/IEC 6429 escape sequences, SGR (Select Graphic Rendition) subset. SGR = { "reset": 0, "bold": 1, "underline": 4, "blink": 5, "negative": 7, "underline_off": 24, "blink_off": 25, "positive": 27, "black": 30, "red": 31, "green": 32, "yellow": 33, "blue": 34, "magenta": 35, "cyan": 36, "white": 37, "fg_reset": 39, "bg_black": 40, "bg_red": 41, "bg_green": 42, "bg_yellow": 43, "bg_blue": 44, "bg_magenta": 45, "bg_cyan": 46, "bg_white": 47, "bg_reset": 49, } # Provide a familar descriptive word for some ansi sequences. FG_COLOR_WORDS = { "black": ["black"], "dark_gray": ["bold", "black"], "blue": ["blue"], "light_blue": ["bold", "blue"], "green": ["green"], "light_green": ["bold", "green"], "cyan": ["cyan"], "light_cyan": ["bold", "cyan"], "red": ["red"], "light_red": ["bold", "red"], "purple": ["magenta"], "light_purple": ["bold", "magenta"], "brown": ["yellow"], "yellow": ["bold", "yellow"], "light_gray": ["white"], "white": ["bold", "white"], } BG_COLOR_WORDS = { "black": ["bg_black"], "red": ["bg_red"], "green": ["bg_green"], "yellow": ["bg_yellow"], "dark_blue": ["bg_blue"], "purple": ["bg_magenta"], "light_blue": ["bg_cyan"], "grey": ["bg_white"], } # Characters inserted at the start and end of ANSI strings # to provide hinting for readline and other clients. ANSI_START = "\001" ANSI_END = "\002" sgr_re = re.compile(r"(%s?\033\[\d+(?:;\d+)*m%s?)" % (ANSI_START, ANSI_END)) def StripAnsiText(text): """Strip ANSI/SGR escape sequences from text.""" return sgr_re.sub("", text) netmiko-2.4.2/netmiko/_textfsm/__init__.py0000664000127700012770000000025213440357405022001 0ustar gitusergituser00000000000000from netmiko._textfsm import _terminal from netmiko._textfsm import _texttable from netmiko._textfsm import _clitable __all__ = ("_terminal", "_texttable", "_clitable") netmiko-2.4.2/netmiko/_textfsm/_texttable.py0000664000127700012770000011043113440357405022376 0ustar gitusergituser00000000000000""" Google's clitable.py is inherently integrated to Linux: This is a workaround for that (basically include modified clitable code without anything that is Linux-specific). _clitable.py is identical to Google's as of 2017-12-17 _texttable.py is identical to Google's as of 2017-12-17 _terminal.py is a highly stripped down version of Google's such that clitable.py works https://github.com/google/textfsm/blob/master/clitable.py A module to represent and manipulate tabular text data. A table of rows, indexed on row number. Each row is a ordered dictionary of row elements that maintains knowledge of the parent table and column headings. Tables can be created from CSV input and in-turn supports a number of display formats such as CSV and variable sized and justified rows. """ # Some of this code is from Google with the following license: # # Copyright 2012 Google Inc. All Rights Reserved. # # 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. from __future__ import absolute_import from __future__ import division from __future__ import print_function import copy from functools import cmp_to_key import textwrap # pylint: disable=redefined-builtin from six.moves import range from netmiko._textfsm import _terminal as terminal class Error(Exception): """Base class for errors.""" class TableError(Error): """Error in TextTable.""" class Row(dict): """Represents a table row. We implement this as an ordered dictionary. The order is the chronological order of data insertion. Methods are supplied to make it behave like a regular dict() and list(). Attributes: row: int, the row number in the container table. 0 is the header row. table: A TextTable(), the associated container table. """ def __init__(self, *args, **kwargs): super(Row, self).__init__(*args, **kwargs) self._keys = list() self._values = list() self.row = None self.table = None self._color = None self._index = {} def _BuildIndex(self): """Recreate the key index.""" self._index = {} for i, k in enumerate(self._keys): self._index[k] = i def __getitem__(self, column): """Support for [] notation. Args: column: Tuple of column names, or a (str) column name, or positional column number, 0-indexed. Returns: A list or string with column value(s). Raises: IndexError: The given column(s) were not found. """ if isinstance(column, (list, tuple)): ret = [] for col in column: ret.append(self[col]) return ret try: return self._values[self._index[column]] except (KeyError, TypeError, ValueError): pass # Perhaps we have a range like '1', ':-1' or '1:'. try: return self._values[column] except (IndexError, TypeError): pass raise IndexError('No such column "%s" in row.' % column) def __contains__(self, value): return value in self._values def __setitem__(self, column, value): for i in range(len(self)): if self._keys[i] == column: self._values[i] = value return # No column found, add a new one. self._keys.append(column) self._values.append(value) self._BuildIndex() def __iter__(self): return iter(self._values) def __len__(self): return len(self._keys) def __str__(self): ret = "" for v in self._values: ret += "%12s " % v ret += "\n" return ret def __repr__(self): return "%s(%r)" % (self.__class__.__name__, str(self)) def get(self, column, default_value=None): """Get an item from the Row by column name. Args: column: Tuple of column names, or a (str) column name, or positional column number, 0-indexed. default_value: The value to use if the key is not found. Returns: A list or string with column value(s) or default_value if not found. """ if isinstance(column, (list, tuple)): ret = [] for col in column: ret.append(self.get(col, default_value)) return ret # Perhaps we have a range like '1', ':-1' or '1:'. try: return self._values[column] except (IndexError, TypeError): pass try: return self[column] except IndexError: return default_value def index(self, column): # pylint: disable=C6409 """Fetches the column number (0 indexed). Args: column: A string, column to fetch the index of. Returns: An int, the row index number. Raises: ValueError: The specified column was not found. """ for i, key in enumerate(self._keys): if key == column: return i raise ValueError('Column "%s" not found.' % column) def iterkeys(self): return iter(self._keys) def items(self): # TODO(harro): self.get(k) should work here but didn't ? return [(k, self.__getitem__(k)) for k in self._keys] def _GetValues(self): """Return the row's values.""" return self._values def _GetHeader(self): """Return the row's header.""" return self._keys def _SetHeader(self, values): """Set the row's header from a list.""" if self._values and len(values) != len(self._values): raise ValueError("Header values not equal to existing data width.") if not self._values: for _ in range(len(values)): self._values.append(None) self._keys = list(values) self._BuildIndex() def _SetColour(self, value_list): """Sets row's colour attributes to a list of values in terminal.SGR.""" if value_list is None: self._color = None return colors = [] for color in value_list: if color in terminal.SGR: colors.append(color) elif color in terminal.FG_COLOR_WORDS: colors += terminal.FG_COLOR_WORDS[color] elif color in terminal.BG_COLOR_WORDS: colors += terminal.BG_COLOR_WORDS[color] else: raise ValueError("Invalid colour specification.") self._color = list(set(colors)) def _GetColour(self): if self._color is None: return None return list(self._color) def _SetValues(self, values): """Set values from supplied dictionary or list. Args: values: A Row, dict indexed by column name, or list. Raises: TypeError: Argument is not a list or dict, or list is not equal row length or dictionary keys don't match. """ def _ToStr(value): """Convert individul list entries to string.""" if isinstance(value, (list, tuple)): result = [] for val in value: result.append(str(val)) return result else: return str(value) # Row with identical header can be copied directly. if isinstance(values, Row): if self._keys != values.header: raise TypeError("Attempt to append row with mismatched header.") self._values = copy.deepcopy(values.values) elif isinstance(values, dict): for key in self._keys: if key not in values: raise TypeError("Dictionary key mismatch with row.") for key in self._keys: self[key] = _ToStr(values[key]) elif isinstance(values, list) or isinstance(values, tuple): if len(values) != len(self._values): raise TypeError("Supplied list length != row length") for (index, value) in enumerate(values): self._values[index] = _ToStr(value) else: raise TypeError( "Supplied argument must be Row, dict or list, not %s", type(values) ) def Insert(self, key, value, row_index): """Inserts new values at a specified offset. Args: key: string for header value. value: string for a data value. row_index: Offset into row for data. Raises: IndexError: If the offset is out of bands. """ if row_index < 0: row_index += len(self) if not 0 <= row_index < len(self): raise IndexError('Index "%s" is out of bounds.' % row_index) new_row = Row() for idx in self.header: if self.index(idx) == row_index: new_row[key] = value new_row[idx] = self[idx] self._keys = new_row.header self._values = new_row.values del new_row self._BuildIndex() color = property(_GetColour, _SetColour, doc="Colour spec of this row") header = property(_GetHeader, _SetHeader, doc="List of row's headers.") values = property(_GetValues, _SetValues, doc="List of row's values.") class TextTable(object): """Class that provides data methods on a tabular format. Data is stored as a list of Row() objects. The first row is always present as the header row. Attributes: row_class: class, A class to use for the Row object. separator: str, field separator when printing table. """ def __init__(self, row_class=Row): """Initialises a new table. Args: row_class: A class to use as the row object. This should be a subclass of this module's Row() class. """ self.row_class = row_class self.separator = ", " self.Reset() def Reset(self): self._row_index = 1 self._table = [[]] self._iterator = 0 # While loop row index def __repr__(self): return "%s(%r)" % (self.__class__.__name__, str(self)) def __str__(self): """Displays table with pretty formatting.""" return self.table def __incr__(self, incr=1): self._SetRowIndex(self._row_index + incr) def __contains__(self, name): """Whether the given column header name exists.""" return name in self.header def __getitem__(self, row): """Fetches the given row number.""" return self._table[row] def __iter__(self): """Iterator that excludes the header row.""" return self.next() def next(self): # Maintain a counter so a row can know what index it is. # Save the old value to support nested interations. old_iter = self._iterator try: for r in self._table[1:]: self._iterator = r.row yield r finally: # Recover the original index after loop termination or exit with break. self._iterator = old_iter def __add__(self, other): """Merges two with identical columns.""" new_table = copy.copy(self) for row in other: new_table.Append(row) return new_table def __copy__(self): """Copy table instance.""" new_table = self.__class__() # pylint: disable=protected-access new_table._table = [self.header] for row in self[1:]: new_table.Append(row) return new_table def Filter(self, function=None): """Construct Textable from the rows of which the function returns true. Args: function: A function applied to each row which returns a bool. If function is None, all rows with empty column values are removed. Returns: A new TextTable() Raises: TableError: When an invalid row entry is Append()'d """ flat = ( lambda x: x if isinstance(x, str) else "".join([flat(y) for y in x]) ) # noqa if function is None: function = lambda row: bool(flat(row.values)) # noqa new_table = self.__class__() # pylint: disable=protected-access new_table._table = [self.header] for row in self: if function(row) is True: new_table.Append(row) return new_table def Map(self, function): """Applies the function to every row in the table. Args: function: A function applied to each row. Returns: A new TextTable() Raises: TableError: When transform is not invalid row entry. The transform must be compatible with Append(). """ new_table = self.__class__() # pylint: disable=protected-access new_table._table = [self.header] for row in self: filtered_row = function(row) if filtered_row: new_table.Append(filtered_row) return new_table # pylint: disable=C6409 # pylint: disable=W0622 def sort(self, cmp=None, key=None, reverse=False): """Sorts rows in the texttable. Args: cmp: func, non default sort algorithm to use. key: func, applied to each element before sorting. reverse: bool, reverse order of sort. """ def _DefaultKey(value): """Default key func is to create a list of all fields.""" result = [] for key in self.header: # Try sorting as numerical value if possible. try: result.append(float(value[key])) except ValueError: result.append(value[key]) return result key = key or _DefaultKey # Exclude header by copying table. new_table = self._table[1:] if cmp is not None: key = cmp_to_key(cmp) new_table.sort(key=key, reverse=reverse) # Regenerate the table with original header self._table = [self.header] self._table.extend(new_table) # Re-write the 'row' attribute of each row for index, row in enumerate(self._table): row.row = index # pylint: enable=W0622 def extend(self, table, keys=None): """Extends all rows in the texttable. The rows are extended with the new columns from the table. Args: table: A texttable, the table to extend this table by. keys: A set, the set of columns to use as the key. If None, the row index is used. Raises: IndexError: If key is not a valid column name. """ if keys: for k in keys: if k not in self._Header(): raise IndexError("Unknown key: '%s'", k) extend_with = [] for column in table.header: if column not in self.header: extend_with.append(column) if not extend_with: return for column in extend_with: self.AddColumn(column) if not keys: for row1, row2 in zip(self, table): for column in extend_with: row1[column] = row2[column] return for row1 in self: for row2 in table: for k in keys: if row1[k] != row2[k]: break else: for column in extend_with: row1[column] = row2[column] break # pylint: enable=C6409 def Remove(self, row): """Removes a row from the table. Args: row: int, the row number to delete. Must be >= 1, as the header cannot be removed. Raises: TableError: Attempt to remove nonexistent or header row. """ if row == 0 or row > self.size: raise TableError("Attempt to remove header row") new_table = [] # pylint: disable=E1103 for t_row in self._table: if t_row.row != row: new_table.append(t_row) if t_row.row > row: t_row.row -= 1 self._table = new_table def _Header(self): """Returns the header row.""" return self._table[0] def _GetRow(self, columns=None): """Returns the current row as a tuple.""" row = self._table[self._row_index] if columns: result = [] for col in columns: if col not in self.header: raise TableError("Column header %s not known in table." % col) result.append(row[self.header.index(col)]) row = result return row def _SetRow(self, new_values, row=0): """Sets the current row to new list. Args: new_values: List|dict of new values to insert into row. row: int, Row to insert values into. Raises: TableError: If number of new values is not equal to row size. """ if not row: row = self._row_index if row > self.size: raise TableError("Entry %s beyond table size %s." % (row, self.size)) self._table[row].values = new_values def _SetHeader(self, new_values): """Sets header of table to the given tuple. Args: new_values: Tuple of new header values. """ row = self.row_class() row.row = 0 for v in new_values: row[v] = v self._table[0] = row def _SetRowIndex(self, row): if not row or row > self.size: raise TableError("Entry %s beyond table size %s." % (row, self.size)) self._row_index = row def _GetRowIndex(self): return self._row_index def _GetSize(self): """Returns number of rows in table.""" if not self._table: return 0 return len(self._table) - 1 def _GetTable(self): """Returns table, with column headers and separators. Returns: The whole table including headers as a string. Each row is joined by a newline and each entry by self.separator. """ result = [] # Avoid the global lookup cost on each iteration. lstr = str for row in self._table: result.append("%s\n" % self.separator.join(lstr(v) for v in row)) return "".join(result) def _SetTable(self, table): """Sets table, with column headers and separators.""" if not isinstance(table, TextTable): raise TypeError("Not an instance of TextTable.") self.Reset() self._table = copy.deepcopy(table._table) # pylint: disable=W0212 # Point parent table of each row back ourselves. for row in self: row.table = self def _SmallestColSize(self, text): """Finds the largest indivisible word of a string. ...and thus the smallest possible column width that can contain that word unsplit over rows. Args: text: A string of text potentially consisting of words. Returns: Integer size of the largest single word in the text. """ if not text: return 0 stripped = terminal.StripAnsiText(text) return max(len(word) for word in stripped.split()) def _TextJustify(self, text, col_size): """Formats text within column with white space padding. A single space is prefixed, and a number of spaces are added as a suffix such that the length of the resultant string equals the col_size. If the length of the text exceeds the column width available then it is split into words and returned as a list of string, each string contains one or more words padded to the column size. Args: text: String of text to format. col_size: integer size of column to pad out the text to. Returns: List of strings col_size in length. Raises: TableError: If col_size is too small to fit the words in the text. """ result = [] if "\n" in text: for paragraph in text.split("\n"): result.extend(self._TextJustify(paragraph, col_size)) return result wrapper = textwrap.TextWrapper( width=col_size - 2, break_long_words=False, expand_tabs=False ) try: text_list = wrapper.wrap(text) except ValueError: raise TableError("Field too small (minimum width: 3)") if not text_list: return [" " * col_size] for current_line in text_list: stripped_len = len(terminal.StripAnsiText(current_line)) ansi_color_adds = len(current_line) - stripped_len # +2 for white space on either side. if stripped_len + 2 > col_size: raise TableError("String contains words that do not fit in column.") result.append(" %-*s" % (col_size - 1 + ansi_color_adds, current_line)) return result def FormattedTable( self, width=80, force_display=False, ml_delimiter=True, color=True, display_header=True, columns=None, ): """Returns whole table, with whitespace padding and row delimiters. Args: width: An int, the max width we want the table to fit in. force_display: A bool, if set to True will display table when the table can't be made to fit to the width. ml_delimiter: A bool, if set to False will not display the multi-line delimiter. color: A bool. If true, display any colours in row.colour. display_header: A bool. If true, display header. columns: A list of str, show only columns with these names. Returns: A string. The tabled output. Raises: TableError: Width too narrow to display table. """ def _FilteredCols(): """Returns list of column names to display.""" if not columns: return self._Header().values return [col for col in self._Header().values if col in columns] # Largest is the biggest data entry in a column. largest = {} # Smallest is the same as above but with linewrap i.e. largest unbroken # word in the data stream. smallest = {} # largest == smallest for a column with a single word of data. # Initialise largest and smallest for all columns. for key in _FilteredCols(): largest[key] = 0 smallest[key] = 0 # Find the largest and smallest values. # Include Title line in equation. # pylint: disable=E1103 for row in self._table: for key, value in row.items(): if key not in _FilteredCols(): continue # Convert lists into a string. if isinstance(value, list): value = ", ".join(value) value = terminal.StripAnsiText(value) largest[key] = max(len(value), largest[key]) smallest[key] = max(self._SmallestColSize(value), smallest[key]) # pylint: enable=E1103 min_total_width = 0 multi_word = [] # Bump up the size of each column to include minimum pad. # Find all columns that can be wrapped (multi-line). # And the minimum width needed to display all columns (even if wrapped). for key in _FilteredCols(): # Each column is bracketed by a space on both sides. # So increase size required accordingly. largest[key] += 2 smallest[key] += 2 min_total_width += smallest[key] # If column contains data that 'could' be split over multiple lines. if largest[key] != smallest[key]: multi_word.append(key) # Check if we have enough space to display the table. if min_total_width > width and not force_display: raise TableError("Width too narrow to display table.") # We have some columns that may need wrapping over several lines. if multi_word: # Find how much space is left over for the wrapped columns to use. # Also find how much space we would need if they were not wrapped. # These are 'spare_width' and 'desired_width' respectively. desired_width = 0 spare_width = width - min_total_width for key in multi_word: spare_width += smallest[key] desired_width += largest[key] # Scale up the space we give each wrapped column. # Proportional to its size relative to 'desired_width' for all columns. # Rinse and repeat if we changed the wrap list in this iteration. # Once done we will have a list of columns that definitely need wrapping. done = False while not done: done = True for key in multi_word: # If we scale past the desired width for this particular column, # then give it its desired width and remove it from the wrapped list. if largest[key] <= round( (largest[key] / float(desired_width)) * spare_width ): smallest[key] = largest[key] multi_word.remove(key) spare_width -= smallest[key] desired_width -= largest[key] done = False # If we scale below the minimum width for this particular column, # then leave it at its minimum and remove it from the wrapped list. elif smallest[key] >= round( (largest[key] / float(desired_width)) * spare_width ): multi_word.remove(key) spare_width -= smallest[key] desired_width -= largest[key] done = False # Repeat the scaling algorithm with the final wrap list. # This time we assign the extra column space by increasing 'smallest'. for key in multi_word: smallest[key] = int( round((largest[key] / float(desired_width)) * spare_width) ) total_width = 0 row_count = 0 result_dict = {} # Format the header lines and add to result_dict. # Find what the total width will be and use this for the ruled lines. # Find how many rows are needed for the most wrapped line (row_count). for key in _FilteredCols(): result_dict[key] = self._TextJustify(key, smallest[key]) if len(result_dict[key]) > row_count: row_count = len(result_dict[key]) total_width += smallest[key] # Store header in header_list, working down the wrapped rows. header_list = [] for row_idx in range(row_count): for key in _FilteredCols(): try: header_list.append(result_dict[key][row_idx]) except IndexError: # If no value than use whitespace of equal size. header_list.append(" " * smallest[key]) header_list.append("\n") # Format and store the body lines result_dict = {} body_list = [] # We separate multi line rows with a single line delimiter. prev_muli_line = False # Unless it is the first line in which there is already the header line. first_line = True for row in self: row_count = 0 for key, value in row.items(): if key not in _FilteredCols(): continue # Convert field contents to a string. if isinstance(value, list): value = ", ".join(value) # Store results in result_dict and take note of wrapped line count. result_dict[key] = self._TextJustify(value, smallest[key]) if len(result_dict[key]) > row_count: row_count = len(result_dict[key]) if row_count > 1: prev_muli_line = True # If current or prior line was multi-line then include delimiter. if not first_line and prev_muli_line and ml_delimiter: body_list.append("-" * total_width + "\n") if row_count == 1: # Our current line was not wrapped, so clear flag. prev_muli_line = False row_list = [] for row_idx in range(row_count): for key in _FilteredCols(): try: row_list.append(result_dict[key][row_idx]) except IndexError: # If no value than use whitespace of equal size. row_list.append(" " * smallest[key]) row_list.append("\n") if color and row.color is not None: # Don't care about colors body_list.append("".join(row_list)) # body_list.append( # terminal.AnsiText(''.join(row_list)[:-1], # command_list=row.color)) # body_list.append('\n') else: body_list.append("".join(row_list)) first_line = False header = "".join(header_list) + "=" * total_width if color and self._Header().color is not None: pass # header = terminal.AnsiText(header, command_list=self._Header().color) # Add double line delimiter between header and main body. if display_header: return "%s\n%s" % (header, "".join(body_list)) return "%s" % "".join(body_list) def LabelValueTable(self, label_list=None): """Returns whole table as rows of name/value pairs. One (or more) column entries are used for the row prefix label. The remaining columns are each displayed as a row entry with the prefix labels appended. Use the first column as the label if label_list is None. Args: label_list: A list of prefix labels to use. Returns: Label/Value formatted table. Raises: TableError: If specified label is not a column header of the table. """ label_list = label_list or self._Header()[0] # Ensure all labels are valid. for label in label_list: if label not in self._Header(): raise TableError("Invalid label prefix: %s." % label) sorted_list = [] for header in self._Header(): if header in label_list: sorted_list.append(header) label_str = "# LABEL %s\n" % ".".join(sorted_list) body = [] for row in self: # Some of the row values are pulled into the label, stored in label_prefix. label_prefix = [] value_list = [] for key, value in row.items(): if key in sorted_list: # Set prefix. label_prefix.append(value) else: value_list.append("%s %s" % (key, value)) body.append( "".join(["%s.%s\n" % (".".join(label_prefix), v) for v in value_list]) ) return "%s%s" % (label_str, "".join(body)) table = property(_GetTable, _SetTable, doc="Whole table") row = property(_GetRow, _SetRow, doc="Current row") header = property(_Header, _SetHeader, doc="List of header entries.") row_index = property(_GetRowIndex, _SetRowIndex, doc="Current row.") size = property(_GetSize, doc="Number of rows in table.") def RowWith(self, column, value): """Retrieves the first non header row with the column of the given value. Args: column: str, the name of the column to check. value: str, The value of the column to check. Returns: A Row() of the first row found, None otherwise. Raises: IndexError: The specified column does not exist. """ for row in self._table[1:]: if row[column] == value: return row return None def AddColumn(self, column, default="", col_index=-1): """Appends a new column to the table. Args: column: A string, name of the column to add. default: Default value for entries. Defaults to ''. col_index: Integer index for where to insert new column. Raises: TableError: Column name already exists. """ if column in self.table: raise TableError("Column %r already in table." % column) if col_index == -1: self._table[0][column] = column for i in range(1, len(self._table)): self._table[i][column] = default else: self._table[0].Insert(column, column, col_index) for i in range(1, len(self._table)): self._table[i].Insert(column, default, col_index) def Append(self, new_values): """Adds a new row (list) to the table. Args: new_values: Tuple, dict, or Row() of new values to append as a row. Raises: TableError: Supplied tuple not equal to table width. """ newrow = self.NewRow() newrow.values = new_values self._table.append(newrow) def NewRow(self, value=""): """Fetches a new, empty row, with headers populated. Args: value: Initial value to set each row entry to. Returns: A Row() object. """ newrow = self.row_class() newrow.row = self.size + 1 newrow.table = self headers = self._Header() for header in headers: newrow[header] = value return newrow def CsvToTable(self, buf, header=True, separator=","): """Parses buffer into tabular format. Strips off comments (preceded by '#'). Optionally parses and indexes by first line (header). Args: buf: String file buffer containing CSV data. header: Is the first line of buffer a header. separator: String that CSV is separated by. Returns: int, the size of the table created. Raises: TableError: A parsing error occurred. """ self.Reset() header_row = self.row_class() if header: line = buf.readline() header_str = "" while not header_str: # Remove comments. header_str = line.split("#")[0].strip() if not header_str: line = buf.readline() header_list = header_str.split(separator) header_length = len(header_list) for entry in header_list: entry = entry.strip() if entry in header_row: raise TableError("Duplicate header entry %r." % entry) header_row[entry] = entry header_row.row = 0 self._table[0] = header_row # xreadlines would be better but not supported by StringIO for testing. for line in buf: # Support commented lines, provide '#' is first character of line. if line.startswith("#"): continue lst = line.split(separator) lst = [l.strip() for l in lst] if header and len(lst) != header_length: # Silently drop illegal line entries continue if not header: header_row = self.row_class() header_length = len(lst) header_row.values = dict( zip(range(header_length), range(header_length)) ) self._table[0] = header_row header = True continue new_row = self.NewRow() new_row.values = lst header_row.row = self.size + 1 self._table.append(new_row) return self.size def index(self, name=None): # pylint: disable=C6409 """Returns index number of supplied column name. Args: name: string of column name. Raises: TableError: If name not found. Returns: Index of the specified header entry. """ try: return self.header.index(name) except ValueError: raise TableError("Unknown index name %s." % name) netmiko-2.4.2/netmiko/scp_handler.py0000664000127700012770000003160413515366231020700 0ustar gitusergituser00000000000000""" Netmiko SCP operations. Supports file get and file put operations. SCP requires a separate SSH connection for a control channel. Currently only supports Cisco IOS and Cisco ASA. """ from __future__ import print_function from __future__ import unicode_literals import re import os import hashlib import scp class SCPConn(object): """ Establish a secure copy channel to the remote network device. Must close the SCP connection to get the file to write to the remote filesystem """ def __init__(self, ssh_conn): self.ssh_ctl_chan = ssh_conn self.establish_scp_conn() def establish_scp_conn(self): """Establish the secure copy connection.""" ssh_connect_params = self.ssh_ctl_chan._connect_params_dict() self.scp_conn = self.ssh_ctl_chan._build_ssh_client() self.scp_conn.connect(**ssh_connect_params) self.scp_client = scp.SCPClient(self.scp_conn.get_transport()) def scp_transfer_file(self, source_file, dest_file): """Put file using SCP (for backwards compatibility).""" self.scp_client.put(source_file, dest_file) def scp_get_file(self, source_file, dest_file): """Get file using SCP.""" self.scp_client.get(source_file, dest_file) def scp_put_file(self, source_file, dest_file): """Put file using SCP.""" self.scp_client.put(source_file, dest_file) def close(self): """Close the SCP connection.""" self.scp_conn.close() class BaseFileTransfer(object): """Class to manage SCP file transfer and associated SSH control channel.""" def __init__( self, ssh_conn, source_file, dest_file, file_system=None, direction="put" ): self.ssh_ctl_chan = ssh_conn self.source_file = source_file self.dest_file = dest_file self.direction = direction auto_flag = ( "cisco_ios" in ssh_conn.device_type or "cisco_xe" in ssh_conn.device_type or "cisco_xr" in ssh_conn.device_type ) if not file_system: if auto_flag: self.file_system = self.ssh_ctl_chan._autodetect_fs() else: raise ValueError("Destination file system not specified") else: self.file_system = file_system if direction == "put": self.source_md5 = self.file_md5(source_file) self.file_size = os.stat(source_file).st_size elif direction == "get": self.source_md5 = self.remote_md5(remote_file=source_file) self.file_size = self.remote_file_size(remote_file=source_file) else: raise ValueError("Invalid direction specified") def __enter__(self): """Context manager setup""" self.establish_scp_conn() return self def __exit__(self, exc_type, exc_value, traceback): """Context manager cleanup.""" self.close_scp_chan() def establish_scp_conn(self): """Establish SCP connection.""" self.scp_conn = SCPConn(self.ssh_ctl_chan) def close_scp_chan(self): """Close the SCP connection to the remote network device.""" self.scp_conn.close() self.scp_conn = None def remote_space_available(self, search_pattern=r"(\d+) \w+ free"): """Return space available on remote device.""" remote_cmd = "dir {}".format(self.file_system) remote_output = self.ssh_ctl_chan.send_command_expect(remote_cmd) match = re.search(search_pattern, remote_output) if "kbytes" in match.group(0) or "Kbytes" in match.group(0): return int(match.group(1)) * 1000 return int(match.group(1)) def _remote_space_available_unix(self, search_pattern=""): """Return space available on *nix system (BSD/Linux).""" self.ssh_ctl_chan._enter_shell() remote_cmd = "/bin/df -k {}".format(self.file_system) remote_output = self.ssh_ctl_chan.send_command( remote_cmd, expect_string=r"[\$#]" ) # Try to ensure parsing is correct: # Filesystem 1K-blocks Used Avail Capacity Mounted on # /dev/bo0s3f 1264808 16376 1147248 1% /cf/var remote_output = remote_output.strip() output_lines = remote_output.splitlines() # First line is the header; second is the actual file system info header_line = output_lines[0] filesystem_line = output_lines[1] if "Filesystem" not in header_line or "Avail" not in header_line.split()[3]: # Filesystem 1K-blocks Used Avail Capacity Mounted on msg = "Parsing error, unexpected output from {}:\n{}".format( remote_cmd, remote_output ) raise ValueError(msg) space_available = filesystem_line.split()[3] if not re.search(r"^\d+$", space_available): msg = "Parsing error, unexpected output from {}:\n{}".format( remote_cmd, remote_output ) raise ValueError(msg) self.ssh_ctl_chan._return_cli() return int(space_available) * 1024 def local_space_available(self): """Return space available on local filesystem.""" destination_stats = os.statvfs(".") return destination_stats.f_bsize * destination_stats.f_bavail def verify_space_available(self, search_pattern=r"(\d+) \w+ free"): """Verify sufficient space is available on destination file system (return boolean).""" if self.direction == "put": space_avail = self.remote_space_available(search_pattern=search_pattern) elif self.direction == "get": space_avail = self.local_space_available() if space_avail > self.file_size: return True return False def check_file_exists(self, remote_cmd=""): """Check if the dest_file already exists on the file system (return boolean).""" if self.direction == "put": if not remote_cmd: remote_cmd = "dir {}/{}".format(self.file_system, self.dest_file) remote_out = self.ssh_ctl_chan.send_command_expect(remote_cmd) search_string = r"Directory of .*{0}".format(self.dest_file) if ( "Error opening" in remote_out or "No such file or directory" in remote_out or "Path does not exist" in remote_out ): return False elif re.search(search_string, remote_out, flags=re.DOTALL): return True else: raise ValueError("Unexpected output from check_file_exists") elif self.direction == "get": return os.path.exists(self.dest_file) def _check_file_exists_unix(self, remote_cmd=""): """Check if the dest_file already exists on the file system (return boolean).""" if self.direction == "put": self.ssh_ctl_chan._enter_shell() remote_cmd = "ls {}".format(self.file_system) remote_out = self.ssh_ctl_chan.send_command( remote_cmd, expect_string=r"[\$#]" ) self.ssh_ctl_chan._return_cli() return self.dest_file in remote_out elif self.direction == "get": return os.path.exists(self.dest_file) def remote_file_size(self, remote_cmd="", remote_file=None): """Get the file size of the remote file.""" if remote_file is None: if self.direction == "put": remote_file = self.dest_file elif self.direction == "get": remote_file = self.source_file if not remote_cmd: remote_cmd = "dir {}/{}".format(self.file_system, remote_file) remote_out = self.ssh_ctl_chan.send_command(remote_cmd) # Strip out "Directory of flash:/filename line remote_out = re.split(r"Directory of .*", remote_out) remote_out = "".join(remote_out) # Match line containing file name escape_file_name = re.escape(remote_file) pattern = r".*({}).*".format(escape_file_name) match = re.search(pattern, remote_out) if match: line = match.group(0) # Format will be 26 -rw- 6738 Jul 30 2016 19:49:50 -07:00 filename file_size = line.split()[2] if "Error opening" in remote_out or "No such file or directory" in remote_out: raise IOError("Unable to find file on remote system") else: return int(file_size) def _remote_file_size_unix(self, remote_cmd="", remote_file=None): """Get the file size of the remote file.""" if remote_file is None: if self.direction == "put": remote_file = self.dest_file elif self.direction == "get": remote_file = self.source_file remote_file = "{}/{}".format(self.file_system, remote_file) if not remote_cmd: remote_cmd = "ls -l {}".format(remote_file) self.ssh_ctl_chan._enter_shell() remote_out = self.ssh_ctl_chan.send_command(remote_cmd, expect_string=r"[\$#]") self.ssh_ctl_chan._return_cli() if "No such file or directory" in remote_out: raise IOError("Unable to find file on remote system") escape_file_name = re.escape(remote_file) pattern = r"^.* ({}).*$".format(escape_file_name) match = re.search(pattern, remote_out, flags=re.M) if match: # Format: -rw-r--r-- 1 pyclass wheel 12 Nov 5 19:07 /var/tmp/test3.txt line = match.group(0) file_size = line.split()[4] return int(file_size) raise ValueError( "Search pattern not found for remote file size during SCP transfer." ) def file_md5(self, file_name): """Compute MD5 hash of file.""" with open(file_name, "rb") as f: file_contents = f.read() file_hash = hashlib.md5(file_contents).hexdigest() return file_hash @staticmethod def process_md5(md5_output, pattern=r"=\s+(\S+)"): """ Process the string to retrieve the MD5 hash Output from Cisco IOS (ASA is similar) .MD5 of flash:file_name Done! verify /md5 (flash:file_name) = 410db2a7015eaa42b1fe71f1bf3d59a2 """ match = re.search(pattern, md5_output) if match: return match.group(1) else: raise ValueError("Invalid output from MD5 command: {}".format(md5_output)) def compare_md5(self): """Compare md5 of file on network device to md5 of local file.""" if self.direction == "put": remote_md5 = self.remote_md5() return self.source_md5 == remote_md5 elif self.direction == "get": local_md5 = self.file_md5(self.dest_file) return self.source_md5 == local_md5 def remote_md5(self, base_cmd="verify /md5", remote_file=None): """Calculate remote MD5 and returns the hash. This command can be CPU intensive on the remote device. """ if remote_file is None: if self.direction == "put": remote_file = self.dest_file elif self.direction == "get": remote_file = self.source_file remote_md5_cmd = "{} {}/{}".format(base_cmd, self.file_system, remote_file) dest_md5 = self.ssh_ctl_chan.send_command(remote_md5_cmd, max_loops=1500) dest_md5 = self.process_md5(dest_md5) return dest_md5 def transfer_file(self): """SCP transfer file.""" if self.direction == "put": self.put_file() elif self.direction == "get": self.get_file() def get_file(self): """SCP copy the file from the remote device to local system.""" source_file = "{}/{}".format(self.file_system, self.source_file) self.scp_conn.scp_get_file(source_file, self.dest_file) self.scp_conn.close() def put_file(self): """SCP copy the file from the local system to the remote device.""" destination = "{}/{}".format(self.file_system, self.dest_file) self.scp_conn.scp_transfer_file(self.source_file, destination) # Must close the SCP connection to get the file written (flush) self.scp_conn.close() def verify_file(self): """Verify the file has been transferred correctly.""" return self.compare_md5() def enable_scp(self, cmd=None): """ Enable SCP on remote device. Defaults to Cisco IOS command """ if cmd is None: cmd = ["ip scp server enable"] elif not hasattr(cmd, "__iter__"): cmd = [cmd] self.ssh_ctl_chan.send_config_set(cmd) def disable_scp(self, cmd=None): """ Disable SCP on remote device. Defaults to Cisco IOS command """ if cmd is None: cmd = ["no ip scp server enable"] elif not hasattr(cmd, "__iter__"): cmd = [cmd] self.ssh_ctl_chan.send_config_set(cmd) netmiko-2.4.2/netmiko/scp_functions.py0000664000127700012770000000731713440357406021300 0ustar gitusergituser00000000000000""" Netmiko SCP operations. Supports file get and file put operations. SCP requires a separate SSH connection for a control channel. Currently only supports Cisco IOS and Cisco ASA. """ from __future__ import print_function from __future__ import unicode_literals from netmiko import FileTransfer, InLineTransfer def verifyspace_and_transferfile(scp_transfer): """Verify space and transfer file.""" if not scp_transfer.verify_space_available(): raise ValueError("Insufficient space available on remote device") scp_transfer.transfer_file() def file_transfer( ssh_conn, source_file, dest_file, file_system=None, direction="put", disable_md5=False, inline_transfer=False, overwrite_file=False, ): """Use Secure Copy or Inline (IOS-only) to transfer files to/from network devices. inline_transfer ONLY SUPPORTS TEXT FILES and will not support binary file transfers. return { 'file_exists': boolean, 'file_transferred': boolean, 'file_verified': boolean, } """ transferred_and_verified = { "file_exists": True, "file_transferred": True, "file_verified": True, } transferred_and_notverified = { "file_exists": True, "file_transferred": True, "file_verified": False, } nottransferred_but_verified = { "file_exists": True, "file_transferred": False, "file_verified": True, } if "cisco_ios" in ssh_conn.device_type or "cisco_xe" in ssh_conn.device_type: cisco_ios = True else: cisco_ios = False if not cisco_ios and inline_transfer: raise ValueError("Inline Transfer only supported for Cisco IOS/Cisco IOS-XE") scp_args = { "ssh_conn": ssh_conn, "source_file": source_file, "dest_file": dest_file, "direction": direction, } if file_system is not None: scp_args["file_system"] = file_system TransferClass = InLineTransfer if inline_transfer else FileTransfer with TransferClass(**scp_args) as scp_transfer: if scp_transfer.check_file_exists(): if overwrite_file: if not disable_md5: if scp_transfer.compare_md5(): return nottransferred_but_verified else: # File exists, you can overwrite it, MD5 is wrong (transfer file) verifyspace_and_transferfile(scp_transfer) if scp_transfer.compare_md5(): return transferred_and_verified else: raise ValueError( "MD5 failure between source and destination files" ) else: # File exists, you can overwrite it, but MD5 not allowed (transfer file) verifyspace_and_transferfile(scp_transfer) return transferred_and_notverified else: # File exists, but you can't overwrite it. if not disable_md5: if scp_transfer.compare_md5(): return nottransferred_but_verified msg = "File already exists and overwrite_file is disabled" raise ValueError(msg) else: verifyspace_and_transferfile(scp_transfer) # File doesn't exist if not disable_md5: if scp_transfer.compare_md5(): return transferred_and_verified else: raise ValueError("MD5 failure between source and destination files") else: return transferred_and_notverified netmiko-2.4.2/netmiko/citrix/0000775000127700012770000000000013534772673017356 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/citrix/__init__.py0000664000127700012770000000017213440357405021453 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.citrix.netscaler_ssh import NetscalerSSH __all__ = ["NetscalerSSH"] netmiko-2.4.2/netmiko/citrix/netscaler_ssh.py0000664000127700012770000000371713440357405022561 0ustar gitusergituser00000000000000import time from netmiko.base_connection import BaseConnection class NetscalerSSH(BaseConnection): """ Netscaler SSH class. """ def session_preparation(self): """Prepare the session after the connection has been established.""" # 0 will defer to the global delay factor delay_factor = self.select_delay_factor(delay_factor=0) self._test_channel_read() self.set_base_prompt() cmd = "{}set cli mode -page OFF{}".format(self.RETURN, self.RETURN) self.disable_paging(command=cmd) time.sleep(1 * delay_factor) self.set_base_prompt() time.sleep(0.3 * delay_factor) self.clear_buffer() def set_base_prompt( self, pri_prompt_terminator="#", alt_prompt_terminator=">", delay_factor=1 ): """Sets self.base_prompt. Netscaler has '>' for the prompt. """ prompt = self.find_prompt(delay_factor=delay_factor) if not prompt[-1] in (pri_prompt_terminator, alt_prompt_terminator): raise ValueError("Router prompt not found: {}".format(repr(prompt))) prompt = prompt.strip() if len(prompt) == 1: self.base_prompt = prompt else: # Strip off trailing terminator self.base_prompt = prompt[:-1] return self.base_prompt def check_config_mode(self): """Netscaler devices do not have a config mode.""" return False def config_mode(self): """Netscaler devices do not have a config mode.""" return "" def exit_config_mode(self): """Netscaler devices do not have a config mode.""" return "" def strip_prompt(self, a_string): """ Strip 'Done' from command output """ output = super(NetscalerSSH, self).strip_prompt(a_string) lines = output.split(self.RESPONSE_RETURN) if "Done" in lines[-1]: return self.RESPONSE_RETURN.join(lines[:-1]) else: return output netmiko-2.4.2/netmiko/mellanox/0000775000127700012770000000000013534772673017673 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/mellanox/mellanox_mlnxos_ssh.py0000664000127700012770000000474213516455041024333 0ustar gitusergituser00000000000000"""Mellanox MLNX-OS Switch support.""" from __future__ import unicode_literals import re from netmiko.cisco_base_connection import CiscoSSHConnection from netmiko import log class MellanoxMlnxosSSH(CiscoSSHConnection): """Mellanox MLNX-OS Switch support.""" def enable(self, cmd="enable", pattern="#", re_flags=re.IGNORECASE): """Enter into enable mode.""" output = "" if not self.check_enable_mode(): self.write_channel(self.normalize_cmd(cmd)) output += self.read_until_prompt_or_pattern( pattern=pattern, re_flags=re_flags ) if not self.check_enable_mode(): raise ValueError("Failed to enter enable mode.") return output def config_mode(self, config_command="config term", pattern="#"): return super(MellanoxMlnxosSSH, self).config_mode( config_command=config_command, pattern=pattern ) def check_config_mode(self, check_string="(config", pattern=r"#"): return super(MellanoxMlnxosSSH, self).check_config_mode( check_string=check_string, pattern=pattern ) def disable_paging(self, command="no cli session paging enable", delay_factor=1): return super(MellanoxMlnxosSSH, self).disable_paging( command=command, delay_factor=delay_factor ) def exit_config_mode(self, exit_config="exit", pattern="#"): """Mellanox does not support a single command to completely exit configuration mode. Consequently, need to keep checking and sending "exit". """ output = "" check_count = 12 while check_count >= 0: if self.check_config_mode(): self.write_channel(self.normalize_cmd(exit_config)) output += self.read_until_pattern(pattern=pattern) else: break check_count -= 1 # One last check for whether we successfully exited config mode if self.check_config_mode(): raise ValueError("Failed to exit configuration mode") log.debug("exit_config_mode: {}".format(output)) return output def save_config( self, cmd="configuration write", confirm=False, confirm_response="" ): """Save Config on Mellanox devices Enters and Leaves Config Mode""" output = self.enable() output += self.config_mode() output += self.send_command(cmd) output += self.exit_config_mode() return output netmiko-2.4.2/netmiko/mellanox/__init__.py0000664000127700012770000000021413516455041021764 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.mellanox.mellanox_mlnxos_ssh import MellanoxMlnxosSSH __all__ = ["MellanoxMlnxosSSH"] netmiko-2.4.2/netmiko/ruckus/0000775000127700012770000000000013534772673017370 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/ruckus/__init__.py0000664000127700012770000000033613440357406021470 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.ruckus.ruckus_fastiron import RuckusFastironSSH from netmiko.ruckus.ruckus_fastiron import RuckusFastironTelnet __all__ = ["RuckusFastironSSH", "RuckusFastironTelnet"] netmiko-2.4.2/netmiko/ruckus/ruckus_fastiron.py0000664000127700012770000000660013510465674023157 0ustar gitusergituser00000000000000from __future__ import unicode_literals import re import time from telnetlib import DO, DONT, ECHO, IAC, WILL, WONT from netmiko.cisco_base_connection import CiscoSSHConnection class RuckusFastironBase(CiscoSSHConnection): """Ruckus FastIron aka ICX support.""" def session_preparation(self): """FastIron requires to be enable mode to disable paging.""" self._test_channel_read() self.set_base_prompt() self.enable() self.disable_paging(command="skip-page-display") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def enable( self, cmd="enable", pattern=r"(ssword|User Name)", re_flags=re.IGNORECASE ): """Enter enable mode. With RADIUS can prompt for User Name SSH@Lab-ICX7250>en User Name:service_netmiko Password: SSH@Lab-ICX7250# """ output = "" if not self.check_enable_mode(): count = 4 i = 1 while i < count: self.write_channel(self.normalize_cmd(cmd)) new_data = self.read_until_prompt_or_pattern( pattern=pattern, re_flags=re_flags ) output += new_data if "User Name" in new_data: self.write_channel(self.normalize_cmd(self.username)) new_data = self.read_until_prompt_or_pattern( pattern=pattern, re_flags=re_flags ) output += new_data if "ssword" in new_data: self.write_channel(self.normalize_cmd(self.secret)) output += self.read_until_prompt() return output time.sleep(1) i += 1 if not self.check_enable_mode(): msg = ( "Failed to enter enable mode. Please ensure you pass " "the 'secret' argument to ConnectHandler." ) raise ValueError(msg) def save_config(self, cmd="write mem", confirm=False, confirm_response=""): """Saves configuration.""" return super(RuckusFastironBase, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) class RuckusFastironTelnet(RuckusFastironBase): def __init__(self, *args, **kwargs): default_enter = kwargs.get("default_enter") kwargs["default_enter"] = "\r\n" if default_enter is None else default_enter super(RuckusFastironTelnet, self).__init__(*args, **kwargs) def _process_option(self, tsocket, command, option): """ Ruckus FastIron/ICX does not always echo commands to output by default. If server expresses interest in 'ECHO' option, then reply back with 'DO ECHO' """ if option == ECHO: tsocket.sendall(IAC + DO + ECHO) elif command in (DO, DONT): tsocket.sendall(IAC + WONT + option) elif command in (WILL, WONT): tsocket.sendall(IAC + DONT + option) def telnet_login(self, *args, **kwargs): # set callback function to handle telnet options. self.remote_conn.set_option_negotiation_callback(self._process_option) return super(RuckusFastironTelnet, self).telnet_login(*args, **kwargs) class RuckusFastironSSH(RuckusFastironBase): pass netmiko-2.4.2/netmiko/linux/0000775000127700012770000000000013534772673017213 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/linux/__init__.py0000664000127700012770000000022513440357405021307 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.linux.linux_ssh import LinuxSSH, LinuxFileTransfer __all__ = ["LinuxSSH", "LinuxFileTransfer"] netmiko-2.4.2/netmiko/linux/linux_ssh.py0000664000127700012770000001375413534772563021611 0ustar gitusergituser00000000000000from __future__ import unicode_literals import os import re import socket import time from netmiko.cisco_base_connection import CiscoSSHConnection from netmiko.cisco_base_connection import CiscoFileTransfer from netmiko.ssh_exception import NetMikoTimeoutException LINUX_PROMPT_PRI = os.getenv("NETMIKO_LINUX_PROMPT_PRI", "$") LINUX_PROMPT_ALT = os.getenv("NETMIKO_LINUX_PROMPT_ALT", "#") LINUX_PROMPT_ROOT = os.getenv("NETMIKO_LINUX_PROMPT_ROOT", "#") class LinuxSSH(CiscoSSHConnection): def session_preparation(self): """Prepare the session after the connection has been established.""" self.ansi_escape_codes = True return super(LinuxSSH, self).session_preparation() def _enter_shell(self): """Already in shell.""" return "" def _return_cli(self): """The shell is the CLI.""" return "" def disable_paging(self, *args, **kwargs): """Linux doesn't have paging by default.""" return "" def set_base_prompt( self, pri_prompt_terminator=LINUX_PROMPT_PRI, alt_prompt_terminator=LINUX_PROMPT_ALT, delay_factor=1, ): """Determine base prompt.""" return super(LinuxSSH, self).set_base_prompt( pri_prompt_terminator=pri_prompt_terminator, alt_prompt_terminator=alt_prompt_terminator, delay_factor=delay_factor, ) def send_config_set(self, config_commands=None, exit_config_mode=True, **kwargs): """Can't exit from root (if root)""" if self.username == "root": exit_config_mode = False return super(LinuxSSH, self).send_config_set( config_commands=config_commands, exit_config_mode=exit_config_mode, **kwargs ) def check_config_mode(self, check_string=LINUX_PROMPT_ROOT): """Verify root""" return self.check_enable_mode(check_string=check_string) def config_mode(self, config_command="sudo su"): """Attempt to become root.""" return self.enable(cmd=config_command) def exit_config_mode(self, exit_config="exit"): return self.exit_enable_mode(exit_command=exit_config) def check_enable_mode(self, check_string=LINUX_PROMPT_ROOT): """Verify root""" return super(LinuxSSH, self).check_enable_mode(check_string=check_string) def exit_enable_mode(self, exit_command="exit"): """Exit enable mode.""" delay_factor = self.select_delay_factor(delay_factor=0) output = "" if self.check_enable_mode(): self.write_channel(self.normalize_cmd(exit_command)) time.sleep(0.3 * delay_factor) self.set_base_prompt() if self.check_enable_mode(): raise ValueError("Failed to exit enable mode.") return output def enable(self, cmd="sudo su", pattern="ssword", re_flags=re.IGNORECASE): """Attempt to become root.""" delay_factor = self.select_delay_factor(delay_factor=0) output = "" if not self.check_enable_mode(): self.write_channel(self.normalize_cmd(cmd)) time.sleep(0.3 * delay_factor) try: output += self.read_channel() if re.search(pattern, output, flags=re_flags): self.write_channel(self.normalize_cmd(self.secret)) self.set_base_prompt() except socket.timeout: raise NetMikoTimeoutException( "Timed-out reading channel, data not available." ) if not self.check_enable_mode(): msg = ( "Failed to enter enable mode. Please ensure you pass " "the 'secret' argument to ConnectHandler." ) raise ValueError(msg) return output def cleanup(self): """Try to Gracefully exit the SSH session.""" self._session_log_fin = True self.write_channel("exit" + self.RETURN) def save_config(self, *args, **kwargs): """Not Implemented""" raise NotImplementedError class LinuxFileTransfer(CiscoFileTransfer): """ Linux SCP File Transfer driver. Mostly for testing purposes. """ def __init__( self, ssh_conn, source_file, dest_file, file_system="/var/tmp", direction="put" ): return super(LinuxFileTransfer, self).__init__( ssh_conn=ssh_conn, source_file=source_file, dest_file=dest_file, file_system=file_system, direction=direction, ) def remote_space_available(self, search_pattern=""): """Return space available on remote device.""" return self._remote_space_available_unix(search_pattern=search_pattern) def check_file_exists(self, remote_cmd=""): """Check if the dest_file already exists on the file system (return boolean).""" return self._check_file_exists_unix(remote_cmd=remote_cmd) def remote_file_size(self, remote_cmd="", remote_file=None): """Get the file size of the remote file.""" return self._remote_file_size_unix( remote_cmd=remote_cmd, remote_file=remote_file ) def remote_md5(self, base_cmd="md5sum", remote_file=None): if remote_file is None: if self.direction == "put": remote_file = self.dest_file elif self.direction == "get": remote_file = self.source_file remote_md5_cmd = "{} {}/{}".format(base_cmd, self.file_system, remote_file) dest_md5 = self.ssh_ctl_chan.send_command( remote_md5_cmd, max_loops=750, delay_factor=2 ) dest_md5 = self.process_md5(dest_md5) return dest_md5 @staticmethod def process_md5(md5_output, pattern=r"^(\S+)\s+"): return super(LinuxFileTransfer, LinuxFileTransfer).process_md5( md5_output, pattern=pattern ) def enable_scp(self, cmd=None): raise NotImplementedError def disable_scp(self, cmd=None): raise NotImplementedError netmiko-2.4.2/netmiko/rad/0000775000127700012770000000000013534772673016622 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/rad/__init__.py0000664000127700012770000000025013440357406020715 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.rad.rad_etx import RadETXSSH from netmiko.rad.rad_etx import RadETXTelnet __all__ = ["RadETXSSH", "RadETXTelnet"] netmiko-2.4.2/netmiko/rad/rad_etx.py0000664000127700012770000000605013440357406020610 0ustar gitusergituser00000000000000from __future__ import unicode_literals from __future__ import print_function import time from netmiko.base_connection import BaseConnection class RadETXBase(BaseConnection): """RAD ETX Support, Tested on RAD 203AX, 205A and 220A.""" def session_preparation(self): self._test_channel_read() self.set_base_prompt() self.disable_paging(command="config term length 0") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def save_config(self, cmd="admin save", confirm=False, confirm_response=""): """Saves Config Using admin save.""" if confirm: output = self.send_command_timing(command_string=cmd) if confirm_response: output += self.send_command_timing(confirm_response) else: # Send enter by default output += self.send_command_timing(self.RETURN) else: # Some devices are slow so match on trailing-prompt if you can output = self.send_command(command_string=cmd) return output def check_enable_mode(self, *args, **kwargs): """The Rad ETX software does not have an enable.""" pass def enable(self, *args, **kwargs): """The Rad ETX software does not have an enable.""" pass def exit_enable_mode(self, *args, **kwargs): """The Rad ETX software does not have an enable.""" pass def config_mode(self, config_command="config", pattern=">config"): """Enter into configuration mode on remote device.""" return super(RadETXBase, self).config_mode( config_command=config_command, pattern=pattern ) def check_config_mode(self, check_string=">config", pattern=""): """ Checks if the device is in configuration mode or not. Rad config starts with baseprompt>config. """ return super(RadETXBase, self).check_config_mode( check_string=check_string, pattern=pattern ) def exit_config_mode(self, exit_config="exit all", pattern="#"): """Exit from configuration mode.""" return super(RadETXBase, self).exit_config_mode( exit_config=exit_config, pattern=pattern ) class RadETXSSH(RadETXBase): """RAD ETX SSH Support.""" def __init__(self, **kwargs): # Found that a global_delay_factor of 2 is needed at minimum for SSH to the Rad ETX. kwargs.setdefault("global_delay_factor", 2) return super(RadETXSSH, self).__init__(**kwargs) class RadETXTelnet(RadETXBase): """RAD ETX Telnet Support.""" def telnet_login( self, username_pattern=r"(?:user>)", alt_prompt_term=r"#\s*$", **kwargs ): """ RAD presents with the following on login user> password> **** """ self.TELNET_RETURN = self.RETURN return super(RadETXTelnet, self).telnet_login( username_pattern=username_pattern, alt_prompt_terminator=alt_prompt_term, **kwargs ) netmiko-2.4.2/netmiko/calix/0000775000127700012770000000000013534772673017154 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/calix/calix_b6.py0000664000127700012770000000657613510465674021225 0ustar gitusergituser00000000000000"""Calix B6 SSH Driver for Netmiko""" from __future__ import unicode_literals import time from os import path from paramiko import SSHClient from netmiko.cisco_base_connection import CiscoSSHConnection class SSHClient_noauth(SSHClient): """Set noauth when manually handling SSH authentication.""" def _auth(self, username, *args): self._transport.auth_none(username) return class CalixB6Base(CiscoSSHConnection): """Common methods for Calix B6, both SSH and Telnet.""" def __init__(self, *args, **kwargs): default_enter = kwargs.get("default_enter") kwargs["default_enter"] = "\r\n" if default_enter is None else default_enter super(CalixB6Base, self).__init__(*args, **kwargs) def session_preparation(self): """Prepare the session after the connection has been established.""" self.ansi_escape_codes = True self._test_channel_read() self.set_base_prompt() self.disable_paging() self.set_terminal_width(command="terminal width 511") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def special_login_handler(self, delay_factor=1): """ Calix B6 presents with the following on login: login as: Password: **** """ delay_factor = self.select_delay_factor(delay_factor) i = 0 time.sleep(delay_factor * 0.25) output = "" while i <= 12: output = self.read_channel() if output: if "login as:" in output: self.write_channel(self.username + self.RETURN) elif "Password:" in output: self.write_channel(self.password + self.RETURN) break time.sleep(delay_factor * 0.5) else: self.write_channel(self.RETURN) time.sleep(delay_factor * 1) i += 1 def check_config_mode(self, check_string=")#", pattern=""): """Checks if the device is in configuration mode""" return super(CalixB6Base, self).check_config_mode(check_string=check_string) def save_config(self, cmd="copy run start", confirm=False, confirm_response=""): return super(CalixB6Base, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) class CalixB6SSH(CalixB6Base): """Calix B6 SSH Driver. To make it work, we have to override the SSHClient _auth method and manually handle the username/password. """ def _build_ssh_client(self): """Prepare for Paramiko SSH connection.""" # Create instance of SSHClient object # If not using SSH keys, we use noauth if not self.use_keys: remote_conn_pre = SSHClient_noauth() else: remote_conn_pre = SSHClient() # Load host_keys for better SSH security if self.system_host_keys: remote_conn_pre.load_system_host_keys() if self.alt_host_keys and path.isfile(self.alt_key_file): remote_conn_pre.load_host_keys(self.alt_key_file) # Default is to automatically add untrusted hosts (make sure appropriate for your env) remote_conn_pre.set_missing_host_key_policy(self.key_policy) return remote_conn_pre class CalixB6Telnet(CalixB6Base): """Calix B6 Telnet Driver.""" pass netmiko-2.4.2/netmiko/calix/__init__.py0000664000127700012770000000022013440357405021243 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.calix.calix_b6 import CalixB6SSH, CalixB6Telnet __all__ = ["CalixB6SSH", "CalixB6Telnet"] netmiko-2.4.2/netmiko/ciena/0000775000127700012770000000000013534772673017133 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/ciena/ciena_saos_ssh.py0000664000127700012770000000127013510465674022460 0ustar gitusergituser00000000000000"""Ciena SAOS support.""" from __future__ import print_function from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class CienaSaosSSH(CiscoSSHConnection): """Ciena SAOS support.""" def session_preparation(self): self._test_channel_read() self.set_base_prompt() self.disable_paging(command="system shell session set more off") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def enable(self, *args, **kwargs): pass def save_config(self, *args, **kwargs): """Not Implemented""" raise NotImplementedError netmiko-2.4.2/netmiko/ciena/__init__.py0000664000127700012770000000017213440357405021230 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.ciena.ciena_saos_ssh import CienaSaosSSH __all__ = ["CienaSaosSSH"] netmiko-2.4.2/netmiko/__init__.py0000664000127700012770000000246313534772563020170 0ustar gitusergituser00000000000000from __future__ import unicode_literals import logging # Logging configuration log = logging.getLogger(__name__) # noqa log.addHandler(logging.NullHandler()) # noqa from netmiko.ssh_dispatcher import ConnectHandler from netmiko.ssh_dispatcher import ssh_dispatcher from netmiko.ssh_dispatcher import redispatch from netmiko.ssh_dispatcher import platforms from netmiko.ssh_dispatcher import FileTransfer from netmiko.scp_handler import SCPConn from netmiko.cisco.cisco_ios import InLineTransfer from netmiko.ssh_exception import NetMikoTimeoutException from netmiko.ssh_exception import NetMikoAuthenticationException from netmiko.ssh_autodetect import SSHDetect from netmiko.base_connection import BaseConnection from netmiko.scp_functions import file_transfer # Alternate naming NetmikoTimeoutError = NetMikoTimeoutException NetmikoAuthError = NetMikoAuthenticationException Netmiko = ConnectHandler __version__ = "2.4.2" __all__ = ( "ConnectHandler", "ssh_dispatcher", "platforms", "SCPConn", "FileTransfer", "NetMikoTimeoutException", "NetMikoAuthenticationException", "NetmikoTimeoutError", "NetmikoAuthError", "InLineTransfer", "redispatch", "SSHDetect", "BaseConnection", "Netmiko", "file_transfer", ) # Cisco cntl-shift-six sequence CNTL_SHIFT_6 = chr(30) netmiko-2.4.2/netmiko/ubiquiti/0000775000127700012770000000000013534772673017707 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/ubiquiti/__init__.py0000664000127700012770000000017513440357406022010 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.ubiquiti.edge_ssh import UbiquitiEdgeSSH __all__ = ["UbiquitiEdgeSSH"] netmiko-2.4.2/netmiko/ubiquiti/edge_ssh.py0000664000127700012770000000321513510465674022035 0ustar gitusergituser00000000000000from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class UbiquitiEdgeSSH(CiscoSSHConnection): """ Implements support for Ubiquity EdgeSwitch devices. Mostly conforms to Cisco IOS style syntax with a few minor changes. This is NOT for EdgeRouter devices. """ def session_preparation(self): self._test_channel_read() self.set_base_prompt() self.enable() self.set_base_prompt() self.disable_paging() self.set_terminal_width() # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def check_config_mode(self, check_string=")#"): """Checks if the device is in configuration mode or not.""" return super(UbiquitiEdgeSSH, self).check_config_mode(check_string=check_string) def config_mode(self, config_command="configure"): """Enter configuration mode.""" return super(UbiquitiEdgeSSH, self).config_mode(config_command=config_command) def exit_config_mode(self, exit_config="exit"): """Exit configuration mode.""" return super(UbiquitiEdgeSSH, self).exit_config_mode(exit_config=exit_config) def exit_enable_mode(self, exit_command="exit"): """Exit enable mode.""" return super(UbiquitiEdgeSSH, self).exit_enable_mode(exit_command=exit_command) def save_config(self, cmd="write memory", confirm=False, confirm_response=""): """Saves configuration.""" return super(UbiquitiEdgeSSH, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) netmiko-2.4.2/netmiko/hp/0000775000127700012770000000000013534772673016463 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/hp/hp_procurve.py0000664000127700012770000001172413534772563021374 0ustar gitusergituser00000000000000from __future__ import print_function from __future__ import unicode_literals import re import time import socket from os import path from paramiko import SSHClient from netmiko.cisco_base_connection import CiscoSSHConnection from netmiko import log class SSHClient_noauth(SSHClient): """Set noauth when manually handling SSH authentication.""" def _auth(self, username, *args): self._transport.auth_none(username) return class HPProcurveBase(CiscoSSHConnection): def session_preparation(self): """ Prepare the session after the connection has been established. """ # HP output contains VT100 escape codes self.ansi_escape_codes = True self._test_channel_read(pattern=r"[>#]") self.set_base_prompt() command = self.RETURN + "no page" self.disable_paging(command=command) self.set_terminal_width(command="terminal width 511") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def enable( self, cmd="enable", pattern="password", re_flags=re.IGNORECASE, default_username="manager", ): """Enter enable mode""" if self.check_enable_mode(): return "" output = self.send_command_timing(cmd) if ( "username" in output.lower() or "login name" in output.lower() or "user name" in output.lower() ): output += self.send_command_timing(default_username) if "password" in output.lower(): output += self.send_command_timing(self.secret) log.debug("{}".format(output)) self.clear_buffer() return output def cleanup(self): """Gracefully exit the SSH session.""" self.exit_config_mode() self.write_channel("logout" + self.RETURN) count = 0 while count <= 5: time.sleep(0.5) output = self.read_channel() if "Do you want to log out" in output: self._session_log_fin = True self.write_channel("y" + self.RETURN) # Don't automatically save the config (user's responsibility) elif "Do you want to save the current" in output: self._session_log_fin = True self.write_channel("n" + self.RETURN) try: self.write_channel(self.RETURN) except socket.error: break count += 1 def save_config(self, cmd="write memory", confirm=False, confirm_response=""): """Save Config.""" return super(HPProcurveBase, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) class HPProcurveSSH(HPProcurveBase): def session_preparation(self): """ Prepare the session after the connection has been established. """ # Procurve over SHH uses 'Press any key to continue' delay_factor = self.select_delay_factor(delay_factor=0) output = "" count = 1 while count <= 30: output += self.read_channel() if "any key to continue" in output: self.write_channel(self.RETURN) break else: time.sleep(0.33 * delay_factor) count += 1 # Try one last time to past "Press any key to continue self.write_channel(self.RETURN) super(HPProcurveSSH, self).session_preparation() def _build_ssh_client(self): """Allow passwordless authentication for HP devices being provisioned.""" # Create instance of SSHClient object. If no SSH keys and no password, then use noauth if not self.use_keys and not self.password: remote_conn_pre = SSHClient_noauth() else: remote_conn_pre = SSHClient() # Load host_keys for better SSH security if self.system_host_keys: remote_conn_pre.load_system_host_keys() if self.alt_host_keys and path.isfile(self.alt_key_file): remote_conn_pre.load_host_keys(self.alt_key_file) # Default is to automatically add untrusted hosts (make sure appropriate for your env) remote_conn_pre.set_missing_host_key_policy(self.key_policy) return remote_conn_pre class HPProcurveTelnet(HPProcurveBase): def telnet_login( self, pri_prompt_terminator="#", alt_prompt_terminator=">", username_pattern=r"Login Name:", pwd_pattern=r"assword", delay_factor=1, max_loops=60, ): """Telnet login: can be username/password or just password.""" super(HPProcurveTelnet, self).telnet_login( pri_prompt_terminator=pri_prompt_terminator, alt_prompt_terminator=alt_prompt_terminator, username_pattern=username_pattern, pwd_pattern=pwd_pattern, delay_factor=delay_factor, max_loops=max_loops, ) netmiko-2.4.2/netmiko/hp/hp_comware.py0000664000127700012770000000703013510465674021153 0ustar gitusergituser00000000000000from __future__ import print_function from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class HPComwareBase(CiscoSSHConnection): def session_preparation(self): """ Prepare the session after the connection has been established. Extra time to read HP banners. """ delay_factor = self.select_delay_factor(delay_factor=0) i = 1 while i <= 4: # Comware can have a banner that prompts you to continue # 'Press Y or ENTER to continue, N to exit.' time.sleep(0.5 * delay_factor) self.write_channel("\n") i += 1 time.sleep(0.3 * delay_factor) self.clear_buffer() self._test_channel_read(pattern=r"[>\]]") self.set_base_prompt() command = self.RETURN + "screen-length disable" self.disable_paging(command=command) # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def config_mode(self, config_command="system-view"): """Enter configuration mode.""" return super(HPComwareBase, self).config_mode(config_command=config_command) def exit_config_mode(self, exit_config="return", pattern=r">"): """Exit config mode.""" return super(HPComwareBase, self).exit_config_mode( exit_config=exit_config, pattern=pattern ) def check_config_mode(self, check_string="]"): """Check whether device is in configuration mode. Return a boolean.""" return super(HPComwareBase, self).check_config_mode(check_string=check_string) def set_base_prompt( self, pri_prompt_terminator=">", alt_prompt_terminator="]", delay_factor=1 ): """ Sets self.base_prompt Used as delimiter for stripping of trailing prompt in output. Should be set to something that is general and applies in multiple contexts. For Comware this will be the router prompt with < > or [ ] stripped off. This will be set on logging in, but not when entering system-view """ prompt = super(HPComwareBase, self).set_base_prompt( pri_prompt_terminator=pri_prompt_terminator, alt_prompt_terminator=alt_prompt_terminator, delay_factor=delay_factor, ) # Strip off leading character prompt = prompt[1:] prompt = prompt.strip() self.base_prompt = prompt return self.base_prompt def enable(self, cmd="system-view"): """enable mode on Comware is system-view.""" return self.config_mode(config_command=cmd) def exit_enable_mode(self, exit_command="return"): """enable mode on Comware is system-view.""" return self.exit_config_mode(exit_config=exit_command) def check_enable_mode(self, check_string="]"): """enable mode on Comware is system-view.""" return self.check_config_mode(check_string=check_string) def save_config(self, cmd="save force", confirm=False, confirm_response=""): """Save Config.""" return super(HPComwareBase, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) class HPComwareSSH(HPComwareBase): pass class HPComwareTelnet(HPComwareBase): def __init__(self, *args, **kwargs): default_enter = kwargs.get("default_enter") kwargs["default_enter"] = "\r\n" if default_enter is None else default_enter super(HPComwareTelnet, self).__init__(*args, **kwargs) netmiko-2.4.2/netmiko/hp/__init__.py0000664000127700012770000000037713440357405020567 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.hp.hp_procurve import HPProcurveSSH, HPProcurveTelnet from netmiko.hp.hp_comware import HPComwareSSH, HPComwareTelnet __all__ = ["HPProcurveSSH", "HPProcurveTelnet", "HPComwareSSH", "HPComwareTelnet"] netmiko-2.4.2/netmiko/snmp_autodetect.py0000664000127700012770000002553613440357406021624 0ustar gitusergituser00000000000000""" This module is used to auto-detect the type of a device in order to automatically create a Netmiko connection. The will avoid to hard coding the 'device_type' when using the ConnectHandler factory function from Netmiko. Example: ------------------ from netmiko.snmp_autodetect import SNMPDetect my_snmp = SNMPDetect(hostname='1.1.1.70', user='pysnmp', auth_key='key1', encrypt_key='key2') device_type = my_snmp.autodetect() ------------------ autodetect will return None if no match. SNMPDetect class defaults to SNMPv3 Note, pysnmp is a required dependency for SNMPDetect and is intentionally not included in netmiko requirements. So installation of pysnmp might be required. """ from __future__ import unicode_literals import re try: from pysnmp.entity.rfc3413.oneliner import cmdgen except ImportError: raise ImportError("pysnmp not installed; please install it: 'pip install pysnmp'") from netmiko.ssh_dispatcher import CLASS_MAPPER from netmiko.py23_compat import text_type # Higher priority indicates a better match. SNMP_MAPPER_BASE = { "arista_eos": { "oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*Arista Networks EOS.*", re.IGNORECASE), "priority": 99, }, "paloalto_panos": { "oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*Palo Alto Networks.*", re.IGNORECASE), "priority": 99, }, "hp_comware": { "oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*HP Comware.*", re.IGNORECASE), "priority": 99, }, "hp_procurve": { "oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".ProCurve", re.IGNORECASE), "priority": 99, }, "cisco_ios": { "oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*Cisco IOS Software,.*", re.IGNORECASE), "priority": 60, }, "cisco_xe": { "oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*IOS-XE Software,.*", re.IGNORECASE), "priority": 99, }, "cisco_xr": { "oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*Cisco IOS XR Software.*", re.IGNORECASE), "priority": 99, }, "cisco_asa": { "oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*Cisco Adaptive Security Appliance.*", re.IGNORECASE), "priority": 99, }, "cisco_nxos": { "oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*Cisco NX-OS.*", re.IGNORECASE), "priority": 99, }, "cisco_wlc": { "oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*Cisco Controller.*", re.IGNORECASE), "priority": 99, }, "f5_tmsh": { "oid": ".1.3.6.1.4.1.3375.2.1.4.1.0", "expr": re.compile(r".*BIG-IP.*", re.IGNORECASE), "priority": 99, }, "fortinet": { "oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r"Forti.*", re.IGNORECASE), "priority": 80, }, "checkpoint": { "oid": ".1.3.6.1.4.1.2620.1.6.16.9.0", "expr": re.compile(r"CheckPoint"), "priority": 79, }, "juniper_junos": { "oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*Juniper.*"), "priority": 99, }, } # Ensure all SNMP device types are supported by Netmiko SNMP_MAPPER = {} std_device_types = list(CLASS_MAPPER.keys()) for device_type in std_device_types: if SNMP_MAPPER_BASE.get(device_type): SNMP_MAPPER[device_type] = SNMP_MAPPER_BASE[device_type] class SNMPDetect(object): """ The SNMPDetect class tries to automatically determine the device type. Typically this will use the MIB-2 SysDescr and regular expressions. Parameters ---------- hostname: str The name or IP address of the hostname we want to guess the type snmp_version : str, optional ('v1', 'v2c' or 'v3') The SNMP version that is running on the device (default: 'v3') snmp_port : int, optional The UDP port on which SNMP is listening (default: 161) community : str, optional The SNMP read community when using SNMPv2 (default: None) user : str, optional The SNMPv3 user for authentication (default: '') auth_key : str, optional The SNMPv3 authentication key (default: '') encrypt_key : str, optional The SNMPv3 encryption key (default: '') auth_proto : str, optional ('des', '3des', 'aes128', 'aes192', 'aes256') The SNMPv3 authentication protocol (default: 'aes128') encrypt_proto : str, optional ('sha', 'md5') The SNMPv3 encryption protocol (default: 'sha') Attributes ---------- hostname: str The name or IP address of the device we want to guess the type snmp_version : str The SNMP version that is running on the device snmp_port : int The UDP port on which SNMP is listening community : str The SNMP read community when using SNMPv2 user : str The SNMPv3 user for authentication auth_key : str The SNMPv3 authentication key encrypt_key : str The SNMPv3 encryption key auth_proto : str The SNMPv3 authentication protocol encrypt_proto : str The SNMPv3 encryption protocol Methods ------- autodetect() Try to determine the device type. """ def __init__( self, hostname, snmp_version="v3", snmp_port=161, community=None, user="", auth_key="", encrypt_key="", auth_proto="sha", encrypt_proto="aes128", ): # Check that the SNMP version is matching predefined type or raise ValueError if snmp_version == "v1" or snmp_version == "v2c": if not community: raise ValueError("SNMP version v1/v2c community must be set.") elif snmp_version == "v3": if not user: raise ValueError("SNMP version v3 user and password must be set") else: raise ValueError("SNMP version must be set to 'v1', 'v2c' or 'v3'") # Check that the SNMPv3 auth & priv parameters match allowed types self._snmp_v3_authentication = { "sha": cmdgen.usmHMACSHAAuthProtocol, "md5": cmdgen.usmHMACMD5AuthProtocol, } self._snmp_v3_encryption = { "des": cmdgen.usmDESPrivProtocol, "3des": cmdgen.usm3DESEDEPrivProtocol, "aes128": cmdgen.usmAesCfb128Protocol, "aes192": cmdgen.usmAesCfb192Protocol, "aes256": cmdgen.usmAesCfb256Protocol, } if auth_proto not in self._snmp_v3_authentication.keys(): raise ValueError( "SNMP V3 'auth_proto' argument must be one of the following: {}".format( self._snmp_v3_authentication.keys() ) ) if encrypt_proto not in self._snmp_v3_encryption.keys(): raise ValueError( "SNMP V3 'encrypt_proto' argument must be one of the following: {}".format( self._snmp_v3_encryption.keys() ) ) self.hostname = hostname self.snmp_version = snmp_version self.snmp_port = snmp_port self.community = community self.user = user self.auth_key = auth_key self.encrypt_key = encrypt_key self.auth_proto = self._snmp_v3_authentication[auth_proto] self.encryp_proto = self._snmp_v3_encryption[encrypt_proto] self._response_cache = {} def _get_snmpv3(self, oid): """ Try to send an SNMP GET operation using SNMPv3 for the specified OID. Parameters ---------- oid : str The SNMP OID that you want to get. Returns ------- string : str The string as part of the value from the OID you are trying to retrieve. """ snmp_target = (self.hostname, self.snmp_port) cmd_gen = cmdgen.CommandGenerator() (error_detected, error_status, error_index, snmp_data) = cmd_gen.getCmd( cmdgen.UsmUserData( self.user, self.auth_key, self.encrypt_key, authProtocol=self.auth_proto, privProtocol=self.encryp_proto, ), cmdgen.UdpTransportTarget(snmp_target, timeout=1.5, retries=2), oid, lookupNames=True, lookupValues=True, ) if not error_detected and snmp_data[0][1]: return text_type(snmp_data[0][1]) return "" def _get_snmpv2c(self, oid): """ Try to send an SNMP GET operation using SNMPv2 for the specified OID. Parameters ---------- oid : str The SNMP OID that you want to get. Returns ------- string : str The string as part of the value from the OID you are trying to retrieve. """ snmp_target = (self.hostname, self.snmp_port) cmd_gen = cmdgen.CommandGenerator() (error_detected, error_status, error_index, snmp_data) = cmd_gen.getCmd( cmdgen.CommunityData(self.community), cmdgen.UdpTransportTarget(snmp_target, timeout=1.5, retries=2), oid, lookupNames=True, lookupValues=True, ) if not error_detected and snmp_data[0][1]: return text_type(snmp_data[0][1]) return "" def _get_snmp(self, oid): """Wrapper for generic SNMP call.""" if self.snmp_version in ["v1", "v2c"]: return self._get_snmpv2c(oid) else: return self._get_snmpv3(oid) def autodetect(self): """ Try to guess the device_type using SNMP GET based on the SNMP_MAPPER dict. The type which is returned is directly matching the name in *netmiko.ssh_dispatcher.CLASS_MAPPER_BASE* dict. Thus you can use this name to retrieve automatically the right ConnectionClass Returns ------- potential_type : str The name of the device_type that must be running. """ # Convert SNMP_MAPPER to a list and sort by priority snmp_mapper_list = [] for k, v in SNMP_MAPPER.items(): snmp_mapper_list.append({k: v}) snmp_mapper_list = sorted( snmp_mapper_list, key=lambda x: list(x.values())[0]["priority"] ) snmp_mapper_list.reverse() for entry in snmp_mapper_list: for device_type, v in entry.items(): oid = v["oid"] regex = v["expr"] # Used cache data if we already queryied this OID if self._response_cache.get(oid): snmp_response = self._response_cache.get(oid) else: snmp_response = self._get_snmp(oid) self._response_cache[oid] = snmp_response # See if we had a match if re.search(regex, snmp_response): return device_type return None netmiko-2.4.2/netmiko/extreme/0000775000127700012770000000000013534772673017525 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/extreme/extreme_wing_ssh.py0000664000127700012770000000104013440357405023430 0ustar gitusergituser00000000000000from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class ExtremeWingSSH(CiscoSSHConnection): """Extreme WiNG support.""" def session_preparation(self): """Disable paging and set Max term width""" self._test_channel_read(pattern=r">|#") self.set_base_prompt() self.disable_paging(command="no page") self.set_terminal_width(command="terminal width 512") time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() netmiko-2.4.2/netmiko/extreme/extreme_ers_ssh.py0000664000127700012770000000300313510465674023264 0ustar gitusergituser00000000000000"""Netmiko support for Extreme Ethernet Routing Switch.""" from __future__ import print_function from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection # Extreme ERS presents Enter Ctrl-Y to begin. CTRL_Y = "\x19" class ExtremeErsSSH(CiscoSSHConnection): """Netmiko support for Extreme Ethernet Routing Switch.""" def special_login_handler(self, delay_factor=1): """ Extreme ERS presents the following as part of the login process: Enter Ctrl-Y to begin. """ delay_factor = self.select_delay_factor(delay_factor) # Handle 'Enter Ctrl-Y to begin' output = "" i = 0 while i <= 12: output = self.read_channel() if output: if "Ctrl-Y" in output: self.write_channel(CTRL_Y) if "sername" in output: self.write_channel(self.username + self.RETURN) elif "ssword" in output: self.write_channel(self.password + self.RETURN) break time.sleep(0.5 * delay_factor) else: self.write_channel(self.RETURN) time.sleep(1 * delay_factor) i += 1 def save_config(self, cmd="save config", confirm=False, confirm_response=""): """Save Config""" return super(ExtremeErsSSH, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) netmiko-2.4.2/netmiko/extreme/extreme_exos.py0000664000127700012770000000550613510465674022606 0ustar gitusergituser00000000000000"""Extreme support.""" from __future__ import unicode_literals import time import re from netmiko.cisco_base_connection import CiscoSSHConnection class ExtremeExosBase(CiscoSSHConnection): """Extreme Exos support. Designed for EXOS >= 15.0 """ def session_preparation(self): self._test_channel_read() self.set_base_prompt() self.disable_paging(command="disable clipaging") self.send_command_timing("disable cli prompting") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def set_base_prompt(self, *args, **kwargs): """ Extreme attaches an id to the prompt. The id increases with every command. It needs to br stripped off to match the prompt. Eg. testhost.1 # testhost.2 # testhost.3 # If new config is loaded and not saved yet, a '* ' prefix appears before the prompt, eg. * testhost.4 # * testhost.5 # """ cur_base_prompt = super(ExtremeExosBase, self).set_base_prompt(*args, **kwargs) # Strip off any leading * or whitespace chars; strip off trailing period and digits match = re.search(r"[\*\s]*(.*)\.\d+", cur_base_prompt) if match: self.base_prompt = match.group(1) return self.base_prompt else: return self.base_prompt def send_command(self, *args, **kwargs): """Extreme needs special handler here due to the prompt changes.""" # Change send_command behavior to use self.base_prompt kwargs.setdefault("auto_find_prompt", False) # refresh self.base_prompt self.set_base_prompt() return super(ExtremeExosBase, self).send_command(*args, **kwargs) def config_mode(self, config_command=""): """No configuration mode on Extreme Exos.""" return "" def check_config_mode(self, check_string="#"): """Checks whether in configuration mode. Returns a boolean.""" return super(ExtremeExosBase, self).check_config_mode(check_string=check_string) def exit_config_mode(self, exit_config=""): """No configuration mode on Extreme Exos.""" return "" def save_config( self, cmd="save configuration primary", confirm=False, confirm_response="" ): """Saves configuration.""" return super(ExtremeExosBase, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) class ExtremeExosSSH(ExtremeExosBase): pass class ExtremeExosTelnet(ExtremeExosBase): def __init__(self, *args, **kwargs): default_enter = kwargs.get("default_enter") kwargs["default_enter"] = "\r\n" if default_enter is None else default_enter super(ExtremeExosTelnet, self).__init__(*args, **kwargs) netmiko-2.4.2/netmiko/extreme/__init__.py0000664000127700012770000000143313440357405021623 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.extreme.extreme_ers_ssh import ExtremeErsSSH from netmiko.extreme.extreme_exos import ExtremeExosSSH from netmiko.extreme.extreme_exos import ExtremeExosTelnet from netmiko.extreme.extreme_netiron import ExtremeNetironSSH from netmiko.extreme.extreme_netiron import ExtremeNetironTelnet from netmiko.extreme.extreme_nos_ssh import ExtremeNosSSH from netmiko.extreme.extreme_slx_ssh import ExtremeSlxSSH from netmiko.extreme.extreme_vsp_ssh import ExtremeVspSSH from netmiko.extreme.extreme_wing_ssh import ExtremeWingSSH __all__ = [ "ExtremeErsSSH", "ExtremeExosSSH", "ExtremeExosTelnet", "ExtremeNetironSSH", "ExtremeNetironTelnet", "ExtremeNosSSH", "ExtremeSlxSSH", "ExtremeVspSSH", "ExtremeWingSSH", ] netmiko-2.4.2/netmiko/extreme/extreme_nos_ssh.py0000664000127700012770000000174413440357405023276 0ustar gitusergituser00000000000000"""Support for Extreme NOS/VDX.""" from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class ExtremeNosSSH(CiscoSSHConnection): """Support for Extreme NOS/VDX.""" def enable(self, *args, **kwargs): """No enable mode on Extreme VDX.""" pass def exit_enable_mode(self, *args, **kwargs): """No enable mode on Extreme VDX.""" pass def special_login_handler(self, delay_factor=1): """Adding a delay after login.""" delay_factor = self.select_delay_factor(delay_factor) self.write_channel(self.RETURN) time.sleep(1 * delay_factor) def save_config( self, cmd="copy running-config startup-config", confirm=True, confirm_response="y", ): """Save Config for Extreme VDX.""" return super(ExtremeNosSSH, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) netmiko-2.4.2/netmiko/extreme/extreme_netiron.py0000664000127700012770000000136013510465674023300 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.cisco_base_connection import CiscoSSHConnection class ExtremeNetironBase(CiscoSSHConnection): def save_config(self, cmd="write memory", confirm=False, confirm_response=""): """Save Config""" return super(ExtremeNetironBase, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) class ExtremeNetironSSH(ExtremeNetironBase): pass class ExtremeNetironTelnet(ExtremeNetironBase): def __init__(self, *args, **kwargs): default_enter = kwargs.get("default_enter") kwargs["default_enter"] = "\r\n" if default_enter is None else default_enter super(ExtremeNetironTelnet, self).__init__(*args, **kwargs) netmiko-2.4.2/netmiko/extreme/extreme_slx_ssh.py0000664000127700012770000000173413440357405023304 0ustar gitusergituser00000000000000"""Support for Extreme SLX.""" from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class ExtremeSlxSSH(CiscoSSHConnection): """Support for Extreme SLX.""" def enable(self, *args, **kwargs): """No enable mode on Extreme SLX.""" pass def exit_enable_mode(self, *args, **kwargs): """No enable mode on Extreme Slx.""" pass def special_login_handler(self, delay_factor=1): """Adding a delay after login.""" delay_factor = self.select_delay_factor(delay_factor) self.write_channel(self.RETURN) time.sleep(1 * delay_factor) def save_config( self, cmd="copy running-config startup-config", confirm=True, confirm_response="y", ): """Save Config for Extreme SLX.""" return super(ExtremeSlxSSH, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) netmiko-2.4.2/netmiko/extreme/extreme_vsp_ssh.py0000664000127700012770000000157413510465674023316 0ustar gitusergituser00000000000000"""Extreme Virtual Services Platform Support.""" from __future__ import print_function from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class ExtremeVspSSH(CiscoSSHConnection): """Extreme Virtual Services Platform Support.""" def session_preparation(self): """Prepare the session after the connection has been established.""" self._test_channel_read() self.set_base_prompt() self.disable_paging(command="terminal more disable") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def save_config(self, cmd="save config", confirm=False, confirm_response=""): """Save Config""" return super(ExtremeVspSSH, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) netmiko-2.4.2/netmiko/ssh_autodetect.py0000664000127700012770000002637413510465674021452 0ustar gitusergituser00000000000000""" The ssh_autodetect module is used to auto-detect the netmiko device_type to use to further initiate a new SSH connection with a remote host. This auto-detection is based on a unique class called **SSHDetect**. Notes ----- The **SSHDetect** class is instantiated using the same parameters than a standard Netmiko connection (see the *netmiko.ssh_dispatacher.ConnectHandler* function). The only acceptable value for the 'device_type' argument is 'autodetect'. The auto-detection is solely based on the *SSH_MAPPER_BASE* dictionary. The keys are the name of the 'device_type' supported for auto-detection and the value is another dictionary describing how to handle the auto-detection. * "cmd" : The command to send to the remote device. **The command output must not require paging.** * "search_patterns" : A list of regex to compare with the output of the command * "priority" : An integer (0-99) which specifies the confidence of the match above * "dispatch" : The function to call to try the autodetection (per default SSHDetect._autodetect_std) Examples -------- # Auto-detection section >>> from netmiko.ssh_autodetect import SSHDetect >>> from netmiko.ssh_dispatcher import ConnectHandler >>> remote_device = {'device_type': 'autodetect', 'host': 'remote.host', 'username': 'test', 'password': 'foo'} >>> guesser = SSHDetect(**remote_device) >>> best_match = guesser.autodetect() >>> print(best_match) # Name of the best device_type to use further >>> print(guesser.potential_matches) # Dictionary of the whole matching result # Netmiko connection creation section >>> remote_device['device_type'] = best_match >>> connection = ConnectHandler(**remote_device) """ from __future__ import unicode_literals import re import time from netmiko.ssh_dispatcher import ConnectHandler from netmiko.base_connection import BaseConnection # 'dispatch' key is the SSHDetect method to call. dispatch key will be popped off dictionary # remaining keys indicate kwargs that will be passed to dispatch method. # Note, the 'cmd' needs to avoid output paging. SSH_MAPPER_BASE = { "alcatel_aos": { "cmd": "show system", "search_patterns": [r"Alcatel-Lucent"], "priority": 99, "dispatch": "_autodetect_std", }, "alcatel_sros": { "cmd": "show version", "search_patterns": ["Nokia", "Alcatel"], "priority": 99, "dispatch": "_autodetect_std", }, "apresia_aeos": { "cmd": "show system", "search_patterns": ["Apresia"], "priority": 99, "dispatch": "_autodetect_std", }, "arista_eos": { "cmd": "show version", "search_patterns": [r"Arista"], "priority": 99, "dispatch": "_autodetect_std", }, "cisco_asa": { "cmd": "show version", "search_patterns": [r"Cisco Adaptive Security Appliance", r"Cisco ASA"], "priority": 99, "dispatch": "_autodetect_std", }, "cisco_ios": { "cmd": "show version", "search_patterns": [ "Cisco IOS Software", "Cisco Internetwork Operating System Software", ], "priority": 99, "dispatch": "_autodetect_std", }, "cisco_nxos": { "cmd": "show version", "search_patterns": [r"Cisco Nexus Operating System", r"NX-OS"], "priority": 99, "dispatch": "_autodetect_std", }, "cisco_xr": { "cmd": "show version", "search_patterns": [r"Cisco IOS XR"], "priority": 99, "dispatch": "_autodetect_std", }, "dell_force10": { "cmd": "show version", "search_patterns": [r"S4048-ON"], "priority": 99, "dispatch": "_autodetect_std", }, "dell_os10": { "cmd": "show version", "search_patterns": [r"Dell EMC Networking OS10-Enterprise"], "priority": 99, "dispatch": "_autodetect_std", }, "f5_tmsh": { "cmd": "show sys version", "search_patterns": [r"BIG-IP"], "priority": 99, "dispatch": "_autodetect_std", }, "f5_linux": { "cmd": "cat /etc/issue", "search_patterns": [r"BIG-IP"], "priority": 99, "dispatch": "_autodetect_std", }, "huawei": { "cmd": "display version", "search_patterns": [ r"Huawei Technologies", r"Huawei Versatile Routing Platform Software", ], "priority": 99, "dispatch": "_autodetect_std", }, "juniper_junos": { "cmd": "show version", "search_patterns": [ r"JUNOS Software Release", r"JUNOS .+ Software", r"JUNOS OS Kernel", r"JUNOS Base Version", ], "priority": 99, "dispatch": "_autodetect_std", }, "linux": { "cmd": "uname -a", "search_patterns": [r"Linux"], "priority": 99, "dispatch": "_autodetect_std", }, "brocade_netiron": { "cmd": "show version", "search_patterns": [r"NetIron"], "priority": 99, "dispatch": "_autodetect_std", }, "extreme_slx": { "cmd": "show version", "search_patterns": [r"SLX-OS Operating System Software"], "priority": 99, "dispatch": "_autodetect_std", }, "ubiquiti_edgeswitch": { "cmd": "show version", "search_patterns": [r"EdgeSwitch"], "priority": 99, "dispatch": "_autodetect_std", }, } class SSHDetect(object): """ The SSHDetect class tries to automatically guess the device type running on the SSH remote end. Be careful that the kwargs 'device_type' must be set to 'autodetect', otherwise it won't work at all. Parameters ---------- *args : list The same *args that you might provide to the netmiko.ssh_dispatcher.ConnectHandler. *kwargs : dict The same *kwargs that you might provide to the netmiko.ssh_dispatcher.ConnectHandler. Attributes ---------- connection : netmiko.terminal_server.TerminalServerSSH A basic connection to the remote SSH end. potential_matches: dict Dict of (device_type, accuracy) that is populated through an interaction with the remote end. Methods ------- autodetect() Try to determine the device type. """ def __init__(self, *args, **kwargs): """ Constructor of the SSHDetect class """ if kwargs["device_type"] != "autodetect": raise ValueError("The connection device_type must be 'autodetect'") self.connection = ConnectHandler(*args, **kwargs) # Call the _test_channel_read() in base to clear initial data output = BaseConnection._test_channel_read(self.connection) self.initial_buffer = output self.potential_matches = {} self._results_cache = {} def autodetect(self): """ Try to guess the best 'device_type' based on patterns defined in SSH_MAPPER_BASE Returns ------- best_match : str or None The device type that is currently the best to use to interact with the device """ for device_type, autodetect_dict in SSH_MAPPER_BASE.items(): tmp_dict = autodetect_dict.copy() call_method = tmp_dict.pop("dispatch") autodetect_method = getattr(self, call_method) accuracy = autodetect_method(**tmp_dict) if accuracy: self.potential_matches[device_type] = accuracy if accuracy >= 99: # Stop the loop as we are sure of our match best_match = sorted( self.potential_matches.items(), key=lambda t: t[1], reverse=True ) self.connection.disconnect() return best_match[0][0] if not self.potential_matches: self.connection.disconnect() return None best_match = sorted( self.potential_matches.items(), key=lambda t: t[1], reverse=True ) self.connection.disconnect() return best_match[0][0] def _send_command(self, cmd=""): """ Handle reading/writing channel directly. It is also sanitizing the output received. Parameters ---------- cmd : str, optional The command to send to the remote device (default : "", just send a new line) Returns ------- output : str The output from the command sent """ self.connection.write_channel(cmd + "\n") time.sleep(1) output = self.connection._read_channel_timing() output = self.connection.strip_ansi_escape_codes(output) output = self.connection.strip_backspaces(output) return output def _send_command_wrapper(self, cmd): """ Send command to the remote device with a caching feature to avoid sending the same command twice based on the SSH_MAPPER_BASE dict cmd key. Parameters ---------- cmd : str The command to send to the remote device after checking cache. Returns ------- response : str The response from the remote device. """ cached_results = self._results_cache.get(cmd) if not cached_results: response = self._send_command(cmd) self._results_cache[cmd] = response return response else: return cached_results def _autodetect_std(self, cmd="", search_patterns=None, re_flags=re.I, priority=99): """ Standard method to try to auto-detect the device type. This method will be called for each device_type present in SSH_MAPPER_BASE dict ('dispatch' key). It will attempt to send a command and match some regular expression from the ouput for each entry in SSH_MAPPER_BASE ('cmd' and 'search_pattern' keys). Parameters ---------- cmd : str The command to send to the remote device after checking cache. search_patterns : list A list of regular expression to look for in the command's output (default: None). re_flags: re.flags, optional Any flags from the python re module to modify the regular expression (default: re.I). priority: int, optional The confidence the match is right between 0 and 99 (default: 99). """ invalid_responses = [ r"% Invalid input detected", r"syntax error, expecting", r"Error: Unrecognized command", r"%Error", r"command not found", r"Syntax Error: unexpected argument", ] if not cmd or not search_patterns: return 0 try: # _send_command_wrapper will use already cached results if available response = self._send_command_wrapper(cmd) # Look for error conditions in output for pattern in invalid_responses: match = re.search(pattern, response, flags=re.I) if match: return 0 for pattern in search_patterns: match = re.search(pattern, response, flags=re_flags) if match: return priority except Exception: return 0 return 0 netmiko-2.4.2/netmiko/accedian/0000775000127700012770000000000013534772673017603 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/accedian/accedian_ssh.py0000664000127700012770000000314013510465674022551 0ustar gitusergituser00000000000000from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class AccedianSSH(CiscoSSHConnection): def session_preparation(self): self._test_channel_read() self.set_base_prompt() # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def check_enable_mode(self, *args, **kwargs): raise AttributeError("Accedian devices do not support enable mode!") def enable(self, *args, **kwargs): raise AttributeError("Accedian devices do not support enable mode!") def exit_enable_mode(self, *args, **kwargs): raise AttributeError("Accedian devices do not support enable mode!") def check_config_mode(self): """Accedian devices do not have a config mode.""" return False def config_mode(self): """Accedian devices do not have a config mode.""" return "" def exit_config_mode(self): """Accedian devices do not have a config mode.""" return "" def set_base_prompt( self, pri_prompt_terminator=":", alt_prompt_terminator="#", delay_factor=2 ): """Sets self.base_prompt: used as delimiter for stripping of trailing prompt in output.""" super(AccedianSSH, self).set_base_prompt( pri_prompt_terminator=pri_prompt_terminator, alt_prompt_terminator=alt_prompt_terminator, delay_factor=delay_factor, ) return self.base_prompt def save_config(self, *args, **kwargs): """Not Implemented""" raise NotImplementedError netmiko-2.4.2/netmiko/accedian/__init__.py0000664000127700012770000000012113440357405021672 0ustar gitusergituser00000000000000from netmiko.accedian.accedian_ssh import AccedianSSH __all__ = ["AccedianSSH"] netmiko-2.4.2/netmiko/ssh_exception.py0000664000127700012770000000064613440357406021274 0ustar gitusergituser00000000000000from __future__ import unicode_literals from paramiko.ssh_exception import SSHException from paramiko.ssh_exception import AuthenticationException class NetMikoTimeoutException(SSHException): """SSH session timed trying to connect to the device.""" pass class NetMikoAuthenticationException(AuthenticationException): """SSH authentication exception based on Paramiko AuthenticationException.""" pass netmiko-2.4.2/netmiko/juniper/0000775000127700012770000000000013534772673017530 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/juniper/juniper.py0000664000127700012770000002143313440357405021545 0ustar gitusergituser00000000000000from __future__ import unicode_literals import re import time from netmiko.base_connection import BaseConnection from netmiko.scp_handler import BaseFileTransfer from netmiko.py23_compat import text_type class JuniperBase(BaseConnection): """ Implement methods for interacting with Juniper Networks devices. Disables `enable()` and `check_enable_mode()` methods. Overrides several methods for Juniper-specific compatibility. """ def session_preparation(self): """ Prepare the session after the connection has been established. Disable paging (the '--more--' prompts). Set the base prompt for interaction ('>'). """ self._test_channel_read() self.enter_cli_mode() self.set_base_prompt() self.disable_paging(command="set cli screen-length 0") self.set_terminal_width(command="set cli screen-width 511") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def _enter_shell(self): """Enter the Bourne Shell.""" return self.send_command("start shell sh", expect_string=r"[\$#]") def _return_cli(self): """Return to the Juniper CLI.""" return self.send_command("exit", expect_string=r"[#>]") def enter_cli_mode(self): """Check if at shell prompt root@ and go into CLI.""" delay_factor = self.select_delay_factor(delay_factor=0) count = 0 cur_prompt = "" while count < 50: self.write_channel(self.RETURN) time.sleep(0.1 * delay_factor) cur_prompt = self.read_channel() if re.search(r"root@", cur_prompt) or re.search(r"^%$", cur_prompt.strip()): self.write_channel("cli" + self.RETURN) time.sleep(0.3 * delay_factor) self.clear_buffer() break elif ">" in cur_prompt or "#" in cur_prompt: break count += 1 def check_enable_mode(self, *args, **kwargs): """No enable mode on Juniper.""" pass def enable(self, *args, **kwargs): """No enable mode on Juniper.""" pass def exit_enable_mode(self, *args, **kwargs): """No enable mode on Juniper.""" pass def check_config_mode(self, check_string="]"): """Checks if the device is in configuration mode or not.""" return super(JuniperBase, self).check_config_mode(check_string=check_string) def config_mode(self, config_command="configure"): """Enter configuration mode.""" return super(JuniperBase, self).config_mode(config_command=config_command) def exit_config_mode(self, exit_config="exit configuration-mode"): """Exit configuration mode.""" output = "" if self.check_config_mode(): output = self.send_command_timing( exit_config, strip_prompt=False, strip_command=False ) if "Exit with uncommitted changes?" in output: output += self.send_command_timing( "yes", strip_prompt=False, strip_command=False ) if self.check_config_mode(): raise ValueError("Failed to exit configuration mode") return output def commit( self, confirm=False, confirm_delay=None, check=False, comment="", and_quit=False, delay_factor=1, ): """ Commit the candidate configuration. Commit the entered configuration. Raise an error and return the failure if the commit fails. Automatically enters configuration mode default: command_string = commit check and (confirm or confirm_dely or comment): Exception confirm_delay and no confirm: Exception confirm: confirm_delay option comment option command_string = commit confirmed or commit confirmed check: command_string = commit check """ delay_factor = self.select_delay_factor(delay_factor) if check and (confirm or confirm_delay or comment): raise ValueError("Invalid arguments supplied with commit check") if confirm_delay and not confirm: raise ValueError( "Invalid arguments supplied to commit method both confirm and check" ) # Select proper command string based on arguments provided command_string = "commit" commit_marker = "commit complete" if check: command_string = "commit check" commit_marker = "configuration check succeeds" elif confirm: if confirm_delay: command_string = "commit confirmed " + text_type(confirm_delay) else: command_string = "commit confirmed" commit_marker = "commit confirmed will be automatically rolled back in" # wrap the comment in quotes if comment: if '"' in comment: raise ValueError("Invalid comment contains double quote") comment = '"{0}"'.format(comment) command_string += " comment " + comment if and_quit: command_string += " and-quit" # Enter config mode (if necessary) output = self.config_mode() # and_quit will get out of config mode on commit if and_quit: prompt = self.base_prompt output += self.send_command_expect( command_string, expect_string=prompt, strip_prompt=False, strip_command=False, delay_factor=delay_factor, ) else: output += self.send_command_expect( command_string, strip_prompt=False, strip_command=False, delay_factor=delay_factor, ) if commit_marker not in output: raise ValueError( "Commit failed with the following errors:\n\n{0}".format(output) ) return output def strip_prompt(self, *args, **kwargs): """Strip the trailing router prompt from the output.""" a_string = super(JuniperBase, self).strip_prompt(*args, **kwargs) return self.strip_context_items(a_string) def strip_context_items(self, a_string): """Strip Juniper-specific output. Juniper will also put a configuration context: [edit] and various chassis contexts: {master:0}, {backup:1} This method removes those lines. """ strings_to_strip = [ r"\[edit.*\]", r"\{master:.*\}", r"\{backup:.*\}", r"\{line.*\}", r"\{primary.*\}", r"\{secondary.*\}", ] response_list = a_string.split(self.RESPONSE_RETURN) last_line = response_list[-1] for pattern in strings_to_strip: if re.search(pattern, last_line): return self.RESPONSE_RETURN.join(response_list[:-1]) return a_string class JuniperSSH(JuniperBase): pass class JuniperTelnet(JuniperBase): def __init__(self, *args, **kwargs): default_enter = kwargs.get("default_enter") kwargs["default_enter"] = "\r\n" if default_enter is None else default_enter super(JuniperTelnet, self).__init__(*args, **kwargs) class JuniperFileTransfer(BaseFileTransfer): """Juniper SCP File Transfer driver.""" def __init__( self, ssh_conn, source_file, dest_file, file_system="/var/tmp", direction="put" ): return super(JuniperFileTransfer, self).__init__( ssh_conn=ssh_conn, source_file=source_file, dest_file=dest_file, file_system=file_system, direction=direction, ) def remote_space_available(self, search_pattern=""): """Return space available on remote device.""" return self._remote_space_available_unix(search_pattern=search_pattern) def check_file_exists(self, remote_cmd=""): """Check if the dest_file already exists on the file system (return boolean).""" return self._check_file_exists_unix(remote_cmd=remote_cmd) def remote_file_size(self, remote_cmd="", remote_file=None): """Get the file size of the remote file.""" return self._remote_file_size_unix( remote_cmd=remote_cmd, remote_file=remote_file ) def remote_md5(self, base_cmd="file checksum md5", remote_file=None): return super(JuniperFileTransfer, self).remote_md5( base_cmd=base_cmd, remote_file=remote_file ) def enable_scp(self, cmd=None): raise NotImplementedError def disable_scp(self, cmd=None): raise NotImplementedError netmiko-2.4.2/netmiko/juniper/__init__.py0000664000127700012770000000027513440357405021631 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.juniper.juniper import JuniperSSH, JuniperTelnet, JuniperFileTransfer __all__ = ["JuniperSSH", "JuniperTelnet", "JuniperFileTransfer"] netmiko-2.4.2/netmiko/flexvnf/0000775000127700012770000000000013534772673017524 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/flexvnf/__init__.py0000664000127700012770000000016513510465674021631 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.flexvnf.flexvnf_ssh import FlexvnfSSH __all__ = ["FlexvnfSSH"] netmiko-2.4.2/netmiko/flexvnf/flexvnf_ssh.py0000664000127700012770000001526613510465674022427 0ustar gitusergituser00000000000000from __future__ import unicode_literals import re import time from netmiko.base_connection import BaseConnection class FlexvnfSSH(BaseConnection): def session_preparation(self): """ Prepare the session after the connection has been established. Disable paging (the '--more--' prompts). Set the base prompt for interaction ('>'). """ self._test_channel_read() self.enter_cli_mode() self.set_base_prompt() self.disable_paging(command="set screen length 0") self.set_terminal_width(command="set screen width 511") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def enter_cli_mode(self): """Check if at shell prompt root@ and go into CLI.""" delay_factor = self.select_delay_factor(delay_factor=0) count = 0 cur_prompt = "" while count < 50: self.write_channel(self.RETURN) time.sleep(0.1 * delay_factor) cur_prompt = self.read_channel() if re.search(r"admin@", cur_prompt) or re.search( r"^\$$", cur_prompt.strip() ): self.write_channel("cli" + self.RETURN) time.sleep(0.3 * delay_factor) self.clear_buffer() break elif ">" in cur_prompt or "%" in cur_prompt: break count += 1 def check_enable_mode(self, *args, **kwargs): """No enable mode on flexvnf.""" pass def enable(self, *args, **kwargs): """No enable mode on flexvnf.""" pass def exit_enable_mode(self, *args, **kwargs): """No enable mode on flexvnf.""" pass def check_config_mode(self, check_string="]"): """Checks if the device is in configuration mode or not.""" return super(FlexvnfSSH, self).check_config_mode(check_string=check_string) def config_mode(self, config_command="configure"): """Enter configuration mode.""" return super(FlexvnfSSH, self).config_mode(config_command=config_command) def exit_config_mode(self, exit_config="exit configuration-mode"): """Exit configuration mode.""" output = "" if self.check_config_mode(): output = self.send_command_timing( exit_config, strip_prompt=False, strip_command=False ) # if 'Exit with uncommitted changes?' in output: if "uncommitted changes" in output: output += self.send_command_timing( "yes", strip_prompt=False, strip_command=False ) if self.check_config_mode(): raise ValueError("Failed to exit configuration mode") return output def commit( self, confirm=False, confirm_delay=None, check=False, comment="", and_quit=False, delay_factor=1, ): """ Commit the candidate configuration. Commit the entered configuration. Raise an error and return the failure if the commit fails. Automatically enters configuration mode default: command_string = commit check and (confirm or confirm_dely or comment): Exception confirm_delay and no confirm: Exception confirm: confirm_delay option comment option command_string = commit confirmed or commit confirmed check: command_string = commit check """ delay_factor = self.select_delay_factor(delay_factor) if check and (confirm or confirm_delay or comment): raise ValueError("Invalid arguments supplied with commit check") if confirm_delay and not confirm: raise ValueError( "Invalid arguments supplied to commit method both confirm and check" ) # Select proper command string based on arguments provided command_string = "commit" commit_marker = "Commit complete." if check: command_string = "commit check" commit_marker = "Validation complete" elif confirm: if confirm_delay: command_string = "commit confirmed " + str(confirm_delay) else: command_string = "commit confirmed" commit_marker = "commit confirmed will be automatically rolled back in" # wrap the comment in quotes if comment: if '"' in comment: raise ValueError("Invalid comment contains double quote") comment = '"{0}"'.format(comment) command_string += " comment " + comment if and_quit: command_string += " and-quit" # Enter config mode (if necessary) output = self.config_mode() # and_quit will get out of config mode on commit if and_quit: prompt = self.base_prompt output += self.send_command_expect( command_string, expect_string=prompt, strip_prompt=True, strip_command=True, delay_factor=delay_factor, ) else: output += self.send_command_expect( command_string, strip_prompt=True, strip_command=True, delay_factor=delay_factor, ) if commit_marker not in output: raise ValueError( "Commit failed with the following errors:\n\n{0}".format(output) ) return output def strip_prompt(self, *args, **kwargs): """Strip the trailing router prompt from the output.""" a_string = super(FlexvnfSSH, self).strip_prompt(*args, **kwargs) return self.strip_context_items(a_string) def strip_context_items(self, a_string): """Strip FLEXVNF-specific output. FLEXVNF will also put a configuration context: [edit] and various chassis contexts: {master:0}, {backup:1} This method removes those lines. """ strings_to_strip = [ r"admin@lab-pg-dev-cp02v.*", r"\[edit.*\]", r"\[edit\]", r"\[ok\]", r"\[.*\]", r"\{master:.*\}", r"\{backup:.*\}", r"\{line.*\}", r"\{primary.*\}", r"\{secondary.*\}", ] response_list = a_string.split(self.RESPONSE_RETURN) last_line = response_list[0] for pattern in strings_to_strip: if re.search(pattern, last_line): return self.RESPONSE_RETURN.join(response_list[:-1]) return a_string netmiko-2.4.2/netmiko/arista/0000775000127700012770000000000013534772673017337 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/arista/arista.py0000664000127700012770000000711413440357405021163 0ustar gitusergituser00000000000000from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection from netmiko.cisco_base_connection import CiscoFileTransfer from netmiko import log class AristaBase(CiscoSSHConnection): def session_preparation(self): """Prepare the session after the connection has been established.""" self._test_channel_read(pattern=r"[>#]") self.set_base_prompt() self.disable_paging() self.set_terminal_width(command="terminal width 511") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def check_config_mode(self, check_string=")#", pattern=""): """ Checks if the device is in configuration mode or not. Arista, unfortunately, does this: loc1-core01(s1)# Can also be (s2) """ log.debug("pattern: {0}".format(pattern)) self.write_channel(self.RETURN) output = self.read_until_pattern(pattern=pattern) log.debug("check_config_mode: {0}".format(repr(output))) output = output.replace("(s1)", "") output = output.replace("(s2)", "") log.debug("check_config_mode: {0}".format(repr(output))) return check_string in output def _enter_shell(self): """Enter the Bourne Shell.""" return self.send_command("bash", expect_string=r"[\$#]") def _return_cli(self): """Return to the CLI.""" return self.send_command("exit", expect_string=r"[#>]") class AristaSSH(AristaBase): pass class AristaTelnet(AristaBase): def __init__(self, *args, **kwargs): default_enter = kwargs.get("default_enter") kwargs["default_enter"] = "\r\n" if default_enter is None else default_enter super(AristaTelnet, self).__init__(*args, **kwargs) class AristaFileTransfer(CiscoFileTransfer): """Arista SCP File Transfer driver.""" def __init__( self, ssh_conn, source_file, dest_file, file_system="/mnt/flash", direction="put", ): return super(AristaFileTransfer, self).__init__( ssh_conn=ssh_conn, source_file=source_file, dest_file=dest_file, file_system=file_system, direction=direction, ) def remote_space_available(self, search_pattern=""): """Return space available on remote device.""" return self._remote_space_available_unix(search_pattern=search_pattern) def check_file_exists(self, remote_cmd=""): """Check if the dest_file already exists on the file system (return boolean).""" return self._check_file_exists_unix(remote_cmd=remote_cmd) def remote_file_size(self, remote_cmd="", remote_file=None): """Get the file size of the remote file.""" return self._remote_file_size_unix( remote_cmd=remote_cmd, remote_file=remote_file ) def remote_md5(self, base_cmd="verify /md5", remote_file=None): if remote_file is None: if self.direction == "put": remote_file = self.dest_file elif self.direction == "get": remote_file = self.source_file remote_md5_cmd = "{} file:{}/{}".format(base_cmd, self.file_system, remote_file) dest_md5 = self.ssh_ctl_chan.send_command( remote_md5_cmd, max_loops=750, delay_factor=4 ) dest_md5 = self.process_md5(dest_md5) return dest_md5 def enable_scp(self, cmd=None): raise NotImplementedError def disable_scp(self, cmd=None): raise NotImplementedError netmiko-2.4.2/netmiko/arista/__init__.py0000664000127700012770000000026513440357405021437 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.arista.arista import AristaSSH, AristaTelnet, AristaFileTransfer __all__ = ["AristaSSH", "AristaTelnet", "AristaFileTransfer"] netmiko-2.4.2/netmiko/alcatel/0000775000127700012770000000000013534772673017461 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/alcatel/alcatel_aos_ssh.py0000664000127700012770000000271713510465674023160 0ustar gitusergituser00000000000000"""Alcatel-Lucent Enterprise AOS support (AOS6 and AOS8).""" from __future__ import print_function from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class AlcatelAosSSH(CiscoSSHConnection): """Alcatel-Lucent Enterprise AOS support (AOS6 and AOS8).""" def session_preparation(self): # Prompt can be anything, but best practice is to end with > or # self._test_channel_read(pattern=r"[>#]") self.set_base_prompt() # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def check_enable_mode(self, *args, **kwargs): """No enable mode on AOS""" pass def enable(self, *args, **kwargs): """No enable mode on AOS""" pass def exit_enable_mode(self, *args, **kwargs): """No enable mode on AOS""" pass def check_config_mode(self, *args, **kwargs): """No config mode on AOS""" pass def config_mode(self, *args, **kwargs): """No config mode on AOS""" return "" def exit_config_mode(self, *args, **kwargs): """No config mode on AOS""" return "" def save_config( self, cmd="write memory flash-synchro", confirm=False, confirm_response="" ): """Save Config""" return super(AlcatelAosSSH, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) netmiko-2.4.2/netmiko/alcatel/__init__.py0000664000127700012770000000031513440357405021555 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.alcatel.alcatel_sros_ssh import AlcatelSrosSSH from netmiko.alcatel.alcatel_aos_ssh import AlcatelAosSSH __all__ = ["AlcatelSrosSSH", "AlcatelAosSSH"] netmiko-2.4.2/netmiko/alcatel/alcatel_sros_ssh.py0000664000127700012770000000571613510465674023366 0ustar gitusergituser00000000000000"""Alcatel-Lucent SROS support.""" from __future__ import print_function from __future__ import unicode_literals import re import time from netmiko.cisco_base_connection import CiscoSSHConnection class AlcatelSrosSSH(CiscoSSHConnection): """Alcatel-Lucent SROS support.""" def session_preparation(self): self._test_channel_read() self.set_base_prompt() self.disable_paging(command="environment no more") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def set_base_prompt(self, *args, **kwargs): """Remove the > when navigating into the different config level.""" cur_base_prompt = super(AlcatelSrosSSH, self).set_base_prompt(*args, **kwargs) match = re.search(r"(.*)(>.*)*#", cur_base_prompt) if match: # strip off >... from base_prompt self.base_prompt = match.group(1) return self.base_prompt def enable(self, cmd="enable-admin", pattern="ssword", re_flags=re.IGNORECASE): """Enter enable mode.""" return super(AlcatelSrosSSH, self).enable( cmd=cmd, pattern=pattern, re_flags=re_flags ) def check_enable_mode(self, check_string="CLI Already in admin mode"): """Check whether we are in enable-admin mode. SROS requires us to do this: *A:HOSTNAME# enable-admin MINOR: CLI Already in admin mode. *A:HOSTNAME# *A:HOSTNAME# enable-admin Password: MINOR: CLI Invalid password. *A:HOSTNAME# """ output = self.send_command_timing("enable-admin") if re.search(r"ssword", output): # Just hit enter as we don't actually want to enter enable here self.write_channel(self.normalize_cmd(self.RETURN)) self.read_until_prompt() return False elif check_string in output: return True raise ValueError("Unexpected response in check_enable_mode() method") def exit_enable_mode(self, exit_command=""): """No corresponding exit of enable mode on SROS.""" pass def config_mode(self, config_command="configure", pattern="#"): """ Enter into configuration mode on SROS device.""" return super(AlcatelSrosSSH, self).config_mode( config_command=config_command, pattern=pattern ) def exit_config_mode(self, exit_config="exit all", pattern="#"): """ Exit from configuration mode.""" return super(AlcatelSrosSSH, self).exit_config_mode( exit_config=exit_config, pattern=pattern ) def check_config_mode(self, check_string="config", pattern="#"): """ Checks if the device is in configuration mode or not. """ return super(AlcatelSrosSSH, self).check_config_mode( check_string=check_string, pattern=pattern ) def save_config(self, *args, **kwargs): """Not Implemented""" raise NotImplementedError netmiko-2.4.2/netmiko/cloudgenix/0000775000127700012770000000000013534772673020215 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/cloudgenix/__init__.py0000664000127700012770000000013713510465674022321 0ustar gitusergituser00000000000000from netmiko.cloudgenix.cloudgenix_ion import CloudGenixIonSSH __all__ = ["CloudGenixIonSSH"] netmiko-2.4.2/netmiko/cloudgenix/cloudgenix_ion.py0000664000127700012770000000501313510465674023566 0ustar gitusergituser00000000000000import time from netmiko import log from netmiko.cisco_base_connection import CiscoSSHConnection from netmiko.py23_compat import string_types class CloudGenixIonSSH(CiscoSSHConnection): def establish_connection(self): super(CloudGenixIonSSH, self).establish_connection(width=100, height=1000) def session_preparation(self, *args, **kwargs): """Prepare the session after the connection has been established.""" self.ansi_escape_codes = True self._test_channel_read() self.write_channel(self.RETURN) self.set_base_prompt(delay_factor=5) def disable_paging(self): """Cloud Genix ION sets terminal height in establish_connection""" return "" def find_prompt(self, delay_factor=1): prompt = super(CloudGenixIonSSH, self).find_prompt() prompt = self.strip_backspaces(prompt).strip() return prompt def strip_command(self, command_string, output): output = super(CloudGenixIonSSH, self).strip_command(command_string, output) # command_string gets repainted potentially multiple times (grab everything after last one) output = output.split(command_string)[-1] return output def check_config_mode(self): """Devices do not have a config mode.""" return False def config_mode(self): """Devices do not have a config mode.""" return "" def exit_config_mode(self): """Devices do not have a config mode.""" return "" def save_config(self, *args, **kwargs): """No save method on ION SSH""" pass def send_config_set( self, config_commands=None, exit_config_mode=False, delay_factor=1, max_loops=150, strip_prompt=False, strip_command=False, config_mode_command=None, ): delay_factor = self.select_delay_factor(delay_factor) if config_commands is None: return "" elif isinstance(config_commands, string_types): config_commands = (config_commands,) if not hasattr(config_commands, "__iter__"): raise ValueError("Invalid argument passed into send_config_set") # Send config commands output = "" for cmd in config_commands: output += self.send_command(cmd) if self.fast_cli: pass else: time.sleep(delay_factor * 0.05) output = self._sanitize_output(output) log.debug("{}".format(output)) return output netmiko-2.4.2/netmiko/netapp/0000775000127700012770000000000013534772673017343 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/netapp/__init__.py0000664000127700012770000000017613440357405021444 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.netapp.netapp_cdot_ssh import NetAppcDotSSH __all__ = ["NetAppcDotSSH"] netmiko-2.4.2/netmiko/netapp/netapp_cdot_ssh.py0000664000127700012770000000253713440357405023065 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.base_connection import BaseConnection class NetAppcDotSSH(BaseConnection): def session_preparation(self): """Prepare the session after the connection has been established.""" self.set_base_prompt() cmd = self.RETURN + "rows 0" + self.RETURN self.disable_paging(command=cmd) def send_command_with_y(self, *args, **kwargs): output = self.send_command_timing(*args, **kwargs) if "{y|n}" in output: output += self.send_command_timing( "y", strip_prompt=False, strip_command=False ) return output def check_config_mode(self, check_string="*>"): return super(NetAppcDotSSH, self).check_config_mode(check_string=check_string) def config_mode( self, config_command="set -privilege diagnostic -confirmations off" ): return super(NetAppcDotSSH, self).config_mode(config_command=config_command) def exit_config_mode(self, exit_config="set -privilege admin -confirmations off"): return super(NetAppcDotSSH, self).exit_config_mode(exit_config=exit_config) def enable(self, *args, **kwargs): """No enable mode on NetApp.""" pass def check_enable_mode(self, *args, **kwargs): pass def exit_enable_mode(self, *args, **kwargs): pass netmiko-2.4.2/netmiko/endace/0000775000127700012770000000000013534772673017273 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/endace/endace_ssh.py0000664000127700012770000000341313510465674021734 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.cisco_base_connection import CiscoSSHConnection import re class EndaceSSH(CiscoSSHConnection): def disable_paging(self, command="no cli session paging enable", delay_factor=1): return super(EndaceSSH, self).disable_paging( command=command, delay_factor=delay_factor ) def enable(self, cmd="enable", pattern="", re_flags=re.IGNORECASE): return super(EndaceSSH, self).enable( cmd=cmd, pattern=pattern, re_flags=re_flags ) def check_config_mode(self, check_string="(config) #"): return super(EndaceSSH, self).check_config_mode(check_string=check_string) def config_mode(self, config_command="conf t", pattern=""): output = "" if not self.check_config_mode(): output = self.send_command_timing( config_command, strip_command=False, strip_prompt=False ) if "to enter configuration mode anyway" in output: output += self.send_command_timing( "YES", strip_command=False, strip_prompt=False ) if not self.check_config_mode(): raise ValueError("Failed to enter configuration mode") return output def exit_config_mode(self, exit_config="exit", pattern="#"): return super(EndaceSSH, self).exit_config_mode( exit_config=exit_config, pattern=pattern ) def save_config( self, cmd="configuration write", confirm=False, confirm_response="" ): self.enable() self.config_mode() return super(EndaceSSH, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) netmiko-2.4.2/netmiko/endace/__init__.py0000664000127700012770000000016513510465674021400 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.endace.endace_ssh import EndaceSSH __all__ = ["EndaceSSH"] netmiko-2.4.2/netmiko/huawei/0000775000127700012770000000000013534772673017336 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/huawei/huawei.py0000664000127700012770000001707513516415527021174 0ustar gitusergituser00000000000000from __future__ import print_function from __future__ import unicode_literals import time import re from netmiko.cisco_base_connection import CiscoBaseConnection from netmiko.ssh_exception import NetMikoAuthenticationException from netmiko import log class HuaweiBase(CiscoBaseConnection): def session_preparation(self): """Prepare the session after the connection has been established.""" self._test_channel_read() self.set_base_prompt() self.disable_paging(command="screen-length 0 temporary") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def config_mode(self, config_command="system-view"): """Enter configuration mode.""" return super(HuaweiBase, self).config_mode(config_command=config_command) def exit_config_mode(self, exit_config="return", pattern=r">"): """Exit configuration mode.""" return super(HuaweiBase, self).exit_config_mode( exit_config=exit_config, pattern=pattern ) def check_config_mode(self, check_string="]"): """Checks whether in configuration mode. Returns a boolean.""" return super(HuaweiBase, self).check_config_mode(check_string=check_string) def check_enable_mode(self, *args, **kwargs): """Huawei has no enable mode.""" pass def enable(self, *args, **kwargs): """Huawei has no enable mode.""" return "" def exit_enable_mode(self, *args, **kwargs): """Huawei has no enable mode.""" return "" def set_base_prompt( self, pri_prompt_terminator=">", alt_prompt_terminator="]", delay_factor=1 ): """ Sets self.base_prompt Used as delimiter for stripping of trailing prompt in output. Should be set to something that is general and applies in multiple contexts. For Comware this will be the router prompt with < > or [ ] stripped off. This will be set on logging in, but not when entering system-view """ log.debug("In set_base_prompt") delay_factor = self.select_delay_factor(delay_factor) self.clear_buffer() self.write_channel(self.RETURN) time.sleep(0.5 * delay_factor) prompt = self.read_channel() prompt = self.normalize_linefeeds(prompt) # If multiple lines in the output take the last line prompt = prompt.split(self.RESPONSE_RETURN)[-1] prompt = prompt.strip() # Check that ends with a valid terminator character if not prompt[-1] in (pri_prompt_terminator, alt_prompt_terminator): raise ValueError("Router prompt not found: {0}".format(prompt)) # Strip off any leading HRP_. characters for USGv5 HA prompt = re.sub(r"^HRP_.", "", prompt, flags=re.M) # Strip off leading and trailing terminator prompt = prompt[1:-1] prompt = prompt.strip() self.base_prompt = prompt log.debug("prompt: {0}".format(self.base_prompt)) return self.base_prompt def save_config(self, cmd="save", confirm=False, confirm_response=""): """ Save Config for HuaweiSSH""" return super(HuaweiBase, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) class HuaweiSSH(HuaweiBase): """Huawei SSH driver.""" pass class HuaweiTelnet(HuaweiBase): """Huawei Telnet driver.""" def telnet_login( self, pri_prompt_terminator=r"]\s*$", alt_prompt_terminator=r">\s*$", username_pattern=r"(?:user:|username|login|user name)", pwd_pattern=r"assword", delay_factor=1, max_loops=20, ): """Telnet login for Huawei Devices""" delay_factor = self.select_delay_factor(delay_factor) password_change_prompt = re.escape("Change now? [Y/N]") combined_pattern = r"({}|{}|{})".format( pri_prompt_terminator, alt_prompt_terminator, password_change_prompt ) output = "" return_msg = "" i = 1 while i <= max_loops: try: output = self.read_channel() return_msg += output # Search for username pattern / send username if re.search(username_pattern, output, flags=re.I): self.write_channel(self.username + self.TELNET_RETURN) time.sleep(1 * delay_factor) output = self.read_channel() return_msg += output # Search for password pattern / send password if re.search(pwd_pattern, output, flags=re.I): self.write_channel(self.password + self.TELNET_RETURN) time.sleep(0.5 * delay_factor) output = self.read_channel() return_msg += output if re.search( pri_prompt_terminator, output, flags=re.M ) or re.search(alt_prompt_terminator, output, flags=re.M): return return_msg # Search for password change prompt, send "N" if re.search(password_change_prompt, output): self.write_channel("N" + self.TELNET_RETURN) output = self.read_until_pattern(pattern=combined_pattern) return_msg += output # Check if proper data received if re.search(pri_prompt_terminator, output, flags=re.M) or re.search( alt_prompt_terminator, output, flags=re.M ): return return_msg self.write_channel(self.TELNET_RETURN) time.sleep(0.5 * delay_factor) i += 1 except EOFError: self.remote_conn.close() msg = "Login failed: {}".format(self.host) raise NetMikoAuthenticationException(msg) # Last try to see if we already logged in self.write_channel(self.TELNET_RETURN) time.sleep(0.5 * delay_factor) output = self.read_channel() return_msg += output if re.search(pri_prompt_terminator, output, flags=re.M) or re.search( alt_prompt_terminator, output, flags=re.M ): return return_msg self.remote_conn.close() msg = "Login failed: {}".format(self.host) raise NetMikoAuthenticationException(msg) class HuaweiVrpv8SSH(HuaweiSSH): def commit(self, comment="", delay_factor=1): """ Commit the candidate configuration. Commit the entered configuration. Raise an error and return the failure if the commit fails. default: command_string = commit comment: command_string = commit comment """ delay_factor = self.select_delay_factor(delay_factor) error_marker = "Failed to generate committed config" command_string = "commit" if comment: command_string += ' comment "{}"'.format(comment) output = self.config_mode() output += self.send_command_expect( command_string, strip_prompt=False, strip_command=False, delay_factor=delay_factor, expect_string=r"]", ) output += self.exit_config_mode() if error_marker in output: raise ValueError( "Commit failed with following errors:\n\n{}".format(output) ) return output def save_config(self, *args, **kwargs): """Not Implemented""" raise NotImplementedError netmiko-2.4.2/netmiko/huawei/__init__.py0000664000127700012770000000031613510465674021441 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.huawei.huawei import HuaweiSSH, HuaweiVrpv8SSH from netmiko.huawei.huawei import HuaweiTelnet __all__ = ["HuaweiSSH", "HuaweiVrpv8SSH", "HuaweiTelnet"] netmiko-2.4.2/netmiko/checkpoint/0000775000127700012770000000000013534772673020203 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/checkpoint/checkpoint_gaia_ssh.py0000664000127700012770000000175513510465674024544 0ustar gitusergituser00000000000000from __future__ import unicode_literals import time from netmiko.base_connection import BaseConnection class CheckPointGaiaSSH(BaseConnection): """ Implements methods for communicating with Check Point Gaia firewalls. """ def session_preparation(self): """ Prepare the session after the connection has been established. Set the base prompt for interaction ('>'). """ self._test_channel_read() self.set_base_prompt() self.disable_paging(command="set clienv rows 0") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def config_mode(self, config_command=""): """No config mode for Check Point devices.""" return "" def exit_config_mode(self, exit_config=""): """No config mode for Check Point devices.""" return "" def save_config(self, *args, **kwargs): """Not Implemented""" raise NotImplementedError netmiko-2.4.2/netmiko/checkpoint/__init__.py0000664000127700012770000000021613440357405022277 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.checkpoint.checkpoint_gaia_ssh import CheckPointGaiaSSH __all__ = ["CheckPointGaiaSSH"] netmiko-2.4.2/netmiko/mrv/0000775000127700012770000000000013534772673016660 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/mrv/mrv_lx.py0000664000127700012770000000244013510465674020533 0ustar gitusergituser00000000000000"""MRV Communications Driver (LX).""" from __future__ import unicode_literals import time import re from netmiko.cisco_base_connection import CiscoSSHConnection class MrvLxSSH(CiscoSSHConnection): """MRV Communications Driver (LX).""" def session_preparation(self): """Prepare the session after the connection has been established.""" self._test_channel_read(pattern=r"[>|>>]") self.set_base_prompt() self.enable() self.disable_paging(command="no pause") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def check_enable_mode(self, check_string=">>"): """MRV has a >> for enable mode instead of # like Cisco""" return super(MrvLxSSH, self).check_enable_mode(check_string=check_string) def enable(self, cmd="enable", pattern="assword", re_flags=re.IGNORECASE): """Enter enable mode.""" return super(MrvLxSSH, self).enable(cmd=cmd, pattern=pattern, re_flags=re_flags) def save_config(self, cmd="save config flash", confirm=False, confirm_response=""): """Saves configuration.""" return super(MrvLxSSH, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) netmiko-2.4.2/netmiko/mrv/__init__.py0000664000127700012770000000025513510465674020765 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.mrv.mrv_lx import MrvLxSSH from netmiko.mrv.mrv_ssh import MrvOptiswitchSSH __all__ = ["MrvOptiswitchSSH", "MrvLxSSH"] netmiko-2.4.2/netmiko/mrv/mrv_ssh.py0000664000127700012770000000306613510465674020712 0ustar gitusergituser00000000000000"""MRV Communications Driver (OptiSwitch).""" from __future__ import unicode_literals import time import re from netmiko.cisco_base_connection import CiscoSSHConnection class MrvOptiswitchSSH(CiscoSSHConnection): """MRV Communications Driver (OptiSwitch).""" def session_preparation(self): """Prepare the session after the connection has been established.""" self._test_channel_read(pattern=r"[>#]") self.set_base_prompt() self.enable() self.disable_paging(command="no cli-paging") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.set_base_prompt() self.clear_buffer() def enable(self, cmd="enable", pattern=r"#", re_flags=re.IGNORECASE): """Enable mode on MRV uses no password.""" output = "" if not self.check_enable_mode(): self.write_channel(self.normalize_cmd(cmd)) output += self.read_until_prompt_or_pattern( pattern=pattern, re_flags=re_flags ) if not self.check_enable_mode(): msg = ( "Failed to enter enable mode. Please ensure you pass " "the 'secret' argument to ConnectHandler." ) raise ValueError(msg) return output def save_config(self, cmd="save config flash", confirm=False, confirm_response=""): """Saves configuration.""" return super(MrvOptiswitchSSH, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) netmiko-2.4.2/netmiko/a10/0000775000127700012770000000000013534772673016435 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/a10/__init__.py0000664000127700012770000000014513440357405020532 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.a10.a10_ssh import A10SSH __all__ = ["A10SSH"] netmiko-2.4.2/netmiko/a10/a10_ssh.py0000664000127700012770000000135713510465674020245 0ustar gitusergituser00000000000000"""A10 support.""" from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class A10SSH(CiscoSSHConnection): """A10 support.""" def session_preparation(self): """A10 requires to be enable mode to disable paging.""" self._test_channel_read() self.set_base_prompt() self.enable() self.disable_paging(command="terminal length 0") # Will not do anything without A10 specific command self.set_terminal_width() # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def save_config(self, *args, **kwargs): """Not Implemented""" raise NotImplementedError netmiko-2.4.2/netmiko/utilities.py0000664000127700012770000002455713516415527020446 0ustar gitusergituser00000000000000"""Miscellaneous utility functions.""" from __future__ import print_function from __future__ import unicode_literals from glob import glob import sys import io import os import serial.tools.list_ports from netmiko.py23_compat import text_type from netmiko._textfsm import _clitable as clitable from netmiko._textfsm._clitable import CliTableError try: from genie.conf.base import Device from genie.libs.parser.utils import get_parser from pyats.datastructures import AttrDict GENIE_INSTALLED = True except ImportError: GENIE_INSTALLED = False # Dictionary mapping 'show run' for vendors with different command SHOW_RUN_MAPPER = { "juniper": "show configuration", "juniper_junos": "show configuration", "extreme": "show configuration", "extreme_ers": "show running-config", "extreme_exos": "show configuration", "extreme_netiron": "show running-config", "extreme_nos": "show running-config", "extreme_slx": "show running-config", "extreme_vdx": "show running-config", "extreme_vsp": "show running-config", "extreme_wing": "show running-config", "hp_comware": "display current-configuration", "huawei": "display current-configuration", "fortinet": "show full-configuration", "checkpoint": "show configuration", "cisco_wlc": "show run-config", "enterasys": "show running-config", "dell_force10": "show running-config", "avaya_vsp": "show running-config", "avaya_ers": "show running-config", "brocade_vdx": "show running-config", "brocade_nos": "show running-config", "brocade_fastiron": "show running-config", "brocade_netiron": "show running-config", "alcatel_aos": "show configuration snapshot", } # Expand SHOW_RUN_MAPPER to include '_ssh' key new_dict = {} for k, v in SHOW_RUN_MAPPER.items(): new_key = k + "_ssh" new_dict[k] = v new_dict[new_key] = v SHOW_RUN_MAPPER = new_dict # Default location of netmiko temp directory for netmiko tools NETMIKO_BASE_DIR = "~/.netmiko" def load_yaml_file(yaml_file): """Read YAML file.""" try: import yaml except ImportError: sys.exit("Unable to import yaml module.") try: with io.open(yaml_file, "rt", encoding="utf-8") as fname: return yaml.safe_load(fname) except IOError: sys.exit("Unable to open YAML file: {0}".format(yaml_file)) def load_devices(file_name=None): """Find and load .netmiko.yml file.""" yaml_devices_file = find_cfg_file(file_name) return load_yaml_file(yaml_devices_file) def find_cfg_file(file_name=None): """ Search for netmiko_tools inventory file in the following order: NETMIKO_TOOLS_CFG environment variable Current directory Home directory Look for file named: .netmiko.yml or netmiko.yml Also allow NETMIKO_TOOLS_CFG to point directly at a file """ if file_name: if os.path.isfile(file_name): return file_name optional_path = os.environ.get("NETMIKO_TOOLS_CFG", "") if os.path.isfile(optional_path): return optional_path search_paths = [optional_path, ".", os.path.expanduser("~")] # Filter optional_path if null search_paths = [path for path in search_paths if path] for path in search_paths: files = glob("{}/.netmiko.yml".format(path)) + glob( "{}/netmiko.yml".format(path) ) if files: return files[0] raise IOError( ".netmiko.yml file not found in NETMIKO_TOOLS environment variable directory, current " "directory, or home directory." ) def display_inventory(my_devices): """Print out inventory devices and groups.""" inventory_groups = ["all"] inventory_devices = [] for k, v in my_devices.items(): if isinstance(v, list): inventory_groups.append(k) elif isinstance(v, dict): inventory_devices.append((k, v["device_type"])) inventory_groups.sort() inventory_devices.sort(key=lambda x: x[0]) print("\nDevices:") print("-" * 40) for a_device, device_type in inventory_devices: device_type = " ({})".format(device_type) print("{:<25}{:>15}".format(a_device, device_type)) print("\n\nGroups:") print("-" * 40) for a_group in inventory_groups: print(a_group) print() def obtain_all_devices(my_devices): """Dynamically create 'all' group.""" new_devices = {} for device_name, device_or_group in my_devices.items(): # Skip any groups if not isinstance(device_or_group, list): new_devices[device_name] = device_or_group return new_devices def obtain_netmiko_filename(device_name): """Create file name based on device_name.""" _, netmiko_full_dir = find_netmiko_dir() return "{}/{}.txt".format(netmiko_full_dir, device_name) def write_tmp_file(device_name, output): file_name = obtain_netmiko_filename(device_name) with open(file_name, "w") as f: f.write(output) return file_name def ensure_dir_exists(verify_dir): """Ensure directory exists. Create if necessary.""" if not os.path.exists(verify_dir): # Doesn't exist create dir os.makedirs(verify_dir) else: # Exists if not os.path.isdir(verify_dir): # Not a dir, raise an exception raise ValueError("{} is not a directory".format(verify_dir)) def find_netmiko_dir(): """Check environment first, then default dir""" try: netmiko_base_dir = os.environ["NETMIKO_DIR"] except KeyError: netmiko_base_dir = NETMIKO_BASE_DIR netmiko_base_dir = os.path.expanduser(netmiko_base_dir) if netmiko_base_dir == "/": raise ValueError("/ cannot be netmiko_base_dir") netmiko_full_dir = "{}/tmp".format(netmiko_base_dir) return (netmiko_base_dir, netmiko_full_dir) def write_bytes(out_data, encoding="ascii"): """Write Python2 and Python3 compatible byte stream.""" if sys.version_info[0] >= 3: if isinstance(out_data, type("")): if encoding == "utf-8": return out_data.encode("utf-8") else: return out_data.encode("ascii", "ignore") elif isinstance(out_data, type(b"")): return out_data else: if isinstance(out_data, type("")): if encoding == "utf-8": return out_data.encode("utf-8") else: return out_data.encode("ascii", "ignore") elif isinstance(out_data, type(str(""))): return out_data msg = "Invalid value for out_data neither unicode nor byte string: {}".format( out_data ) raise ValueError(msg) def check_serial_port(name): """returns valid COM Port.""" try: cdc = next(serial.tools.list_ports.grep(name)) return cdc[0] except StopIteration: msg = "device {} not found. ".format(name) msg += "available devices are: " ports = list(serial.tools.list_ports.comports()) for p in ports: msg += "{},".format(text_type(p)) raise ValueError(msg) def get_template_dir(): """Find and return the ntc-templates/templates dir.""" try: template_dir = os.path.expanduser(os.environ["NET_TEXTFSM"]) index = os.path.join(template_dir, "index") if not os.path.isfile(index): # Assume only base ./ntc-templates specified template_dir = os.path.join(template_dir, "templates") except KeyError: # Construct path ~/ntc-templates/templates home_dir = os.path.expanduser("~") template_dir = os.path.join(home_dir, "ntc-templates", "templates") index = os.path.join(template_dir, "index") if not os.path.isdir(template_dir) or not os.path.isfile(index): msg = """ Valid ntc-templates not found, please install https://github.com/networktocode/ntc-templates and then set the NET_TEXTFSM environment variable to point to the ./ntc-templates/templates directory.""" raise ValueError(msg) return os.path.abspath(template_dir) def clitable_to_dict(cli_table): """Converts TextFSM cli_table object to list of dictionaries.""" objs = [] for row in cli_table: temp_dict = {} for index, element in enumerate(row): temp_dict[cli_table.header[index].lower()] = element objs.append(temp_dict) return objs def get_structured_data(raw_output, platform, command): """Convert raw CLI output to structured data using TextFSM template.""" template_dir = get_template_dir() index_file = os.path.join(template_dir, "index") textfsm_obj = clitable.CliTable(index_file, template_dir) attrs = {"Command": command, "Platform": platform} try: # Parse output through template textfsm_obj.ParseCmd(raw_output, attrs) structured_data = clitable_to_dict(textfsm_obj) output = raw_output if structured_data == [] else structured_data return output except CliTableError: return raw_output def get_structured_data_genie(raw_output, platform, command): if not sys.version_info >= (3, 4): raise ValueError("Genie requires Python >= 3.4") if not GENIE_INSTALLED: msg = ( "\nGenie and PyATS are not installed. Please PIP install both Genie and PyATS:\n" "pip install genie\npip install pyats\n" ) raise ValueError(msg) if "cisco" not in platform: return raw_output genie_device_mapper = { "cisco_ios": "ios", "cisco_xe": "iosxe", "cisco_xr": "iosxr", "cisco_nxos": "nxos", "cisco_asa": "asa", } os = None # platform might be _ssh, _telnet, _serial strip that off if platform.count("_") > 1: base_platform = platform.split("_")[:-1] base_platform = "_".join(base_platform) else: base_platform = platform os = genie_device_mapper.get(base_platform) if os is None: return raw_output # Genie specific construct for doing parsing (based on Genie in Ansible) device = Device("new_device", os=os) device.custom.setdefault("abstraction", {}) device.custom["abstraction"]["order"] = ["os"] device.cli = AttrDict({"execute": None}) try: # Test of whether their is a parser for the given command (will return Exception if fails) get_parser(command, device) parsed_output = device.parse(command, output=raw_output) return parsed_output except Exception: return raw_output netmiko-2.4.2/netmiko/netmiko_globals.py0000664000127700012770000000012413440357405021560 0ustar gitusergituser00000000000000from __future__ import unicode_literals MAX_BUFFER = 65535 BACKSPACE_CHAR = "\x08" netmiko-2.4.2/netmiko/dell/0000775000127700012770000000000013534772673016774 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/dell/dell_dnos6.py0000664000127700012770000000205413510465674021372 0ustar gitusergituser00000000000000"""Dell N2/3/4000 base driver- supports DNOS6.""" from __future__ import unicode_literals from netmiko.dell.dell_powerconnect import DellPowerConnectBase import time class DellDNOS6Base(DellPowerConnectBase): def session_preparation(self): """Prepare the session after the connection has been established.""" self.ansi_escape_codes = True self._test_channel_read() self.set_base_prompt() self.enable() self.disable_paging(command="terminal length 0") self.set_terminal_width() # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def save_config( self, cmd="copy running-configuration startup-configuration", confirm=False, confirm_response="", ): """Saves Config""" return super(DellDNOS6Base, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) class DellDNOS6SSH(DellDNOS6Base): pass class DellDNOS6Telnet(DellDNOS6Base): pass netmiko-2.4.2/netmiko/dell/dell_powerconnect.py0000664000127700012770000000736713440357405023055 0ustar gitusergituser00000000000000"""Dell PowerConnect Driver.""" from __future__ import unicode_literals from paramiko import SSHClient import time from os import path from netmiko.cisco_base_connection import CiscoBaseConnection class SSHClient_noauth(SSHClient): def _auth(self, username, *args): self._transport.auth_none(username) return class DellPowerConnectBase(CiscoBaseConnection): """Dell PowerConnect Driver.""" def session_preparation(self): """Prepare the session after the connection has been established.""" self.ansi_escape_codes = True self._test_channel_read() self.set_base_prompt() self.enable() self.disable_paging(command="terminal datadump") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def set_base_prompt( self, pri_prompt_terminator=">", alt_prompt_terminator="#", delay_factor=1 ): """Sets self.base_prompt: used as delimiter for stripping of trailing prompt in output.""" prompt = super(DellPowerConnectBase, self).set_base_prompt( pri_prompt_terminator=pri_prompt_terminator, alt_prompt_terminator=alt_prompt_terminator, delay_factor=delay_factor, ) prompt = prompt.strip() self.base_prompt = prompt return self.base_prompt def check_config_mode(self, check_string="(config)#"): """Checks if the device is in configuration mode""" return super(DellPowerConnectBase, self).check_config_mode( check_string=check_string ) def config_mode(self, config_command="config"): """Enter configuration mode.""" return super(DellPowerConnectBase, self).config_mode( config_command=config_command ) class DellPowerConnectSSH(DellPowerConnectBase): """Dell PowerConnect Driver. To make it work, we have to override the SSHClient _auth method. If we use login/password, the ssh server use the (none) auth mechanism. """ def _build_ssh_client(self): """Prepare for Paramiko SSH connection. See base_connection.py file for any updates. """ # Create instance of SSHClient object # If user does not provide SSH key, we use noauth if not self.use_keys: remote_conn_pre = SSHClient_noauth() else: remote_conn_pre = SSHClient() # Load host_keys for better SSH security if self.system_host_keys: remote_conn_pre.load_system_host_keys() if self.alt_host_keys and path.isfile(self.alt_key_file): remote_conn_pre.load_host_keys(self.alt_key_file) # Default is to automatically add untrusted hosts (make sure appropriate for your env) remote_conn_pre.set_missing_host_key_policy(self.key_policy) return remote_conn_pre def special_login_handler(self, delay_factor=1): """ Powerconnect presents with the following on login User Name: Password: **** """ delay_factor = self.select_delay_factor(delay_factor) i = 0 time.sleep(delay_factor * 0.5) output = "" while i <= 12: output = self.read_channel() if output: if "User Name:" in output: self.write_channel(self.username + self.RETURN) elif "Password:" in output: self.write_channel(self.password + self.RETURN) break time.sleep(delay_factor * 1) else: self.write_channel(self.RETURN) time.sleep(delay_factor * 1.5) i += 1 class DellPowerConnectTelnet(DellPowerConnectBase): """Dell PowerConnect Telnet Driver.""" pass netmiko-2.4.2/netmiko/dell/dell_os10_ssh.py0000664000127700012770000001033113510465674021775 0ustar gitusergituser00000000000000"""Dell EMC Networking OS10 Driver - supports dellos10.""" from netmiko.cisco_base_connection import CiscoSSHConnection from netmiko.scp_handler import BaseFileTransfer import os import re class DellOS10SSH(CiscoSSHConnection): """Dell EMC Networking OS10 Driver - supports dellos10.""" def save_config( self, cmd="copy running-configuration startup-configuration", confirm=False, confirm_response="", ): """Saves Config""" return super(DellOS10SSH, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) class DellOS10FileTransfer(BaseFileTransfer): """Dell EMC Networking OS10 SCP File Transfer driver.""" def __init__( self, ssh_conn, source_file, dest_file, file_system=None, direction="put" ): if file_system is None: file_system = "/home/admin" super(DellOS10FileTransfer, self).__init__( ssh_conn=ssh_conn, source_file=source_file, dest_file=dest_file, file_system=file_system, direction=direction, ) self.folder_name = "/config" def remote_file_size(self, remote_cmd="", remote_file=None): """Get the file size of the remote file.""" if remote_file is None: if self.direction == "put": remote_file = self.dest_file elif self.direction == "get": remote_file = self.source_file remote_cmd = 'system "ls -l {}/{}"'.format(self.file_system, remote_file) remote_out = self.ssh_ctl_chan.send_command(remote_cmd) for line in remote_out.splitlines(): if remote_file in line: file_size = line.split()[4] break if "Error opening" in remote_out or "No such file or directory" in remote_out: raise IOError("Unable to find file on remote system") else: return int(file_size) def remote_space_available(self, search_pattern=r"(\d+) bytes free"): """Return space available on remote device.""" remote_cmd = 'system "df {}"'.format(self.folder_name) remote_output = self.ssh_ctl_chan.send_command_expect(remote_cmd) for line in remote_output.splitlines(): if self.folder_name in line: space_available = line.split()[-3] break return int(space_available) @staticmethod def process_md5(md5_output, pattern=r"(.*) (.*)"): return super(DellOS10FileTransfer, DellOS10FileTransfer).process_md5( md5_output, pattern=r"(.*) (.*)" ) def remote_md5(self, base_cmd="verify /md5", remote_file=None): """Calculate remote MD5 and returns the hash. """ if remote_file is None: if self.direction == "put": remote_file = self.dest_file elif self.direction == "get": remote_file = self.source_file remote_md5_cmd = 'system "md5sum {}/{}"'.format(self.file_system, remote_file) dest_md5 = self.ssh_ctl_chan.send_command(remote_md5_cmd, max_loops=1500) dest_md5 = self.process_md5(dest_md5) return dest_md5.strip() def check_file_exists(self, remote_cmd="dir home"): """Check if the dest_file already exists on the file system (return boolean).""" if self.direction == "put": remote_out = self.ssh_ctl_chan.send_command_expect(remote_cmd) search_string = r"Directory contents .*{}".format(self.dest_file) return bool(re.search(search_string, remote_out, flags=re.DOTALL)) elif self.direction == "get": return os.path.exists(self.dest_file) def put_file(self): """SCP copy the file from the local system to the remote device.""" destination = "{}".format(self.dest_file) self.scp_conn.scp_transfer_file(self.source_file, destination) # Must close the SCP connection to get the file written (flush) self.scp_conn.close() def get_file(self): """SCP copy the file from the remote device to local system.""" source_file = "{}".format(self.source_file) self.scp_conn.scp_get_file(source_file, self.dest_file) self.scp_conn.close() netmiko-2.4.2/netmiko/dell/dell_force10_ssh.py0000664000127700012770000000106113510465674022452 0ustar gitusergituser00000000000000"""Dell Force10 Driver - supports DNOS9.""" from __future__ import unicode_literals from netmiko.cisco_base_connection import CiscoSSHConnection class DellForce10SSH(CiscoSSHConnection): """Dell Force10 Driver - supports DNOS9.""" def save_config( self, cmd="copy running-configuration startup-configuration", confirm=False, confirm_response="", ): """Saves Config""" return super(DellForce10SSH, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) netmiko-2.4.2/netmiko/dell/__init__.py0000664000127700012770000000122413440357405021070 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.dell.dell_dnos6 import DellDNOS6SSH from netmiko.dell.dell_dnos6 import DellDNOS6Telnet from netmiko.dell.dell_force10_ssh import DellForce10SSH from netmiko.dell.dell_os10_ssh import DellOS10SSH, DellOS10FileTransfer from netmiko.dell.dell_powerconnect import DellPowerConnectSSH from netmiko.dell.dell_powerconnect import DellPowerConnectTelnet from netmiko.dell.dell_isilon_ssh import DellIsilonSSH __all__ = [ "DellForce10SSH", "DellPowerConnectSSH", "DellPowerConnectTelnet", "DellOS10SSH", "DellOS10FileTransfer", "DellIsilonSSH", "DellDNOS6SSH", "DellDNOS6Telnet", ] netmiko-2.4.2/netmiko/dell/dell_isilon_ssh.py0000664000127700012770000000607313440357405022512 0ustar gitusergituser00000000000000from __future__ import unicode_literals import time import re from netmiko.base_connection import BaseConnection class DellIsilonSSH(BaseConnection): def set_base_prompt( self, pri_prompt_terminator="$", alt_prompt_terminator="#", delay_factor=1 ): """Determine base prompt.""" return super(DellIsilonSSH, self).set_base_prompt( pri_prompt_terminator=pri_prompt_terminator, alt_prompt_terminator=alt_prompt_terminator, delay_factor=delay_factor, ) def strip_ansi_escape_codes(self, string_buffer): """Remove Null code""" output = re.sub(r"\x00", "", string_buffer) return super(DellIsilonSSH, self).strip_ansi_escape_codes(output) def session_preparation(self): """Prepare the session after the connection has been established.""" self.ansi_escape_codes = True self.zsh_mode() self.find_prompt(delay_factor=1) self.set_base_prompt() # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def zsh_mode(self, delay_factor=1, prompt_terminator="$"): """Run zsh command to unify the environment""" delay_factor = self.select_delay_factor(delay_factor) self.clear_buffer() command = self.RETURN + "zsh" + self.RETURN self.write_channel(command) time.sleep(1 * delay_factor) self.set_prompt() self.clear_buffer() def set_prompt(self, prompt_terminator="$"): prompt = "PROMPT='%m{}'".format(prompt_terminator) command = self.RETURN + prompt + self.RETURN self.write_channel(command) def disable_paging(self, *args, **kwargs): """Isilon doesn't have paging by default.""" pass def check_enable_mode(self, *args, **kwargs): """No enable mode on Isilon.""" pass def enable(self, *args, **kwargs): """No enable mode on Isilon.""" pass def exit_enable_mode(self, *args, **kwargs): """No enable mode on Isilon.""" pass def check_config_mode(self, check_string="#"): return super(DellIsilonSSH, self).check_config_mode(check_string=check_string) def config_mode(self, config_command="sudo su"): """Attempt to become root.""" delay_factor = self.select_delay_factor(delay_factor=1) output = "" if not self.check_config_mode(): output += self.send_command_timing( config_command, strip_prompt=False, strip_command=False ) if "Password:" in output: output = self.write_channel(self.normalize_cmd(self.secret)) self.set_prompt(prompt_terminator="#") time.sleep(1 * delay_factor) self.set_base_prompt() if not self.check_config_mode(): raise ValueError("Failed to configuration mode") return output def exit_config_mode(self, exit_config="exit"): """Exit enable mode.""" return super(DellIsilonSSH, self).exit_config_mode(exit_config=exit_config) netmiko-2.4.2/netmiko/f5/0000775000127700012770000000000013534772673016366 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/f5/f5_linux_ssh.py0000664000127700012770000000017413357263137021341 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.linux.linux_ssh import LinuxSSH class F5LinuxSSH(LinuxSSH): pass netmiko-2.4.2/netmiko/f5/__init__.py0000664000127700012770000000025313440357405020463 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.f5.f5_tmsh_ssh import F5TmshSSH from netmiko.f5.f5_linux_ssh import F5LinuxSSH __all__ = ["F5TmshSSH", "F5LinuxSSH"] netmiko-2.4.2/netmiko/f5/f5_tmsh_ssh.py0000664000127700012770000000174113440357405021151 0ustar gitusergituser00000000000000from __future__ import unicode_literals import time from netmiko.base_connection import BaseConnection class F5TmshSSH(BaseConnection): def session_preparation(self): """Prepare the session after the connection has been established.""" self._test_channel_read() self.set_base_prompt() self.tmsh_mode() self.set_base_prompt() self.disable_paging( command="modify cli preference pager disabled display-threshold 0" ) self.clear_buffer() cmd = 'run /util bash -c "stty cols 255"' self.set_terminal_width(command=cmd) def tmsh_mode(self, delay_factor=1): """tmsh command is equivalent to config command on F5.""" delay_factor = self.select_delay_factor(delay_factor) self.clear_buffer() command = "{}tmsh{}".format(self.RETURN, self.RETURN) self.write_channel(command) time.sleep(1 * delay_factor) self.clear_buffer() return None netmiko-2.4.2/netmiko/keymile/0000775000127700012770000000000013534772673017513 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/keymile/keymile_ssh.py0000664000127700012770000000337713534772563022411 0ustar gitusergituser00000000000000import time from netmiko.cisco.cisco_ios import CiscoIosBase class KeymileSSH(CiscoIosBase): def __init__(self, **kwargs): kwargs.setdefault("default_enter", "\r\n") return super(KeymileSSH, self).__init__(**kwargs) def session_preparation(self): """Prepare the session after the connection has been established.""" self._test_channel_read(pattern=r">") self.set_base_prompt() time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def disable_paging(self, *args, **kwargs): """Keymile does not use paging.""" return "" def check_config_mode(self, *args, **kwargs): """Keymile does not use config mode.""" return False def config_mode(self, *args, **kwargs): """Keymile does not use config mode.""" return "" def exit_config_mode(self, *args, **kwargs): """Keymile does not use config mode.""" return "" def check_enable_mode(self, *args, **kwargs): """Keymile does not use enable mode.""" return False def enable(self, *args, **kwargs): """Keymile does not use enable mode.""" return "" def exit_enable_mode(self, *args, **kwargs): """Keymile does not use enable mode.""" return "" def strip_prompt(self, a_string): """Remove appending empty line and prompt from output""" self._write_session_log(a_string) a_string = a_string[:-1] return super(KeymileSSH, self).strip_prompt(a_string=a_string) def set_base_prompt(self, pri_prompt_terminator=">", **kwargs): """ set prompt termination to >""" return super(KeymileSSH, self).set_base_prompt( pri_prompt_terminator=pri_prompt_terminator ) netmiko-2.4.2/netmiko/keymile/keymile_nos_ssh.py0000664000127700012770000000272513534772563023264 0ustar gitusergituser00000000000000import time import re from netmiko.cisco.cisco_ios import CiscoIosBase from netmiko.ssh_exception import NetMikoAuthenticationException class KeymileNOSSSH(CiscoIosBase): def session_preparation(self): """Prepare the session after the connection has been established.""" self.set_base_prompt() self.disable_paging() time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def _test_channel_read(self, count=40, pattern=""): """Since Keymile NOS always returns True on paramiko.connect() we check the output for substring Login incorrect after connecting.""" output = super(KeymileNOSSSH, self)._test_channel_read( count=count, pattern=pattern ) pattern = r"Login incorrect" if re.search(pattern, output): self.paramiko_cleanup() msg = "Authentication failure: unable to connect" msg += "{device_type} {host}:{port}".format( device_type=self.device_type, host=self.host, port=self.port ) msg += self.RESPONSE_RETURN + "Login incorrect" raise NetMikoAuthenticationException(msg) else: return output def special_login_handler(self, delay_factor=1): """Since Keymile NOS always returns True on paramiko.connect() we check the output for substring Login incorrect after connecting.""" self._test_channel_read(pattern=r"(>|Login incorrect)") netmiko-2.4.2/netmiko/keymile/__init__.py0000664000127700012770000000030013534772563021613 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.keymile.keymile_ssh import KeymileSSH from netmiko.keymile.keymile_nos_ssh import KeymileNOSSSH __all__ = ["KeymileSSH", "KeymileNOSSSH"] netmiko-2.4.2/netmiko/aruba/0000775000127700012770000000000013534772673017146 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/aruba/aruba_ssh.py0000664000127700012770000000236513440357405021461 0ustar gitusergituser00000000000000"""Aruba OS support""" from __future__ import unicode_literals import time import re from netmiko.cisco_base_connection import CiscoSSHConnection class ArubaSSH(CiscoSSHConnection): """Aruba OS support""" def __init__(self, **kwargs): if kwargs.get("default_enter") is None: kwargs["default_enter"] = "\r" return super(ArubaSSH, self).__init__(**kwargs) def session_preparation(self): """Aruba OS requires enable mode to disable paging.""" delay_factor = self.select_delay_factor(delay_factor=0) time.sleep(1 * delay_factor) self._test_channel_read() self.set_base_prompt() self.enable() self.disable_paging(command="no paging") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def check_config_mode(self, check_string="(config) #", pattern=""): """ Checks if the device is in configuration mode or not. Aruba uses "() (config) #" as config prompt """ if not pattern: pattern = re.escape(self.base_prompt[:16]) return super(ArubaSSH, self).check_config_mode( check_string=check_string, pattern=pattern ) netmiko-2.4.2/netmiko/aruba/__init__.py0000664000127700012770000000015513440357405021244 0ustar gitusergituser00000000000000from __future__ import unicode_literals from netmiko.aruba.aruba_ssh import ArubaSSH __all__ = ["ArubaSSH"] netmiko-2.4.2/netmiko/cisco/0000775000127700012770000000000013534772673017154 5ustar gitusergituser00000000000000netmiko-2.4.2/netmiko/cisco/cisco_s300.py0000664000127700012770000000174513440357405021366 0ustar gitusergituser00000000000000from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection class CiscoS300SSH(CiscoSSHConnection): """ Support for Cisco SG300 series of devices. Note, must configure the following to disable SG300 from prompting for username twice: configure terminal ip ssh password-auth """ def session_preparation(self): """Prepare the session after the connection has been established.""" self.ansi_escape_codes = True self._test_channel_read() self.set_base_prompt() self.disable_paging(command="terminal datadump") self.set_terminal_width(command="terminal width 511") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) def save_config(self, cmd="write memory", confirm=True, confirm_response="Y"): return super(CiscoS300SSH, self).save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) netmiko-2.4.2/netmiko/cisco/cisco_xr.py0000664000127700012770000001666113510701547021333 0ustar gitusergituser00000000000000from __future__ import print_function from __future__ import unicode_literals import time import re from netmiko.cisco_base_connection import CiscoBaseConnection, CiscoFileTransfer from netmiko.py23_compat import text_type class CiscoXrBase(CiscoBaseConnection): def session_preparation(self): """Prepare the session after the connection has been established.""" self._test_channel_read() self.set_base_prompt() self.disable_paging() self.set_terminal_width(command="terminal width 511") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() def send_config_set(self, config_commands=None, exit_config_mode=True, **kwargs): """IOS-XR requires you not exit from configuration mode.""" return super(CiscoXrBase, self).send_config_set( config_commands=config_commands, exit_config_mode=False, **kwargs ) def commit( self, confirm=False, confirm_delay=None, comment="", label="", delay_factor=1 ): """ Commit the candidate configuration. default (no options): command_string = commit confirm and confirm_delay: command_string = commit confirmed label (which is a label name): command_string = commit label