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