├── .gitignore ├── .travis.yml ├── README.md ├── TODO.md ├── netlib ├── __init__.py ├── conn_type.py └── user_keyring.py ├── requirements.txt ├── scripts └── tests.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | build/* 3 | dist/* 4 | *.egg-info 5 | env/* 6 | test.py 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | matrix: 4 | include: 5 | - os: linux 6 | sudo: required 7 | python: 2.7 8 | - os: linux 9 | sudo: required 10 | python: 3.5 11 | - os: linux 12 | sudo: required 13 | python: 3.6 14 | 15 | install: 16 | - "python setup.py install" 17 | 18 | script: 19 | - "python scripts/tests.py" 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NetLib 2 | 3 | [![Build Status](https://travis-ci.org/netopsio/netlib.svg)](https://travis-ci.org/netopsio/netlib) 4 | 5 | Netlib is an attempt at re-writing 6 | ['pyRouterLib'](https://github.com/jtdub/pyRouterLib). The goal is to create a 7 | library that is much more efficient and easier to use to establish SSH and Telnet 8 | connections to network devices, such as routers and switches. 9 | 10 | ## Install 11 | 12 | Install the Python library. 13 | 14 | ``` 15 | git clone https://github.com/jtdub/netlib.git 16 | cd netlib 17 | 18 | # For Python2 Support: 19 | sudo python setup.py install 20 | 21 | # For Python3 Support: 22 | sudo python3 setup.py install 23 | ``` 24 | 25 | ## Access via Telnet and SSH 26 | 27 | Currently, the SSH and Telnet modules have been created. Both modules have a 28 | very similar API structure, with the biggest difference between the two are how 29 | connections are established to network devices. 30 | 31 | To use either the SSH or Telnet module, you need to import the library into your script: 32 | 33 | ``` 34 | from netlib.conn_type import SSH 35 | from netlib.conn_type import Telnet 36 | ``` 37 | 38 | From there, you define your connection parameters: 39 | 40 | ``` 41 | telnet = Telnet('somerouter', 'username', 'password') 42 | ssh = SSH('somerouter', 'username', 'password') 43 | ``` 44 | 45 | Once the basic parameters have been set, you establish a connection to the 46 | device. 47 | 48 | ``` 49 | telnet.connect() 50 | ssh.connect() 51 | ``` 52 | 53 | Once you are connected, you are free to send commands to you network device. If 54 | you intend to iterate through output that is long, then you can disable paging 55 | on the output. 56 | 57 | ``` 58 | telnet.disable_paging() 59 | ssh.disable_paging() 60 | 61 | telnet.command('show version') 62 | ssh.command('show version') 63 | ``` 64 | 65 | If you need to enter a privileged mode, you can use the set_enable api. 66 | 67 | ``` 68 | telnet.set_enable('supersecretpassword') 69 | ssh.set_enable('supersecretpassword') 70 | ``` 71 | 72 | When you've completed your task on the device, you can close your connections. 73 | 74 | ``` 75 | telnet.close() 76 | ssh.close() 77 | ``` 78 | 79 | At this point, those are the features that both libraries share. As the telnet 80 | library and ssh library vary on how they parse data, there is a need for extra 81 | functionality on the ssh library. Here is the API functionality that is 82 | specific to the ssh library: 83 | 84 | ``` 85 | ssh.clear_buffer() 86 | ``` 87 | 88 | The SSH library stores output into a buffer. Sometimes this buffer can present 89 | results that aren't expected. Clearing the buffer should mitigate the 90 | unexpected results. 91 | 92 | ## User Credentials 93 | 94 | When working with a large number of devices, it's inconvenient to have to type 95 | your credentials in a large number of times and storing your credentials 96 | directly into a script can be insecure. Therefore, I created a library that 97 | allows you to store them, securely utilizing the 'keyring' python library. 98 | 'keyring' utilizes your operating systems native method for storing passwords. 99 | For example, in MacOS X, the Keychain utility is utilized. 100 | 101 | The past methods of password storage, simple_creds and simple_yaml have been depricated 102 | and removed from netlib. 103 | 104 | Utilizing the keyring is simple. Import the module: 105 | 106 | ``` 107 | >>> from netlib.user_keyring import KeyRing 108 | ``` 109 | 110 | Asign it to a variable, calling your username: 111 | ``` 112 | >>> user = KeyRing(username='jtdub') 113 | ``` 114 | 115 | If there are no credentials for the keyring, the get_creds() method will call the set_creds() 116 | method: 117 | ``` 118 | >>> user.get_creds() 119 | No credentials keyring exist. Creating new credentials. 120 | Enter your user password: 121 | Confirm your user password: 122 | Enter your enable password: 123 | Confirm your enable password: 124 | ``` 125 | 126 | Otherwise, the creds will be pulled from the keyring: 127 | ``` 128 | >>> user.get_creds() 129 | {'username': 'jtdub', 'enable': u'enablepass', 'password': u'testpass'} 130 | ``` 131 | 132 | The set_creds() method can be called directly. It will over-write existing creds if they exist: 133 | ``` 134 | >>> user.set_creds() 135 | Enter your user password: 136 | Confirm your user password: 137 | Enter your enable password: 138 | Confirm your enable password: 139 | >>> user.get_creds() 140 | {'username': 'jtdub', 'enable': u'newenable', 'password': u'newpass'} 141 | ``` 142 | 143 | Of course, the keyring can be deleted all together utilizing the del_creds() method: 144 | ``` 145 | >>> user.del_creds() 146 | Enter your user password: 147 | Deleting keyring credentials for jtdub 148 | >>> 149 | ``` 150 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Ideas 2 | 3 | * Create a library that will take a list of IP Addresses and perform a whois, outputting the information in a JSON format. 4 | * Create a library that will create a network tree based upon CDP/LLDP neighbors. 5 | -------------------------------------------------------------------------------- /netlib/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | from __future__ import absolute_import 3 | from __future__ import print_function 4 | from __future__ import unicode_literals 5 | 6 | __name__ = 'netlib' 7 | __version__ = '0.1.0' 8 | -------------------------------------------------------------------------------- /netlib/conn_type.py: -------------------------------------------------------------------------------- 1 | class SSH(object): 2 | 3 | def __init__(self, device_name, username, password, buffer="65535", 4 | delay="1", port="22"): 5 | import paramiko 6 | import time 7 | import re 8 | self.paramiko = paramiko 9 | self.time = time 10 | self.re = re 11 | self.device_name = device_name 12 | self.username = username 13 | self.password = password 14 | self.buffer = int(buffer) 15 | self.delay = int(delay) 16 | self.port = int(port) 17 | 18 | def connect(self): 19 | self.pre_conn = self.paramiko.SSHClient() 20 | self.pre_conn.set_missing_host_key_policy( 21 | self.paramiko.AutoAddPolicy()) 22 | self.pre_conn.connect(self.device_name, username=self.username, 23 | password=self.password, allow_agent=False, 24 | look_for_keys=False, port=self.port) 25 | self.client_conn = self.pre_conn.invoke_shell() 26 | self.time.sleep(float(self.delay)) 27 | return self.client_conn.recv(self.buffer) 28 | 29 | def close(self): 30 | return self.pre_conn.close() 31 | 32 | def clear_buffer(self): 33 | if self.client_conn.recv_ready(): 34 | return self.client_conn.recv(self.buffer).decode('utf-8', 'ignore') 35 | else: 36 | return None 37 | 38 | def set_enable(self, enable_password): 39 | if self.re.search('>$', self.command('\n')): 40 | enable = self.command('enable') 41 | if self.re.search('Password', enable): 42 | send_pwd = self.command(enable_password) 43 | return send_pwd 44 | elif self.re.search('#$', self.command('\n')): 45 | return "Action: None. Already in enable mode." 46 | else: 47 | return "Error: Unable to determine user privilege status." 48 | 49 | def disable_paging(self, command='term len 0'): 50 | self.client_conn.sendall(command + "\n") 51 | self.clear_buffer() 52 | 53 | def command(self, command): 54 | self.client_conn.sendall(command + "\n") 55 | not_done = True 56 | output = str() 57 | while not_done: 58 | self.time.sleep(float(self.delay)) 59 | if self.client_conn.recv_ready(): 60 | output += self.client_conn.recv(self.buffer).decode('utf-8') 61 | else: 62 | not_done = False 63 | return output 64 | 65 | def commands(self, commands_list): 66 | output = str() 67 | if list(commands_list): 68 | for command in commands_list: 69 | output += self.command(command) 70 | else: 71 | output += self.command(commands_list) 72 | return output 73 | 74 | 75 | class Telnet(object): 76 | 77 | def __init__(self, device_name, username, password, delay="2", port="23"): 78 | import telnetlib 79 | import time 80 | import re 81 | self.telnetlib = telnetlib 82 | self.time = time 83 | self.re = re 84 | self.device_name = device_name 85 | self.username = username 86 | self.password = password 87 | self.delay = float(delay) 88 | self.port = int(port) 89 | 90 | def connect(self): 91 | self.access = self.telnetlib.Telnet(self.device_name, self.port) 92 | login_prompt = self.access.read_until(b"\(Username: \)|\(login: \)", 93 | self.delay) 94 | if b'login' in login_prompt: 95 | self.is_nexus = True 96 | self.access.write(self.username.encode('ascii') + b'\n') 97 | elif b'Username' in login_prompt: 98 | self.is_nexus = False 99 | self.access.write(self.username.encode('ascii') + b'\n') 100 | password_prompt = self.access.read_until(b'Password:', 101 | self.delay) 102 | self.access.write(self.password.encode('ascii') + b'\n') 103 | return self.access 104 | 105 | def close(self): 106 | return self.access.close() 107 | 108 | def clear_buffer(self): 109 | pass 110 | 111 | def set_enable(self, enable_password): 112 | if self.re.search(b'>$', self.command('\n')): 113 | self.access.write(b'enable\n') 114 | enable = self.access.read_until(b'Password') 115 | return self.access.write(enable_password.encode('ascii') + b'\n') 116 | elif self.re.search(b'#$', self.command('\n')): 117 | return "Action: None. Already in enable mode." 118 | else: 119 | return "Error: Unable to determine user privilege status." 120 | 121 | def disable_paging(self, command='term len 0'): 122 | self.access.write(command.encode('ascii') + b'\n') 123 | return self.access.read_until(b"\(#\)|\(>\)", self.delay) 124 | 125 | def command(self, command): 126 | self.access.write(command.encode('ascii') + b'\n') 127 | return self.access.read_until(b"\(#\)|\(>\)", self.delay) 128 | 129 | def commands(self, commands_list): 130 | output = str() 131 | if list(commands_list): 132 | for command in commands_list: 133 | output += self.command(command) 134 | else: 135 | output += self.command(commands_list) 136 | return output 137 | -------------------------------------------------------------------------------- /netlib/user_keyring.py: -------------------------------------------------------------------------------- 1 | class KeyRing(object): 2 | 3 | def __init__(self, username): 4 | import keyring 5 | import getpass 6 | self.username = username 7 | self.keyring = keyring 8 | self.getpass = getpass 9 | 10 | def get_creds(self): 11 | if self.keyring.get_password('nl_user_pass', 12 | username=self.username) is None: 13 | print('No credentials keyring exist. Creating new credentials.') 14 | self.set_creds() 15 | else: 16 | user_pass = self.keyring.get_password('nl_user_pass', 17 | username=self.username) 18 | enable_pass = self.keyring.get_password('nl_enable_pass', 19 | username=self.username) 20 | return {'username': self.username, 21 | 'password': str(user_pass), 22 | 'enable': str(enable_pass)} 23 | 24 | def set_creds(self): 25 | match = False 26 | while match is False: 27 | password1 = self.getpass.getpass('Enter your user password: ') 28 | password2 = self.getpass.getpass('Confirm your user password: ') 29 | if password1 == password2: 30 | user_password = password1 31 | match = True 32 | match = False 33 | while match is False: 34 | password1 = self.getpass.getpass('Enter your enable password: ') 35 | password2 = self.getpass.getpass('Confirm your enable password: ') 36 | if password1 == password2: 37 | enable_password = password1 38 | match = True 39 | user_pass = self.keyring.set_password('nl_user_pass', 40 | username=self.username, 41 | password=user_password) 42 | enable_pass = self.keyring.set_password('nl_enable_pass', 43 | username=self.username, 44 | password=enable_password) 45 | self.get_creds() 46 | 47 | def del_creds(self): 48 | tries = 0 49 | max_tries = 5 50 | while tries <= max_tries: 51 | user_pass = self.keyring.get_password('nl_user_pass', 52 | username=self.username) 53 | ask_pass = self.getpass.getpass('Enter your user password: ') 54 | if user_pass == ask_pass: 55 | print('Deleting keyring credentials for {}'.format( 56 | self.username)) 57 | self.keyring.delete_password('nl_user_pass', 58 | username=self.username) 59 | self.keyring.delete_password('nl_enable_pass', 60 | username=self.username) 61 | tries = max_tries + 1 62 | else: 63 | tries += 1 64 | print('Error: Incorrect password.') 65 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | keyring>=9.3 2 | keyrings.alt>=1.1.1 3 | paramiko>=2.0.1 4 | pycrypto>=2.6.1 5 | -------------------------------------------------------------------------------- /scripts/tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import unittest 4 | 5 | class TestImports(unittest.TestCase): 6 | 7 | def test_import_keyring(self): 8 | try: 9 | from netlib.user_keyring import KeyRing 10 | user_keyring = True 11 | except: 12 | user_keyring = False 13 | raise 14 | self.assertTrue(user_keyring) 15 | 16 | def test_import_ssh(self): 17 | try: 18 | from netlib.conn_type import SSH 19 | ssh = True 20 | except: 21 | ssh = False 22 | raise 23 | self.assertTrue(ssh) 24 | 25 | def test_import_telnet(self): 26 | try: 27 | from netlib.conn_type import Telnet 28 | telnet = True 29 | except: 30 | telnet = False 31 | raise 32 | self.assertTrue(telnet) 33 | 34 | 35 | if __name__ == '__main__': 36 | unittest.main() 37 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | from netlib import __version__ 5 | 6 | 7 | setup( 8 | name='netlib', 9 | version=__version__, 10 | url='https://github.com/netopsio/netlib', 11 | author='James Williams', 12 | license='MIT', 13 | install_requires=[ 14 | 'paramiko', 15 | 'pycrypto', 16 | 'keyring', 17 | 'keyrings.alt' 18 | ], 19 | description='Simple access to network devices, such as routers and switches, via Telnet and SSH.', 20 | packages=[ 21 | 'netlib', 22 | ], 23 | ) 24 | --------------------------------------------------------------------------------