blob: c99f3258a04b6396306fa846c906b344c6e52725 [file] [log] [blame]
Lei YU6947ae12017-10-15 21:01:32 +08001#!/usr/bin/env python
2"""
3Usage: upload_and_update.py <--file tarball>
4 <--tftp user@tftp-ip:/path/to/tftproot>
5 [--password SSH-PASSWORD-TO-TFTP]
6 --bmc <bmc-ip>
7 [-v]
8
9This scripts copies OpenBMC tarball to TFTP server,
10and uses REST APIs to update the tarball to BMC.
11
12Note on tftp server the tarball will be renamed to tarball_<user>
13"""
14
15import argparse
16import json
17import os
18import subprocess
19from subprocess import check_call, CalledProcessError
20from time import sleep
21
22
23def get_tftp_ip(tftp):
24 if '@' in tftp:
25 ip = tftp.split('@')[1].split(':')[0]
26 else:
27 ip = tftp.split(':')[0]
28 return ip
29
30
31def get_filename(tarball):
32 return os.path.basename(tarball)
33
34def get_server_filename(tftp, tarball):
35 if '@' in tftp:
36 user = tftp.split('@')[0]
37 else:
38 import getpass
39 user = getpass.getuser()
40 return get_filename(tarball) + "_" + user
41
42
43def checkBmcAlive(bmc):
44 cmds = ['ping', '-c', '1', bmc]
45 try:
46 check_call(cmds, stdout=FNULL, stderr=FNULL)
47 except CalledProcessError:
48 return False
49 else:
50 return True
51
52
53def login(bmc):
54 url = 'https://%s/login' % bmc
55 cmds = ['curl', '-s', '-c', 'cjar', '-k', '-X', 'POST', '-H',
56 'Content-Type: application/json', '-d',
57 '{"data": [ "root", "0penBmc"]}', url]
58 try:
59 check_call(cmds, stdout=FNULL, stderr=FNULL)
60 except CalledProcessError:
61 return False
62 else:
63 return True
64
65
66def prepare(bmc):
67 url = 'https://%s/org/openbmc/control/flash/bmc/action/prepareForUpdate' % bmc
68 cmds = ['curl', '-s', '-b', 'cjar', '-k', '-X', 'POST', '-H',
69 'Content-Type: application/json', '-d',
70 '{"data": []}', url]
71 check_call(cmds, stdout=FNULL, stderr=FNULL)
72
73
74def preserveNetwork(bmc):
75 url = 'https://%s/org/openbmc/control/flash/bmc/attr/preserve_network_settings' % bmc
76 cmds = ['curl', '-s', '-b', 'cjar', '-k', '-X', 'PUT', '-H',
77 'Content-Type: application/json', '-d',
78 '{"data": 1}', url]
79 check_call(cmds, stdout=FNULL, stderr=FNULL)
80
81
82def updateViaTFTP(tftp, tarball, bmc):
83 tftp_ip = get_tftp_ip(tftp)
84 serverfile = get_server_filename(tftp, tarball)
85 url = 'https://%s/org/openbmc/control/flash/bmc/action/updateViaTftp' % bmc
86 cmds = ['curl', '-s', '-b', 'cjar', '-k', '-X', 'POST', '-H',
87 'Content-Type: application/json', '-d',
88 '{"data": ["%s", "%s"]}' % (tftp_ip, serverfile), url]
89 check_call(cmds, stdout=FNULL, stderr=FNULL)
90
91
92def applyUpdate(bmc):
93 url = 'https://%s/org/openbmc/control/flash/bmc/action/Apply' % bmc
94 cmds = ['curl', '-s', '-b', 'cjar', '-k', '-X', 'POST', '-H',
95 'Content-Type: application/json', '-d',
96 '{"data": []}', url]
97 check_call(cmds, stdout=FNULL, stderr=FNULL)
98
99
100def getProgress(bmc):
101 url = 'https://%s/org/openbmc/control/flash/bmc/action/GetUpdateProgress' % bmc
102 cmds = ['curl', '-s', '-b', 'cjar', '-k', '-X', 'POST', '-H',
103 'Content-Type: application/json', '-d',
104 '{"data": []}', url]
105 try:
106 output = subprocess.check_output(cmds, stderr=FNULL)
107 except CalledProcessError as e:
108 # Sometimes curl fails with timeout error, let's ignore it
109 return ''
110 if FNULL is None: # Do not print log when FNULL is devnull
111 print output
112 return json.loads(output)['data']
113
114
115def reboot(bmc):
116 url = 'https://%s/org/openbmc/control/bmc0/action/warmReset' % bmc
117 cmds = ['curl', '-s', '-b', 'cjar', '-k', '-X', 'POST', '-H',
118 'Content-Type: application/json', '-d',
119 '{"data": []}', url]
120 check_call(cmds, stdout=FNULL, stderr=FNULL)
121
122
123def waitForState(state, bmc):
124 status = getProgress(bmc)
125 while state not in status:
126 if 'Error' in status:
127 raise Exception(status)
128 print 'Still waiting for status: \'%s\', current: \'%s\'' % (state, status.split('\n', 1)[0])
129 sleep(5)
130 status = getProgress(bmc)
131
132
133def upload(tftp, password, tarball):
134 target = "%s/%s" % (tftp, get_server_filename(tftp, tarball))
135 print 'Uploading \'%s\' to \'%s\' ...' % (tarball, target)
136 if password is None:
137 cmds = ['scp', tarball, target]
138 else:
139 cmds = ['sshpass', '-p', password, 'scp', tarball, target]
140 # print cmds
141 check_call(cmds, stdout=FNULL, stderr=FNULL)
142
143
144def update(tftp, tarball, bmc):
145 print 'Update...'
146
147 login(bmc)
148 print 'Prepare BMC to update'
149 prepare(bmc)
150
151 # After prepare, BMC will reboot, let's wait for it
152 print 'Waiting BMC to reboot...'
153 sleep(30)
154 while not checkBmcAlive(bmc):
155 sleep(5)
156 print 'BMC is back'
157
158 login(bmc)
159 print 'Logged in'
160
161 print 'Preserve network...'
162 preserveNetwork(bmc)
163
164 print 'Update via TFTP...'
165 updateViaTFTP(tftp, tarball, bmc)
166
167 print 'Waiting for downloading...'
168 sleep(10)
169 waitForState('Image ready to apply', bmc)
170
171 print 'Apply image...'
172 applyUpdate(bmc)
173 sleep(10)
174 waitForState('Apply Complete', bmc)
175
176 print 'Reboot BMC...'
177 reboot(bmc)
178 sleep(30)
179 while not checkBmcAlive(bmc):
180 sleep(5)
181 pass
182
183
184def main():
185 parser = argparse.ArgumentParser(
186 description='Upload tarball to remote TFTP server and update it on BMC')
187 parser.add_argument('-f', '--file', required=True, dest='tarball',
188 help='The tarball to upload and update')
189 parser.add_argument('-t', '--tftp', required=True, dest='tftp',
190 help='The TFTP address including username and full path')
191 parser.add_argument('-p', '--password', dest='password',
192 help='The password of TFTP server')
193 parser.add_argument('-b', '--bmc', required=True, dest='bmc',
194 help='The BMC IP address')
195 parser.add_argument('-v', '--verbose', action='store_true',
196 help='Print verbose log')
197
198 args = parser.parse_args()
199 args = vars(args)
200
201 if args['tftp'] is None or args['tarball'] is None or args['bmc'] is None:
202 parser.print_help()
203 exit(1)
204 global FNULL
205 if args['verbose']:
206 FNULL = None # Print log to stdout/stderr, for debug purpose
207 else:
208 FNULL = open(os.devnull, 'w') # Redirect stdout/stderr to devnull
209
210 if checkBmcAlive(args['bmc']):
211 print 'BMC is alive'
212 else:
213 print 'BMC is down, check it first'
214 exit(1)
215
216 upload(args['tftp'], args['password'], args['tarball'])
217 update(args['tftp'], args['tarball'], args['bmc'])
218
219 print 'Completed!'
220
221if __name__ == "__main__":
222 main()