mirror of
				https://github.com/ARM-software/workload-automation.git
				synced 2025-10-30 22:54:18 +00:00 
			
		
		
		
	Initial commit of open source Workload Automation.
This commit is contained in:
		
							
								
								
									
										16
									
								
								wlauto/modules/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								wlauto/modules/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| #    Copyright 2014-2015 ARM Limited | ||||
| # | ||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| # you may not use this file except in compliance with the License. | ||||
| # You may obtain a copy of the License at | ||||
| # | ||||
| #     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| # | ||||
| # Unless required by applicable law or agreed to in writing, software | ||||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| # See the License for the specific language governing permissions and | ||||
| # limitations under the License. | ||||
| # | ||||
|  | ||||
|  | ||||
							
								
								
									
										64
									
								
								wlauto/modules/active_cooling.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								wlauto/modules/active_cooling.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| #    Copyright 2014-2015 ARM Limited | ||||
| # | ||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| # you may not use this file except in compliance with the License. | ||||
| # You may obtain a copy of the License at | ||||
| # | ||||
| #     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| # | ||||
| # Unless required by applicable law or agreed to in writing, software | ||||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| # See the License for the specific language governing permissions and | ||||
| # limitations under the License. | ||||
| # | ||||
|  | ||||
|  | ||||
| from wlauto import Module, Parameter | ||||
| from wlauto.utils.serial_port import open_serial_connection | ||||
|  | ||||
|  | ||||
| class MbedFanActiveCooling(Module): | ||||
|  | ||||
|     name = 'mbed-fan' | ||||
|  | ||||
|     capabilities = ['active_cooling'] | ||||
|  | ||||
|     parameters = [ | ||||
|         Parameter('port', default='/dev/ttyACM0', | ||||
|                   description="""The serial port for the active cooling solution (see above)."""), | ||||
|         Parameter('buad', kind=int, default=115200, | ||||
|                   description="""Baud for the serial port (see above)."""), | ||||
|         Parameter('fan_pin', kind=int, default=0, | ||||
|                   description="""Which controller pin on the mbed the fan for the active cooling solution is | ||||
|                   connected to (controller pin 0 is physical pin 22 on the mbed)."""), | ||||
|     ] | ||||
|  | ||||
|     timeout = 30 | ||||
|  | ||||
|     def start_active_cooling(self): | ||||
|         with open_serial_connection(timeout=self.timeout, | ||||
|                                     port=self.port, | ||||
|                                     baudrate=self.buad) as target: | ||||
|             target.sendline('motor_{}_1'.format(self.fan_pin)) | ||||
|  | ||||
|     def stop_active_cooling(self): | ||||
|         with open_serial_connection(timeout=self.timeout, | ||||
|                                     port=self.port, | ||||
|                                     baudrate=self.buad) as target: | ||||
|             target.sendline('motor_{}_0'.format(self.fan_pin)) | ||||
|  | ||||
|  | ||||
| class OdroidXU3ctiveCooling(Module): | ||||
|  | ||||
|     name = 'odroidxu3-fan' | ||||
|  | ||||
|     capabilities = ['active_cooling'] | ||||
|  | ||||
|     def start_active_cooling(self): | ||||
|         self.owner.set_sysfile_value('/sys/devices/odroid_fan.15/fan_mode', 0, verify=False) | ||||
|         self.owner.set_sysfile_value('/sys/devices/odroid_fan.15/pwm_duty', 255, verify=False) | ||||
|  | ||||
|     def stop_active_cooling(self): | ||||
|         self.owner.set_sysfile_value('/sys/devices/odroid_fan.15/fan_mode', 0, verify=False) | ||||
|         self.owner.set_sysfile_value('/sys/devices/odroid_fan.15/pwm_duty', 1, verify=False) | ||||
							
								
								
									
										253
									
								
								wlauto/modules/flashing.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								wlauto/modules/flashing.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,253 @@ | ||||
