Skip to content

Commit 6d717dd

Browse files
Create ndu_upgrade.py
At some point in the repo history, this file got deleted. Adding it back
1 parent 08e24ee commit 6d717dd

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
"""
2+
ONTAP REST API Python Client Library Sample Scripts
3+
4+
This script was developed by NetApp to help demonstrate NetApp
5+
technologies. This script is not officially supported as a
6+
standard NetApp product.
7+
8+
NOTE: AUTOBOOT must already be set to true or else ANDU will complain and it
9+
cannot be set via REST today (not even the CLI passthrough) so this script
10+
isn't doing it. If you need to, run this on your cluster:
11+
12+
run * bootargs set AUTOBOOT true
13+
14+
workflow:
15+
1. Show the current cluster software version and available packages
16+
2. Download a new software package
17+
3. PATCH the cluster software to use the new package
18+
4. Monitor the progress
19+
20+
Copyright (c) 2020 NetApp, Inc. All Rights Reserved.
21+
22+
Licensed under the BSD 3-Clause "New" or "Revised" License (the "License");
23+
you may not use this file except in compliance with the License.
24+
25+
You may obtain a copy of the License at
26+
https://opensource.org/licenses/BSD-3-Clause
27+
28+
"""
29+
30+
import argparse
31+
from http.server import SimpleHTTPRequestHandler
32+
import os
33+
from pprint import pprint
34+
import socket
35+
import socketserver
36+
from threading import Thread
37+
import time
38+
39+
from netapp_ontap.error import NetAppRestError
40+
from netapp_ontap.resources import CLI, Node, Software, SoftwarePackage, SoftwarePackageDownload
41+
42+
from utils import (
43+
Argument, parse_args, setup_logging, setup_connection, step, substep,
44+
LiveMultilineOutput
45+
)
46+
47+
48+
def show_current_cluster_image():
49+
"""Verify our current software status"""
50+
51+
step("Show the packages")
52+
substep("Show the current cluster image")
53+
software = Software()
54+
software.get()
55+
print("Current software package:", software)
56+
57+
substep("Show the available software packages")
58+
print("Available software packages:", list(SoftwarePackage.get_collection()))
59+
return software
60+
61+
62+
def download_new_cluster_image(parsed_args: argparse.Namespace):
63+
"""Start our HTTP server and tell ONTAP to start downloading the new image"""
64+
65+
step("Download a new software package")
66+
substep("Start an HTTP server to serve the file")
67+
image_server = Thread(target=serve_image_download, args=(parsed_args,))
68+
image_server.start()
69+
70+
substep("Have ONTAP download the package")
71+
local_ip = socket.gethostbyname(socket.gethostname())
72+
url = "http://%s:%s/%s" % (local_ip, parsed_args.port, parsed_args.image_path)
73+
download_package = SoftwarePackageDownload(url=url)
74+
download_package.post(poll_timeout=1200, poll_interval=60)
75+
image_server.join()
76+
77+
substep("Show the new package")
78+
packages = list(SoftwarePackage.get_collection())
79+
print("Available software packages:", packages)
80+
return packages[0]
81+
82+
83+
def update_cluster_image(software, new_package):
84+
"""Set the new version of the software on the cluster"""
85+
86+
step("Update the cluster to the new image")
87+
software.version = new_package.version
88+
try:
89+
if Node.count_collection() > 1:
90+
# make sure cluster HA is enabled
91+
cli = CLI()
92+
cli.execute("cluster ha modify", body={"configured": "true"})
93+
software.patch(poll_timeout=1200, poll_interval=30, skip_warnings="true")
94+
except NetAppRestError:
95+
print("Current cluster software status:")
96+
software.get()
97+
pprint(software)
98+
99+
100+
def monitor_progress(software):
101+
"""Until all nodes are complete, monitor and print the status of the upgrade"""
102+
103+
step("Watch the progress")
104+
105+
errors = 0
106+
with LiveMultilineOutput() as output:
107+
while True:
108+
try:
109+
software.get(fields="state,status_details,elapsed_duration,estimated_duration")
110+
if not software.state == "in_progress":
111+
break
112+
errors = 0
113+
new_list = []
114+
for detail in software.status_details:
115+
new_list.append(
116+
"[%s]: %s - %s"
117+
% (detail.node.name, detail.name, detail.issue.message)
118+
)
119+
percent_complete = (
120+
software.elapsed_duration / software.estimated_duration
121+
) * 100
122+
new_list.append("%.2f%% complete (estimate)" % percent_complete)
123+
output.change(new_list)
124+
except Exception: # pylint: disable=broad-except
125+
errors += 1
126+
if errors > 10:
127+
break
128+
finally:
129+
time.sleep(30)
130+
if errors > 10:
131+
raise RuntimeError("ERROR: No longer could communicate with the server. Is it down?")
132+
133+
134+
def serve_image_download(parsed_args):
135+
"""Start a temporary HTTP server to allow ONTAP to download the image file"""
136+
137+
if parsed_args.image_path.startswith("/"):
138+
os.chdir("/")
139+
port = parsed_args.port
140+
local_ip = socket.gethostbyname(socket.gethostname())
141+
httpd = socketserver.TCPServer(("0.0.0.0", port), SimpleHTTPRequestHandler)
142+
print("serving package download at http://%s:%s" % (local_ip, port))
143+
144+
# ONTAP does 3 HEAD requests followed by the GET we care about
145+
httpd.handle_request()
146+
httpd.handle_request()
147+
httpd.handle_request()
148+
httpd.handle_request()
149+
150+
151+
def main() -> None:
152+
"""Main function"""
153+
154+
arguments = [
155+
Argument("-c", "--cluster", "API server IP:port details", required=True),
156+
Argument(
157+
"-p", "--port", "The port to open as an HTTP server to serve the ONTAP image from",
158+
default=7654, arg_type=int,
159+
),
160+
Argument(
161+
"-i", "--image-path", "The path to an ONTAP image which will be downloaded by ONTAP",
162+
required=True,
163+
)
164+
]
165+
args = parse_args("This script will update the nodes of an ONTAP cluster.", arguments)
166+
setup_logging()
167+
setup_connection(args.cluster, args.api_user, args.api_pass)
168+
169+
software = show_current_cluster_image()
170+
new_package = download_new_cluster_image(args)
171+
update_cluster_image(software, new_package)
172+
monitor_progress(software)
173+
174+
software.get()
175+
print("Upgrade complete! Current version: %s" % software.version)
176+
177+
178+
if __name__ == "__main__":
179+
main()

0 commit comments

Comments
 (0)