diff --git a/AUTHORS b/AUTHORS index ce995cf7..c6395675 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,3 @@ +Dominique Robert Jesus Mendez +Yannis Ansermoz diff --git a/README.md b/README.md index 5294da3a..29f00082 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![PyPI](https://img.shields.io/pypi/v/napalm-ruckus-fastiron.svg)](https://pypi.python.org/pypi/napalm-ruckus-fastiron) +#[![PyPI](https://img.shields.io/pypi/v/napalm-ruckus-fastiron.svg)](https://pypi.python.org/pypi/napalm-ruckus-fastiron) [![PyPI](https://img.shields.io/pypi/dm/napalm-ruckus-fastiron.svg)](https://pypi.python.org/pypi/napalm-ruckus-fastiron) NAPALM (Network Automation and Programmability Abstraction Layer with Multivendor support) is a Python library that implements a set of functions to interact with different router vendor devices using a unified API. @@ -50,7 +50,7 @@ Roapmapped Requirements ======= -- Netmiko v2.0.2 +- Netmiko v3.4.0 - FastIron v8.0.30 Netmiko methods @@ -67,3 +67,5 @@ Netmiko methods Authors ======= * Jesús Mendez ([mendezj@staticoverride.us](mailto:mendezj@staticoverride.us)) + * Dominique Robert ([drobert@free.fr](mailto:drobert@free.fr)) + * Yannis Ansermoz ([yannis.ansermoz@edificom.ch](maito:yannis.ansermoz@edificom.ch)) diff --git a/napalm_ruckus_fastiron/FastIron.py b/napalm_ruckus_fastiron/FastIron.py index 26942d0c..6103bbe0 100644 --- a/napalm_ruckus_fastiron/FastIron.py +++ b/napalm_ruckus_fastiron/FastIron.py @@ -19,6 +19,7 @@ # std libs # import sys from netmiko import ConnectHandler +import paramiko import socket import sys # import re @@ -32,11 +33,18 @@ # import napalm.base.constants as c # from napalm.base import validate from napalm.base import NetworkDriver - +from napalm.base.helpers import ( + canonical_interface_name, + transform_lldp_capab, + textfsm_extractor, +) class FastIronDriver(NetworkDriver): """Napalm driver for FastIron.""" + CMD_SHW_LOGG = "show logging host" + CMD_SHW_RUN_LOGG = "show run | i logging host" + def __init__(self, hostname, username, password, timeout=60, **optional_args): """Constructor.""" @@ -55,6 +63,13 @@ def __init__(self, hostname, username, password, timeout=60, **optional_args): self.use_secret = optional_args.get('use_secret', False) self.image_type = None + # Cache command output + self.version = None + self.show_running_config = None + self.show_lag_deployed = None + self.show_int = None + self.interface_map = None + def __del__(self): """ This method is used to cleanup when the program is terminated suddenly. @@ -73,6 +88,9 @@ def open(self): else: secret = '' + paramiko.Transport._preferred_kex = ('diffie-hellman-group14-sha1', 'diffie-hellman-group1-sha1') + # secopts = paramiko.Transport.get_security_options() + # secopts.key_types += ['ssh-rsa'] self.device = ConnectHandler(device_type='ruckus_fastiron', ip=self.hostname, # saves device parameters port=self.port, @@ -82,11 +100,13 @@ def open(self): secret=secret, verbose=True) self.device.session_preparation() - # image_type = self.device.send_command("show version") # find the image type - # if image_type.find("SPS") != -1: - # self.image_type = "Switch" - # else: - # self.image_type = "Router" + version_output = self.device.send_command("show version") # find the image type + if version_output.find("SPS") != -1: + self.image_type = "Switch" + else: + self.image_type = "Router" + + self.version = FastIronDriver.__facts_os_version(version_output) except Exception: raise ConnectionException("Cannot connect to switch: %s:%s" % (self.hostname, @@ -106,7 +126,7 @@ def is_alive(self): consideration other parameters, e.g.: NETCONF session might not be usable, although the underlying SSH session is still open etc. """ - null = chr(0) + null = "" # chr(0) try: # send null byte see if alive self.device.send_command(null) return {'is_alive': self.device.remote_conn.transport.is_active()} @@ -225,7 +245,9 @@ def __facts_uptime(my_string): # TODO check for hours its missing.... @staticmethod def __facts_model(string): - model = FastIronDriver.__retrieve_all_locations(string, "Stackable", 0)[0] + model = FastIronDriver.__retrieve_all_locations(string, "HW:", 0)[0] + if "Stackable" in model: + model = model.replace("Stackable", "").strip() return model # returns the model of the switch @staticmethod @@ -292,22 +314,27 @@ def __port_time(shw_int_port): def __get_interface_speed(shw_int_speed): speed = list() # creates list for val in shw_int_speed: # speed words contained and compared - if val == 'auto,' or val == '1Gbit,': # appends speed hat + val = val.lower() + if val == 'auto,' or val == '1gbit,': # appends speed hat speed.append(1000) - elif val == '10Mbit,': + elif val == '10mbit,': speed.append(10) - elif val == '100Mbit,': + elif val == '100mbit,': speed.append(100) - elif val == '2.5Gbit,': - speed.append(2500) - elif val == '5Gbit,': + elif val == '2g,': + speed.append(2000) + elif val == '2.5gbit,': + speed.append(5000) + elif val == '5gbit,': speed.append(5000) - elif val == '10Gbit,': + elif val == '10gbit,': speed.append(10000) - elif val == '40Gbit,': + elif val == '40gbit,': speed.append(40000) - elif val == '100Gbit,': + elif val == '100gbit,': speed.append(100000) + elif val == 'optic-based,': + speed.append(10000) else: raise FastIronDriver.PortSpeedException(val) @@ -346,7 +373,7 @@ def __get_interface_name(shw_int_name, size): @staticmethod def __is_greater(value, threshold): # compares two values returns true if value - if float(value) >= float(threshold): # is greater or equal to threshold + if float(value) >= float(threshold): # is greater or equal to threshold return True return False @@ -515,6 +542,7 @@ def __comparing_list(list_1, list_2, symbol): temp_list, is_found = FastIronDriver.__compare_blocks(cb_1, config_blocks_2, cmd, symbol) + print("---- ", temp_list) if is_found == 0: for value in cb_1: @@ -756,6 +784,90 @@ def get_facts(self): # TODO check os_version as it returns general not switch 'interface_list': FastIronDriver.__physical_interface_list(interfaces_up) } + def get_lags(self): + result = {} + + if not self.show_lag_deployed: + self.show_lag_deployed = self.device.send_command('show lag deployed') + info = textfsm_extractor( + self, "show_lag_deployed", self.show_lag_deployed + ) + for lag in info: + port = 'lag{}'.format(lag['id']) + result[port] = { + 'is_up': True, + 'is_enabled': True, + 'description': lag['name'], + 'last_flapped': -1, + 'speed': 0, + 'mac_address': '', + 'children': self.interfaces_to_list(lag['ports']) + } + + return result + + def get_vlans(self): + if not self.show_running_config: + self.show_running_config = self.device.send_command('show running-config') + info = textfsm_extractor( + self, "show_running_config_vlan", self.show_running_config + ) + + result = {} + for vlan in info: + if (vlan['taggedports'] or vlan['untaggedports']): + result[vlan['vlan']] = { + 'name': vlan['name'], + 'interfaces': self.interface_list_conversion( + vlan['ve'], + vlan['taggedports'], + vlan['untaggedports'] + ) + } + + return result + + def interface_list_conversion(self, Ves, taggedports, untaggedports): + + return {'Ves': Ves, 'tag': taggedports, 'untag': untaggedports} + + def get_logging_hosts(self): + """ + Returns a dictionary of dictionaries. The keys for the first dictionary will be the \ + position. The inner dictionary will containing the following data for \ + each host: + * host (@IP) + * udp port (string) + """ + #CMD_SHW_LOGG = "show logging host" + + my_dict = {} + pos = 0 + logging_in_config = self.device.send_command(self.CMD_SHW_RUN_LOGG) + hosts = FastIronDriver.__creates_list_of_nlines(logging_in_config) + for host in hosts: + if "udp" in host: + udp_port = host.split()[4] + else: + udp_port = "514" + + my_dict.update({pos: {"host": host.split()[2], "udp_port": udp_port}}) + pos += 1 + + return my_dict + + def set_logging_hosts(self, list_hosts=None, clean=False, enable=True): + """ + """ + print("---------------") + if list_hosts: + for host in list_hosts: + l_h = host.get("host") + l_p = host.get("udp_port", "514") + print(l_h, l_p) + + return True + def get_interfaces(self): """ Returns a dictionary of dictionaries. The keys for the first dictionary will be the \ @@ -773,20 +885,23 @@ def get_interfaces(self): flap_output = self.device.send_command('show interface | i Port') speed_output = self.device.send_command('show interface | i speed') nombre = self.device.send_command('show interface | i name') + interfaces = FastIronDriver.__facts_interface_list(int_brief) int_up = FastIronDriver.__facts_interface_list(int_brief, pos=1, del_word="Link") mac_ad = FastIronDriver.__facts_interface_list(int_brief, pos=9, del_word="MAC") + flapped = FastIronDriver.__port_time(flap_output) size = len(interfaces) is_en = FastIronDriver.__facts_interface_list(int_brief, pos=2, del_word="State") + int_speed = FastIronDriver.__facts_interface_list(speed_output, pos=2) actual_spd = FastIronDriver.__get_interface_speed(int_speed) - flapped = FastIronDriver.__get_interfaces_speed(flapped, size) actual_spd = FastIronDriver.__get_interfaces_speed(actual_spd, size) nombre = FastIronDriver.__get_interface_name(nombre, size) + for val in range(0, len(interfaces)): # TODO check size and converto to napalm format my_dict.update({interfaces[val]: { 'is up': int_up[val], @@ -812,9 +927,15 @@ def get_lldp_neighbors(self): my_test = FastIronDriver.__matrix_format(my_input) for seq in range(0, len(my_test)): + try: + l_port = my_test[seq][3] + hostn = my_test[seq][len(my_test[seq])-1] + except: + hostn = "" + print("Port Hostname", l_port, hostn) my_dict.update({my_test[seq][0]: { - 'hostname': my_test[seq][len(my_test[seq])-1], - 'port': my_test[seq][3], + 'hostname': hostn, + 'port': l_port, }}) return my_dict @@ -917,7 +1038,7 @@ def get_lldp_neighbors_detail(self, interface=''): print("please enter an interface") return None - output = self.device.send_command('show lldp neighbor detail port ' + interface) + output = self.device.send_command('show lldp neighbor detail ports ' + interface) output = output.replace(':', ' ') output = output.replace('"', '') output = (output.replace('+', ' ')) @@ -1054,6 +1175,13 @@ def get_ntp_peers(self): The keys of the dictionary represent the IP Addresses of the peers. Inner dictionaries do not have yet any available keys. + si < V8.0.92 : + sh ntp ass + address ref clock st when poll reach delay offset disp + +~172.20.99.250 172.20.99.248 5 35 64 377 1.446 -2.8859 4.003 + *~172.26.58.72 172.20.99.248 5 42 64 377 1.668 -8.6629 2.557 + * synced, # selected, + candidate, - outlayer, x falseticker, ~ configured + Example:: { @@ -1070,11 +1198,16 @@ def get_ntp_peers(self): nline = FastIronDriver.__creates_list_of_nlines(output) ntp_peers = dict() for val in range(len(nline)-1): - val = nline[val].replace("~", " ") - val = val.split() - ntp_peers.update({ - val[1]: {} - }) + if nline[val].strip() != "": + #val = nline[val].replace("~", " ") + + val = nline[val].split() + print('--------') + print(val[1]) + print('--------') + ntp_peers.update({ + val[1]: {} + }) return ntp_peers @@ -1091,11 +1224,11 @@ def get_ntp_servers(self): nline = FastIronDriver.__creates_list_of_nlines(output) ntp_servers = dict() for val in range(len(nline)-1): - val = nline[val].replace("~", " ") - val = val.split() - ntp_servers.update({ - val[2]: {} - }) + if nline[val].strip() != "": + val = nline[val].split() + ntp_servers.update({ + val[1]: {} + }) return ntp_servers @@ -1126,27 +1259,33 @@ def get_ntp_stats(self): for sentence in nline: isbool = False # sentence = sentence.split() - remote, refid, stra, when, hostpoll, \ - reach, delay, offset, jitter = sentence.split() + SentSplit = sentence.split() + if len(SentSplit) == 9: + remote, refid, stra, when, hostpoll, \ + reach, delay, offset, jitter = SentSplit - if "*" in sentence: - isbool = True + elif len(SentSplit) == 11: + #print(SentSplit) + remote, adrip, domain, refid, stra, when, hostpoll, \ + reach, delay, offset, jitter = SentSplit - # sentence[0] = sentence[0].replace('*', '') - # sentence[0] = sentence[0].replace('+', '') - # sentence[0] = sentence[0].replace('~', '') + else: + # Pas de ligne + continue - remote = remote.replace('*', '') - remote = remote.replace('+', '') - remote = remote.replace('~', '') + if "*" in sentence: + isbool = True + remote = remote.replace('*', '') + remote = remote.replace('+', '') + remote = remote.replace('~', '') my_list.append({ - 'remote': remote, + 'remote': adrip, 'referenceid': refid, 'synchronized': isbool, 'stratum': int(stra), 'type': u'-', - 'when': int(when), + 'when': '-' if when == '-' else int(when), 'hostpoll': int(hostpoll), 'reachability': float(reach), 'delay': float(delay), diff --git a/napalm_ruckus_fastiron/utils/textfsm_templates/.placeholder b/napalm_ruckus_fastiron/utils/textfsm_templates/.placeholder new file mode 100644 index 00000000..e69de29b diff --git a/napalm_ruckus_fastiron/utils/textfsm_templates/show_interface.tpl b/napalm_ruckus_fastiron/utils/textfsm_templates/show_interface.tpl new file mode 100644 index 00000000..359bb6ca --- /dev/null +++ b/napalm_ruckus_fastiron/utils/textfsm_templates/show_interface.tpl @@ -0,0 +1,17 @@ +Value Port (\S+) +Value Link (up|down|disabled|empty|ERR-DISABLED) +Value Uptime (.*) +Value Mac (\S+) +Value Speed (\S+) +Value PortState (\S+) +Value Name (.*) +Value MTU (\d+) + +Start + ^${Port} is ${Link} + ^\s+Port (up|down) for ${Uptime} + ^\s+Hardware is \S+, address is ${Mac} + ^\s+Configured speed ${Speed}, + ^.*port state is ${PortState} + ^\s+Port name is ${Name} + ^\s+.*MTU ${MTU} -> Record diff --git a/napalm_ruckus_fastiron/utils/textfsm_templates/show_interface_brief.tpl b/napalm_ruckus_fastiron/utils/textfsm_templates/show_interface_brief.tpl new file mode 100644 index 00000000..0060250f --- /dev/null +++ b/napalm_ruckus_fastiron/utils/textfsm_templates/show_interface_brief.tpl @@ -0,0 +1,15 @@ +Value Port (\S+) +Value Link (Up|Down|Disabled|Empty|Disable|ERR-DIS) +Value PortState (\S+) +Value Dupl (\S+) +Value Speed (\S+) +Value Trunk (\S+) +Value Tag (Yes|No|N/A) +Value Pvid (\S+) +Value Pri (\S+) +Value Mac (\S+) +Value Name (.*) + +Start + ^${Port}\s+${Link}\s+${PortState}\s+${Dupl}\s+${Speed}\s+${Trunk}\s+${Tag}\s+${Pvid}\s+${Pri}\s+${Mac}(\s+)?(${Name})? -> Record + diff --git a/napalm_ruckus_fastiron/utils/textfsm_templates/show_interface_detail.tpl b/napalm_ruckus_fastiron/utils/textfsm_templates/show_interface_detail.tpl new file mode 100644 index 00000000..90da0c18 --- /dev/null +++ b/napalm_ruckus_fastiron/utils/textfsm_templates/show_interface_detail.tpl @@ -0,0 +1,12 @@ +Value Interface (\S+) +Value Link (up|down|disabled|empty) +Value Mac (\S+) +Value Name (.*) +Value IPAddress (\S+) +Value MTU (\d+) + +Start + ^${Interface} is ${Link}, + ^\s+Hardware is .*, address is ${Mac} + ^\s+Port name is ${Name} + ^\s+Internet address is ${IPAddress}, IP MTU ${MTU} bytes -> Record \ No newline at end of file diff --git a/napalm_ruckus_fastiron/utils/textfsm_templates/show_lag_deployed.tpl b/napalm_ruckus_fastiron/utils/textfsm_templates/show_lag_deployed.tpl new file mode 100644 index 00000000..f0e33951 --- /dev/null +++ b/napalm_ruckus_fastiron/utils/textfsm_templates/show_lag_deployed.tpl @@ -0,0 +1,7 @@ +Value Name (\S+) +Value Id (\d+) +Value Ports (.*) + +Start + ^=== LAG "${Name}" ID ${Id} + ^\s+Ports:\s+${Ports} -> Record diff --git a/napalm_ruckus_fastiron/utils/textfsm_templates/show_lldp_neighbors_detail.tpl b/napalm_ruckus_fastiron/utils/textfsm_templates/show_lldp_neighbors_detail.tpl new file mode 100644 index 00000000..6091ab48 --- /dev/null +++ b/napalm_ruckus_fastiron/utils/textfsm_templates/show_lldp_neighbors_detail.tpl @@ -0,0 +1,19 @@ +Value Filldown Port (\S+) +Value RemoteChassisId (\S+) +Value RemotePortId (\S+) +Value RemotePortDescription (\S+) +Value Required RemoteSystemName (\S+) +Value RemoteSystemDescription (.*) +Value RemoteSystemCapab (.*) +Value RemoteSystemCapabEnabled (.*) + +Start + ^Local port: ${Port} + ^\s+Neighbor: ${RemoteChassisId}, + ^\s+\+ Port ID \(interface name\): ${RemotePortId} + ^\s+\+ System name\s+: ${RemoteSystemName} + ^\s+\+ Port description\s+: ${RemotePortDescription} + ^\s+\+ System description\s+: ${RemoteSystemDescription} + ^\s+\+ System capabilities : ${RemoteSystemCapab} + ^\s+ Enabled capabilities: ${RemoteSystemCapabEnabled} + ^$$ -> Record diff --git a/napalm_ruckus_fastiron/utils/textfsm_templates/show_mac_address_all.tpl b/napalm_ruckus_fastiron/utils/textfsm_templates/show_mac_address_all.tpl new file mode 100644 index 00000000..6107654c --- /dev/null +++ b/napalm_ruckus_fastiron/utils/textfsm_templates/show_mac_address_all.tpl @@ -0,0 +1,9 @@ +Value Macaddress (\S+) +Value Port (\S+) +Value Type (\S+) +Value Index (\d+) +Value VLAN (\d+) +Value Action (\S+) + +Start + ^${Macaddress}\s+${Port}\s+${Type}\s+(${Index}\s+)?${VLAN}\s+${Action} -> Record diff --git a/napalm_ruckus_fastiron/utils/textfsm_templates/show_running_config_interface.tpl b/napalm_ruckus_fastiron/utils/textfsm_templates/show_running_config_interface.tpl new file mode 100644 index 00000000..377d4442 --- /dev/null +++ b/napalm_ruckus_fastiron/utils/textfsm_templates/show_running_config_interface.tpl @@ -0,0 +1,6 @@ +Value Interface (\S+) +Value InterfaceNum (\S+) + +Start + ^interface ${Interface} ${InterfaceNum} + ^! -> Record diff --git a/napalm_ruckus_fastiron/utils/textfsm_templates/show_running_config_interface_ip.tpl b/napalm_ruckus_fastiron/utils/textfsm_templates/show_running_config_interface_ip.tpl new file mode 100644 index 00000000..143707ce --- /dev/null +++ b/napalm_ruckus_fastiron/utils/textfsm_templates/show_running_config_interface_ip.tpl @@ -0,0 +1,11 @@ +Value Filldown Interface (\S+) +Value Filldown InterfaceNum (\S+) +Value Ipv4address (\S+) +Value Ipv6address (\S+) +Value Netmask (\S+) + +Start + ^interface ${Interface} ${InterfaceNum} + ^\s+ip address ${Ipv4address} ${Netmask} -> Record + ^\s+ipv6 address ${Ipv6address} -> Record + ^! -> Clearall diff --git a/napalm_ruckus_fastiron/utils/textfsm_templates/show_running_config_vlan.tpl b/napalm_ruckus_fastiron/utils/textfsm_templates/show_running_config_vlan.tpl new file mode 100644 index 00000000..39211984 --- /dev/null +++ b/napalm_ruckus_fastiron/utils/textfsm_templates/show_running_config_vlan.tpl @@ -0,0 +1,12 @@ +Value Vlan (\d+) +Value Name (\S+) +Value Ve (\S+) +Value TaggedPorts (.*) +Value UntaggedPorts (.*) + +Start + ^vlan ${Vlan}(?: name )?${Name}? + ^\s+tagged ${TaggedPorts} + ^\s+(?:no )?untagged ${UntaggedPorts} + ^\s+router-interface ve ${Ve} + ^! -> Record \ No newline at end of file diff --git a/napalm_ruckus_fastiron/version.py b/napalm_ruckus_fastiron/version.py new file mode 100644 index 00000000..c93a7061 --- /dev/null +++ b/napalm_ruckus_fastiron/version.py @@ -0,0 +1,10 @@ +__version__ = "1.0.31" + +""" +Ce Module Permet de donner la version du Module : + +Notes: +------ +Inspiré de : + http://sametmax.com/creer-un-setup-py-et-mettre-sa-bibliotheque-python-en-ligne-sur-pypi/ +""" diff --git a/requirements.txt b/requirements.txt index 2f1fa4c5..acea829c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -napalm>=2.0.0 -netmiko>=2.0.2 +napalm~=4.1.0 +# netmiko~=4.3.0 diff --git a/setup.py b/setup.py index 9b8566e5..6018472c 100644 --- a/setup.py +++ b/setup.py @@ -1,27 +1,39 @@ """setup.py file.""" from setuptools import find_packages, setup -__author__ = 'Jesus Mendez ' +__author__ = 'Edificom SA ' +from napalm_ruckus_fastiron.version import __version__ with open("requirements.txt", "r") as fs: reqs = [r for r in fs.read().splitlines()] setup( name="napalm-ruckus-fastiron", - version="1.0.26", - packages=find_packages(), - author="Jesus Mendez", - author_email="mendezj@staticoverride.us", + version=__version__, + + author="Edificom SA", + author_email="dev-auto@edificom.ch", description="Network Automation and Programmability Abstraction Layer with Multivendor support", + + long_description=open('README.md').read(), + long_description_content_type="text/markdown", + classifiers=[ - 'Topic :: Utilities', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Operating System :: POSIX :: Linux', - 'Operating System :: MacOS', + "Topic :: Utilities", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Operating System :: POSIX :: Linux", ], - url="https://github.com/Static0verride/napalm-ruckus-fastiron", + url="https://github.com/EdificomSA/napalm-ruckus-fastiron", + license="MIT", + + packages=find_packages(), include_package_data=True, install_requires=reqs, )