Skip to content

Commit 40cce14

Browse files
examples: Add EOL Planning script
Change-Id: I38694bd709f482a03e6a9286e84590bd402ff835
1 parent c55612d commit 40cce14

File tree

1 file changed

+256
-0
lines changed

1 file changed

+256
-0
lines changed

examples/Connector/eol_planning.py

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
#!/usr/bin/python3
2+
3+
# /// script
4+
# requires-python = ">=3.12"
5+
# dependencies = [
6+
# "cloudvision",
7+
# "polars"
8+
# ]
9+
# [tool.uv]
10+
# exclude-newer = "2024-08-05T00:00:00Z"
11+
# ///
12+
# Example:
13+
# uv run eol_planning.py --apiserver www.cv-prod-uk-1.arista.io:443 --auth=token,token.tok
14+
15+
# Copyright (c) 2025 Arista Networks, Inc.
16+
# Use of this source code is governed by the Apache License 2.0
17+
# that can be found in the COPYING file.
18+
from cloudvision.Connector.grpc_client import GRPCClient, create_query
19+
from cloudvision.Connector.codec.custom_types import FrozenDict
20+
from cloudvision.Connector.codec import Wildcard
21+
from utils import pretty_print
22+
import argparse
23+
from parser import base
24+
import polars as pl
25+
from pprint import pprint as pp
26+
27+
debug = False
28+
29+
30+
def get(client, dataset, pathElts):
31+
''' Returns a query on a path element'''
32+
result = {}
33+
query = [
34+
create_query([(pathElts, [])], dataset)
35+
]
36+
37+
for batch in client.get(query):
38+
for notif in batch["notifications"]:
39+
if debug:
40+
pretty_print(notif["updates"])
41+
result.update(notif["updates"])
42+
return result
43+
44+
45+
def cards(client):
46+
''' Returns the cards of a switch (Supervisors + Linecards + Fabric Modules)
47+
'''
48+
pathElts = [
49+
"Devices",
50+
Wildcard(),
51+
"versioned-data",
52+
"hardware",
53+
"inventory",
54+
"card"
55+
]
56+
dataset = "analytics"
57+
result = {}
58+
query = [
59+
create_query([(pathElts, [])], dataset)
60+
]
61+
62+
for batch in client.get(query):
63+
for notif in batch["notifications"]:
64+
if not notif["updates"]:
65+
continue
66+
path_elts = notif["path_elements"]
67+
elem_key = path_elts[1]
68+
elem_val = result.get(elem_key, {})
69+
elem_val.update(notif["updates"])
70+
result[elem_key] = elem_val
71+
72+
return result
73+
74+
75+
def unfreeze(o):
76+
''' Used to unfreeze Frozen dictionaries'''
77+
if isinstance(o, (dict, FrozenDict)):
78+
return dict({k: unfreeze(v) for k, v in o.items()})
79+
80+
if isinstance(o, (str)):
81+
return o
82+
83+
try:
84+
return [unfreeze(i) for i in o]
85+
except TypeError:
86+
pass
87+
88+
return o
89+
90+
91+
def getInventory(client):
92+
''' Get device information.
93+
'''
94+
path_elts = ["DatasetInfo", "Devices"]
95+
dataset = "analytics"
96+
return get(client, dataset, path_elts)
97+
98+
99+
def getLifecyclesHW(client):
100+
pathElts = [
101+
"lifecycles",
102+
"hardware"
103+
]
104+
dataset = "analytics"
105+
return get(client, dataset, pathElts)
106+
107+
108+
def getDeviceLifecyclesSW(client):
109+
pathElts = [
110+
"lifecycles",
111+
"devices",
112+
"software"
113+
]
114+
dataset = "analytics"
115+
return get(client, dataset, pathElts)
116+
117+
118+
def getLifecyclesSW(client):
119+
pathElts = [
120+
"lifecycles",
121+
"software"
122+
]
123+
dataset = "analytics"
124+
return get(client, dataset, pathElts)
125+
126+
127+
def getSKUs(client):
128+
pathElts = [
129+
"BugAlerts",
130+
"skus"
131+
]
132+
dataset = "analytics"
133+
return get(client, dataset, pathElts)
134+
135+
136+
def main(apiserverAddr, token=None, certs=None, ca=None, key=None):
137+
138+
with GRPCClient(apiserverAddr, token=token, key=key, ca=ca, certs=certs) as client:
139+
hw_inventory = unfreeze(cards(client))
140+
inventory = unfreeze(getInventory(client))
141+
sw_eol = unfreeze(getLifecyclesSW(client))
142+
hw_eol = unfreeze(getLifecyclesHW(client))
143+
device_sw_eol = unfreeze(getDeviceLifecyclesSW(client))
144+
skus = unfreeze(getSKUs(client))
145+
df = pl.DataFrame(hw_inventory, strict=False)
146+
eol = []
147+
for device in hw_inventory:
148+
for cardKey, cardValue in hw_inventory[device].items():
149+
parent_serial_number = device
150+
if "modelName" not in cardValue:
151+
continue
152+
model_name = cardValue["modelName"]
153+
154+
if "serialNum" in cardValue:
155+
serial_number = cardValue["serialNum"]
156+
else:
157+
serial_number = ""
158+
if "lifecycle" in cardValue:
159+
lifecycle = cardValue["lifecycle"]
160+
if "endOfLife" in lifecycle:
161+
eol_date = lifecycle["endOfLife"]
162+
else:
163+
eol_date = ""
164+
if "endOfHardwareRMARequests" in lifecycle:
165+
eol_rma = lifecycle["endOfHardwareRMARequests"]
166+
else:
167+
eol_rma = ""
168+
if "endOfSale" in lifecycle:
169+
eol_sale = lifecycle["endOfSale"]
170+
else:
171+
eol_sale = ""
172+
if "endOfTACSupport" in lifecycle:
173+
eol_tac = lifecycle["endOfTACSupport"]
174+
else:
175+
eol_tac = ""
176+
else:
177+
eol_date = ""
178+
eol_rma = ""
179+
eol_sale = ""
180+
eol_tac = ""
181+
eol.append(
182+
{
183+
"hostname": inventory[device]["hostname"],
184+
"Serial Number": serial_number,
185+
"Parent Serial Number": parent_serial_number,
186+
"Model Name": model_name,
187+
"Current Software End Of Life": "",
188+
"Hardware End Of Life": eol_date,
189+
"Hardware End of RMA Requests": eol_rma,
190+
"Hardware End of Sale": eol_sale,
191+
"Hardware End of TAC Support": eol_tac,
192+
"Last Supported Software Train": "",
193+
"TerminAttr": "",
194+
"Version": ""
195+
}
196+
)
197+
filteredSkus = {}
198+
for key, value in skus.items():
199+
if "DCS-" in key:
200+
filteredSkus[key] = {}
201+
relNum = ""
202+
if value["releaseDeprecated"] != []:
203+
deprecatedReleaseNum = value["releaseDeprecated"][0].split('.')
204+
relNum = str(deprecatedReleaseNum[0]) + "."
205+
relNum += str(int(deprecatedReleaseNum[1]) - 1)
206+
filteredSkus[key]["releaseDeprecated"] = relNum
207+
for device in inventory:
208+
for hwKey, hwValue in hw_eol.items():
209+
if inventory[device]["modelName"] == hwKey:
210+
eol_date = hwValue["endOfLife"]
211+
eol_sale = hwValue["endOfSale"]
212+
eol_rma = hwValue["endOfHardwareRMARequests"]
213+
eol_tac = hwValue["endOfTACSupport"]
214+
sw_eol_date = ""
215+
for swKey, swValue in device_sw_eol.items():
216+
if device == swKey:
217+
sw_eol_date = swValue["endOfSupport"]
218+
for skuKey, skuValue in filteredSkus.items():
219+
if inventory[device]["modelName"] == skuKey:
220+
last_supported_train = skuValue["releaseDeprecated"]
221+
else:
222+
last_supported_train = ""
223+
for sw, swEol in sw_eol.items():
224+
if last_supported_train:
225+
last_supported_train_eol = swEol["endOfSupport"]
226+
else:
227+
last_supported_train_eol = ""
228+
eol.append(
229+
{
230+
"hostname": inventory[device]["hostname"],
231+
"Serial Number": device,
232+
"Parent Serial Number": device,
233+
"Model Name": inventory[device]["modelName"],
234+
"Current Software End Of Life": sw_eol_date,
235+
"Hardware End Of Life": eol_date,
236+
"Hardware End of RMA Requests": eol_rma,
237+
"Hardware End of Sale": eol_sale,
238+
"Hardware End of TAC Support": eol_tac,
239+
"Last Supported Software Train": last_supported_train_eol,
240+
"TerminAttr": inventory[device]["terminAttrVersion"],
241+
"Version": inventory[device]["eosVersion"]
242+
}
243+
)
244+
245+
df = pl.DataFrame(eol)
246+
print(df)
247+
df.write_csv("eol.csv")
248+
249+
return 0
250+
251+
252+
if __name__ == "__main__":
253+
parser = argparse.ArgumentParser()
254+
args = base.parse_args()
255+
exit(main(args.apiserver, certs=args.certFile, key=args.keyFile,
256+
ca=args.caFile, token=args.tokenFile))

0 commit comments

Comments
 (0)