| #    Copyright 2014-2015 ARM Limited | ||||
| # | ||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| # you may not use this file except in compliance with the License. | ||||
| # You may obtain a copy of the License at | ||||
| # | ||||
| #     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| # | ||||
| # Unless required by applicable law or agreed to in writing, software | ||||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| # See the License for the specific language governing permissions and | ||||
| # limitations under the License. | ||||
| # | ||||
|  | ||||
| # pylint: disable=attribute-defined-outside-init | ||||
| import os | ||||
| import time | ||||
| import tarfile | ||||
| import tempfile | ||||
| import shutil | ||||
|  | ||||
| from wlauto import Module, Parameter | ||||
| from wlauto.exceptions import ConfigError, DeviceError | ||||
| from wlauto.utils.android import fastboot_flash_partition, fastboot_command | ||||
| from wlauto.utils.serial_port import open_serial_connection | ||||
| from wlauto.utils.uefi import UefiMenu | ||||
| from wlauto.utils.types import boolean | ||||
| from wlauto.utils.misc import merge_dicts | ||||
|  | ||||
|  | ||||
| class Flasher(Module): | ||||
|     """ | ||||
|     Implements a mechanism for flashing a device. The images to be flashed can be | ||||
|     specified either as a tarball "image bundle" (in which case instructions for | ||||
|     flashing are provided as flasher-specific metadata also in the bundle), or as | ||||
|     individual image files, in which case instructions for flashing as specified | ||||
|     as part of  flashing config. | ||||
|  | ||||
|     .. note:: It is important that when resolving configuration, concrete flasher | ||||
|               implementations prioritise settings specified in the config over those | ||||
|               in the bundle (if they happen to clash). | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     capabilities = ['flash'] | ||||
|  | ||||
|     def flash(self, image_bundle=None, images=None): | ||||
|         """ | ||||
|         Flashes the specified device using the specified config. As a post condition, | ||||
|         the device must be ready to run workloads upon returning from this method (e.g. | ||||
|         it must be fully-booted into the OS). | ||||
|  | ||||
|         """ | ||||
|         raise NotImplementedError() | ||||
|  | ||||
|  | ||||
| class FastbootFlasher(Flasher): | ||||
|  | ||||
|     name = 'fastboot' | ||||
|     description = """ | ||||
|     Enables automated flashing of images using the fastboot utility. | ||||
|     To use this flasher, a set of image files to be flused are required. | ||||
|     In addition a mapping between partitions and image file is required. There are two ways | ||||
|     to specify those requirements: | ||||
|  | ||||
|     - Image mapping: In this mode, a mapping between partitions and images is given in the agenda. | ||||
|     - Image Bundle: In This mode a tarball is specified, which must contain all image files as well | ||||
|       as well as a partition file, named ``partitions.txt`` which contains the mapping between | ||||
|       partitions and images. | ||||
|  | ||||
|     The format of ``partitions.txt`` defines one mapping per line as such: :: | ||||
|  | ||||
|         kernel zImage-dtb | ||||
|         ramdisk ramdisk_image | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     delay = 0.5 | ||||
|     serial_timeout = 30 | ||||
|     partitions_file_name = 'partitions.txt' | ||||
|  | ||||
|     def flash(self, image_bundle=None, images=None): | ||||
|         self.prelude_done = False | ||||
|         to_flash = {} | ||||
|         if image_bundle:  # pylint: disable=access-member-before-definition | ||||
|             image_bundle = expand_path(image_bundle) | ||||
|             to_flash = self._bundle_to_images(image_bundle) | ||||
|         to_flash = merge_dicts(to_flash, images or {}, should_normalize=False) | ||||
|         for partition, image_path in to_flash.iteritems(): | ||||
|             self.logger.debug('flashing {}'.format(partition)) | ||||
|             self._flash_image(self.owner, partition, expand_path(image_path)) | ||||
|         fastboot_command('reboot') | ||||
|  | ||||
|     def _validate_image_bundle(self, image_bundle): | ||||
|         if not tarfile.is_tarfile(image_bundle): | ||||
|             raise ConfigError('File {} is not a tarfile'.format(image_bundle)) | ||||
|         with tarfile.open(image_bundle) as tar: | ||||
|             files = [tf.name for tf in tar.getmembers()] | ||||
|             if not any(pf in files for pf in (self.partitions_file_name, '{}/{}'.format(files[0], self.partitions_file_name))): | ||||
|                 ConfigError('Image bundle does not contain the required partition file (see documentation)') | ||||
|  | ||||
|     def _bundle_to_images(self, image_bundle): | ||||
|         """ | ||||
|         Extracts the bundle to a temporary location and creates a mapping between the contents of the bundle | ||||
|         and images to be flushed. | ||||
|         """ | ||||
|         self._validate_image_bundle(image_bundle) | ||||
|         extract_dir = tempfile.mkdtemp() | ||||
|         with tarfile.open(image_bundle) as tar: | ||||
|             tar.extractall(path=extract_dir) | ||||
|             files = [tf.name for tf in tar.getmembers()] | ||||
|             if self.partitions_file_name not in files: | ||||
|                 extract_dir = os.path.join(extract_dir, files[0]) | ||||
|         partition_file = os.path.join(extract_dir, self.partitions_file_name) | ||||
|         return get_mapping(extract_dir, partition_file) | ||||
|  | ||||
|     def _flash_image(self, device, partition, image_path): | ||||
|         if not self.prelude_done: | ||||
|             self._fastboot_prelude(device) | ||||
|         fastboot_flash_partition(partition, image_path) | ||||
|         time.sleep(self.delay) | ||||
|  | ||||
|     def _fastboot_prelude(self, device): | ||||
|         with open_serial_connection(port=device.port, | ||||
|                                     baudrate=device.baudrate, | ||||
|                                     timeout=self.serial_timeout, | ||||
|                                     init_dtr=0, | ||||
|                                     get_conn=False) as target: | ||||
|             device.reset() | ||||
|             time.sleep(self.delay) | ||||
|             target.sendline(' ') | ||||
|             time.sleep(self.delay) | ||||
|             target.sendline('fast') | ||||
|             time.sleep(self.delay) | ||||
|         self.prelude_done = True | ||||
|  | ||||
|  | ||||
| class VersatileExpressFlasher(Flasher): | ||||
|  | ||||
|     name = 'vexpress' | ||||
|  | ||||
|     parameters = [ | ||||
|         Parameter('image_name', default='Image', | ||||
|                   description='The name of the kernel image to boot.'), | ||||
|         Parameter('image_args', default=None, | ||||
|                   description='Kernel arguments with which the image will be booted.'), | ||||
|         Parameter('fdt_support', kind=boolean, default=True, | ||||
|                   description='Specifies whether the image has device tree support.'), | ||||
|         Parameter('initrd', default=None, | ||||
|                   description='If the kernel image uses an INITRD, this can be used to specify it.'), | ||||
|         Parameter('fdt_path', default=None, | ||||
|                   description='If specified, this will be set as the FDT path.'), | ||||
|     ] | ||||
|  | ||||
|     def flash(self, image_bundle=None, images=None): | ||||
|         device = self.owner | ||||
|         if not hasattr(device, 'port') or not hasattr(device, 'microsd_mount_point'): | ||||
|             msg = 'Device {} does not appear to support VExpress flashing.' | ||||
|             raise ConfigError(msg.format(device.name)) | ||||
|         with open_serial_connection(port=device.port, | ||||
|                                     baudrate=device.baudrate, | ||||
|                                     timeout=device.timeout, | ||||
|                                     init_dtr=0) as target: | ||||
|             target.sendline('usb_on')  # this will cause the MicroSD to be mounted on the host | ||||
|             device.wait_for_microsd_mount_point(target) | ||||
|             self.deploy_images(device, image_bundle, images) | ||||
|  | ||||
|         self.logger.debug('Resetting the device.') | ||||
|         device.hard_reset(target) | ||||
|  | ||||
|         with open_serial_connection(port=device.port, | ||||
|                                     baudrate=device.baudrate, | ||||
|                                     timeout=device.timeout, | ||||
|                                     init_dtr=0) as target: | ||||
|             menu = UefiMenu(target) | ||||
|             menu.open(timeout=120) | ||||
|             if menu.has_option(device.uefi_entry): | ||||
|                 self.logger.debug('Deleting existing device entry.') | ||||
|                 menu.delete_entry(device.uefi_entry) | ||||
|             self.create_uefi_enty(device, menu) | ||||
|             menu.select(device.uefi_entry) | ||||
|             target.expect(device.android_prompt, timeout=device.timeout) | ||||
|  | ||||
|     def create_uefi_enty(self, device, menu): | ||||
|         menu.create_entry(device.uefi_entry, | ||||
|                           self.image_name, | ||||
|                           self.image_args, | ||||
|                           self.fdt_support, | ||||
|                           self.initrd, | ||||
|                           self.fdt_path) | ||||
|  | ||||
|     def deploy_images(self, device, image_bundle=None, images=None): | ||||
|         try: | ||||
|             if image_bundle: | ||||
|                 self.deploy_image_bundle(device, image_bundle) | ||||
|             if images: | ||||
|                 self.overlay_images(device, images) | ||||
|             os.system('sync') | ||||
|         except (IOError, OSError), e: | ||||
|             msg = 'Could not deploy images to {}; got: {}' | ||||
|             raise DeviceError(msg.format(device.microsd_mount_point, e)) | ||||
|  | ||||
|     def deploy_image_bundle(self, device, bundle): | ||||
|         self.logger.debug('Validating {}'.format(bundle)) | ||||
|         validate_image_bundle(bundle) | ||||
|         self.logger.debug('Extracting {} into {}...'.format(bundle, device.microsd_mount_point)) | ||||
|         with tarfile.open(bundle) as tar: | ||||
|             tar.extractall(device.microsd_mount_point) | ||||
|  | ||||
|     def overlay_images(self, device, images): | ||||
|         for dest, src in images.iteritems(): | ||||
|             dest = os.path.join(device.microsd_mount_point, dest) | ||||
|             self.logger.debug('Copying {} to {}'.format(src, dest)) | ||||
|             shutil.copy(src, dest) | ||||
|  | ||||
|  | ||||
| # utility functions | ||||
|  | ||||
| def get_mapping(base_dir, partition_file): | ||||
|     mapping = {} | ||||
|     with open(partition_file) as pf: | ||||
|         for line in pf: | ||||
|             pair = line.split() | ||||
|             if len(pair) != 2: | ||||
|                 ConfigError('partitions.txt is not properly formated') | ||||
|             image_path = os.path.join(base_dir, pair[1]) | ||||
|             if not os.path.isfile(expand_path(image_path)): | ||||
|                 ConfigError('file {} was not found in the bundle or was misplaced'.format(pair[1])) | ||||
|             mapping[pair[0]] = image_path | ||||
|     return mapping | ||||
|  | ||||
|  | ||||
| def expand_path(original_path): | ||||
|     path = os.path.abspath(os.path.expanduser(original_path)) | ||||
|     if not os.path.exists(path): | ||||
|         raise ConfigError('{} does not exist.'.format(path)) | ||||
|     return path | ||||
|  | ||||
|  | ||||
| def validate_image_bundle(bundle): | ||||
|     if not tarfile.is_tarfile(bundle): | ||||
|         raise ConfigError('Image bundle {} does not appear to be a valid TAR file.'.format(bundle)) | ||||
|     with tarfile.open(bundle) as tar: | ||||
|         try: | ||||
|             tar.getmember('config.txt') | ||||
|         except KeyError: | ||||
|             try: | ||||
|                 tar.getmember('./config.txt') | ||||
|             except KeyError: | ||||
|                 msg = 'Tarball {} does not appear to be a valid image bundle (did not see config.txt).' | ||||
|                 raise ConfigError(msg.format(bundle)) | ||||
|  | ||||
							
								
								
									
										52
									
								
								wlauto/modules/reset.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								wlauto/modules/reset.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| #    Copyright 2014-2015 ARM Limited | ||||
