From 950e868eb33cd6db5c4bb3a2bac031986277cdd9 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Tue, 8 Oct 2024 10:00:26 +0200 Subject: [PATCH 1/2] yanger: handle all frr uptime formats FRR prints uptime different based on how long it is. Prior to this commit yanger expected uptime to be on the format HH:MM:SS which it is as long as it's shorter than a day. If it's longer the format changes, this commit handles this. The commit also adds unit test data triggering the different formats (week). Side note: cli-pretty could be made to represent the data on "FRR format" again, which is perhaps more human friendly. Signed-off-by: Richard Alpe --- src/statd/python/yanger/yanger.py | 32 +++++++++++++++++-- test/case/cli/cli-output/show-routes-ipv4.txt | 6 ++-- .../cli/system-output/vtysh-ip4-route.json | 6 ++-- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/statd/python/yanger/yanger.py b/src/statd/python/yanger/yanger.py index cf093139..236d0e7e 100755 --- a/src/statd/python/yanger/yanger.py +++ b/src/statd/python/yanger/yanger.py @@ -6,6 +6,7 @@ import json import sys # (built-in module) import os import argparse +from re import match from datetime import datetime, timedelta, timezone TESTPATH = "" @@ -235,8 +236,35 @@ def add_hardware(hw_out): def uptime2datetime(uptime): - """Convert uptime (HH:MM:SS) to YANG format (YYYY-MM-DDTHH:MM:SS+00:00)""" - h, m, s = map(int, uptime.split(':')) + """ + Convert uptime to YANG format (YYYY-MM-DDTHH:MM:SS+00:00) + + Handles the following input formats (frrtime): + HH:MM:SS + XdXXhXXm + XXwXdXXh + """ + h = m = s = 0 + + # Format HH:MM:SS + if match(r'^\d{2}:\d{2}:\d{2}$', uptime): + h, m, s = map(int, uptime.split(':')) + + # Format XdXXhXXm (days, hours, minutes) + elif match(r'^\d+d\d{2}h\d{2}m$', uptime): + days = int(uptime.split('d')[0]) + h = int(uptime.split('d')[1].split('h')[0]) + m = int(uptime.split('h')[1].split('m')[0]) + h += days * 24 + + # Format XwXdXXh (weeks, days, hours) + elif match(r'^\d{2}w\d{1}d\d{2}h$', uptime): + weeks = int(uptime.split('w')[0]) + days = int(uptime.split('w')[1].split('d')[0]) + h = int(uptime.split('d')[1].split('h')[0]) + h += weeks * 7 * 24 + h += days * 24 + uptime_delta = timedelta(hours=h, minutes=m, seconds=s) current_time = datetime_now() last_updated = current_time - uptime_delta diff --git a/test/case/cli/cli-output/show-routes-ipv4.txt b/test/case/cli/cli-output/show-routes-ipv4.txt index 187eea5d..d160dc95 100644 --- a/test/case/cli/cli-output/show-routes-ipv4.txt +++ b/test/case/cli/cli-output/show-routes-ipv4.txt @@ -6,6 +6,6 @@ 10.0.13.0/30 110/2000 e5 ospfv2 0h0m0s >* 10.0.13.0/30 0/0 e5 direct 0h0m0s 10.0.23.0/30 110/1 e6 ospfv2 0h0m0s ->* 10.0.23.0/30 0/0 e6 direct 0h0m0s - 192.168.3.0/24 110/1 e2 ospfv2 0h0m0s ->* 192.168.3.0/24 0/0 e2 direct 0h0m0s +>* 10.0.23.0/30 0/0 e6 direct 20h10m5s + 192.168.3.0/24 110/1 e2 ospfv2 133h5m0s +>* 192.168.3.0/24 0/0 e2 direct 205h0m0s diff --git a/test/case/cli/system-output/vtysh-ip4-route.json b/test/case/cli/system-output/vtysh-ip4-route.json index e263ea0e..20b2cf8b 100644 --- a/test/case/cli/system-output/vtysh-ip4-route.json +++ b/test/case/cli/system-output/vtysh-ip4-route.json @@ -228,7 +228,7 @@ "internalNextHopActiveNum":1, "nexthopGroupId":23, "installedNexthopGroupId":23, - "uptime":"00:00:00", + "uptime":"20:10:05", "nexthops":[ { "flags":3, @@ -256,7 +256,7 @@ "internalNextHopNum":1, "internalNextHopActiveNum":1, "nexthopGroupId":33, - "uptime":"00:00:00", + "uptime":"5d13h05m", "nexthops":[ { "flags":1, @@ -286,7 +286,7 @@ "internalNextHopActiveNum":1, "nexthopGroupId":17, "installedNexthopGroupId":17, - "uptime":"00:00:00", + "uptime":"01w1d13h", "nexthops":[ { "flags":3, From 03245f3631517570ac38fa365a4c9e2587a7e241 Mon Sep 17 00:00:00 2001 From: Richard Alpe Date: Tue, 8 Oct 2024 10:52:06 +0200 Subject: [PATCH 2/2] cli: print FRR uptime on same format as FRR Use the same uptime representation as FRR does. Example output: DESTINATION PREF NEXT-HOP PROTO UPTIME >* 10.0.23.0/30 0/0 e6 direct 20:10:05 192.168.3.0/24 110/1 e2 ospfv2 5d13h05m >* 192.168.3.0/24 0/0 e2 direct 01w1d13h Signed-off-by: Richard Alpe --- src/statd/python/cli_pretty/cli_pretty.py | 20 ++++++++++++++++--- test/case/cli/cli-output/show-routes-ipv4.txt | 20 +++++++++---------- test/case/cli/cli-output/show-routes-ipv6.txt | 20 +++++++++---------- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/statd/python/cli_pretty/cli_pretty.py b/src/statd/python/cli_pretty/cli_pretty.py index 91040bfa..12635f39 100755 --- a/src/statd/python/cli_pretty/cli_pretty.py +++ b/src/statd/python/cli_pretty/cli_pretty.py @@ -150,6 +150,9 @@ class Route: def datetime2uptime(self): """Convert 'last-updated' string to uptime in AAhBBmCCs format.""" + ONE_DAY_SECOND = 60 * 60 * 24 + ONE_WEEK_SECOND = ONE_DAY_SECOND * 7 + if not self.last_updated: return "0h0m0s" @@ -164,10 +167,21 @@ class Route: current_time = datetime_now() uptime_delta = current_time - last_updated - hours, remainder = divmod(int(uptime_delta.total_seconds()), 3600) - minutes, seconds = divmod(remainder, 60) + total_seconds = int(uptime_delta.total_seconds()) + total_days = total_seconds // ONE_DAY_SECOND + total_weeks = total_days // 7 - return f"{hours}h{minutes}m{seconds}s" + hours = (total_seconds % ONE_DAY_SECOND) // 3600 + minutes = (total_seconds % 3600) // 60 + seconds = total_seconds % 60 + + if total_seconds < ONE_DAY_SECOND: + return f"{hours:02}:{minutes:02}:{seconds:02}" + elif total_seconds < ONE_WEEK_SECOND: + return f"{total_days}d{hours:02}h{minutes:02}m" + else: + days_remaining = total_days % 7 + return f"{total_weeks:02}w{days_remaining}d{hours:02}h" def print(self): PadRoute.set(self.ip) diff --git a/test/case/cli/cli-output/show-routes-ipv4.txt b/test/case/cli/cli-output/show-routes-ipv4.txt index d160dc95..ffdc7477 100644 --- a/test/case/cli/cli-output/show-routes-ipv4.txt +++ b/test/case/cli/cli-output/show-routes-ipv4.txt @@ -1,11 +1,11 @@  DESTINATION PREF NEXT-HOP PROTO UPTIME ->* 0.0.0.0/0 110/2 10.0.23.1 ospfv2 0h0m0s ->* 10.0.0.1/32 110/4000 10.0.13.1 ospfv2 0h0m0s - 10.0.0.3/32 110/0 lo ospfv2 0h0m0s ->* 10.0.0.3/32 0/0 lo direct 0h0m0s - 10.0.13.0/30 110/2000 e5 ospfv2 0h0m0s ->* 10.0.13.0/30 0/0 e5 direct 0h0m0s - 10.0.23.0/30 110/1 e6 ospfv2 0h0m0s ->* 10.0.23.0/30 0/0 e6 direct 20h10m5s - 192.168.3.0/24 110/1 e2 ospfv2 133h5m0s ->* 192.168.3.0/24 0/0 e2 direct 205h0m0s +>* 0.0.0.0/0 110/2 10.0.23.1 ospfv2 00:00:00 +>* 10.0.0.1/32 110/4000 10.0.13.1 ospfv2 00:00:00 + 10.0.0.3/32 110/0 lo ospfv2 00:00:00 +>* 10.0.0.3/32 0/0 lo direct 00:00:00 + 10.0.13.0/30 110/2000 e5 ospfv2 00:00:00 +>* 10.0.13.0/30 0/0 e5 direct 00:00:00 + 10.0.23.0/30 110/1 e6 ospfv2 00:00:00 +>* 10.0.23.0/30 0/0 e6 direct 20:10:05 + 192.168.3.0/24 110/1 e2 ospfv2 5d13h05m +>* 192.168.3.0/24 0/0 e2 direct 01w1d13h diff --git a/test/case/cli/cli-output/show-routes-ipv6.txt b/test/case/cli/cli-output/show-routes-ipv6.txt index 8fa8250d..d854d1fc 100644 --- a/test/case/cli/cli-output/show-routes-ipv6.txt +++ b/test/case/cli/cli-output/show-routes-ipv6.txt @@ -1,11 +1,11 @@  DESTINATION PREF NEXT-HOP PROTO UPTIME ->* ::/0 1/0 2001:db8:3c4d:50::1 static 0h0m0s ->* 2001:db8:3c4d:50::/64 0/0 e6 direct 0h0m0s ->* 2001:db8:3c4d:200::1/128 0/0 lo direct 0h0m0s - * fe80::/64 0/0 e7 direct 0h0m0s - * fe80::/64 0/0 e6 direct 0h0m0s - * fe80::/64 0/0 e5 direct 0h0m0s - * fe80::/64 0/0 e4 direct 0h0m0s - * fe80::/64 0/0 e3 direct 0h0m0s - * fe80::/64 0/0 e2 direct 0h0m0s ->* fe80::/64 0/0 e1 direct 0h0m0s +>* ::/0 1/0 2001:db8:3c4d:50::1 static 00:00:00 +>* 2001:db8:3c4d:50::/64 0/0 e6 direct 00:00:00 +>* 2001:db8:3c4d:200::1/128 0/0 lo direct 00:00:00 + * fe80::/64 0/0 e7 direct 00:00:00 + * fe80::/64 0/0 e6 direct 00:00:00 + * fe80::/64 0/0 e5 direct 00:00:00 + * fe80::/64 0/0 e4 direct 00:00:00 + * fe80::/64 0/0 e3 direct 00:00:00 + * fe80::/64 0/0 e2 direct 00:00:00 +>* fe80::/64 0/0 e1 direct 00:00:00