Skip to content

Commit

Permalink
Merge pull request #110 from networktocode/develop
Browse files Browse the repository at this point in the history
1.1.0 release
  • Loading branch information
jeffkala committed Apr 23, 2022
2 parents 2e48376 + 4c4641b commit 081d226
Show file tree
Hide file tree
Showing 70 changed files with 53,220 additions and 85 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## v1.1.0 - 2022-04

### Added

- #90 Uptime Conversions.
- #102 Add Ansible mapping for Nokia SrOS.
- #105 Add min_grouping_sizing to vlanlist_to_config method.
- #106 Add Nokia SrOS Config Parser.

### Changed

- #104 Optimize vlanconfig_to_list using builtin Regex methods.

### Fixed

- #99 Fixed decimal place in bits_to_name.
- #107 Fix issue when backup or intended is empty.


## v1.0.0 - 2021-11

### Added
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ This library intends to keep the following tenets:

Functions are grouped with like functions, such as IP or MAC address based functions. Included to date are groupings of:

* Bandwidth - Provides the ability to convert between various bandwidth values.
* Banner - Provides the ability to normalize the various banner delimiters.
* BGP ASN - Provides the ability to convert BGP ASN from integer to dot notation.
* Configuration
* Cleaning - Provides the ability to remove or replace lines based on regex matches.
Expand All @@ -30,6 +32,7 @@ Functions are grouped with like functions, such as IP or MAC address based funct
* Ping - Provides the ability to ping, currently only tcp ping.
* Protocol Mapper - Provides a mapping for protocol names to numbers and vice versa.
* Route - Provides the ability to provide a list of routes and an IP Address and return the longest prefix matched route.
* Time -Provides the ability to convert between integer time and string times.
* VLANs - Provide the ability to convert configuration into lists or lists into configuration.

# Installation
Expand Down Expand Up @@ -139,6 +142,7 @@ Relevant PR's
* https://github.com/ansible/ansible/pull/39901
* https://github.com/ansible/ansible/pull/26566

In building out the time conversion, the regex patterns are based on NAPALM implementation with their consent.

# Contributing

Expand Down
4 changes: 2 additions & 2 deletions docs/source/attribution/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ Attribution
###########

.. mdinclude:: ../../../README.md
:start-line: 105
:end-line: 140
:start-line: 108
:end-line: 145
5 changes: 3 additions & 2 deletions docs/source/contributing/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ Contributing
############

.. mdinclude:: ../../../README.md
:start-line: 145
:end-line: 229
:start-line: 148
:end-line: 233

4 changes: 2 additions & 2 deletions docs/source/examples/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ Examples
########

.. mdinclude:: ../../../README.md
:start-line: 50
:end-line: 102
:start-line: 53
:end-line: 105
4 changes: 2 additions & 2 deletions docs/source/installation/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ Installation
############

.. mdinclude:: ../../../README.md
:start-line: 36
:end-line: 47
:start-line: 39
:end-line: 50
4 changes: 4 additions & 0 deletions docs/source/netutils/configs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ Fortinet Fortios Parser
F5 Parser
-----------------------
- The "ltm rule" configuration sections are not uniform nor standardized; therefor, these sections are completely removed from the configuration in a preprocessing event.

Nokia SROS Parser
-----------------
- The section banners have been simplified to extract the section header itself. This means that `echo "System Configuration"` will be converted to just "System Configuration".
1 change: 1 addition & 0 deletions docs/source/netutils/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ Netutils Functions
ping/index
protocol_mapper/index
route/index
time/index
utilities/index
vlan/index
6 changes: 6 additions & 0 deletions docs/source/netutils/time/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*******
Time
*******

.. automodule:: netutils.time
:members:
2 changes: 1 addition & 1 deletion docs/source/overview/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ Overview

.. mdinclude:: ../../../README.md
:start-line: 0
:end-line: 33
:end-line: 36
2 changes: 1 addition & 1 deletion netutils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Initialization file for library."""

__version__ = "1.0.0"
__version__ = "1.1.0"
11 changes: 6 additions & 5 deletions netutils/bandwidth.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,19 +113,20 @@ def bits_to_name( # pylint: disable=too-many-branches,too-many-return-statement
Example:
>>> from netutils.bandwidth import bits_to_name
>>> bits_to_name(125000)
'125.0Kbps'
'125Kbps'
>>> bits_to_name(1000000000)
'1.0Gbps'
'1Gbps'
"""
if not isinstance(speed, int):
raise ValueError(f"Speed of {speed} was not a valid speed integer.")