| # | ||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| # you may not use this file except in compliance with the License. | ||||
| # You may obtain a copy of the License at | ||||
| # | ||||
| #     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| # | ||||
| # Unless required by applicable law or agreed to in writing, software | ||||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| # See the License for the specific language governing permissions and | ||||
| # limitations under the License. | ||||
| # | ||||
|  | ||||
| import time | ||||
|  | ||||
| from wlauto import Module, Parameter | ||||
| from wlauto.exceptions import DeviceError | ||||
| from wlauto.utils.netio import KshellConnection | ||||
|  | ||||
|  | ||||
| class NetioSwitchReset(Module): | ||||
|  | ||||
|     #pylint: disable=E1101 | ||||
|     name = 'netio_switch' | ||||
|     capabilities = ['reset_power'] | ||||
|  | ||||
|     parameters = [ | ||||
|         Parameter('host', default='ippowerbar', | ||||
|                   description='IP address or DNS name of the Netio power switch.'), | ||||
|         Parameter('port', kind=int, default=1234, | ||||
|                   description='Port on which KSHELL is listening.'), | ||||
|         Parameter('username', default='admin', | ||||
|                   description='User name for the administrator on the Netio.'), | ||||
|         Parameter('password', default='admin', | ||||
|                   description='User name for the administrator on the Netio.'), | ||||
|         Parameter('psu', kind=int, default=1, | ||||
|                   description='The device port number on the Netio, i.e. which ' | ||||
|                               'PSU port the device is connected to.'), | ||||
|     ] | ||||
|  | ||||
|     def hard_reset(self): | ||||
|         try: | ||||
|             conn = KshellConnection(host=self.host, port=self.port) | ||||
|             conn.login(self.username, self.password) | ||||
|             conn.disable_port(self.psu) | ||||
|             time.sleep(2) | ||||
|             conn.enable_port(self.psu) | ||||
|             conn.close() | ||||
|         except Exception as e: | ||||
|             raise DeviceError('Could not reset power: {}'.format(e)) | ||||
		Reference in New Issue
	
	Block a user