netmiko-2.4.2/ 0000775 0001277 0001277 00000000000 13534772673 014406 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/MANIFEST.in 0000664 0001277 0001277 00000000067 13265436437 016143 0 ustar gituser gituser 0000000 0000000 include *.md
include requirements.txt
include LICENSE
netmiko-2.4.2/PLATFORMS.md 0000664 0001277 0001277 00000002063 13511742056 016263 0 ustar gituser gituser 0000000 0000000 # 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.cfg 0000664 0001277 0001277 00000000415 13534772673 016227 0 ustar gituser gituser 0000000 0000000 [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.md 0000664 0001277 0001277 00000001745 13265436437 016050 0 ustar gituser gituser 0000000 0000000 # 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.txt 0000664 0001277 0001277 00000000062 13342300534 017644 0 ustar gituser gituser 0000000 0000000 --index-url https://pypi.python.org/simple/
-e .
netmiko-2.4.2/VENDOR.md 0000664 0001277 0001277 00000004374 13265436437 015731 0 ustar gituser gituser 0000000 0000000 Steps 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.py 0000664 0001277 0001277 00000003641 13516415527 016114 0 ustar gituser gituser 0000000 0000000 from 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/LICENSE 0000664 0001277 0001277 00000002066 13265436437 015413 0 ustar gituser gituser 0000000 0000000 The 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/ 0000775 0001277 0001277 00000000000 13534772673 017546 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko.egg-info/dependency_links.txt 0000664 0001277 0001277 00000000001 13534772673 023614 0 ustar gituser gituser 0000000 0000000
netmiko-2.4.2/netmiko.egg-info/requires.txt 0000664 0001277 0001277 00000000217 13534772673 022146 0 ustar gituser gituser 0000000 0000000 setuptools>=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.txt 0000664 0001277 0001277 00000000010 13534772673 022267 0 ustar gituser gituser 0000000 0000000 netmiko
netmiko-2.4.2/netmiko.egg-info/SOURCES.txt 0000664 0001277 0001277 00000007247 13534772673 021444 0 ustar gituser gituser 0000000 0000000 COMMON_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.py netmiko-2.4.2/netmiko.egg-info/PKG-INFO 0000664 0001277 0001277 00000025262 13534772673 020652 0 ustar gituser gituser 0000000 0000000 Metadata-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: 
[](https://pypi.python.org/pypi/netmiko)
[](https://pepy.tech/project/netmiko)

[](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.md 0000664 0001277 0001277 00000021010 13516455041 015642 0 ustar gituser gituser 0000000 0000000 
[](https://pypi.python.org/pypi/netmiko)
[](https://pepy.tech/project/netmiko)

[](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.md 0000664 0001277 0001277 00000005615 13265436437 017016 0 ustar gituser gituser 0000000 0000000 ### 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-INFO 0000664 0001277 0001277 00000025262 13534772673 015512 0 ustar gituser gituser 0000000 0000000 Metadata-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: 
[](https://pypi.python.org/pypi/netmiko)
[](https://pepy.tech/project/netmiko)

[](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/ 0000775 0001277 0001277 00000000000 13534772673 016054 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/vyos/ 0000775 0001277 0001277 00000000000 13534772673 017054 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/vyos/vyos_ssh.py 0000664 0001277 0001277 00000010367 13510465674 021304 0 ustar gituser gituser 0000000 0000000 from __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__.py 0000664 0001277 0001277 00000000151 13440357406 021147 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.vyos.vyos_ssh import VyOSSSH
__all__ = ["VyOSSSH"]
netmiko-2.4.2/netmiko/py23_compat.py 0000664 0001277 0001277 00000000752 13440357406 020557 0 ustar gituser gituser 0000000 0000000 """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/ 0000775 0001277 0001277 00000000000 13534772673 017175 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/eltex/eltex_ssh.py 0000664 0001277 0001277 00000001252 13510465674 021537 0 ustar gituser gituser 0000000 0000000 from __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__.py 0000664 0001277 0001277 00000000155 13440357405 021273 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.eltex.eltex_ssh import EltexSSH
__all__ = ["EltexSSH"]
netmiko-2.4.2/netmiko/ipinfusion/ 0000775 0001277 0001277 00000000000 13534772673 020237 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/ipinfusion/__init__.py 0000664 0001277 0001277 00000000312 13440357405 022330 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.ipinfusion.ipinfusion_ocnos import (
IpInfusionOcNOSSSH,
IpInfusionOcNOSTelnet,
)
__all__ = ["IpInfusionOcNOSSSH", "IpInfusionOcNOSTelnet"]
netmiko-2.4.2/netmiko/ipinfusion/ipinfusion_ocnos.py 0000664 0001277 0001277 00000005057 13440357405 024170 0 ustar gituser gituser 0000000 0000000 from __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/ 0000775 0001277 0001277 00000000000 13534772673 017345 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/quanta/quanta_mesh_ssh.py 0000664 0001277 0001277 00000001443 13510465674 023075 0 ustar gituser gituser 0000000 0000000 from __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__.py 0000664 0001277 0001277 00000000176 13440357406 021447 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.quanta.quanta_mesh_ssh import QuantaMeshSSH
__all__ = ["QuantaMeshSSH"]
netmiko-2.4.2/netmiko/terminal_server/ 0000775 0001277 0001277 00000000000 13534772673 021255 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/terminal_server/__init__.py 0000664 0001277 0001277 00000000360 13440357406 023352 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000001637 13440357406 025024 0 ustar gituser gituser 0000000 0000000 """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/ 0000775 0001277 0001277 00000000000 13534772673 017706 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/fortinet/__init__.py 0000664 0001277 0001277 00000000171 13440357405 022002 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.fortinet.fortinet_ssh import FortinetSSH
__all__ = ["FortinetSSH"]
netmiko-2.4.2/netmiko/fortinet/fortinet_ssh.py 0000664 0001277 0001277 00000007231 13510465674 022764 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000201765 13534772563 021570 0 ustar gituser gituser 0000000 0000000 """
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/ 0000775 0001277 0001277 00000000000 13534772673 020071 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/enterasys/enterasys_ssh.py 0000664 0001277 0001277 00000001206 13510465674 023326 0 ustar gituser gituser 0000000 0000000 """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__.py 0000664 0001277 0001277 00000000175 13440357405 022171 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.enterasys.enterasys_ssh import EnterasysSSH
__all__ = ["EnterasysSSH"]
netmiko-2.4.2/netmiko/coriant/ 0000775 0001277 0001277 00000000000 13534772673 017513 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/coriant/__init__.py 0000664 0001277 0001277 00000000115 13440357405 021605 0 ustar gituser gituser 0000000 0000000 from netmiko.coriant.coriant_ssh import CoriantSSH
__all__ = ["CoriantSSH"]
netmiko-2.4.2/netmiko/coriant/coriant_ssh.py 0000664 0001277 0001277 00000002735 13510465674 022402 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000024132 13534772563 021431 0 ustar gituser gituser 0000000 0000000 """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.py 0000664 0001277 0001277 00000021006 13511742056 022721 0 ustar gituser gituser 0000000 0000000 """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/ 0000775 0001277 0001277 00000000000 13534772673 016663 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/ovs/ovs_linux_ssh.py 0000664 0001277 0001277 00000000175 13265436437 022137 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.linux.linux_ssh import LinuxSSH
class OvsLinuxSSH(LinuxSSH):
pass
netmiko-2.4.2/netmiko/ovs/__init__.py 0000664 0001277 0001277 00000000165 13440357405 020762 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.ovs.ovs_linux_ssh import OvsLinuxSSH
__all__ = ["OvsLinuxSSH"]
netmiko-2.4.2/netmiko/_textfsm/ 0000775 0001277 0001277 00000000000 13534772673 017705 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/_textfsm/_clitable.py 0000664 0001277 0001277 00000033630 13516415527 022172 0 ustar gituser gituser 0000000 0000000 """
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.py 0000664 0001277 0001277 00000005635 13440357405 022226 0 ustar gituser gituser 0000000 0000000 """
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__.py 0000664 0001277 0001277 00000000252 13440357405 022001 0 ustar gituser gituser 0000000 0000000 from 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.py 0000664 0001277 0001277 00000110431 13440357405 022376 0 ustar gituser gituser 0000000 0000000 """
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.py 0000664 0001277 0001277 00000031604 13515366231 020700 0 ustar gituser gituser 0000000 0000000 """
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.py 0000664 0001277 0001277 00000007317 13440357406 021300 0 ustar gituser gituser 0000000 0000000 """
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/ 0000775 0001277 0001277 00000000000 13534772673 017356 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/citrix/__init__.py 0000664 0001277 0001277 00000000172 13440357405 021453 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.citrix.netscaler_ssh import NetscalerSSH
__all__ = ["NetscalerSSH"]
netmiko-2.4.2/netmiko/citrix/netscaler_ssh.py 0000664 0001277 0001277 00000003717 13440357405 022561 0 ustar gituser gituser 0000000 0000000 import 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/ 0000775 0001277 0001277 00000000000 13534772673 017673 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/mellanox/mellanox_mlnxos_ssh.py 0000664 0001277 0001277 00000004742 13516455041 024333 0 ustar gituser gituser 0000000 0000000 """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__.py 0000664 0001277 0001277 00000000214 13516455041 021764 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.mellanox.mellanox_mlnxos_ssh import MellanoxMlnxosSSH
__all__ = ["MellanoxMlnxosSSH"]
netmiko-2.4.2/netmiko/ruckus/ 0000775 0001277 0001277 00000000000 13534772673 017370 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/ruckus/__init__.py 0000664 0001277 0001277 00000000336 13440357406 021470 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000006600 13510465674 023157 0 ustar gituser gituser 0000000 0000000 from __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/ 0000775 0001277 0001277 00000000000 13534772673 017213 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/linux/__init__.py 0000664 0001277 0001277 00000000225 13440357405 021307 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.linux.linux_ssh import LinuxSSH, LinuxFileTransfer
__all__ = ["LinuxSSH", "LinuxFileTransfer"]
netmiko-2.4.2/netmiko/linux/linux_ssh.py 0000664 0001277 0001277 00000013754 13534772563 021611 0 ustar gituser gituser 0000000 0000000 from __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/ 0000775 0001277 0001277 00000000000 13534772673 016622 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/rad/__init__.py 0000664 0001277 0001277 00000000250 13440357406 020715 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000006050 13440357406 020610 0 ustar gituser gituser 0000000 0000000 from __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/ 0000775 0001277 0001277 00000000000 13534772673 017154 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/calix/calix_b6.py 0000664 0001277 0001277 00000006576 13510465674 021225 0 ustar gituser gituser 0000000 0000000 """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__.py 0000664 0001277 0001277 00000000220 13440357405 021243 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.calix.calix_b6 import CalixB6SSH, CalixB6Telnet
__all__ = ["CalixB6SSH", "CalixB6Telnet"]
netmiko-2.4.2/netmiko/ciena/ 0000775 0001277 0001277 00000000000 13534772673 017133 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/ciena/ciena_saos_ssh.py 0000664 0001277 0001277 00000001270 13510465674 022460 0 ustar gituser gituser 0000000 0000000 """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__.py 0000664 0001277 0001277 00000000172 13440357405 021230 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.ciena.ciena_saos_ssh import CienaSaosSSH
__all__ = ["CienaSaosSSH"]
netmiko-2.4.2/netmiko/__init__.py 0000664 0001277 0001277 00000002463 13534772563 020170 0 ustar gituser gituser 0000000 0000000 from __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/ 0000775 0001277 0001277 00000000000 13534772673 017707 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/ubiquiti/__init__.py 0000664 0001277 0001277 00000000175 13440357406 022010 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.ubiquiti.edge_ssh import UbiquitiEdgeSSH
__all__ = ["UbiquitiEdgeSSH"]
netmiko-2.4.2/netmiko/ubiquiti/edge_ssh.py 0000664 0001277 0001277 00000003215 13510465674 022035 0 ustar gituser gituser 0000000 0000000 from __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/ 0000775 0001277 0001277 00000000000 13534772673 016463 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/hp/hp_procurve.py 0000664 0001277 0001277 00000011724 13534772563 021374 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000007030 13510465674 021153 0 ustar gituser gituser 0000000 0000000 from __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__.py 0000664 0001277 0001277 00000000377 13440357405 020567 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000025536 13440357406 021624 0 ustar gituser gituser 0000000 0000000 """
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/ 0000775 0001277 0001277 00000000000 13534772673 017525 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/extreme/extreme_wing_ssh.py 0000664 0001277 0001277 00000001040 13440357405 023430 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000003003 13510465674 023264 0 ustar gituser gituser 0000000 0000000 """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.py 0000664 0001277 0001277 00000005506 13510465674 022606 0 ustar gituser gituser 0000000 0000000 """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__.py 0000664 0001277 0001277 00000001433 13440357405 021623 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000001744 13440357405 023276 0 ustar gituser gituser 0000000 0000000 """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.py 0000664 0001277 0001277 00000001360 13510465674 023300 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000001734 13440357405 023304 0 ustar gituser gituser 0000000 0000000 """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.py 0000664 0001277 0001277 00000001574 13510465674 023316 0 ustar gituser gituser 0000000 0000000 """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.py 0000664 0001277 0001277 00000026374 13510465674 021452 0 ustar gituser gituser 0000000 0000000 """
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/ 0000775 0001277 0001277 00000000000 13534772673 017603 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/accedian/accedian_ssh.py 0000664 0001277 0001277 00000003140 13510465674 022551 0 ustar gituser gituser 0000000 0000000 from __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__.py 0000664 0001277 0001277 00000000121 13440357405 021672 0 ustar gituser gituser 0000000 0000000 from netmiko.accedian.accedian_ssh import AccedianSSH
__all__ = ["AccedianSSH"]
netmiko-2.4.2/netmiko/ssh_exception.py 0000664 0001277 0001277 00000000646 13440357406 021274 0 ustar gituser gituser 0000000 0000000 from __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/ 0000775 0001277 0001277 00000000000 13534772673 017530 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/juniper/juniper.py 0000664 0001277 0001277 00000021433 13440357405 021545 0 ustar gituser gituser 0000000 0000000 from __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__.py 0000664 0001277 0001277 00000000275 13440357405 021631 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.juniper.juniper import JuniperSSH, JuniperTelnet, JuniperFileTransfer
__all__ = ["JuniperSSH", "JuniperTelnet", "JuniperFileTransfer"]
netmiko-2.4.2/netmiko/flexvnf/ 0000775 0001277 0001277 00000000000 13534772673 017524 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/flexvnf/__init__.py 0000664 0001277 0001277 00000000165 13510465674 021631 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.flexvnf.flexvnf_ssh import FlexvnfSSH
__all__ = ["FlexvnfSSH"]
netmiko-2.4.2/netmiko/flexvnf/flexvnf_ssh.py 0000664 0001277 0001277 00000015266 13510465674 022427 0 ustar gituser gituser 0000000 0000000 from __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/ 0000775 0001277 0001277 00000000000 13534772673 017337 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/arista/arista.py 0000664 0001277 0001277 00000007114 13440357405 021163 0 ustar gituser gituser 0000000 0000000 from __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__.py 0000664 0001277 0001277 00000000265 13440357405 021437 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.arista.arista import AristaSSH, AristaTelnet, AristaFileTransfer
__all__ = ["AristaSSH", "AristaTelnet", "AristaFileTransfer"]
netmiko-2.4.2/netmiko/alcatel/ 0000775 0001277 0001277 00000000000 13534772673 017461 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/alcatel/alcatel_aos_ssh.py 0000664 0001277 0001277 00000002717 13510465674 023160 0 ustar gituser gituser 0000000 0000000 """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__.py 0000664 0001277 0001277 00000000315 13440357405 021555 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000005716 13510465674 023366 0 ustar gituser gituser 0000000 0000000 """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/ 0000775 0001277 0001277 00000000000 13534772673 020215 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/cloudgenix/__init__.py 0000664 0001277 0001277 00000000137 13510465674 022321 0 ustar gituser gituser 0000000 0000000 from netmiko.cloudgenix.cloudgenix_ion import CloudGenixIonSSH
__all__ = ["CloudGenixIonSSH"]
netmiko-2.4.2/netmiko/cloudgenix/cloudgenix_ion.py 0000664 0001277 0001277 00000005013 13510465674 023566 0 ustar gituser gituser 0000000 0000000 import 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/ 0000775 0001277 0001277 00000000000 13534772673 017343 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/netapp/__init__.py 0000664 0001277 0001277 00000000176 13440357405 021444 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.netapp.netapp_cdot_ssh import NetAppcDotSSH
__all__ = ["NetAppcDotSSH"]
netmiko-2.4.2/netmiko/netapp/netapp_cdot_ssh.py 0000664 0001277 0001277 00000002537 13440357405 023065 0 ustar gituser gituser 0000000 0000000 from __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/ 0000775 0001277 0001277 00000000000 13534772673 017273 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/endace/endace_ssh.py 0000664 0001277 0001277 00000003413 13510465674 021734 0 ustar gituser gituser 0000000 0000000 from __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__.py 0000664 0001277 0001277 00000000165 13510465674 021400 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.endace.endace_ssh import EndaceSSH
__all__ = ["EndaceSSH"]
netmiko-2.4.2/netmiko/huawei/ 0000775 0001277 0001277 00000000000 13534772673 017336 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/huawei/huawei.py 0000664 0001277 0001277 00000017075 13516415527 021174 0 ustar gituser gituser 0000000 0000000 from __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__.py 0000664 0001277 0001277 00000000316 13510465674 021441 0 ustar gituser gituser 0000000 0000000 from __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/ 0000775 0001277 0001277 00000000000 13534772673 020203 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/checkpoint/checkpoint_gaia_ssh.py 0000664 0001277 0001277 00000001755 13510465674 024544 0 ustar gituser gituser 0000000 0000000 from __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__.py 0000664 0001277 0001277 00000000216 13440357405 022277 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.checkpoint.checkpoint_gaia_ssh import CheckPointGaiaSSH
__all__ = ["CheckPointGaiaSSH"]
netmiko-2.4.2/netmiko/mrv/ 0000775 0001277 0001277 00000000000 13534772673 016660 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/mrv/mrv_lx.py 0000664 0001277 0001277 00000002440 13510465674 020533 0 ustar gituser gituser 0000000 0000000 """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__.py 0000664 0001277 0001277 00000000255 13510465674 020765 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000003066 13510465674 020712 0 ustar gituser gituser 0000000 0000000 """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/ 0000775 0001277 0001277 00000000000 13534772673 016435 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/a10/__init__.py 0000664 0001277 0001277 00000000145 13440357405 020532 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.a10.a10_ssh import A10SSH
__all__ = ["A10SSH"]
netmiko-2.4.2/netmiko/a10/a10_ssh.py 0000664 0001277 0001277 00000001357 13510465674 020245 0 ustar gituser gituser 0000000 0000000 """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.py 0000664 0001277 0001277 00000024557 13516415527 020446 0 ustar gituser gituser 0000000 0000000 """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.py 0000664 0001277 0001277 00000000124 13440357405 021560 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
MAX_BUFFER = 65535
BACKSPACE_CHAR = "\x08"
netmiko-2.4.2/netmiko/dell/ 0000775 0001277 0001277 00000000000 13534772673 016774 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/dell/dell_dnos6.py 0000664 0001277 0001277 00000002054 13510465674 021372 0 ustar gituser gituser 0000000 0000000 """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.py 0000664 0001277 0001277 00000007367 13440357405 023055 0 ustar gituser gituser 0000000 0000000 """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.py 0000664 0001277 0001277 00000010331 13510465674 021775 0 ustar gituser gituser 0000000 0000000 """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.py 0000664 0001277 0001277 00000001061 13510465674 022452 0 ustar gituser gituser 0000000 0000000 """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__.py 0000664 0001277 0001277 00000001224 13440357405 021070 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000006073 13440357405 022512 0 ustar gituser gituser 0000000 0000000 from __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/ 0000775 0001277 0001277 00000000000 13534772673 016366 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/f5/f5_linux_ssh.py 0000664 0001277 0001277 00000000174 13357263137 021341 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.linux.linux_ssh import LinuxSSH
class F5LinuxSSH(LinuxSSH):
pass
netmiko-2.4.2/netmiko/f5/__init__.py 0000664 0001277 0001277 00000000253 13440357405 020463 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000001741 13440357405 021151 0 ustar gituser gituser 0000000 0000000 from __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/ 0000775 0001277 0001277 00000000000 13534772673 017513 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/keymile/keymile_ssh.py 0000664 0001277 0001277 00000003377 13534772563 022411 0 ustar gituser gituser 0000000 0000000 import 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.py 0000664 0001277 0001277 00000002725 13534772563 023264 0 ustar gituser gituser 0000000 0000000 import 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__.py 0000664 0001277 0001277 00000000300 13534772563 021613 0 ustar gituser gituser 0000000 0000000 from __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/ 0000775 0001277 0001277 00000000000 13534772673 017146 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/aruba/aruba_ssh.py 0000664 0001277 0001277 00000002365 13440357405 021461 0 ustar gituser gituser 0000000 0000000 """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__.py 0000664 0001277 0001277 00000000155 13440357405 021244 0 ustar gituser gituser 0000000 0000000 from __future__ import unicode_literals
from netmiko.aruba.aruba_ssh import ArubaSSH
__all__ = ["ArubaSSH"]
netmiko-2.4.2/netmiko/cisco/ 0000775 0001277 0001277 00000000000 13534772673 017154 5 ustar gituser gituser 0000000 0000000 netmiko-2.4.2/netmiko/cisco/cisco_s300.py 0000664 0001277 0001277 00000001745 13440357405 021366 0 ustar gituser gituser 0000000 0000000 from __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.py 0000664 0001277 0001277 00000016661 13510701547 021333 0 ustar gituser gituser 0000000 0000000 from __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