-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathexample-upgrade-devices-async.py
More file actions
executable file
·152 lines (123 loc) · 5.05 KB
/
example-upgrade-devices-async.py
File metadata and controls
executable file
·152 lines (123 loc) · 5.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/usr/bin/env python3
#
# This is an example script to concurrently upgrade a set of devices by their tags.
# This is the same as example-upgrade-devices, but uses async API calls so that
# the request act on multiple devices at the same time.
#
# Example run:
# PASSWORD=controllerpassword python3 ./example-upgrade-devices-async.py {tags}
#
# where {tags} is a comma-separated list of tags
#
import asyncio
import sys
import time
from helper_funcs import *
#
# Controller APIs
#
from pfapi.models import *
from pfapi.api.system import get_status, get_system_update_info, perform_system_update, get_system_update_progress
###########################################
settings = get_settings()
tags = settings.TAGS
if not tags:
print("Comma-separated list of tags not provided, applying to all devices")
# Create a session client, which is used as the parent to all the per-device API clients.
sessionClient = RequestClient(controller_url=settings.CONTROLLER_URL)
if not sessionClient.login(settings.USER, settings.PASSWORD):
print("Login failed... quitting")
sys.exit(1)
online_devs = get_online_devices(sessionClient, tags)
# -------------- example async functions for upgrading the devices --------------
async def upgrade_device(dev):
devaddr = dev.device.address.split(':',1)[0]
devname = dev.device_name.split('.',1)[0]
print(f"{dev.device_name} - Checking available system update(s)")
info = await get_system_update_info.asyncio(client=dev.client)
try:
if info.update_messages is not None and len(info.update_messages) > 0:
print(f"{devname} - Update messages:")
prefix = f"{devname} ({devaddr}) - "
msg_str = '\n'.join([prefix+m for m in info.update_messages])
print(msg_str + '\n')
if 'not been registered' in msg_str:
try:
i = msg_str.index(' <a href')
msg_str = msg_str[:i]
except:
pass
return msg_str
except Exception as e:
if isinstance(info, Error):
return f"{devname} {devaddr} - system update check returned Error {info.errmsg}"
else:
return f"{devname} {devaddr} - system update check returned {info}"
try:
if info.latest_base_system == "" or info.curr_base_system == "" or info.latest_base_system == info.curr_base_system:
print(f"{devname} ({devaddr}) - skipping upgrade, no new version available")
return f"{devname} ({devaddr}) - skipping upgrade, no new version available"
except Exception as e:
print(f"{devname} {devaddr} - skipping device due to exception: {e}")
return f"{devname} {devaddr} - skipping device due to exception: {e}"
print(f"{devname} {devaddr} - Upgrading device to {info.latest_base_system}")
upgradeOpt = SystemUpdateOptions(firmware_branch=info.firmware_branch)
result = await perform_system_update.asyncio(client=dev.client, body=upgradeOpt)
print(f"{devname} {devaddr} - perform_system_update request returned: {result}")
print(f"{devname} {devaddr} - commenced upgrade; progress follows:")
# query upgrade progress until it has completed or fails
lastMsgs = ""
progressStarted = False
while True:
progress = await get_system_update_progress.asyncio(client=dev.client)
if not progressStarted and progress.completed > 0:
progressStarted = True
if progressStarted and progress.completed == 0:
# Completion would reset to 0 if the log file cycles or the
# system has rebooted. Mark as a completed upgrade.
return f"{devname} {devaddr} - Upgrade completed"
if progress.messages is None:
if progressStarted:
break
# progress messages might not be available if upgrade has not logged anything
continue
try:
prefix = f"{devname} ({devaddr}): "
msgs_all = '\n'.join([prefix+m for m in progress.messages])
if len(msgs_all) > len(lastMsgs):
print(msgs_all[len(lastMsgs):], end="")
# print the current completed percent (guessed)
#print(" {{completed {}%}} ".format(progress.completed), end="")
lastMsgs = msgs_all
time.sleep(1.0)
except Exception as e:
print("Exception", e)
print("Progress returned", progress)
return f"{devname} {devaddr} - Upgrade exception {e}, messages: {progress}"
if progress.completed == 100:
return f"{devname} {devaddr} - Upgrade completed"
async def batch_upgrade(devs):
'''
Batch alias requests to the list of device clients
:param list[RequestClient] devs: a list of device request clients
'''
print("\nSending requests to:")
print('\n'.join(['\t- '+ d.device_id + ' ' + d.device_name for d in devs]) + "\n")
# Bulk request send, and wait for all of them to complete with asyncio.gather()
tasks = []
for dev in devs:
tasks.append(upgrade_device(dev))
results = await asyncio.gather(*tasks)
print("\nResults:")
for result in results:
print(f"\t- {result}")
# Bulk upgrade request to the online devices.
print("\nCommencing upgrades on devices:")
# Work in blocks of N_DEVICES (10) devices so
# that it's lighter on the controller and easier
# to track progress.
N_DEVICES=10
for i in range(0, len(online_devs), N_DEVICES):
asyncio.run(batch_upgrade(online_devs[i:i+N_DEVICES]))
# Stop the refresh timer to exit, otherwise it will wait until the timer event happens
sessionClient.stop()