Last active
May 9, 2017 08:53
-
-
Save mine260309/21ceff0f69b42df0ef12a85c83ec01c3 to your computer and use it in GitHub Desktop.
A single util to upload and update OpenBMC image
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
Usage: upload_and_update.py <--file tarball> | |
<--tftp user@tftp-ip:/path/to/tftproot> | |
[--password SSH-PASSWORD-TO-TFTP] | |
--bmc <bmc-ip> | |
[-v] | |
This scripts copies OpenBMC tarball to TFTP server, | |
and uses REST APIs to update the tarball to BMC. | |
Note on tftp server the tarball will be renamed to tarball_<user> | |
""" | |
import argparse | |
import json | |
import os | |
import subprocess | |
from subprocess import check_call, CalledProcessError | |
from time import sleep | |
def get_tftp_ip(tftp): | |
if '@' in tftp: | |
ip = tftp.split('@')[1].split(':')[0] | |
else: | |
ip = tftp.split(':')[0] | |
return ip | |
def get_filename(tarball): | |
return os.path.basename(tarball) | |
def get_server_filename(tftp, tarball): | |
if '@' in tftp: | |
user = tftp.split('@')[0] | |
else: | |
import getpass | |
user = getpass.getuser() | |
return get_filename(tarball) + "_" + user | |
def checkBmcAlive(bmc): | |
cmds = ['ping', '-c', '1', bmc] | |
try: | |
check_call(cmds, stdout=FNULL, stderr=FNULL) | |
except CalledProcessError: | |
return False | |
else: | |
return True | |
def login(bmc): | |
url = 'https://%s/login' % bmc | |
cmds = ['curl', '-s', '-c', 'cjar', '-k', '-X', 'POST', '-H', | |
'Content-Type: application/json', '-d', | |
'{"data": [ "root", "0penBmc"]}', url] | |
try: | |
check_call(cmds, stdout=FNULL, stderr=FNULL) | |
except CalledProcessError: | |
return False | |
else: | |
return True | |
def prepare(bmc): | |
url = 'https://%s/org/openbmc/control/flash/bmc/action/prepareForUpdate' % bmc | |
cmds = ['curl', '-s', '-b', 'cjar', '-k', '-X', 'POST', '-H', | |
'Content-Type: application/json', '-d', | |
'{"data": []}', url] | |
check_call(cmds, stdout=FNULL, stderr=FNULL) | |
def preserveNetwork(bmc): | |
url = 'https://%s/org/openbmc/control/flash/bmc/attr/preserve_network_settings' % bmc | |
cmds = ['curl', '-s', '-b', 'cjar', '-k', '-X', 'PUT', '-H', | |
'Content-Type: application/json', '-d', | |
'{"data": 1}', url] | |
check_call(cmds, stdout=FNULL, stderr=FNULL) | |
def updateViaTFTP(tftp, tarball, bmc): | |
tftp_ip = get_tftp_ip(tftp) | |
serverfile = get_server_filename(tftp, tarball) | |
url = 'https://%s/org/openbmc/control/flash/bmc/action/updateViaTftp' % bmc | |
cmds = ['curl', '-s', '-b', 'cjar', '-k', '-X', 'POST', '-H', | |
'Content-Type: application/json', '-d', | |
'{"data": ["%s", "%s"]}' % (tftp_ip, serverfile), url] | |
check_call(cmds, stdout=FNULL, stderr=FNULL) | |
def applyUpdate(bmc): | |
url = 'https://%s/org/openbmc/control/flash/bmc/action/Apply' % bmc | |
cmds = ['curl', '-s', '-b', 'cjar', '-k', '-X', 'POST', '-H', | |
'Content-Type: application/json', '-d', | |
'{"data": []}', url] | |
check_call(cmds, stdout=FNULL, stderr=FNULL) | |
def getProgress(bmc): | |
url = 'https://%s/org/openbmc/control/flash/bmc/action/GetUpdateProgress' % bmc | |
cmds = ['curl', '-s', '-b', 'cjar', '-k', '-X', 'POST', '-H', | |
'Content-Type: application/json', '-d', | |
'{"data": []}', url] | |
try: | |
output = subprocess.check_output(cmds, stderr=FNULL) | |
except CalledProcessError as e: | |
# Sometimes curl fails with timeout error, let's ignore it | |
return '' | |
if FNULL is None: # Do not print log when FNULL is devnull | |
print output | |
return json.loads(output)['data'] | |
def reboot(bmc): | |
url = 'https://%s/org/openbmc/control/bmc0/action/warmReset' % bmc | |
cmds = ['curl', '-s', '-b', 'cjar', '-k', '-X', 'POST', '-H', | |
'Content-Type: application/json', '-d', | |
'{"data": []}', url] | |
check_call(cmds, stdout=FNULL, stderr=FNULL) | |
def waitForState(state, bmc): | |
status = getProgress(bmc) | |
while state not in status: | |
if 'Error' in status: | |
raise Exception(status) | |
print 'Still waiting for status: \'%s\', current: \'%s\'' % (state, status.split('\n', 1)[0]) | |
sleep(5) | |
status = getProgress(bmc) | |
def upload(tftp, password, tarball): | |
target = "%s/%s" % (tftp, get_server_filename(tftp, tarball)) | |
print 'Uploading \'%s\' to \'%s\' ...' % (tarball, target) | |
if password is None: | |
cmds = ['scp', tarball, target] | |
else: | |
cmds = ['sshpass', '-p', password, 'scp', tarball, target] | |
# print cmds | |
check_call(cmds, stdout=FNULL, stderr=FNULL) | |
def update(tftp, tarball, bmc): | |
print 'Update...' | |
login(bmc) | |
print 'Prepare BMC to update' | |
prepare(bmc) | |
# After prepare, BMC will reboot, let's wait for it | |
print 'Waiting BMC to reboot...' | |
sleep(30) | |
while not checkBmcAlive(bmc): | |
sleep(5) | |
login(bmc) | |
print 'BMC is back' | |
print 'Preserve network...' | |
preserveNetwork(bmc) | |
print 'Update via TFTP...' | |
updateViaTFTP(tftp, tarball, bmc) | |
print 'Waiting for downloading...' | |
sleep(10) | |
waitForState('Image ready to apply', bmc) | |
print 'Apply image...' | |
applyUpdate(bmc) | |
sleep(10) | |
waitForState('Apply Complete', bmc) | |
print 'Reboot BMC...' | |
reboot(bmc) | |
sleep(30) | |
while not checkBmcAlive(bmc): | |
sleep(5) | |
pass | |
def main(): | |
parser = argparse.ArgumentParser( | |
description='Upload tarball to remote TFTP server and update it on BMC') | |
parser.add_argument('-f', '--file', required=True, dest='tarball', | |
help='The tarball to upload and update') | |
parser.add_argument('-t', '--tftp', required=True, dest='tftp', | |
help='The TFTP address including username and full path') | |
parser.add_argument('-p', '--password', dest='password', | |
help='The password of TFTP server') | |
parser.add_argument('-b', '--bmc', required=True, dest='bmc', | |
help='The BMC IP address') | |
parser.add_argument('-v', '--verbose', action='store_true', | |
help='Print verbose log') | |
args = parser.parse_args() | |
args = vars(args) | |
if args['tftp'] is None or args['tarball'] is None or args['bmc'] is None: | |
parser.print_help() | |
exit(1) | |
global FNULL | |
if args['verbose']: | |
FNULL = None # Print log to stdout/stderr, for debug purpose | |
else: | |
FNULL = open(os.devnull, 'w') # Redirect stdout/stderr to devnull | |
if checkBmcAlive(args['bmc']): | |
print 'BMC is alive' | |
else: | |
print 'BMC is down, check it first' | |
exit(1) | |
upload(args['tftp'], args['password'], args['tarball']) | |
update(args['tftp'], args['tarball'], args['bmc']) | |
print 'Completed!' | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment