[moblab] Call new upstart command that correctly restarts apache.
restart moblab-apache-init does not cleanly or correctly restart
apache, call a new upstart config that calls the correct command
to restart apache.
Replace the use of the boto library with a gsutil command.
Use the same path to the gsutil command that the devserver uses.
BUG=chromium:714294
TEST=locally on moblab, unittests
Change-Id: I0befe734d2c6130dadcfdaa5984aec23bba07807
Reviewed-on: https://chromium-review.googlesource.com/497169
Commit-Ready: Keith Haddow <haddowk@chromium.org>
Tested-by: Keith Haddow <haddowk@chromium.org>
Reviewed-by: Keith Haddow <haddowk@chromium.org>
Reviewed-by: Michael Tang <ntang@chromium.org>
diff --git a/frontend/afe/moblab_rpc_interface.py b/frontend/afe/moblab_rpc_interface.py
index d73aea8..ee7a60b 100644
--- a/frontend/afe/moblab_rpc_interface.py
+++ b/frontend/afe/moblab_rpc_interface.py
@@ -7,14 +7,6 @@
on moblab.
"""
-# The boto module is only available/used in Moblab for validation of cloud
-# storage access. The module is not available in the test lab environment,
-# and the import error is handled.
-try:
- import boto
-except ImportError:
- boto = None
-
import ConfigParser
import common
import logging
@@ -31,7 +23,7 @@
from autotest_lib.frontend.afe import rpc_utils
from autotest_lib.server import frontend
from autotest_lib.server.hosts import moblab_host
-
+from chromite.lib import gs
_CONFIG = global_config.global_config
MOBLAB_BOTO_LOCATION = '/home/moblab/.boto'
@@ -45,7 +37,7 @@
# Contants used in Json RPC field names.
_IMAGE_STORAGE_SERVER = 'image_storage_server'
_GS_ACCESS_KEY_ID = 'gs_access_key_id'
-_GS_SECRETE_ACCESS_KEY = 'gs_secret_access_key'
+_GS_SECRET_ACCESS_KEY = 'gs_secret_access_key'
_RESULT_STORAGE_SERVER = 'results_storage_server'
_USE_EXISTING_BOTO_FILE = 'use_existing_boto_file'
@@ -55,6 +47,12 @@
# File where information about the current device is stored.
_ETC_LSB_RELEASE = '/etc/lsb-release'
+# Full path to the correct gsutil command to run.
+_GSUTIL_CMD = gs.GSContext.GetDefaultGSUtilBin()
+
+class BucketPerformanceTestException(Exception):
+ pass
+
@rpc_utils.moblab_only
def get_config_values():
"""Returns all config values parsed from global and shadow configs.
@@ -254,8 +252,8 @@
def _get_network_info():
"""Gets the network information.
- TCP socket is used to test the connectivity. If there is no connectivity, try to
- get the public IP with UDP socket.
+ TCP socket is used to test the connectivity. If there is no connectivity,
+ try to get the public IP with UDP socket.
@return: a tuple as (public_ip_address, connected_to_internet).
"""
@@ -320,9 +318,9 @@
if _GS_ACCESS_KEY_ID in options:
value = boto_config.get('Credentials', _GS_ACCESS_KEY_ID)
cloud_storage_info[_GS_ACCESS_KEY_ID] = value
- if _GS_SECRETE_ACCESS_KEY in options:
- value = boto_config.get('Credentials', _GS_SECRETE_ACCESS_KEY)
- cloud_storage_info[_GS_SECRETE_ACCESS_KEY] = value
+ if _GS_SECRET_ACCESS_KEY in options:
+ value = boto_config.get('Credentials', _GS_SECRET_ACCESS_KEY)
+ cloud_storage_info[_GS_SECRET_ACCESS_KEY] = value
return rpc_utils.prepare_for_serialization(cloud_storage_info)
@@ -338,61 +336,12 @@
return match.group('bucket')
return None
-
-def _is_valid_boto_key(key_id, key_secret):
- """Checks if the boto key is valid.
-
- @param: key_id: The boto key id string.
- @param: key_secret: The boto key string.
-
- @return: A tuple as (valid_boolean, details_string).
- """
- if not key_id or not key_secret:
- return (False, "Empty key id or secret.")
- conn = boto.connect_gs(key_id, key_secret)
- try:
- buckets = conn.get_all_buckets()
- return (True, None)
- except boto.exception.GSResponseError:
- details = "The boto access key is not valid"
- return (False, details)
- finally:
- conn.close()
-
-
-def _is_valid_bucket(key_id, key_secret, bucket_name):
- """Checks if a bucket is valid and accessible.
-
- @param: key_id: The boto key id string.
- @param: key_secret: The boto key string.
- @param: bucket name string.
-
- @return: A tuple as (valid_boolean, details_string).
- """
- if not key_id or not key_secret or not bucket_name:
- return (False, "Server error: invalid argument")
- conn = boto.connect_gs(key_id, key_secret)
- bucket = conn.lookup(bucket_name)
- conn.close()
- if bucket:
- return (True, None)
- return (False, "Bucket %s does not exist." % bucket_name)
-
-
-def _is_valid_bucket_url(key_id, key_secret, bucket_url):
- """Validates the bucket url is accessible.
-
- @param: key_id: The boto key id string.
- @param: key_secret: The boto key string.
- @param: bucket url string.
-
- @return: A tuple as (valid_boolean, details_string).
- """
- bucket_name = _get_bucket_name_from_url(bucket_url)
- if bucket_name:
- return _is_valid_bucket(key_id, key_secret, bucket_name)
- return (False, "Bucket url %s is not valid" % bucket_url)
-
+def _is_valid_boto_key(key_id, key_secret, directory):
+ try:
+ _run_bucket_performance_test(key_id, key_secret, directory)
+ except BucketPerformanceTestException as e:
+ return(False, str(e))
+ return(True, None)
def _validate_cloud_storage_info(cloud_storage_info):
"""Checks if the cloud storage information is valid.
@@ -405,17 +354,9 @@
details = None
if not cloud_storage_info[_USE_EXISTING_BOTO_FILE]:
key_id = cloud_storage_info[_GS_ACCESS_KEY_ID]
- key_secret = cloud_storage_info[_GS_SECRETE_ACCESS_KEY]
- valid, details = _is_valid_boto_key(key_id, key_secret)
-
- if valid:
- valid, details = _is_valid_bucket_url(
- key_id, key_secret, cloud_storage_info[_IMAGE_STORAGE_SERVER])
-
- # allows result bucket to be empty.
- if valid and cloud_storage_info[_RESULT_STORAGE_SERVER]:
- valid, details = _is_valid_bucket_url(
- key_id, key_secret, cloud_storage_info[_RESULT_STORAGE_SERVER])
+ key_secret = cloud_storage_info[_GS_SECRET_ACCESS_KEY]
+ valid, details = _is_valid_boto_key(
+ key_id, key_secret, cloud_storage_info[_IMAGE_STORAGE_SERVER])
return (valid, details)
@@ -464,16 +405,18 @@
boto_config.add_section('Credentials')
boto_config.set('Credentials', _GS_ACCESS_KEY_ID,
cloud_storage_info[_GS_ACCESS_KEY_ID])
- boto_config.set('Credentials', _GS_SECRETE_ACCESS_KEY,
- cloud_storage_info[_GS_SECRETE_ACCESS_KEY])
+ boto_config.set('Credentials', _GS_SECRET_ACCESS_KEY,
+ cloud_storage_info[_GS_SECRET_ACCESS_KEY])
_write_config_file(MOBLAB_BOTO_LOCATION, boto_config, True)
_CONFIG.parse_config_file()
- services = ['moblab-devserver-init', 'moblab-apache-init',
+ services = ['moblab-devserver-init',
'moblab-devserver-cleanup-init', ' moblab-gsoffloader_s-init',
- 'moblab-base-container-init', 'moblab-scheduler-init', 'moblab-gsoffloader-init']
- cmd = ';/sbin/restart '.join(services)
- os.system(cmd)
+ 'moblab-base-container-init', 'moblab-scheduler-init',
+ 'moblab-gsoffloader-init']
+ cmd = ';sudo /sbin/restart '.join(services)
+ cmd += ';sudo /usr/sbin/apache2 -k graceful'
+ os.system("export ATEST_RESULTS_DIR=/usr/local/autotest/results;" + cmd)
return _create_operation_status_response(True, None)
@@ -581,7 +524,8 @@
if label:
label.host_set.add(host_obj)
return (True, 'Added label %s to DUT %s' % (label_name, ipaddress))
- return (False, 'Failed to add label %s to DUT %s' % (label_name, ipaddress))
+ return (False,
+ 'Failed to add label %s to DUT %s' % (label_name, ipaddress))
@rpc_utils.moblab_only
@@ -599,11 +543,13 @@
def _get_connected_dut_labels(requested_label, only_first_label=True):
- """ Query the DUT's attached to the moblab and return a filtered list of labels.
+ """ Query the DUT's attached to the moblab and return a filtered list
+ of labels.
@param requested_label: the label name you are requesting.
- @param only_first_label: if the device has the same label name multiple times only
- return the first label value in the list.
+ @param only_first_label: if the device has the same label name multiple
+ times only return the first label value in the
+ list.
@return: A de-duped list of requested dut labels attached to the moblab.
"""
@@ -668,7 +614,8 @@
return _get_builds_for_in_directory(board_name + '-firmware')
-def _get_builds_for_in_directory(directory_name, milestone_limit=3, build_limit=20):
+def _get_builds_for_in_directory(directory_name, milestone_limit=3,
+ build_limit=20):
""" Fetch the most recent builds for the last three milestones from gcs.
@@ -676,16 +623,18 @@
storage bucket to search.
- @return: A string list no longer than <milestone_limit> x <build_limit> items,
- containing the most recent <build_limit> builds from the last
- milestone_limit milestones.
+ @return: A string list no longer than <milestone_limit> x <build_limit>
+ items, containing the most recent <build_limit> builds from the
+ last milestone_limit milestones.
"""
output = StringIO.StringIO()
gs_image_location =_CONFIG.get_config_value('CROS', _IMAGE_STORAGE_SERVER)
- utils.run('gsutil', args=('ls', gs_image_location + directory_name), stdout_tee=output)
+ utils.run(_GSUTIL_CMD, args=('ls', gs_image_location + directory_name),
+ stdout_tee=output)
lines = output.getvalue().split('\n')
output.close()
- builds = [line.replace(gs_image_location,'').strip('/ ') for line in lines if line != '']
+ builds = [line.replace(gs_image_location,'').strip('/ ')
+ for line in lines if line != '']
build_matcher = re.compile(r'^.*\/R([0-9]*)-.*')
build_map = {}
for build in builds:
@@ -706,8 +655,44 @@
return build_list
+def _run_bucket_performance_test(key_id, key_secret, bucket_name,
+ test_size='1M', iterations='1',
+ result_file='/tmp/gsutil_perf.json'):
+ """Run a gsutil perfdiag on a supplied bucket and output the results"
+
+ @param key_id: boto key of the bucket to be accessed
+ @param key_secret: boto secret of the bucket to be accessed
+ @param bucket_name: bucket to be tested.
+ @param test_size: size of file to use in test, see gsutil perfdiag help.
+ @param iterations: number of times each test is run.
+ @param result_file: name of file to write results out to.
+
+ @return None
+ @raises BucketPerformanceTestException if the command fails.
+ """
+ try:
+ utils.run(_GSUTIL_CMD, args=(
+ '-o', 'Credentials:gs_access_key_id=%s' % key_id,
+ '-o', 'Credentials:gs_secret_access_key=%s' % key_secret,
+ 'perfdiag', '-s', test_size, '-o', result_file,
+ '-n', iterations,
+ bucket_name))
+ except error.CmdError as e:
+ logging.error(e)
+ # Extract useful error from the stacktrace
+ errormsg = str(e)
+ start_error_pos = errormsg.find("<Error>")
+ end_error_pos = errormsg.find("</Error>", start_error_pos)
+ extracted_error_msg = errormsg[start_error_pos:end_error_pos]
+ raise BucketPerformanceTestException(
+ extracted_error_msg if extracted_error_msg else errormsg)
+ # TODO(haddowk) send the results to the cloud console when that feature is
+ # enabled.
+
+
@rpc_utils.moblab_only
-def run_suite(board, build, suite, ro_firmware=None, rw_firmware=None, pool=None):
+def run_suite(board, build, suite, ro_firmware=None, rw_firmware=None,
+ pool=None):
""" RPC handler to run a test suite.
@param board: a board name connected to the moblab.
@@ -728,3 +713,4 @@
afe.run('create_suite_job', board=board, builds=builds, name=suite,
pool=pool, run_prod_code=False, test_source_build=build,
wait_for_results=False)
+
diff --git a/frontend/afe/moblab_rpc_interface_unittest.py b/frontend/afe/moblab_rpc_interface_unittest.py
index 1633405..d7eb30a 100644
--- a/frontend/afe/moblab_rpc_interface_unittest.py
+++ b/frontend/afe/moblab_rpc_interface_unittest.py
@@ -10,12 +10,7 @@
# The boto module is only available/used in Moblab for validation of cloud
# storage access. The module is not available in the test lab environment,
# and the import error is handled.
-try:
- import boto
-except ImportError:
- boto = None
import ConfigParser
-import logging
import mox
import StringIO
import unittest
@@ -31,6 +26,7 @@
from autotest_lib.frontend.afe import rpc_utils
from autotest_lib.server import utils
from autotest_lib.server.hosts import moblab_host
+from autotest_lib.client.common_lib import utils as common_lib_utils
class MoblabRpcInterfaceTest(mox.MoxTestBase,
@@ -160,20 +156,6 @@
moblab_rpc_interface.reset_config_settings()
- def testSetBotoKey(self):
- """Ensure that the botokey path supplied is copied correctly."""
- self.setIsMoblab(True)
- boto_key = '/tmp/boto'
- moblab_rpc_interface.os.path = self.mox.CreateMockAnything()
- moblab_rpc_interface.os.path.exists(boto_key).AndReturn(
- True)
- moblab_rpc_interface.shutil = self.mox.CreateMockAnything()
- moblab_rpc_interface.shutil.copyfile(
- boto_key, moblab_rpc_interface.MOBLAB_BOTO_LOCATION)
- self.mox.ReplayAll()
- moblab_rpc_interface.set_boto_key(boto_key)
-
-
def testSetLaunchControlKey(self):
"""Ensure that the Launch Control key path supplied is copied correctly.
"""
@@ -278,16 +260,11 @@
'gs_secret_access_key': 'secret',
'image_storage_server': 'gs://bucket1',
'results_storage_server': 'gs://bucket2'}
- self.mox.StubOutWithMock(moblab_rpc_interface, '_is_valid_boto_key')
- self.mox.StubOutWithMock(moblab_rpc_interface, '_is_valid_bucket')
- moblab_rpc_interface._is_valid_boto_key(
- 'key', 'secret').AndReturn((True, None))
- moblab_rpc_interface._is_valid_bucket(
- 'key', 'secret', 'bucket1').AndReturn((True, None))
- moblab_rpc_interface._is_valid_bucket(
- 'key', 'secret', 'bucket2').AndReturn((True, None))
- rpc_utils.prepare_for_serialization(
- {'status_ok': True })
+ self.mox.StubOutWithMock(moblab_rpc_interface,
+ '_run_bucket_performance_test')
+ moblab_rpc_interface._run_bucket_performance_test(
+ 'key', 'secret', 'gs://bucket1').AndReturn((True, None))
+ rpc_utils.prepare_for_serialization({'status_ok': True })
self.mox.ReplayAll()
moblab_rpc_interface.validate_cloud_storage_info(cloud_storage_info)
self.mox.VerifyAll()
@@ -311,75 +288,6 @@
'bucket_name-123/a/b/c'))
- def testIsValidBotoKeyValid(self):
- """Tests the boto key validation flow."""
- if boto is None:
- logging.info('skip test since boto module not installed')
- return
- conn = self.mox.CreateMockAnything()
- self.mox.StubOutWithMock(boto, 'connect_gs')
- boto.connect_gs('key', 'secret').AndReturn(conn)
- conn.get_all_buckets().AndReturn(['a', 'b'])
- conn.close()
- self.mox.ReplayAll()
- valid, details = moblab_rpc_interface._is_valid_boto_key('key', 'secret')
- self.assertTrue(valid)
- self.mox.VerifyAll()
-
-
- def testIsValidBotoKeyInvalid(self):
- """Tests the boto key validation with invalid key."""
- if boto is None:
- logging.info('skip test since boto module not installed')
- return
- conn = self.mox.CreateMockAnything()
- self.mox.StubOutWithMock(boto, 'connect_gs')
- boto.connect_gs('key', 'secret').AndReturn(conn)
- conn.get_all_buckets().AndRaise(
- boto.exception.GSResponseError('bad', 'reason'))
- conn.close()
- self.mox.ReplayAll()
- valid, details = moblab_rpc_interface._is_valid_boto_key('key', 'secret')
- self.assertFalse(valid)
- self.assertEquals('The boto access key is not valid', details)
- self.mox.VerifyAll()
-
-
- def testIsValidBucketValid(self):
- """Tests the bucket vaildation flow."""
- if boto is None:
- logging.info('skip test since boto module not installed')
- return
- conn = self.mox.CreateMockAnything()
- self.mox.StubOutWithMock(boto, 'connect_gs')
- boto.connect_gs('key', 'secret').AndReturn(conn)
- conn.lookup('bucket').AndReturn('bucket')
- conn.close()
- self.mox.ReplayAll()
- valid, details = moblab_rpc_interface._is_valid_bucket(
- 'key', 'secret', 'bucket')
- self.assertTrue(valid)
- self.mox.VerifyAll()
-
-
- def testIsValidBucketInvalid(self):
- """Tests the bucket validation flow with invalid key."""
- if boto is None:
- logging.info('skip test since boto module not installed')
- return
- conn = self.mox.CreateMockAnything()
- self.mox.StubOutWithMock(boto, 'connect_gs')
- boto.connect_gs('key', 'secret').AndReturn(conn)
- conn.lookup('bucket').AndReturn(None)
- conn.close()
- self.mox.ReplayAll()
- valid, details = moblab_rpc_interface._is_valid_bucket(
- 'key', 'secret', 'bucket')
- self.assertFalse(valid)
- self.assertEquals("Bucket bucket does not exist.", details)
- self.mox.VerifyAll()
-
-
def testGetShadowConfigFromPartialUpdate(self):
"""Tests getting shadow configuration based on partial upate."""
partial_config = {
@@ -462,5 +370,27 @@
self.mox.VerifyAll()
+ def testRunBucketPerformanceTestFail(self):
+ self.mox.StubOutWithMock(common_lib_utils, 'run')
+ common_lib_utils.run(moblab_rpc_interface._GSUTIL_CMD,
+ args=(
+ '-o', 'Credentials:gs_access_key_id=key',
+ '-o', 'Credentials:gs_secret_access_key=secret',
+ 'perfdiag', '-s', '1K',
+ '-o', 'testoutput',
+ '-n', '10',
+ 'gs://bucket1')).AndRaise(
+ error.CmdError("fakecommand", common_lib_utils.CmdResult(),
+ "xxxxxx<Error>yyyyyyyyyy</Error>"))
+
+ self.mox.ReplayAll()
+ self.assertRaisesRegexp(
+ moblab_rpc_interface.BucketPerformanceTestException,
+ '<Error>yyyyyyyyyy',
+ moblab_rpc_interface._run_bucket_performance_test,
+ 'key', 'secret', 'gs://bucket1', '1K', '10', 'testoutput')
+ self.mox.VerifyAll()
+
+
if __name__ == '__main__':
unittest.main()