You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
377 lines
14 KiB
377 lines
14 KiB
#!/usr/bin/python2
|
|
# Copyright 2015 The Chromium Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""Function tests of lxc module. To be able to run this test, following setup
|
|
is required:
|
|
1. lxc is installed.
|
|
2. Autotest code exists in /usr/local/autotest, with site-packages installed.
|
|
(run utils/build_externals.py)
|
|
3. The user runs the test should have sudo access. Run the test with sudo.
|
|
Note that the test does not require Autotest database and frontend.
|
|
"""
|
|
|
|
|
|
import argparse
|
|
import logging
|
|
import os
|
|
import tempfile
|
|
|
|
import common
|
|
from autotest_lib.client.bin import utils
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.site_utils import lxc
|
|
from autotest_lib.site_utils.lxc import base_image
|
|
from autotest_lib.site_utils.lxc import unittest_setup
|
|
|
|
|
|
TEST_JOB_ID = 123
|
|
TEST_JOB_FOLDER = '123-debug_user'
|
|
# Create a temp directory for functional tests. The directory is not under /tmp
|
|
# for Moblab to be able to run the test.
|
|
# But first, ensure that the containing directory exists:
|
|
|
|
if not os.path.exists(lxc.DEFAULT_CONTAINER_PATH):
|
|
os.makedirs(lxc.DEFAULT_CONTAINER_PATH)
|
|
TEMP_DIR = tempfile.mkdtemp(dir=lxc.DEFAULT_CONTAINER_PATH,
|
|
prefix='container_test_')
|
|
RESULT_PATH = os.path.join(TEMP_DIR, 'results', str(TEST_JOB_ID))
|
|
# Link to download a test package of autotest server package.
|
|
# Ideally the test should stage a build on devserver and download the
|
|
# autotest_server_package from devserver. This test is focused on testing
|
|
# container, so it's prefered to avoid dependency on devserver.
|
|
AUTOTEST_SERVER_PKG = ('http://storage.googleapis.com/abci-ssp/'
|
|
'autotest-containers/autotest_server_package.tar.bz2')
|
|
|
|
# Test log file to be created in result folder, content is `test`.
|
|
TEST_LOG = 'test.log'
|
|
# Name of test script file to run in container.
|
|
TEST_SCRIPT = 'test.py'
|
|
# Test script to run in container to verify autotest code setup.
|
|
TEST_SCRIPT_CONTENT = """
|
|
import socket
|
|
import sys
|
|
|
|
# Test import
|
|
import common
|
|
import chromite
|
|
|
|
# This test has to be before the import of autotest_lib, because ts_mon requires
|
|
# httplib2 module in chromite/third_party. The one in Autotest site-packages is
|
|
# out dated.
|
|
%(ts_mon_test)s
|
|
|
|
from autotest_lib.server import utils
|
|
from autotest_lib.site_utils import lxc
|
|
|
|
with open(sys.argv[1], 'w') as f:
|
|
f.write('test')
|
|
|
|
# Confirm hostname starts with `test-`
|
|
if not socket.gethostname().startswith('test-'):
|
|
raise Exception('The container\\\'s hostname must start with `test-`.')
|
|
|
|
# Test installing packages
|
|
lxc.install_packages(['atop'], ['acora'])
|
|
|
|
"""
|
|
|
|
TEST_SCRIPT_CONTENT_TS_MON = """
|
|
# Test ts_mon metrics can be set up.
|
|
from chromite.lib import ts_mon_config
|
|
ts_mon_config.SetupTsMonGlobalState('some_test', suppress_exception=False)
|
|
"""
|
|
|
|
CREATE_FAKE_TS_MON_CONFIG_SCRIPT = 'create_fake_key.py'
|
|
|
|
CREATE_FAKE_TS_MON_CONFIG_SCRIPT_CONTENT = """
|
|
import os
|
|
import rsa
|
|
|
|
EXPECTED_TS_MON_CONFIG_NAME = '/etc/chrome-infra/ts-mon.json'
|
|
|
|
FAKE_TS_MON_CONFIG_CONTENT = '''
|
|
{
|
|
"credentials":"/tmp/service_account_prodx_mon.json",
|
|
"endpoint":"https://xxx.googleapis.com/v1:insert",
|
|
"use_new_proto": true
|
|
}'''
|
|
|
|
FAKE_SERVICE_ACCOUNT_CRED_JSON = '''
|
|
{
|
|
"type": "service_account",
|
|
"project_id": "test_project",
|
|
"private_key_id": "aaa",
|
|
"private_key": "%s",
|
|
"client_email": "xxx",
|
|
"client_id": "111",
|
|
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
"token_uri": "https://accounts.google.com/o/oauth2/token",
|
|
"auth_provider_x509_cert_url":
|
|
"https://www.googleapis.com/oauth2/v1/certs",
|
|
"client_x509_cert_url":
|
|
"https://www.googleapis.com/robot/v1/metadata/x509/xxx"
|
|
}'''
|
|
|
|
|
|
TEST_KEY = '''------BEGIN PRIVATE KEY-----
|
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzg4K2SXqf9LAM
|
|
52a/t2HfpY5y49sbrgRb1llP6c8RVWhUX/pGdjbcIM97+1CJEWBN8Vmraoe4+71o
|
|
1idTPehJfHRNeyXQUnro8CmnSxE9tLHtdKj0pzvO+yqT66O6Iw1aUAIX+dG4Us9Q
|
|
Z22ypFHaJ74lKw9JFwAFTJ/TF1rXUXqgufYTNNqP3Ra7wCHF8BmtjwRYAlvsR9CO
|
|
c4eVC1+qhq/8/EOMCgF/rsbZW93r/nz5xgsSX0k6WkAz5WX2mniHfmBFpmr039jZ
|
|
0eI1mEMGDAYuUn05++dNveo/ZOZj3wBlFzyfNSeeWJB5SdKPTvN3H/Iu0Aw+Rtb6
|
|
szwNClaFAgMBAAECggEAHZ8cjVRUJ/tiJorzlTyfKZ6hwhsPv4JIRVg6LhnceZWA
|
|
jPW2cHSWyl2epyx55lhH7iyeeY7vXOqrX1aBMDb1stSWw2dH/tdxYSkqEmksa+R6
|
|
fL6kl5RV5epjpPt77Z3VmPq9UbP/M310qKWcgB8lw4wN0AfKMqsZLYauk9BVhNRu
|
|
Bgah9O7BmcXS+mp49w0Xyfo1UBvzW8R6UnBhHbf9aOY8ObMD0Jj/wDjlYMqSSIKR
|
|
9/8GZWQEKe6q0PyRRdNNtdzbpBrR0fIw6/T9pfDR2fBAcpNvD50eJk2jRiRDTWFJ
|
|
rVSc0bvZFb74Rc3LbMSXW/6Kb7I2IG1XsWw7nxp92QKBgQDgzdIxZrkNZ3Tbuzng
|
|
SG4atjnaCXoekOHK7VZVYd30S0AAizeGu1sjpUVQgsf+qkFskXAQp2/2f+Wiuq2G
|
|
+nJYvXwZ/r9IcUs/oD3Fa2ezCVz1N/HOSPFAZK9XZuZbL8sXEYIPGJWH5F8Sanmb
|
|
xNp9IUynlpwgM2JlZNeTCkv4PQKBgQDMbL/AF3LSpKvwi+QvYVkX/gChQmNMr4pP
|
|
TM/GI4D03tNrzsut3oerKMUw0c5MxonkAJpuACN6baRyBOBxRYQSt8wWkORg9iqy
|
|
a7aHnQqIGRafydW1/Snhr2DJSSaViHfO0oaA1r61zgMUTnSGb3UjyxJQp65dvPac
|
|
BhpR9wpz6QKBgQDR2S/CL8rEqXObfi1roREu3DYqw7f8enBb1qtFrsLbPbd0CoD9
|
|
wz0zjB6lJj/9CP9jkmwTD8njR8ab3jkIDBfboJ4NQhFbVW7R6QpglH9L0Iy2189g
|
|
KhUScCqBoyubqYSidxR6dQ94uATLkxsL/nmaXxBITL5XDMBoN/dIak86XQKBgDqa
|
|
oo4LKtvAYZpgQFZk7gm2w693PMhrOpdpSddfrkSE7M9nRXTe6r3ivkU0oJPaBwXa
|
|
Nmt6lrEuZYpaY42VhDtpfZSqjQ5PBAaKYpWWK8LAjn/YeO/nV+5fPLv3wJv1t4MP
|
|
T4f4CExOdwuHQliX81kDioicyZwN5BTumvUMgW6hAoGAF29kI1KthKaHN9P1DchI
|
|
qqoHb9FPdZ5I6HDQpn6fr9ut7+9kVqexUrQ2AMvcVei6gDWW6P3yDCdTKcV9qtts
|
|
1JOP2aSmXvibflx/bNfnhu988qJDhJ3CCjfc79fjwntUIXNPsFmwC9W5lnlSMKHM
|
|
rH4RdmnjeCIG1PZ35m/yUSU=
|
|
-----END PRIVATE KEY-----'''
|
|
|
|
if not os.path.exists(EXPECTED_TS_MON_CONFIG_NAME):
|
|
try:
|
|
os.makedirs(os.path.dirname(EXPECTED_TS_MON_CONFIG_NAME))
|
|
except OSError:
|
|
# Directory already exists.
|
|
pass
|
|
|
|
with open(EXPECTED_TS_MON_CONFIG_NAME, 'w') as f:
|
|
f.write(FAKE_TS_MON_CONFIG_CONTENT)
|
|
with open ('/tmp/service_account_prodx_mon.json', 'w') as f:
|
|
f.write(FAKE_SERVICE_ACCOUNT_CRED_JSON % repr(TEST_KEY)[2:-1])
|
|
"""
|
|
|
|
# Name of the test control file.
|
|
TEST_CONTROL_FILE = 'attach.1'
|
|
TEST_DUT = '172.27.213.193'
|
|
TEST_RESULT_PATH = lxc.RESULT_DIR_FMT % TEST_JOB_FOLDER
|
|
# Test autoserv command.
|
|
AUTOSERV_COMMAND = (('/usr/bin/python -u /usr/local/autotest/server/autoserv '
|
|
'-p -r %(result_path)s/%(test_dut)s -m %(test_dut)s '
|
|
'-u debug_user -l test -s -P %(job_id)s-debug_user/'
|
|
'%(test_dut)s -n %(result_path)s/%(test_control_file)s '
|
|
'--verify_job_repo_url') %
|
|
{'job_id': TEST_JOB_ID,
|
|
'result_path': TEST_RESULT_PATH,
|
|
'test_dut': TEST_DUT,
|
|
'test_control_file': TEST_CONTROL_FILE})
|
|
# Content of the test control file.
|
|
TEST_CONTROL_CONTENT = """
|
|
def run(machine):
|
|
job.run_test('dummy_PassServer',
|
|
host=hosts.create_host(machine))
|
|
|
|
parallel_simple(run, machines)
|
|
"""
|
|
|
|
|
|
def setup_base(container_path):
|
|
"""Test setup base container works.
|
|
|
|
@param bucket: ContainerBucket to interact with containers.
|
|
"""
|
|
logging.info('Rebuild base container in folder %s.', container_path)
|
|
image = base_image.BaseImage(container_path, lxc.BASE)
|
|
image.setup()
|
|
logging.info('Base container created: %s', image.get().name)
|
|
|
|
|
|
def setup_test(bucket, container_id, skip_cleanup):
|
|
"""Test container can be created from base container.
|
|
|
|
@param bucket: ContainerBucket to interact with containers.
|
|
@param container_id: ID of the test container.
|
|
@param skip_cleanup: Set to True to skip cleanup, used to troubleshoot
|
|
container failures.
|
|
|
|
@return: A Container object created for the test container.
|
|
"""
|
|
logging.info('Create test container.')
|
|
os.makedirs(RESULT_PATH)
|
|
container = bucket.setup_test(container_id, TEST_JOB_ID,
|
|
AUTOTEST_SERVER_PKG, RESULT_PATH,
|
|
skip_cleanup=skip_cleanup,
|
|
job_folder=TEST_JOB_FOLDER,
|
|
dut_name='192.168.0.3')
|
|
|
|
# Inject "AUTOSERV/testing_mode: True" in shadow config to test autoserv.
|
|
container.attach_run('echo $\'[AUTOSERV]\ntesting_mode: True\' >>'
|
|
' /usr/local/autotest/shadow_config.ini')
|
|
|
|
if not utils.is_moblab():
|
|
# Create fake '/etc/chrome-infra/ts-mon.json' if it doesn't exist.
|
|
create_key_script = os.path.join(
|
|
RESULT_PATH, CREATE_FAKE_TS_MON_CONFIG_SCRIPT)
|
|
with open(create_key_script, 'w') as script:
|
|
script.write(CREATE_FAKE_TS_MON_CONFIG_SCRIPT_CONTENT)
|
|
container_result_path = lxc.RESULT_DIR_FMT % TEST_JOB_FOLDER
|
|
container_create_key_script = os.path.join(
|
|
container_result_path, CREATE_FAKE_TS_MON_CONFIG_SCRIPT)
|
|
container.attach_run('python %s' % container_create_key_script)
|
|
|
|
return container
|
|
|
|
|
|
def test_share(container):
|
|
"""Test container can share files with the host.
|
|
|
|
@param container: The test container.
|
|
"""
|
|
logging.info('Test files written to result directory can be accessed '
|
|
'from the host running the container..')
|
|
host_test_script = os.path.join(RESULT_PATH, TEST_SCRIPT)
|
|
with open(host_test_script, 'w') as script:
|
|
if utils.is_moblab():
|
|
script.write(TEST_SCRIPT_CONTENT % {'ts_mon_test': ''})
|
|
else:
|
|
script.write(TEST_SCRIPT_CONTENT %
|
|
{'ts_mon_test': TEST_SCRIPT_CONTENT_TS_MON})
|
|
|
|
container_result_path = lxc.RESULT_DIR_FMT % TEST_JOB_FOLDER
|
|
container_test_script = os.path.join(container_result_path, TEST_SCRIPT)
|
|
container_test_script_dest = os.path.join('/usr/local/autotest/utils/',
|
|
TEST_SCRIPT)
|
|
container_test_log = os.path.join(container_result_path, TEST_LOG)
|
|
host_test_log = os.path.join(RESULT_PATH, TEST_LOG)
|
|
# Move the test script out of result folder as it needs to import common.
|
|
container.attach_run('mv %s %s' % (container_test_script,
|
|
container_test_script_dest))
|
|
container.attach_run('python %s %s' % (container_test_script_dest,
|
|
container_test_log))
|
|
if not os.path.exists(host_test_log):
|
|
raise Exception('Results created in container can not be accessed from '
|
|
'the host.')
|
|
with open(host_test_log, 'r') as log:
|
|
if log.read() != 'test':
|
|
raise Exception('Failed to read the content of results in '
|
|
'container.')
|
|
|
|
|
|
def test_autoserv(container):
|
|
"""Test container can run autoserv command.
|
|
|
|
@param container: The test container.
|
|
"""
|
|
logging.info('Test autoserv command.')
|
|
logging.info('Create test control file.')
|
|
host_control_file = os.path.join(RESULT_PATH, TEST_CONTROL_FILE)
|
|
with open(host_control_file, 'w') as control_file:
|
|
control_file.write(TEST_CONTROL_CONTENT)
|
|
|
|
logging.info('Run autoserv command.')
|
|
container.attach_run(AUTOSERV_COMMAND)
|
|
|
|
logging.info('Confirm results are available from host.')
|
|
# Read status.log to check the content is not empty.
|
|
container_status_log = os.path.join(TEST_RESULT_PATH, TEST_DUT,
|
|
'status.log')
|
|
status_log = container.attach_run(command='cat %s' % container_status_log
|
|
).stdout
|
|
if len(status_log) < 10:
|
|
raise Exception('Failed to read status.log in container.')
|
|
|
|
|
|
def test_package_install(container):
|
|
"""Test installing package in container.
|
|
|
|
@param container: The test container.
|
|
"""
|
|
# Packages are installed in TEST_SCRIPT_CONTENT. Verify the packages in
|
|
# this method.
|
|
container.attach_run('which atop')
|
|
container.attach_run('python -c "import acora"')
|
|
|
|
|
|
def test_ssh(container, remote):
|
|
"""Test container can run ssh to remote server.
|
|
|
|
@param container: The test container.
|
|
@param remote: The remote server to ssh to.
|
|
|
|
@raise: error.CmdError if container can't ssh to remote server.
|
|
"""
|
|
logging.info('Test ssh to %s.', remote)
|
|
container.attach_run('ssh %s -a -x -o StrictHostKeyChecking=no '
|
|
'-o BatchMode=yes -o UserKnownHostsFile=/dev/null '
|
|
'-p 22 "true"' % remote)
|
|
|
|
|
|
def parse_options():
|
|
"""Parse command line inputs.
|
|
"""
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('-d', '--dut', type=str,
|
|
help='Test device to ssh to.',
|
|
default=None)
|
|
parser.add_argument('-r', '--devserver', type=str,
|
|
help='Test devserver to ssh to.',
|
|
default=None)
|
|
parser.add_argument('-v', '--verbose', action='store_true',
|
|
default=False,
|
|
help='Print out ALL entries.')
|
|
parser.add_argument('-s', '--skip_cleanup', action='store_true',
|
|
default=False,
|
|
help='Skip deleting test containers.')
|
|
return parser.parse_args()
|
|
|
|
|
|
def main(options):
|
|
"""main script.
|
|
|
|
@param options: Options to run the script.
|
|
"""
|
|
# Verify that the test is running as the correct user.
|
|
unittest_setup.verify_user()
|
|
|
|
log_level = (logging.DEBUG if options.verbose else logging.INFO)
|
|
unittest_setup.setup_logging(log_level)
|
|
|
|
setup_base(TEMP_DIR)
|
|
bucket = lxc.ContainerBucket(TEMP_DIR)
|
|
|
|
container_id = lxc.ContainerId.create(TEST_JOB_ID)
|
|
container = setup_test(bucket, container_id, options.skip_cleanup)
|
|
test_share(container)
|
|
test_autoserv(container)
|
|
if options.dut:
|
|
test_ssh(container, options.dut)
|
|
if options.devserver:
|
|
test_ssh(container, options.devserver)
|
|
# Packages are installed in TEST_SCRIPT, verify the packages are installed.
|
|
test_package_install(container)
|
|
logging.info('All tests passed.')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
options = parse_options()
|
|
try:
|
|
main(options)
|
|
except:
|
|
# If the cleanup code below raises additional errors, they obfuscate the
|
|
# actual error in the test. Highlight the error to aid in debugging.
|
|
logging.exception('ERROR:\n%s', error.format_error())
|
|
raise
|
|
finally:
|
|
if not options.skip_cleanup:
|
|
logging.info('Cleaning up temporary directory %s.', TEMP_DIR)
|
|
try:
|
|
lxc.ContainerBucket(TEMP_DIR).destroy_all()
|
|
finally:
|
|
utils.run('sudo rm -rf "%s"' % TEMP_DIR)
|