Skip to content

Commit

Permalink
nxos: implement uptime for get_bgp_neighbors
Browse files Browse the repository at this point in the history
So far we were just ignoring the time value on the switch, and returning -1.

This adds a dependency on python-dateutil to calculate years and months lengths, depending on the current date. Alternatively we can just go for a fixed arbitrary time length for years and months.

Signed-off-by: Guido Trotter <[email protected]>
  • Loading branch information
Guido Trotter committed Jun 20, 2024
1 parent f0efc0b commit fcee20a
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 4 deletions.
61 changes: 60 additions & 1 deletion napalm/nxos/nxos.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations under
# the License.

import datetime
import ipaddress
import json
import os
Expand All @@ -25,6 +26,7 @@
from abc import abstractmethod
from builtins import super
from collections import defaultdict
from dateutil import relativedelta

# import third party lib
from typing import (
Expand Down Expand Up @@ -79,6 +81,55 @@
},
)

# Some uptimes are given in ISO8601 duration format
# This regexp implements a subset of that format, as used by the
# nxos switches, with integer values only.
ISO8601_INTEGER_PERIOD_REGEX = re.compile(
r"""(?x)
^P(?!\b) # start with the literal letter P, with no word boundaries
((?P<years>[0-9]+)Y)?
((?P<months>[0-9]+)M)?
((?P<weeks>[0-9]+)W)?
((?P<days>[0-9]+)D)?
(T # optional time component
((?P<hours>[0-9]+)H)?
((?P<minutes>[0-9]+)M)?
((?P<seconds>[0-9]+)S)?
)?$
"""
)


def duration_to_seconds(
duration: str, endtime: Optional[datetime.datetime] = None
) -> int:
"""
Try to convert an ISO8601 to seconds, using the current time as a base.
The current time is needed as the duration may include "years" and "months",
which have a variable number of seconds depending on when we start counting from.
"""
if not endtime:
# Use fromtimestamp for ease/uniformity of mocking in unit tests
endtime = datetime.datetime.fromtimestamp(time.time())
match = ISO8601_INTEGER_PERIOD_REGEX.match(duration)
if not match:
raise ValueError("Can't parse ISO8601 duration")

mg = match.groupdict(default="0")
rd = relativedelta.relativedelta(
years=int(mg["years"]),
months=int(mg["months"]),
weeks=int(mg["weeks"]),
days=int(mg["days"]),
hours=int(mg["hours"]),
minutes=int(mg["minutes"]),
seconds=int(mg["seconds"]),
)
starttime = endtime - rd
timediff = endtime - starttime
return int(timediff.total_seconds())


def ensure_netmiko_conn(func: F) -> F:
"""Decorator that ensures Netmiko connection exists."""
Expand Down Expand Up @@ -1090,6 +1141,9 @@ def get_bgp_neighbors(self) -> Dict[str, models.BGPStateNeighborsPerVRFDict]:
except NXAPICommandError:
vrf_list = []

# Use fromtimestamp for ease/uniformity of mocking in unit tests
dtnow = datetime.datetime.fromtimestamp(time.time())

for vrf_dict in vrf_list:
result_vrf_dict: models.BGPStateNeighborsPerVRFDict = {
"router_id": str(vrf_dict["vrf-router-id"]),
Expand Down Expand Up @@ -1117,13 +1171,18 @@ def get_bgp_neighbors(self) -> Dict[str, models.BGPStateNeighborsPerVRFDict]:
bgp_state = bgp_state_dict[state]
afid_dict = af_name_dict[int(af_dict["af-id"])]
safi_name = afid_dict[int(saf_dict["safi"])]
uptime = -1
try:
uptime = duration_to_seconds(neighbor_dict["time"], dtnow)
except ValueError:
pass

result_peer_dict: models.BGPStateNeighborDict = {
"local_as": as_number(vrf_dict["vrf-local-as"]),
"remote_as": remoteas,
"remote_id": neighborid,
"is_enabled": bgp_state["is_enabled"],
"uptime": -1,
"uptime": uptime,
"description": "",
"is_up": bgp_state["is_up"],
"address_family": {
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ ttp
ttp_templates
netutils>=1.0.0
typing-extensions>=4.3.0
python-dateutil
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@
"received_prefixes": 13
}
},
"uptime": -1,
"uptime": 4088562,
"remote_as": 64862,
"description": "",
"remote_id": "10.1.1.179",
Expand All @@ -175,7 +175,7 @@
"received_prefixes": 0
}
},
"uptime": -1,
"uptime": 1496562,
"remote_as": 64862,
"description": "",
"remote_id": "10.1.1.177",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"inq": "0",
"outq": "0",
"neighboras": "64862",
"time": "6w3d",
"time": "P1M17DT7H42M42S",
"state": "Established",
"prefixreceived": "13"
}
Expand Down
4 changes: 4 additions & 0 deletions test/nxos/test_getters.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ def test_get_interfaces(self, test_case):
assert helpers.test_model(models.InterfaceDict, interface_data)

return get_interfaces

test_get_bgp_neighbors = patch("time.time", mock_time)(
BaseTestGetters.test_get_bgp_neighbors
)

0 comments on commit fcee20a

Please sign in to comment.