pyEOS-0.63/0000755000076500000240000000000012540032754013401 5ustar dbarrosostaff00000000000000pyEOS-0.63/MANIFEST.in0000644000076500000240000000003112503547660015137 0ustar dbarrosostaff00000000000000include requirements.txt pyEOS-0.63/PKG-INFO0000644000076500000240000000054412540032754014501 0ustar dbarrosostaff00000000000000Metadata-Version: 1.1 Name: pyEOS Version: 0.63 Summary: Python API to interact with network devices running EOS Home-page: https://github.com/spotify/pyeos/ Author: David Barroso Author-email: dbarroso@spotify.com License: UNKNOWN Download-URL: https://github.com/spotify/pyeos/tarball/0.63 Description: UNKNOWN Keywords: EOS,networking Platform: UNKNOWN pyEOS-0.63/pyEOS/0000755000076500000240000000000012540032754014400 5ustar dbarrosostaff00000000000000pyEOS-0.63/pyEOS/__init__.py0000644000076500000240000000120212503547660016512 0ustar dbarrosostaff00000000000000# Copyright 2014 Spotify AB. All rights reserved. # # The contents of this file are 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 eos import EOSpyEOS-0.63/pyEOS/eos.py0000644000076500000240000002105412540032634015537 0ustar dbarrosostaff00000000000000# Copyright 2014 Spotify AB. All rights reserved. # # The contents of this file are 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 jsonrpclib import Server from jsonrpclib import ProtocolError import exceptions class EOS: def __init__(self, hostname, username, password, use_ssl=True): """ Represents a device running EOS. The object will contain the following interesting attributes: * **running_config** - The configuration retrieved from the device using the method load_running_config * **candidate_config** - The configuration we desire for the device. Can be populated using the method load_candidate_config :param hostname: IP or FQDN of the device you want to connect to :param username: Username :param password: Password :param use_ssl: If set you True we will connect to the eAPI using https, otherwise http will be used """ self.hostname = hostname self.username = username self.device = None self.password = password self.use_ssl = use_ssl self.candidate_config = None self.original_config = None def __getattr__(self, item): def wrapper(*args, **kwargs): pipe = kwargs.pop('pipe', None) if pipe is None: cmd = [item.replace('_', ' ')] else: cmd = ['{} | {}'.format(item.replace('_', ' '), pipe)] return self.run_commands(cmd, **kwargs)[1] if item.startswith('show'): return wrapper else: raise AttributeError("type object '%s' has no attribute '%s'" % (self.__class__.__name__, item)) @staticmethod def _load_file(filename): string = '' with open(filename, 'r') as f: for line in f.readlines(): if line.strip() != '': string += '{}\n'.format(line.strip()) return string def open(self): """ Opens the connection with the device. """ if self.use_ssl: url = 'https://%s:%s@%s/command-api' % (self.username, self.password, self.hostname) else: url = 'http://%s:%s@%s/command-api' % (self.username, self.password, self.hostname) self.device = Server(url) def run_commands(self, commands, version=1, auto_format=False, format='json', timestamps=True): """ This method will run as many commands as you want. The 'enable' command will be prepended automatically so you don't have to worry about that. :param commands: List of commands you want to run :param version: Version of the eAPI you want to connect to. By default is 1. :param auto_format: If set to True API calls not supporting returning JSON messages will be converted automatically to text. By default is False. :param format: Format you want to get; 'json' or 'text'. By default is json. This will trigger a CommandUnconverted exception if set to 'json' and auto_format is set to False. It will return text if set to 'json' but auto_format is set to True. :param timestamps: This will return some useful information like when was the command executed and how long it took. """ if 'enable' is not commands[0]: commands.insert(0, 'enable') if auto_format: format = 'json' try: result = self.device.runCmds( version=version, cmds=commands, format=format, timestamps=timestamps, ) except ProtocolError as e: code = e[0][0] error = e[0][1] if code == 1003: # code 1003 means the command is not yet converted to json if auto_format: result = self.device.runCmds( version=version, cmds=commands, format='text', timestamps=timestamps ) else: raise exceptions.CommandUnconverted(error) # code -32602 means "Unexpected parameter 'timestamps' for method 'runCmds' provided" elif code == -32602: result = self.device.runCmds( version=version, cmds=commands, format=format ) elif code == 1002: # code 1002 means the command was wrong raise exceptions.CommandError(error) elif code == 1000: # code 1000 means a command is wrong when doing a "config replace" raise exceptions.ConfigReplaceError(e) else: raise exceptions.UnknownError((code, error)) return result def close(self): """ Dummy, method. Today it does not do anything but it would be interesting to use it to fake closing a connection. """ pass def get_config(self, format='json'): """ :param format: Either 'json' or 'text' :return: The running configuration of the device. """ if format == 'json': return self.run_commands(['sh running-config'])[1]['cmds'] elif format == 'text': return self.run_commands(['sh running-config'], format='text')[1]['output'] def load_candidate_config(self, filename=None, config=None): """ Populates the attribute candidate_config with the desired configuration. You can populate it from a file or from a string. If you send both a filename and a string containing the configuration, the file takes precedence. :param filename: Path to the file containing the desired configuration. By default is None. :param config: String containing the desired configuration. """ if filename is not None: self.candidate_config = self._load_file(filename) else: self.candidate_config = config def compare_config(self, session=None): """ :return: A string showing the difference between the running_config and the candidate_config. The running_config is loaded automatically just before doing the comparison so there is no neeed for you to do it. """ # We get the config in text format because you get better printability by parsing and using an OrderedDict cmds = self.candidate_config.splitlines() cmds.insert(0, 'configure session pyeos-diff') cmds.insert(1, 'rollback clean-config') self.run_commands(cmds) diff = self.run_commands(['show session-config named pyeos-diff diffs'], format='text') self.run_commands(['configure session pyeos-diff', 'abort']) return diff[1]['output'][:-1] def replace_config(self, config=None, force=False): """ Applies the configuration changes on the device. You can either commit the changes on the candidate_config attribute or you can send the desired configuration as a string. Note that the current configuration of the device is replaced with the new configuration. :param config: String containing the desired configuration. If set to None the candidate_config will be used :param force: If set to False we rollback changes if we detect a config error. """ if config is None: config = self.candidate_config if force: force_text = 'ignore-errors' else: force_text = '' body = { 'cmd': 'configure replace terminal: %s' % force_text, 'input': config } self.original_config = self.get_config(format='text') result = self.run_commands([body]) if 'Invalid' not in result[1]['messages'][0]: return result else: raise exceptions.CommandError(result[1]['messages'][0]) def rollback(self): """ If used after a commit, the configuration will be reverted to the previous state. """ return self.replace_config(config=self.original_config, force=True) pyEOS-0.63/pyEOS/exceptions.py0000644000076500000240000000143512503550036017133 0ustar dbarrosostaff00000000000000# Copyright 2014 Spotify AB. All rights reserved. # # The contents of this file are 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. class ConfigReplaceError(Exception): pass class CommandUnconverted(Exception): pass class CommandError(Exception): pass class UnknownError(Exception): passpyEOS-0.63/pyEOS.egg-info/0000755000076500000240000000000012540032754016072 5ustar dbarrosostaff00000000000000pyEOS-0.63/pyEOS.egg-info/dependency_links.txt0000644000076500000240000000000112540032754022140 0ustar dbarrosostaff00000000000000 pyEOS-0.63/pyEOS.egg-info/PKG-INFO0000644000076500000240000000054412540032754017172 0ustar dbarrosostaff00000000000000Metadata-Version: 1.1 Name: pyEOS Version: 0.63 Summary: Python API to interact with network devices running EOS Home-page: https://github.com/spotify/pyeos/ Author: David Barroso Author-email: dbarroso@spotify.com License: UNKNOWN Download-URL: https://github.com/spotify/pyeos/tarball/0.63 Description: UNKNOWN Keywords: EOS,networking Platform: UNKNOWN pyEOS-0.63/pyEOS.egg-info/requires.txt0000644000076500000240000000001312540032754020464 0ustar dbarrosostaff00000000000000jsonrpclib pyEOS-0.63/pyEOS.egg-info/SOURCES.txt0000644000076500000240000000036212540032754017757 0ustar dbarrosostaff00000000000000MANIFEST.in requirements.txt setup.cfg setup.py pyEOS/__init__.py pyEOS/eos.py pyEOS/exceptions.py pyEOS.egg-info/PKG-INFO pyEOS.egg-info/SOURCES.txt pyEOS.egg-info/dependency_links.txt pyEOS.egg-info/requires.txt pyEOS.egg-info/top_level.txtpyEOS-0.63/pyEOS.egg-info/top_level.txt0000644000076500000240000000000612540032754020620 0ustar dbarrosostaff00000000000000pyEOS pyEOS-0.63/requirements.txt0000644000076500000240000000001212503547660016664 0ustar dbarrosostaff00000000000000jsonrpclibpyEOS-0.63/setup.cfg0000644000076500000240000000014412540032754015221 0ustar dbarrosostaff00000000000000[metadata] description-file = README.md [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 pyEOS-0.63/setup.py0000644000076500000240000000163112540032655015114 0ustar dbarrosostaff00000000000000from setuptools import setup, find_packages from pip.req import parse_requirements import uuid # parse_requirements() returns generator of pip.req.InstallRequirement objects install_reqs = parse_requirements('requirements.txt', session=uuid.uuid1()) # reqs is a list of requirement # e.g. ['django==1.5.1', 'mezzanine==1.4.6'] reqs = [str(ir.req) for ir in install_reqs] version = '0.63' setup( name='pyEOS', version=version, py_modules=['pyEOS'], packages=find_packages(), install_requires=reqs, include_package_data=True, description = 'Python API to interact with network devices running EOS', author = 'David Barroso', author_email = 'dbarroso@spotify.com', url = 'https://github.com/spotify/pyeos/', # use the URL to the github repo download_url = 'https://github.com/spotify/pyeos/tarball/%s' % version, keywords = ['EOS', 'networking'], classifiers = [], )