13 from uuid
import uuid4
14 from os
import urandom
15 from functools
import wraps
16 from multiprocessing
import pool, cpu_count
17 from http.server
import SimpleHTTPRequestHandler, HTTPServer
19 from fake_http_server.fake_test_server
import FakeTestServerBackground
20 from sota_tools.treehub_server
import create_repo
23 logger = logging.getLogger(__name__)
28 def __init__(self, aktualizr_primary_exe, aktualizr_info_exe, id,
29 uptane_server, wait_port=9040, wait_timeout=60, log_level=1,
30 primary_port=None, secondaries=None, secondary_wait_sec=600, output_logs=True,
31 run_mode='once', director=None, image_repo=None,
32 sysroot=None, treehub=None, ostree_mock_path=None, **kwargs):
43 with open(path.join(self.
_storage_dir.name,
'secondary_config.json'),
'w+')
as secondary_config_file:
44 secondary_cfg = json.loads(Aktualizr.SECONDARY_CONFIG_TEMPLATE.
45 format(port=primary_port
if primary_port
else wait_port,
46 timeout=wait_timeout))
47 json.dump(secondary_cfg, secondary_config_file)
51 with open(path.join(self.
_storage_dir.name,
'config.toml'),
'w+')
as config_file:
52 config_file.write(Aktualizr.CONFIG_TEMPLATE.format(server_url=uptane_server.base_url,
54 serial=id[1], hw_ID=id[0],
60 director=director.base_url
if director
else '',
61 image_repo=image_repo.base_url
if image_repo
else '',
62 pacman_type=
'ostree' if treehub
and sysroot
else 'none',
63 ostree_sysroot=sysroot.path
if sysroot
else '',
64 treehub_server=treehub.base_url
if treehub
else '',
69 if secondaries
is not None:
75 if sysroot
and ostree_mock_path:
76 self.
_run_env[
'LD_PRELOAD'] = os.path.abspath(ostree_mock_path)
77 self.
_run_env[
'OSTREE_DEPLOYMENT_VERSION_FILE'] = sysroot.version_file
81 server = "{server_url}" 84 base_path = "{import_path}" 85 tls_cacert_path = "ca.pem" 86 tls_pkey_path = "pkey.pem" 87 tls_clientcert_path = "client.pem" 90 primary_ecu_serial = "{serial}" 91 primary_ecu_hardware_id = "{hw_ID}" 94 path = "{storage_dir}" 96 sqldb_path = "{db_path}" 99 type = "{pacman_type}" 100 sysroot = "{ostree_sysroot}" 101 ostree_server = "{treehub_server}" 106 secondary_config_file = "{secondary_cfg_file}" 107 secondary_preinstall_wait_sec = {secondary_wait_sec} 108 director_server = "{director}" 109 repo_server = "{image_repo}" 112 reboot_sentinel_dir = "{sentinel_dir}" 113 reboot_sentinel_name = "{sentinel_name}" 117 loglevel = {log_level} 121 SECONDARY_CONFIG_TEMPLATE =
''' 124 "secondaries_wait_port": {port}, 125 "secondaries_wait_timeout": {timeout}, 131 def set_mode(self, mode):
134 def add_secondary(self, secondary):
136 sec_cfg = json.load(config_file)
137 sec_cfg[
"IP"][
"secondaries"].append({
"addr":
"127.0.0.1:{}".format(secondary.port)})
139 json.dump(sec_cfg, config_file)
141 def update_wait_timeout(self, timeout):
143 sec_cfg = json.load(config_file)
144 sec_cfg[
"IP"][
"secondaries_wait_timeout"] = timeout
146 json.dump(sec_cfg, config_file)
148 def run(self, run_mode):
154 def get_info(self, retry=30):
156 for ii
in range(0, retry):
158 timeout=60, stdout=subprocess.PIPE, env=self.
_run_env)
159 if info_exe_res.returncode == 0
and \
160 str(info_exe_res.stdout).find(
'Provisioned on server: yes') != -1
and \
161 str(info_exe_res.stdout).find(
'Current Primary ECU running version:') != -1:
164 if info_exe_res
and info_exe_res.returncode == 0:
165 return str(info_exe_res.stdout)
167 logger.error(
'Failed to get an aktualizr\'s status info, stdout: {}, stderr: {}'.
168 format(str(info_exe_res.stdout), str(info_exe_res.stderr)))
173 def is_ecu_registered(self, ecu_id):
175 if not ((device_status.find(ecu_id[0]) != -1)
and (device_status.find(ecu_id[1]) != -1)):
177 not_registered_field =
"Removed or not registered ECUs:" 178 not_reg_start = device_status.find(not_registered_field)
179 return not_reg_start == -1
or (device_status.find(ecu_id[1], not_reg_start) == -1)
181 def get_current_image_info(self, ecu_id):
182 if self.
id == ecu_id:
187 def get_current_pending_image_info(self, ecu_id):
194 def _get_current_image_info(self, ecu_id, secondary_image_hash_field='installed image hash:
'): 197 ecu_serial = ecu_id[1]
198 ecu_info_position = aktualizr_status.find(ecu_serial)
199 if ecu_info_position == -1:
202 start = aktualizr_status.find(secondary_image_hash_field, ecu_info_position)
203 end = aktualizr_status.find(
'\\n', start)
204 hash_val = aktualizr_status[start + len(secondary_image_hash_field):end]
214 def get_current_primary_image_info(self):
215 primary_hash_field =
'Current Primary ECU running version: ' 218 start = aktualizr_status.find(primary_hash_field)
219 end = aktualizr_status.find(
'\\n', start)
220 return aktualizr_status[start + len(primary_hash_field):end]
222 logger.error(
"Failed to get aktualizr info/status")
227 def get_primary_pending_version(self):
228 primary_hash_field =
'Pending Primary ECU version: ' 230 start = aktualizr_status.find(primary_hash_field)
231 end = aktualizr_status.find(
'\\n', start)
232 return aktualizr_status[start + len(primary_hash_field):end]
237 stderr=
None if self.
_output_logs else subprocess.STDOUT,
240 logger.debug(
"Aktualizr has been started")
243 def __exit__(self, exc_type, exc_val, exc_tb):
246 logger.debug(
"Aktualizr has been stopped")
248 def terminate(self, sig=signal.SIGTERM):
252 return self.
_process.stdout.read().decode(errors=
'replace')
254 def wait_for_completion(self, timeout=120):
257 def wait_for_provision(self, timeout=60):
258 deadline = time.time() + timeout
259 while timeout == 0
or time.time() < deadline:
261 if info
is not None and 'Provisioned on server: yes' in info:
266 def emulate_reboot(self):
274 def copy_keys(dest_path):
276 shutil.copy(KeyStore.ca(), dest_path)
277 shutil.copy(KeyStore.pkey(), dest_path)
278 shutil.copy(KeyStore.cert(), dest_path)
282 return path.join(KeyStore.base_dir,
'tests/test_data/prov_testupdate/ca.pem')
286 return path.join(KeyStore.base_dir,
'tests/test_data/prov_testupdate/pkey.pem')
290 return path.join(KeyStore.base_dir,
'tests/test_data/prov_testupdate/client.pem')
295 def __init__(self, aktualizr_secondary_exe, id, port=None, primary_port=None,
296 sysroot=None, treehub=None, output_logs=True, force_reboot=False,
297 ostree_mock_path=None, **kwargs):
313 with open(path.join(self.
storage_dir.name,
'config.toml'),
'w+')
as config_file:
314 config_file.write(IPSecondary.CONFIG_TEMPLATE.format(serial=id[1], hw_ID=id[0],
315 force_reboot=1
if force_reboot
else 0,
316 reboot_command=reboot_command,
319 db_path=path.join(self.
storage_dir.name,
'db.sql'),
320 pacman_type=
'ostree' if treehub
and sysroot
else 'none',
321 ostree_sysroot=sysroot.path
if sysroot
else '',
322 treehub_server=treehub.base_url
if treehub
else '',
329 if sysroot
and ostree_mock_path:
330 self.
_run_env[
'LD_PRELOAD'] = os.path.abspath(ostree_mock_path)
331 self.
_run_env[
'OSTREE_DEPLOYMENT_VERSION_FILE'] = sysroot.version_file
334 CONFIG_TEMPLATE =
''' 336 ecu_serial = "{serial}" 337 ecu_hardware_id = "{hw_ID}" 338 force_install_completion = {force_reboot} 342 primary_ip = "127.0.0.1" 343 primary_port = {primary_port} 347 path = "{storage_dir}" 348 sqldb_path = "{db_path}" 351 type = "{pacman_type}" 352 sysroot = "{ostree_sysroot}" 353 ostree_server = "{treehub_server}" 357 reboot_sentinel_dir = "{sentinel_dir}" 358 reboot_sentinel_name = "{sentinel_name}" 359 reboot_command = "{reboot_command}" 362 def is_running(self):
363 return True if self.
_process.poll()
is None else False 367 tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
369 port = tcp.getsockname()[1]
376 stderr=
None if self.
_output_logs else subprocess.STDOUT,
379 logger.debug(
"IP Secondary {} has been started: {}".format(self.
id, self.
port))
382 def __exit__(self, exc_type, exc_val, exc_tb):
385 logger.debug(
"IP Secondary {} has been stopped".format(self.
id))
387 def wait_for_completion(self, timeout=120):
390 def emulate_reboot(self):
395 def __init__(self, doc_root, ifc, port, client_handler_map={}):
396 super(UptaneRepo, self).__init__(server_address=(ifc, port), RequestHandlerClass=self.
Handler)
398 self.
base_url =
'http://{}:{}'.format(self.server_address[0], self.server_address[1])
403 lambda request: (self.
Handler.handler_map.get(
'POST', {})).get(request.path,
404 self.
Handler.default_handler)(request)
407 lambda request: (self.
Handler.handler_map.get(
'PUT', {})).get(request.path,
408 self.
Handler.default_handler)(request)
411 lambda request: (self.
Handler.handler_map.get(
'GET', {})).get(request.path,
412 self.
Handler.default_get)(request)
414 for method, method_handlers
in client_handler_map.items():
415 for url, handler
in method_handlers.items():
416 if self.
Handler.handler_map.get(method,
None)
is None:
417 self.
Handler.handler_map[method] = {}
418 self.
Handler.handler_map[method][url] = handler
421 def __init__(self, request, client_address, server):
426 def default_handler(self):
427 self.send_response(200)
430 def default_get(self):
432 self.send_response(404)
435 self.send_response(200)
437 with open(self.
file_path,
'rb')
as source:
438 self.copyfile(source, self.wfile)
444 return os.path.join(self.
doc_root, self.path[1:])
447 self._server_thread = threading.Thread(target=self.serve_forever)
448 self._server_thread.start()
454 if self._server_thread:
455 self._server_thread.join(timeout=60)
456 self._server_thread =
None 461 def __exit__(self, exc_type, exc_val, exc_tb):
468 - serves signed metadata about images 469 - receives device manifest which includes installation report if any installation has happened 472 director_subdir =
"repo/director" 474 def __init__(self, uptane_repo_root, ifc, port, client_handler_map={}):
475 super(DirectorRepo, self).__init__(os.path.join(uptane_repo_root, self.
director_subdir), ifc=ifc, port=port,
476 client_handler_map=client_handler_map)
484 def handle_manifest(self):
485 self.send_response(200)
489 data_size = int(self.headers[
'Content-Length'])
490 data_string = self.rfile.read(data_size)
491 json_data = json.loads(data_string)
492 except Exception
as exc:
496 install_report = json_data[
'signed'].get(
'installation_report',
"")
498 self.server.set_install_event(json_data)
500 handler_map = {
'PUT': {
'/manifest': handle_manifest}}
502 def set_install_event(self, manifest):
503 with self._installed_condition:
504 self._manifest = manifest
505 self._last_install_res = manifest[
'signed'][
'installation_report'][
'report'][
'result'][
'success']
506 self._installed_condition.notifyAll()
508 def wait_for_install(self, timeout=180):
509 with self._installed_condition:
510 self._installed_condition.wait(timeout=timeout)
511 return self._last_install_res
513 def get_install_result(self):
514 with self._installed_condition:
515 return self._last_install_res
517 def get_manifest(self):
518 with self._installed_condition:
519 return self._manifest
521 def get_ecu_manifest(self, ecu_serial):
522 return self.get_manifest()[
'signed'][
'ecu_version_manifests'][ecu_serial]
524 def get_ecu_manifest_filepath(self, ecu_serial):
525 return self.get_ecu_manifest(ecu_serial)[
'signed'][
'installed_image'][
'filepath']
530 This server serves signed metadata about images 531 as well as images by default (it's possible to serve images from another server by using the 'custom URI' feature) 534 image_subdir =
"repo/repo" 536 def __init__(self, uptane_repo_root, ifc, port, client_handler_map={}):
537 super(ImageRepo, self).__init__(os.path.join(uptane_repo_root, self.
image_subdir), ifc=ifc, port=port,
538 client_handler_map=client_handler_map)
543 This server serves images 545 image_subdir =
"repo/repo" 547 def __init__(self, root, ifc, port, client_handler_map={}):
548 super(CustomRepo, self).__init__(os.path.join(root, self.
image_subdir),
549 ifc=ifc, port=port, client_handler_map=client_handler_map)
554 This server serves requests from an OSTree client, i.e. emulates/mocks the treehub server 556 def __init__(self, ifc, port, client_handler_map={}):
557 self.
root = tempfile.mkdtemp()
558 super(Treehub, self).__init__(self.
root, ifc=ifc, port=port, client_handler_map=client_handler_map)
562 return super(Treehub, self).__enter__()
564 def __exit__(self, exc_type, exc_val, exc_tb):
565 super(Treehub, self).__exit__(exc_type, exc_val, exc_tb)
566 shutil.rmtree(self.
root, ignore_errors=
True)
569 def default_get(self):
571 self.send_response(404)
579 def __init__(self, number_of_failures=1, bytes_to_send_before_interruption=10, url=''):
585 def __call__(self, request_handler):
587 request_handler.send_response(200)
588 file_size = os.path.getsize(request_handler.file_path)
589 request_handler.send_header(
'Content-Length', file_size)
590 request_handler.end_headers()
592 with open(request_handler.file_path,
'rb')
as source:
594 request_handler.wfile.write(data)
598 request_handler.default_get()
600 def map(self, url=''):
606 dummy_filez = (b
'\x00\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06' +
607 b
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81\xa4\x00\x00\x00\x00' +
608 b
'\x00\x19\x33\x34\x32\x36\x31\xe5\x02\x00')
610 def __init__(self, number_of_failures=1, url='', fake_filez=False):
616 def __call__(self, request_handler):
618 request_handler.send_response(200)
619 request_handler.end_headers()
623 request_handler.wfile.write(b
'malformed image')
627 request_handler.default_get()
635 def __init__(self, number_of_failures=1, url=''):
640 def __call__(self, request_handler):
642 request_handler.send_response(200)
643 file_size = os.path.getsize(request_handler.file_path)
644 request_handler.end_headers()
646 with open(request_handler.file_path,
'rb')
as source:
648 data = source.read(1)
651 request_handler.wfile.write(data)
652 request_handler.wfile.flush()
658 request_handler.default_get()
665 def __init__(self, number_of_redirects=1, url=''):
670 def __call__(self, request_handler):
672 request_handler.send_response(301)
673 request_handler.send_header(
'Location', request_handler.server.base_url + request_handler.path)
674 request_handler.end_headers()
677 request_handler.default_get()
684 def __init__(self, number_of_failures=1):
688 def __call__(self, request_handler):
690 request_handler.send_response(200)
691 request_handler.end_headers()
692 request_handler.wfile.write(b
'{"non-uptane-json": "some-value"}')
696 request_handler.default_get()
703 def __init__(self, repo_manager_exe, server_port=0, director_port=0, image_repo_port=0, custom_repo_port=0):
715 def create_generic_server(self, **kwargs):
718 def create_director_repo(self, handler_map={}):
721 def create_image_repo(self, handler_map={}):
724 def create_custom_repo(self, handler_map={}):
732 def target_dir(self):
736 def target_file(self):
737 return path.join(self.
image_dir,
'targets.json')
739 def add_image(self, id, image_filename, target_name=None, image_size=1024, custom_url=''):
741 targetname = target_name
if target_name
else image_filename
743 with open(path.join(self.
image_dir, image_filename),
'wb')
as image_file:
744 image_file.write(urandom(image_size))
747 '--command',
'image',
'--filename', image_filename,
'--targetname', targetname,
'--hwid', id[0]]
750 image_creation_cmdline.append(
'--url')
751 image_creation_cmdline.append(custom_url)
753 subprocess.run(image_creation_cmdline, cwd=self.
image_dir, check=
True)
757 '--command',
'addtarget',
'--targetname', targetname,
758 '--hwid', id[0],
'--serial', id[1]], check=
True)
764 targets = json.load(target_file) 765 target_hash = targets["signed"][
"targets"][targetname][
"hashes"][
"sha256"]
769 def add_ostree_target(self, id, rev_hash, target_name=None, expires_within_sec=(60 * 5)):
771 target_name = rev_hash
if target_name
is None else "{}-{}".format(target_name, rev_hash)
773 '--command',
'image',
775 '--targetname', target_name,
776 '--targetsha256', rev_hash,
777 '--targetlength',
'0',
778 '--targetformat',
'OSTREE',
780 subprocess.run(image_creation_cmdline, check=
True)
782 expiration_time = time.time() + expires_within_sec
783 expiration_time_str = time.strftime(
"%Y-%m-%dT%H:%M:%SZ", time.gmtime(expiration_time))
786 '--command',
'addtarget',
788 '--targetname', target_name,
791 '--expires', expiration_time_str],
802 def __exit__(self, exc_type, exc_val, exc_tb):
803 shutil.rmtree(self.
root_dir, ignore_errors=
True)
805 def _generate_repo(self):
807 '--command',
'generate',
'--keytype',
'ED25519'], check=
True)
810 def with_aktualizr(start=True, output_logs=False, id=(
'primary-hw-ID-001', str(uuid4())), wait_timeout=60,
811 secondary_wait_sec=600, log_level=1, aktualizr_primary_exe=
'src/aktualizr_primary/aktualizr',
812 aktualizr_info_exe=
'src/aktualizr_info/aktualizr-info',
816 def wrapper(*args, ostree_mock_path=None, **kwargs):
817 aktualizr =
Aktualizr(aktualizr_primary_exe=aktualizr_primary_exe,
818 aktualizr_info_exe=aktualizr_info_exe, id=id,
819 wait_timeout=wait_timeout,
820 secondary_wait_sec=secondary_wait_sec,
821 log_level=log_level, output_logs=output_logs,
822 run_mode=run_mode, ostree_mock_path=ostree_mock_path, **kwargs)
825 result = test(*args, **kwargs, aktualizr=aktualizr)
827 result = test(*args, **kwargs, aktualizr=aktualizr)
835 def with_uptane_backend(start_generic_server=True, repo_manager_exe='src/uptane_generator/uptane-generator'):
838 def wrapper(*args, **kwargs):
839 repo_manager_exe_abs_path = path.abspath(repo_manager_exe)
841 if start_generic_server:
842 with repo.create_generic_server()
as uptane_server:
843 result = test(*args, **kwargs, uptane_repo=repo, uptane_server=uptane_server)
845 result = test(*args, **kwargs, uptane_repo=repo)
851 def with_director(start=True, handlers=[]):
854 def wrapper(*args, uptane_repo, **kwargs):
855 def func(handler_map={}):
856 director = uptane_repo.create_director_repo(handler_map=handler_map)
859 result = test(*args, **kwargs, uptane_repo=uptane_repo, director=director)
861 result = test(*args, **kwargs, uptane_repo=uptane_repo, director=director)
864 if handlers
and len(handlers) > 0:
865 for handler
in handlers:
866 result = func(handler.map(kwargs.get(
'test_path',
'')))
876 def with_imagerepo(start=True, handlers=[]):
879 def wrapper(*args, uptane_repo, **kwargs):
880 def func(handler_map={}):
881 image_repo = uptane_repo.create_image_repo(handler_map=handler_map)
884 result = test(*args, **kwargs, uptane_repo=uptane_repo, image_repo=image_repo)
886 result = test(*args, **kwargs, uptane_repo=uptane_repo, image_repo=image_repo)
889 if handlers
and len(handlers) > 0:
890 for handler
in handlers:
891 result = func(handler.map(kwargs.get(
'test_path',
'')))
901 def with_secondary(start=True, output_logs=False, id=(
'secondary-hw-ID-001',
None),
902 force_reboot=
False, arg_name=
'secondary',
903 aktualizr_secondary_exe=
'src/aktualizr_secondary/aktualizr-secondary'):
906 def wrapper(*args, **kwargs):
909 id1 = (id1[0], str(uuid4()))
910 secondary =
IPSecondary(aktualizr_secondary_exe=aktualizr_secondary_exe, output_logs=output_logs, id=id1, force_reboot=force_reboot, **kwargs)
911 sl = kwargs.get(
"secondaries", []) + [secondary]
912 kwargs.update({arg_name: secondary,
"secondaries": sl})
913 if "primary_port" not in kwargs:
914 kwargs[
"primary_port"] = secondary.primary_port
917 result = test(*args, **kwargs)
919 result = test(*args, **kwargs)
925 def with_path(paths):
928 def wrapper(*args, **kwargs):
929 for test_path
in paths:
930 result = test(*args, **kwargs, test_path=test_path)
939 def __init__(self, aktualizr, uptane_repo, images_to_install=[]):
942 for image
in images_to_install:
945 'filename': image[1],
946 'hash': uptane_repo.add_image(image[0], image[1], custom_url=image[2]
if len(image) > 2
else '')
949 def are_images_installed(self):
952 if not (image[
'hash'] == self.
aktualizr.get_current_image_info(image[
'ecu_id'])):
959 def with_install_manager(default_images=True):
962 def wrapper(*args, aktualizr, uptane_repo, secondary=None, images_to_install=[], **kwargs):
963 if default_images
and (
not images_to_install
or len(images_to_install) == 0):
964 images_to_install = [(aktualizr.id,
'primary-image.img')]
966 images_to_install.append((secondary.id,
'secondary-image.img'))
967 install_mngr =
InstallManager(aktualizr, uptane_repo, images_to_install)
968 result = test(*args, **kwargs, aktualizr=aktualizr, secondary=secondary,
969 uptane_repo=uptane_repo, install_mngr=install_mngr)
975 def with_images(images_to_install):
978 def wrapper(*args, **kwargs):
979 return test(*args, **kwargs, images_to_install=images_to_install)
984 def with_customrepo(start=True, handlers=[]):
987 def wrapper(*args, uptane_repo, **kwargs):
988 def func(handler_map={}):
989 custom_repo = uptane_repo.create_custom_repo(handler_map=handler_map)
992 result = test(*args, **kwargs, uptane_repo=uptane_repo, custom_repo=custom_repo)
994 result = test(*args, **kwargs, uptane_repo=uptane_repo, custom_repo=custom_repo)
997 if handlers
and len(handlers) > 0:
998 for handler
in handlers:
999 result = func(handler.map(kwargs.get(
'test_path',
'')))
1010 repo_path =
'ostree_repo' 1012 def __enter__(self):
1013 self.
_root = tempfile.mkdtemp()
1017 subprocess.run([
'cp',
'-r', self.
repo_path, self.
_root], check=
True)
1021 version_file.writelines([
'{}\n'.format(initial_revision),
1023 '{}\n'.format(initial_revision),
1024 '{}\n'.format(
'0')])
1028 def __exit__(self, exc_type, exc_val, exc_tb):
1029 shutil.rmtree(self.
_root, ignore_errors=
True)
1031 def get_revision(self):
1032 rev_cmd_res = subprocess.run([
'ostree',
'rev-parse',
'--repo', self.
path +
'/ostree/repo',
'generate-remote:generated'],
1033 timeout=60, check=
True, stdout=subprocess.PIPE)
1035 return rev_cmd_res.stdout.decode(
'ascii').rstrip(
'\n')
1037 def update_revision(self, rev):
1039 version_file.writelines([
'{}\n'.format(rev),
1042 '{}\n'.format(
'1')])
1045 def with_sysroot(ostree_mock_path='tests/libostree_mock.so'):
1046 def decorator(test):
1048 def wrapper(*args, **kwargs):
1050 return test(*args, **kwargs, sysroot=sysroot, ostree_mock_path=ostree_mock_path)
1055 def with_treehub(handlers=[], port=0):
1056 def decorator(test):
1058 def wrapper(*args, **kwargs):
1059 def func(handler_map={}):
1060 with
Treehub(
'localhost', port=port, client_handler_map=handler_map)
as treehub:
1061 return test(*args, **kwargs, treehub=treehub)
1063 if handlers
and len(handlers) > 0:
1064 for handler
in handlers:
1065 result = func(handler.map(kwargs.get(
'test_path',
'')))
1076 def Process(self, *args, **kwds):
1077 proc = super(NonDaemonPool, self).
Process(*args, **kwds)
1079 class NonDaemonProcess(proc.__class__):
1080 """Monkey-patch process to ensure it is never daemonized""" 1087 def daemon(self, val):
1090 proc.__class__ = NonDaemonProcess
1096 def __init__(self, tests):
1100 def __enter__(self):
1103 def __exit__(self, exc_type, exc_value, traceback):
1108 def test_runner(test):
1109 logger.info(
'>>> Running {}...'.format(test.__name__))
1110 test_run_result = test()
1111 logger.info(
'>>> {}: {}\n'.format(
'OK' if test_run_result
else 'FAILED', test.__name__))
1112 return test_run_result
1117 for result
in results:
1118 total_result = total_result
and result
def get_info(self, retry=30)
_bytes_to_send_before_interruption
def _get_current_image_info(self, ecu_id, secondary_image_hash_field='installed image hash:')
def add_secondary(self, secondary)
def get_current_primary_image_info(self)