blob: dc62207080cd1d59bbe756413ad17ba6a736939e [file] [log] [blame]
# Copyright 2019 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Test which updates chameleond on the Bluetooth Peer device
This is not a test per se. This 'test' checks if the chameleond commit on the
Bluetooth peer device and updates it if it below the expected value.
The expected commit and the installation bundle is downloaded from google cloud
storage.
"""
import logging
import os
import sys
import time
import tempfile
from datetime import datetime
from autotest_lib.client.common_lib import error
from autotest_lib.server import test
from autotest_lib.client.bin import utils
# The location of the package in the cloud
GS_PUBLIC = 'gs://chromeos-localmirror/distfiles/bluetooth_peer_bundle/'
# NAME of the file that stores commit info in the cloud
COMMIT_FILENAME = 'latest_bluetooth_commit'
# The following needs to be kept in sync with values chameleond code
BUNDLE_TEMPLATE='chameleond-0.0.2-{}.tar.gz' # Name of the chamleond package
BUNDLE_DIR = 'chameleond-0.0.2'
BUNDLE_VERSION = '9999'
CHAMELEON_BOARD = 'fpga_tio'
def run_cmd(peer, cmd):
"""A wrapper around host.run()."""
try:
logging.info('executing command %s on peer',cmd)
result = peer.host.run(cmd)
logging.info('exit_status is %s', result.exit_status)
logging.info('stdout is %s stderr is %s', result.stdout, result.stderr)
output = result.stderr if result.stderr else result.stdout
if result.exit_status == 0:
return True, output
else:
return False, output
except error.AutoservRunError as e:
logging.error('Error while running cmd %s %s', cmd, e)
return False, None
def is_update_needed(peer, latest_commit):
""" Check if update is required
Update if the commit hash doesn't match
@returns: True/False
"""
return not is_commit_hash_equal(peer, latest_commit)
def is_commit_hash_equal(peer, latest_commit):
""" Check if chameleond commit hash is the expected one"""
try:
commit = peer.get_bt_commit_hash()
except:
logging.error('Getting the commit hash failed %s', sys.exc_info())
return False
logging.debug('commit %s found on peer %s', commit, peer.host)
return commit == latest_commit
def perform_update(peer, latest_commit):
""" Update the chameleond on the peer"""
logging.info('copy the file over to the peer')
try:
cur_dir = '/tmp/'
bundle = BUNDLE_TEMPLATE.format(latest_commit)
bundle_path = os.path.join(cur_dir, bundle)
logging.debug('package location is %s', bundle_path)
peer.host.send_file(bundle_path, '/tmp/')
except:
logging.error('copying the file failed %s ', sys.exc_info())
logging.error(str(os.listdir(cur_dir)))
return False
HOST_NOW = datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')
logging.info('running make on peer')
cmd = ('cd %s && rm -rf %s && tar zxf %s &&'
'cd %s && find -exec touch -c {} \; &&'
'make install REMOTE_INSTALL=TRUE '
'HOST_NOW="%s" BUNDLE_VERSION=%s '
'CHAMELEON_BOARD=%s && rm %s%s') % (cur_dir,BUNDLE_DIR, bundle,
BUNDLE_DIR, HOST_NOW,
BUNDLE_VERSION,
CHAMELEON_BOARD, cur_dir,
bundle)
logging.debug(cmd)
status, _ = run_cmd(peer, cmd)
if not status:
logging.info('make failed')
return False
logging.info('chameleond installed on peer')
return True
def restart_check_chameleond(peer):
"""restart chamleond and make sure it is running."""
restart_cmd = 'sudo /etc/init.d/chameleond restart'
start_cmd = 'sudo /etc/init.d/chameleond start'
status_cmd = 'sudo /etc/init.d/chameleond status'
status, _ = run_cmd(peer, restart_cmd)
if not status:
status, _ = run_cmd(peer, start_cmd)
if not status:
logging.error('restarting/starting chamleond failed')
#
#TODO: Refactor so that we wait for all peer devices all together.
#
# Wait till chameleond initialization is complete
time.sleep(5)
status, output = run_cmd(peer, status_cmd)
expected_output = 'chameleond is running'
return status and expected_output in output
def update_peer(peer, latest_commit):
"""Update the chameleond on peer devices if required"""
if not perform_update(peer, latest_commit):
return False, 'Update failed'
if not restart_check_chameleond(peer):
return False, 'Unable to start chameleond'
if is_update_needed(peer, latest_commit):
return False, 'Commit not updated after upgrade'
logging.info('updating chameleond succeded')
return True, ''
def update_peers(host, latest_commit):
"""Update the chameleond on peer devices"""
peer_list = host.btpeer_list[:]
if host.chameleon is not None:
peer_list.append(host.chameleon)
if peer_list == []:
raise error.TestError('Bluetooth Peer not present')
status = {}
for peer in peer_list:
if peer.get_platform() != 'RASPI':
logging.error('Unsupported peer %s',str(peer.host))
continue
status[peer] = {'updated': False,
'reason' : None,
'update_needed' : True}
if not is_update_needed(peer,latest_commit):
status[peer]['update_needed'] = False
logging.info('Update not needed on peer %s', str(peer.host))
continue
status[peer]['updated'], status[peer]['reason'] = update_peer(
peer, latest_commit)
logging.debug(status)
# If none of the peer need update raise TestNA
if not any([v['update_needed'] for v in status.values()]):
raise error.TestNAError('Update not needed')
# If any of the peers failed update, raise failure with the reason
if not all([v['updated'] for k,v in status.items() if v['update_needed']]):
for peer, v in status.items():
if not v['updated']:
logging.error('peer %s failed %s', str(peer.host), v['reason'])
raise error.TestFail()
logging.info('All eligible peers updated')
def get_latest_commit():
""" Get the latest commit
Download the file containing the latest commit and
parse it contents, and cleanup.
@returns (True,commit) in case of success (False, None) in case of failure
"""
try:
commit = None
src = GS_PUBLIC + COMMIT_FILENAME
with tempfile.NamedTemporaryFile(suffix='bt_commit') as tmp_file:
tmp_filename = tmp_file.name
cmd = 'gsutil cp {} {}'.format(src, tmp_filename)
result = utils.run(cmd)
if result.exit_status != 0:
logging.debug('Downloading commit file failed with %s', result.exit_status)
return (False, None)
with open(tmp_filename) as f:
content = f.read()
logging.debug('content of the file is %s', content)
commit = content.strip('\n').strip()
logging.info('latest commit is %s', commit)
if commit is None:
return (False, None)
else:
return (True, commit)
except Exception as e:
logging.error('exception %s in get_latest_commit', str(e))
return (False, None)
def download_installation_files(host, commit):
""" Download the chameleond installation bundle"""
src_path = GS_PUBLIC + BUNDLE_TEMPLATE.format(commit)
dest_path = '/tmp/' + BUNDLE_TEMPLATE.format(commit)
logging.debug('chamelond bundle path is %s', src_path)
logging.debug('bundle path in DUT is %s', dest_path)
cmd = 'gsutil cp {} {}'.format(src_path, dest_path)
try:
result = utils.run(cmd)
if result.exit_status != 0:
logging.debug('Downloading the chameleond bundle failed with %d',
result.exit_status)
return False
host.send_file(dest_path, dest_path)
logging.debug('file send to %s %s',host, dest_path)
return True
except Exception as e:
logging.error('exception %s in download_installation_files', str(e))
raise error.TestFail('Failed to copy the bundle file to the DUT')
def cleanup(host, commit):
""" Cleanup the installation file from server."""
try:
dest_path = '/tmp/' + BUNDLE_TEMPLATE.format(commit)
if os.path.exists(dest_path):
logging.debug('Remove file %s', dest_path)
os.remove(dest_path)
else:
logging.debug('File %s not found', dest_path)
result = host.run('rm {}'.format(dest_path))
if result.exit_status != 0:
logging.error('Unable to delete %s on dut', dest_path)
except Exception as e:
logging.error('Exception %s in cleanup', str(e))
raise error.TestFail('Cleanup failed')
class bluetooth_PeerUpdate(test.test):
"""
This test updates chameleond on Bluetooth peer devices
"""
version = 1
def run_once(self, host):
""" Update Bluetooth peer device
@param host: the DUT, usually a chromebook
"""
try:
self.host = host
commit = None
(status, commit) = get_latest_commit()
if commit is None:
raise error.TestFail('Unable to get current commit')
if not download_installation_files(self.host, commit):
raise error.TestFail('Unable to download installation files ')
update_peers(self.host, commit)
finally:
cleanup(host, commit)