for bit_type, val in BITS_MAPPING.items():
if val["low"] <= speed < val["high"]:
try:
return f"{round(speed / val['low'], nbr_decimal)}{bit_type}"
except ZeroDivisionError:
if nbr_decimal == 0:
nbr_decimal = None
if val["low"] == 0:
return f"{round(speed, nbr_decimal)}{bit_type}"
return f"{round(speed / val['low'], nbr_decimal)}{bit_type}"
raise ValueError(f"Speed of {speed} was not a valid speed representation.")


Expand Down
4 changes: 2 additions & 2 deletions netutils/config/compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"juniper_junos": parser.JunosConfigParser,
"cisco_asa": parser.ASAConfigParser,
"fortinet_fortios": parser.FortinetConfigParser,
"nokia_sros": parser.NokiaConfigParser,
}

default_feature = {
Expand Down Expand Up @@ -319,8 +320,7 @@ def feature_compliance(feature, backup_cfg, intended_cfg, network_os):
}
)
else:
if backup_cfg and intended_cfg:
feature_data.update(_check_configs_differences(intended_cfg, backup_cfg, network_os))
feature_data.update(_check_configs_differences(intended_cfg, backup_cfg, network_os))
if feature["ordered"] is True:
feature_data["compliant"] = feature_data["ordered_compliant"]
elif feature["ordered"] is False:
Expand Down
89 changes: 88 additions & 1 deletion netutils/config/parser.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""Parsers for different network operating systems."""
# pylint: disable=no-member,super-with-arguments,invalid-overridden-method,raise-missing-from,invalid-overridden-method,inconsistent-return-statements,super-with-arguments,redefined-argument-from-local,no-else-break,useless-super-delegation
# pylint: disable=no-member,super-with-arguments,invalid-overridden-method,raise-missing-from,invalid-overridden-method,inconsistent-return-statements,super-with-arguments,redefined-argument-from-local,no-else-break,useless-super-delegation,too-many-lines

import re
from collections import namedtuple

from netutils.banner import normalise_delimiter_caret_c

ConfigLine = namedtuple("ConfigLine", "config_line,parents")
Expand Down Expand Up @@ -571,6 +572,32 @@ class NXOSConfigParser(CiscoConfigParser, BaseSpaceConfigParser):

regex_banner = re.compile(r"^banner\s+\S+\s+(?P<banner_delimiter>\S)")

def __init__(self, config):
"""Create ConfigParser Object.
Args:
config (str): The config text to parse.
"""
self.unique_config_lines = set()
self.same_line_children = set()
super(NXOSConfigParser, self).__init__(config)

def _build_banner(self, config_line):
"""Handle banner config lines.
Args:
config_line (str): The start of the banner config.
Returns:
str: The next configuration line in the configuration text.
None: When banner end is the end of the config text.
Raises:
ValueError: When the parser is unable to identify the End of the Banner.
"""
config_line = normalise_delimiter_caret_c(self.banner_end, config_line)
return super(NXOSConfigParser, self)._build_banner(config_line)


class EOSConfigParser(BaseSpaceConfigParser):
"""EOSConfigParser implementation fo ConfigParser Class."""
Expand Down Expand Up @@ -922,3 +949,63 @@ def _build_nested_config(self, line):
self.indent_level = spaces

self._update_config_lines(line)


class NokiaConfigParser(BaseSpaceConfigParser):
"""Nokia SrOS config parser."""

comment_chars = ["#"]
banner_start = []

def __init__(self, config):
"""Create ConfigParser Object.
Args:
config (str): The config text to parse.
"""
super(NokiaConfigParser, self).__init__(config)

def _is_section_title(self, line): # pylint: disable=no-self-use
"""Determine if line is a section title in banner.
Args:
line (str): A config line from the device.
Returns:
bool: True if line is a sectiont, else False.
"""
if re.match(r"^echo\s\".+\"", string=line):
return True
return False

def _get_section_title(self, line): # pylint: disable=no-self-use
"""Determine section title from banner.
Args:
line (str): A config line from the device that has been found to be a section title.
Returns:
str|bool: The section's title from the section banner, else False.
"""
section_title = re.match(r"^echo\s\"(?P<section_name>.+)\"", string=line)
if section_title:
return section_title.group("section_name")
return False

@property
def config_lines_only(self):
"""Remove spaces and comments from config lines.
Returns:
str: The non-space and non-comment lines from ``config``.
"""
if self._config is None:
config_lines = []
for line in self.config.splitlines():
if line and not self.is_comment(line) and not line.isspace():
if self._is_section_title(line):
config_lines.append(self._get_section_title(line))
else:
config_lines.append(line.rstrip())
self._config = "\n".join(config_lines)
return self._config
25 changes: 25 additions & 0 deletions netutils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,3 +385,28 @@
ETX_HEX = "\x03"
CARET_C = "^C"
CARET = "^"

TIME_MAPPINGS = (
("years", 31536000),
("weeks", 604800),
("days", 86400),
("hours", 3600),
("minutes", 60),
("seconds", 1),
)

UPTIME_REGEX_PATTERNS = [
(
r"((?P<years>\d+) year(s)?,\s+)?((?P<weeks>\d+) week(s)?,\s+)?"
r"((?P<days>\d+) day(s)?,\s+)?((?P<hours>\d+) "
r"hour(s)?,\s+)?((?P<minutes>\d+) minute(s)?)"
),
(
r"((?P<days>\d+) day(s)?,\s+)?"
r"((?P<hours>\d+)):((?P<minutes>\d+)):((?P<seconds>\d+))" # pylint: disable=implicit-str-concat
),
(
r"(((?P<years>\d+)y)?(?P<weeks>\d+)w)?((?P<days>\d+)d)?((?P<hours>\d+)h)?"
r"((?P<minutes>\d+)m)?((?P<seconds>\d+)s)"
), # pylint: disable=implicit-str-concat
]
2 changes: 2 additions & 0 deletions netutils/lib_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
"community.network.routeros": "mikrotik_routeros",
"community.network.netvisor": "pluribus",
"community.network.icx": "ruckus_icx",
"community.network.sros": "nokia_sros",
"vyos.vyos.vyos": "vyos",
}

Expand Down Expand Up @@ -240,6 +241,7 @@
"lenovo_cnos": "community.network.cnos",
"lenovo_enos": "community.network.enos",
"mikrotik_routeros": "community.network.routeros",
"nokia_sros": "community.network.sros",
"pluribus": "community.network.netvisor",
"ruckus_icx": "community.network.icx",
"vyos": "vyos.vyos.vyos",
Expand Down
69 changes: 69 additions & 0 deletions netutils/time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""Functions for working with time."""
import re
from .constants import TIME_MAPPINGS, UPTIME_REGEX_PATTERNS


def uptime_seconds_to_string(uptime_seconds):
"""Converts uptime in seconds to uptime in string format.
Args:
uptime_seconds (int): Uptime in seconds.
Returns:
str: Uptime in string format.
Example:
>>> from netutils.time import uptime_seconds_to_string
>>> uptime_seconds_to_string(7250)
'2 hours, 50 seconds'
"""
result = []
for interval, count in TIME_MAPPINGS:
value = uptime_seconds // count
if value:
uptime_seconds -= value * count
if value == 1:
interval = interval.rstrip("s")
result.append(f"{value} {interval}")

return ", ".join(result)


def uptime_string_to_seconds(uptime_string):
"""Converts uptime string seconds.
Args:
uptime_string (str): Uptime in string format
Returns:
int: Uptime string converted to seconds.
Example:
>>> from netutils.time import uptime_string_to_seconds
>>> uptime_string_to_seconds("58 minutes")
3480
>>> from netutils.time import uptime_string_to_seconds
>>> uptime_string_to_seconds("4m15s")
255
Raises:
ValueError: When uptime_string is unable to be parsed by regex.
"""
compiled_regex_list = [re.compile(reg_pattern) for reg_pattern in UPTIME_REGEX_PATTERNS]

uptime_dict = {}
for regex in compiled_regex_list:
match = regex.search(uptime_string)

if match:
uptime_dict = match.groupdict()
break

if not match:
raise ValueError("Unable to parse uptime string.")

uptime_seconds = 0
for time_interval, value in TIME_MAPPINGS:
if uptime_dict.get(time_interval):
uptime_seconds += int(uptime_dict.get(time_interval)) * value
return uptime_seconds
Loading

0 comments on commit 081d226

Please sign in to comment.