def run(self, connection, args=None): # substitutions substitutions = {'{emptyimage}': self.get_common_data('prepare-empty-image', 'output')} sub_command = self.get_common_data('prepare-qemu-commands', 'sub_command') sub_command = substitute(sub_command, substitutions) command_line = ' '.join(sub_command) commands = [] # get the download args in run() image_arg = self.data['download_action']['iso'].get('image_arg', None) action_arg = self.data['download_action']['iso'].get('file', None) substitutions["{%s}" % 'iso'] = action_arg commands.append(image_arg) command_line += ' '.join(substitute(commands, substitutions)) preseed_file = self.get_common_data('file', 'preseed') if not preseed_file: raise JobError("Unable to identify downloaded preseed filename.") substitutions = {'{preseed}': preseed_file} append_args = self.get_common_data('prepare-qemu-commands', 'append') append_args = substitute([append_args], substitutions) command_line += ' '.join(append_args) self.logger.info(command_line) shell = ShellCommand(command_line, self.timeout, logger=self.logger) if shell.exitstatus: raise JobError("%s command exited %d: %s" % (sub_command[0], shell.exitstatus, shell.readlines())) self.logger.debug("started a shell command") shell_connection = ShellSession(self.job, shell) shell_connection.prompt_str = self.get_common_data('prepare-qemu-commands', 'prompts') shell_connection = super(IsoCommandLine, self).run(shell_connection, args) return shell_connection
def run(self, connection, args=None): connection = super(UefiSubstituteCommands, self).run(connection, args) try: ip_addr = dispatcher_ip() except InfrastructureError as exc: raise RuntimeError("Unable to get dispatcher IP address: %s" % exc) substitution_dictionary = { '{SERVER_IP}': ip_addr, '{RAMDISK}': self.get_common_data('file', 'ramdisk'), '{KERNEL}': self.get_common_data('file', 'kernel'), '{DTB}': self.get_common_data('file', 'dtb'), 'TEST_MENU_NAME': "LAVA %s test image" % self.parameters['commands'] } if 'download_action' in self.data and 'nfsrootfs' in self.data[ 'download_action']: substitution_dictionary['{NFSROOTFS}'] = self.get_common_data( 'file', 'nfsroot') for item in self.items: if 'enter' in item['select']: item['select']['enter'] = substitute( [item['select']['enter']], substitution_dictionary)[0] if 'items' in item['select']: # items is already a list, so pass without wrapping in [] item['select']['items'] = substitute(item['select']['items'], substitution_dictionary) return connection
def test_uefi_job(self): self.assertIsNotNone(self.job) self.job.validate() uefi_menu = [action for action in self.job.pipeline.actions if action.name == 'uefi-menu-action'][0] selector = [action for action in uefi_menu.internal_pipeline.actions if action.name == 'uefi-menu-selector'][0] self.assertEqual( selector.selector.prompt, "Start:" ) self.assertIsInstance(selector.items, list) description_ref = pipeline_reference('mustang-uefi.yaml') self.assertEqual(description_ref, self.job.pipeline.describe(False)) # just dummy strings substitution_dictionary = { '{SERVER_IP}': '10.4.0.1', '{RAMDISK}': None, '{KERNEL}': 'uImage', '{DTB}': 'mustang.dtb', '{NFSROOTFS}': 'tmp/tmp21dfed/', '{TEST_MENU_NAME}': 'LAVA NFS Test Image' } for block in selector.items: if 'select' in block: if 'enter' in block['select']: block['select']['enter'] = substitute([block['select']['enter']], substitution_dictionary) if 'items' in block['select']: block['select']['items'] = substitute(block['select']['items'], substitution_dictionary) count = 0 check_block = [ {'items': ['Boot Manager'], 'wait': 'Choice:'}, {'items': ['Remove Boot Device Entry'], 'fallback': 'Return to Main Menu', 'wait': 'Delete entry'}, {'items': ['LAVA NFS Test Image'], 'wait': 'Choice:'}, {'items': ['Add Boot Device Entry'], 'wait': 'Select the Boot Device:'}, {'items': ['TFTP on MAC Address: 00:01:73:69:5A:EF'], 'wait': 'Get the IP address from DHCP:'}, {'enter': ['y'], 'wait': 'Get the TFTP server IP address:'}, {'enter': ['10.4.0.1'], 'wait': 'File path of the EFI Application or the kernel :'}, {'enter': ['uImage'], 'wait': 'Is an EFI Application?'}, {'enter': ['n'], 'wait': 'Boot Type:'}, {'enter': ['f'], 'wait': 'Add an initrd:'}, {'enter': ['n'], 'wait': 'Get the IP address from DHCP:'}, {'enter': ['y'], 'wait': 'Get the TFTP server IP address:'}, {'enter': ['10.4.0.1'], 'wait': 'File path of the FDT :'}, {'enter': ['mustang.dtb'], 'wait': 'Arguments to pass to the binary:'}, {'enter': ['console=ttyS0,115200 earlyprintk=uart8250-32bit,0x1c020000 debug root=/dev/nfs rw ' 'nfsroot=10.4.0.1:tmp/tmp21dfed/,tcp,hard,intr ip=dhcp'], 'wait': 'Description for this new Entry:'}, {'enter': ['LAVA NFS Test Image'], 'wait': 'Choice:'}, {'items': ['Return to main menu'], 'wait': 'Start:'}, {'items': ['LAVA NFS Test Image']}, ] for item in selector.items: self.assertEqual( item['select'], check_block[count]) count += 1
def test_uefi_job(self): self.assertIsNotNone(self.job) self.job.validate() uefi_menu = [action for action in self.job.pipeline.actions if action.name == 'uefi-menu-action'][0] selector = [action for action in uefi_menu.internal_pipeline.actions if action.name == 'uefi-menu-selector'][0] self.assertEqual( selector.selector.prompt, "Start:" ) self.assertIsInstance(selector.items, list) description_ref = self.pipeline_reference('mustang-uefi.yaml') self.assertEqual(description_ref, self.job.pipeline.describe(False)) # just dummy strings substitution_dictionary = { '{SERVER_IP}': '10.4.0.1', '{RAMDISK}': None, '{KERNEL}': 'uImage', '{DTB}': 'mustang.dtb', '{NFSROOTFS}': 'tmp/tmp21dfed/', '{TEST_MENU_NAME}': 'LAVA NFS Test Image' } for block in selector.items: if 'select' in block: if 'enter' in block['select']: block['select']['enter'] = substitute([block['select']['enter']], substitution_dictionary) if 'items' in block['select']: block['select']['items'] = substitute(block['select']['items'], substitution_dictionary) count = 0 check_block = [ {'items': ['Boot Manager'], 'wait': 'Choice:'}, {'items': ['Remove Boot Device Entry'], 'fallback': 'Return to Main Menu', 'wait': 'Delete entry'}, {'items': ['LAVA NFS Test Image'], 'wait': 'Choice:'}, {'items': ['Add Boot Device Entry'], 'wait': 'Select the Boot Device:'}, {'items': ['TFTP on MAC Address: 00:01:73:69:5A:EF'], 'wait': 'Get the IP address from DHCP:'}, {'enter': ['y'], 'wait': 'Get the TFTP server IP address:'}, {'enter': ['10.4.0.1'], 'wait': 'File path of the EFI Application or the kernel :'}, {'enter': ['uImage'], 'wait': 'Is an EFI Application?'}, {'enter': ['n'], 'wait': 'Boot Type:'}, {'enter': ['f'], 'wait': 'Add an initrd:'}, {'enter': ['n'], 'wait': 'Get the IP address from DHCP:'}, {'enter': ['y'], 'wait': 'Get the TFTP server IP address:'}, {'enter': ['10.4.0.1'], 'wait': 'File path of the FDT :'}, {'enter': ['mustang.dtb'], 'wait': 'Arguments to pass to the binary:'}, {'enter': ['console=ttyS0,115200 earlyprintk=uart8250-32bit,0x1c020000 debug root=/dev/nfs rw ' 'nfsroot=10.4.0.1:tmp/tmp21dfed/,tcp,hard,intr ip=dhcp'], 'wait': 'Description for this new Entry:'}, {'enter': ['LAVA NFS Test Image'], 'wait': 'Choice:'}, {'items': ['Return to main menu'], 'wait': 'Start:'}, {'items': ['LAVA NFS Test Image']}, ] for item in selector.items: self.assertEqual( item['select'], check_block[count]) count += 1
def run(self, connection, max_end_time, args=None): connection = super(UefiSubstituteCommands, self).run(connection, max_end_time, args) ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) substitution_dictionary = { '{SERVER_IP}': ip_addr, '{RAMDISK}': self.get_namespace_data(action='compress-ramdisk', label='file', key='ramdisk'), '{KERNEL}': self.get_namespace_data(action='download-action', label='file', key='kernel'), '{DTB}': self.get_namespace_data(action='download-action', label='file', key='dtb'), 'TEST_MENU_NAME': "LAVA %s test image" % self.parameters['commands'] } nfs_address = self.get_namespace_data(action='persistent-nfs-overlay', label='nfs_address', key='nfsroot') nfs_root = self.get_namespace_data(action='download-action', label='file', key='nfsrootfs') if nfs_root: substitution_dictionary['{NFSROOTFS}'] = self.get_namespace_data( action='extract-rootfs', label='file', key='nfsroot') substitution_dictionary['{NFS_SERVER_IP}'] = ip_addr elif nfs_address: substitution_dictionary['{NFSROOTFS}'] = nfs_address substitution_dictionary[ '{NFS_SERVER_IP}'] = self.get_namespace_data( action='persistent-nfs-overlay', label='nfs_address', key='serverip') for item in self.items: if 'enter' in item['select']: item['select']['enter'] = substitute( [item['select']['enter']], substitution_dictionary)[0] if 'items' in item['select']: # items is already a list, so pass without wrapping in [] item['select']['items'] = substitute(item['select']['items'], substitution_dictionary) return connection
def run(self, connection, args=None): """ Read data from the download action and replace in context Use common data for all values passed into the substitutions so that multiple actions can use the same code. """ # Multiple deployments would overwrite the value if parsed in the validate step. # FIXME: implement isolation for repeated steps. connection = super(BootloaderCommandOverlay, self).run(connection, args) try: ip_addr = dispatcher_ip() except InfrastructureError as exc: raise RuntimeError("Unable to get dispatcher IP address: %s" % exc) substitutions = {'{SERVER_IP}': ip_addr} substitutions['{RAMDISK}'] = self.get_common_data('file', 'ramdisk') substitutions['{KERNEL}'] = self.get_common_data('file', 'kernel') substitutions['{LAVA_MAC}'] = self.lava_mac nfs_url = self.get_common_data('nfs_url', 'nfsroot') if 'download_action' in self.data and 'nfsrootfs' in self.data[ 'download_action']: substitutions['{NFSROOTFS}'] = self.get_common_data( 'file', 'nfsroot') substitutions['{NFS_SERVER_IP}'] = ip_addr elif nfs_url: substitutions['{NFSROOTFS}'] = nfs_url substitutions['{NFS_SERVER_IP}'] = self.get_common_data( 'nfs_url', 'serverip') substitutions['{ROOT}'] = self.get_common_data( 'uuid', 'root') # UUID label, not a file substitutions['{ROOT_PART}'] = self.get_common_data( 'uuid', 'boot_part') if self.use_bootscript: script = "/script.ipxe" bootscript = self.get_common_data('tftp', 'tftp_dir') + script bootscripturi = "tftp://%s/%s" % ( ip_addr, os.path.dirname(substitutions['{KERNEL}']) + script) write_bootscript(substitute(self.commands, substitutions), bootscript) bootscript_commands = ['dhcp net0', "chain %s" % bootscripturi] self.data[self.type]['commands'] = bootscript_commands else: self.data[self.type]['commands'] = substitute( self.commands, substitutions) self.logger.debug("Parsed boot commands: %s" % '; '.join(self.data[self.type]['commands'])) return connection
def validate(self): super(FlashPyOCDAction, self).validate() boot = self.job.device['actions']['boot']['methods']['pyocd'] pyocd_binary = boot['parameters']['command'] self.errors = infrastructure_error(pyocd_binary) self.base_command = [pyocd_binary] self.base_command.extend(boot['parameters'].get('options', [])) if self.job.device['board_id'] == '0000000000': self.errors = "board_id unset" substitutions = {} self.base_command.extend(['--board', self.job.device['board_id']]) namespace = self.parameters['namespace'] for action in self.data[namespace]['download-action'].keys(): pyocd_full_command = [] image_arg = self.get_namespace_data(action='download-action', label=action, key='image_arg') action_arg = self.get_namespace_data(action='download-action', label=action, key='file') if image_arg: if not isinstance(image_arg, str): self.errors = "image_arg is not a string (try quoting it)" continue substitutions["{%s}" % action] = action_arg pyocd_full_command.extend(self.base_command) pyocd_full_command.extend( substitute([image_arg], substitutions)) self.exec_list.append(pyocd_full_command) else: pyocd_full_command.extend(self.base_command) pyocd_full_command.extend([action_arg]) self.exec_list.append(pyocd_full_command) if len(self.exec_list) < 1: self.errors = "No PyOCD command to execute"
def validate(self): super(MonitorQemuAction, self).validate() try: boot = self.job.device['actions']['boot']['methods']['qemu'] qemu_binary = which(boot['parameters']['command']) self.sub_command = [qemu_binary] self.sub_command.extend(boot['parameters'].get('options', [])) except AttributeError as exc: raise InfrastructureError(exc) except (KeyError, TypeError): self.errors = "Invalid parameters for %s" % self.name substitutions = {} commands = [] for action in self.data['download_action'].keys(): if action == 'offset' or action == 'available_loops' or action == 'uefi': continue image_arg = self.data['download_action'][action].get('image_arg', None) action_arg = self.data['download_action'][action].get('file', None) if not image_arg or not action_arg: self.errors = "Missing image_arg for %s. " % action continue substitutions["{%s}" % action] = action_arg commands.append(image_arg) self.sub_command.extend(substitute(commands, substitutions)) if not self.sub_command: self.errors = "No QEMU command to execute"
def run(self, connection, max_end_time, args=None): if 'deployment_data' not in self.parameters: return connection if self.parameters["deployment_data"].get('installer_extra_cmd', None): if self.parameters.get('os', None) == "debian_installer": add_late_command( self.get_namespace_data(action='download-action', label='preseed', key='file'), self.parameters["deployment_data"]["installer_extra_cmd"]) if self.parameters.get('os', None) == "centos_installer": ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) overlay = self.get_namespace_data(action='download-action', label='file', key='overlay') substitutions = { '{OVERLAY_URL}': 'tftp://' + ip_addr + '/' + overlay } post_command = substitute([ self.parameters["deployment_data"]["installer_extra_cmd"] ], substitutions) add_to_kickstart( self.get_namespace_data(action='download-action', label='preseed', key='file'), post_command[0]) return connection
def run(self, connection, args=None): """ Read data from the download action and replace in context Use common data for all values passed into the substitutions so that multiple actions can use the same code. """ # Multiple deployments would overwrite the value if parsed in the validate step. # FIXME: implement isolation for repeated steps. connection = super(BootloaderCommandOverlay, self).run(connection, args) try: ip_addr = dispatcher_ip() except InfrastructureError as exc: raise RuntimeError("Unable to get dispatcher IP address: %s" % exc) substitutions = { '{SERVER_IP}': ip_addr } substitutions['{PRESEED_CONFIG}'] = self.get_common_data('file', 'preseed') substitutions['{DTB}'] = self.get_common_data('file', 'dtb') substitutions['{RAMDISK}'] = self.get_common_data('file', 'ramdisk') substitutions['{KERNEL}'] = self.get_common_data('file', 'kernel') nfs_url = self.get_common_data('nfs_url', 'nfsroot') if 'download_action' in self.data and 'nfsrootfs' in self.data['download_action']: substitutions['{NFSROOTFS}'] = self.get_common_data('file', 'nfsroot') substitutions['{NFS_SERVER_IP}'] = ip_addr elif nfs_url: substitutions['{NFSROOTFS}'] = nfs_url substitutions['{NFS_SERVER_IP}'] = self.get_common_data('nfs_url', 'serverip') substitutions['{ROOT}'] = self.get_common_data('uuid', 'root') # UUID label, not a file substitutions['{ROOT_PART}'] = self.get_common_data('uuid', 'boot_part') self.data[self.type]['commands'] = substitute(self.commands, substitutions) self.logger.debug("Parsed boot commands: %s" % '; '.join(self.data[self.type]['commands'])) return connection
def validate(self): super(MonitorQemuAction, self).validate() try: boot = self.job.device['actions']['boot']['methods']['qemu'] qemu_binary = which(boot['parameters']['command']) self.sub_command = [qemu_binary] self.sub_command.extend(boot['parameters'].get('options', [])) except AttributeError as exc: raise InfrastructureError(exc) except (KeyError, TypeError): self.errors = "Invalid parameters for %s" % self.name substitutions = {} commands = [] for action in self.data['download_action'].keys(): if action == 'offset' or action == 'available_loops' or action == 'uefi': continue image_arg = self.data['download_action'][action].get( 'image_arg', None) action_arg = self.data['download_action'][action].get('file', None) if not image_arg or not action_arg: self.errors = "Missing image_arg for %s. " % action continue substitutions["{%s}" % action] = action_arg commands.append(image_arg) self.sub_command.extend(substitute(commands, substitutions)) if not self.sub_command: self.errors = "No QEMU command to execute"
def validate(self): super(CallQemuAction, self).validate() if "prompts" not in self.parameters: self.errors = "Unable to identify boot prompts from job definition." if "download_action" not in self.data: self.errors = "No download_action data" try: # FIXME: need a schema and do this inside the NewDevice with a QemuDevice class? (just for parsing) boot = self.job.device["actions"]["boot"]["methods"]["qemu"] qemu_binary = which(boot["parameters"]["command"]) self.sub_command = [qemu_binary] self.sub_command.extend(boot["parameters"].get("options", [])) # FIXME: AttributeError is an InfrastructureError in fact except (KeyError, TypeError, AttributeError): self.errors = "Invalid parameters" substitutions = {} commands = [] for action in self.data["download_action"].keys(): if action != "offset" or action != "available_loops": image_arg = self.data["download_action"][action]["image_arg"] action_arg = self.data["download_action"][action]["file"] if not image_arg or not action_arg: self.errors = "Missing image_arg for %s. " % action continue substitutions["{%s}" % action] = action_arg commands.append(image_arg) self.sub_command.extend(substitute(commands, substitutions)) if not self.sub_command: self.errors = "No QEMU command to execute"
def validate(self): super(CallQemuAction, self).validate() if 'prompts' not in self.parameters: self.errors = "Unable to identify boot prompts from job definition." if 'download_action' not in self.data: self.errors = "No download_action data" try: # FIXME: need a schema and do this inside the NewDevice with a QemuDevice class? (just for parsing) boot = self.job.device['actions']['boot']['methods']['qemu'] qemu_binary = which(boot['parameters']['command']) self.sub_command = [qemu_binary] self.sub_command.extend(boot['parameters'].get('options', [])) # FIXME: AttributeError is an InfrastructureError in fact except (KeyError, TypeError, AttributeError): self.errors = "Invalid parameters" substitutions = {} commands = [] for action in self.data['download_action'].keys(): if action != 'offset' or action != 'available_loops': image_arg = self.data['download_action'][action]['image_arg'] action_arg = self.data['download_action'][action]['file'] if not image_arg or not action_arg: self.errors = "Missing image_arg for %s. " % action continue substitutions["{%s}" % action] = action_arg commands.append(image_arg) self.sub_command.extend(substitute(commands, substitutions)) if not self.sub_command: self.errors = "No QEMU command to execute"
def test_overlay_action(self): # pylint: disable=too-many-locals parameters = { 'device_type': 'd02', 'job_name': 'grub-standard-ramdisk', 'job_timeout': '15m', 'action_timeout': '5m', 'priority': 'medium', 'output_dir': mkdtemp(), 'actions': { 'boot': { 'method': 'grub', 'commands': 'ramdisk', 'prompts': ['linaro-test', 'root@debian:~#'] }, 'deploy': { 'ramdisk': 'initrd.gz', 'kernel': 'zImage', 'dtb': 'broken.dtb' } } } device = NewDevice(os.path.join(os.path.dirname(__file__), '../devices/d02-01.yaml')) job = Job(4212, None, None, None, parameters) job.device = device pipeline = Pipeline(job=job, parameters=parameters['actions']['boot']) job.set_pipeline(pipeline) overlay = BootloaderCommandOverlay() pipeline.add_action(overlay) try: ip_addr = dispatcher_ip() except InfrastructureError as exc: raise RuntimeError("Unable to get dispatcher IP address: %s" % exc) parsed = [] kernel = parameters['actions']['deploy']['kernel'] ramdisk = parameters['actions']['deploy']['ramdisk'] dtb = parameters['actions']['deploy']['dtb'] substitution_dictionary = { '{SERVER_IP}': ip_addr, # the addresses need to be hexadecimal '{RAMDISK}': ramdisk, '{KERNEL}': kernel, '{DTB}': dtb } params = device['actions']['boot']['methods'] commands = params['grub']['ramdisk']['commands'] self.assertIn('net_bootp', commands) self.assertIn("linux (tftp,{SERVER_IP})/{KERNEL} console=ttyS0,115200 earlycon=uart8250,mmio32,0x80300000 root=/dev/ram0 ip=dhcp", commands) self.assertIn('initrd (tftp,{SERVER_IP})/{RAMDISK}', commands) self.assertIn('devicetree (tftp,{SERVER_IP})/{DTB}', commands) params['grub']['ramdisk']['commands'] = substitute(params['grub']['ramdisk']['commands'], substitution_dictionary) substituted_commands = params['grub']['ramdisk']['commands'] self.assertIs(type(substituted_commands), list) self.assertIn('net_bootp', substituted_commands) self.assertNotIn("linux (tftp,{SERVER_IP})/{KERNEL} console=ttyS0,115200 earlycon=uart8250,mmio32,0x80300000 root=/dev/ram0 ip=dhcp", substituted_commands) self.assertIn("linux (tftp,%s)/%s console=ttyS0,115200 earlycon=uart8250,mmio32,0x80300000 root=/dev/ram0 ip=dhcp" % (ip_addr, kernel), substituted_commands) self.assertNotIn('initrd (tftp,{SERVER_IP})/{RAMDISK}', parsed) self.assertNotIn('devicetree (tftp,{SERVER_IP})/{DTB}', parsed)
def run(self, connection, args=None): if self.parameters["deployment_data"].get('installer_extra_cmd', None): if self.parameters.get('os', None) == "debian_installer": add_late_command(self.data['download_action']['preseed']['file'], self.parameters["deployment_data"]["installer_extra_cmd"]) if self.parameters.get('os', None) == "centos_installer": substitutions = {} substitutions['{OVERLAY_URL}'] = 'tftp://' + dispatcher_ip() + '/' + self.get_common_data('file', 'overlay') post_command = substitute([self.parameters["deployment_data"]["installer_extra_cmd"]], substitutions) add_to_kickstart(self.data['download_action']['preseed']['file'], post_command[0])
def run(self, connection, args=None): """ Read data from the download action and replace in context Use common data for all values passed into the substitutions so that multiple actions can use the same code. """ # Multiple deployments would overwrite the value if parsed in the validate step. # FIXME: implement isolation for repeated steps. connection = super(UBootCommandOverlay, self).run(connection, args) try: ip_addr = dispatcher_ip() except InfrastructureError as exc: raise RuntimeError("Unable to get dispatcher IP address: %s" % exc) substitutions = { '{SERVER_IP}': ip_addr } kernel_addr = self.job.device['parameters'][self.parameters['type']]['kernel'] dtb_addr = self.job.device['parameters'][self.parameters['type']]['dtb'] ramdisk_addr = self.job.device['parameters'][self.parameters['type']]['ramdisk'] substitutions['{KERNEL_ADDR}'] = kernel_addr substitutions['{DTB_ADDR}'] = dtb_addr substitutions['{RAMDISK_ADDR}'] = ramdisk_addr if not self.get_common_data('tftp', 'ramdisk') and not self.get_common_data('file', 'ramdisk'): ramdisk_addr = '-' bootcommand = self.parameters['type'] if self.parameters['type'] == 'uimage': bootcommand = 'bootm' elif self.parameters['type'] == 'zimage': bootcommand = 'bootz' elif self.parameters['type'] == 'image': bootcommand = 'booti' substitutions['{BOOTX}'] = "%s %s %s %s" % ( bootcommand, kernel_addr, ramdisk_addr, dtb_addr) substitutions['{RAMDISK}'] = self.get_common_data('file', 'ramdisk') substitutions['{KERNEL}'] = self.get_common_data('file', 'kernel') substitutions['{DTB}'] = self.get_common_data('file', 'dtb') nfs_url = self.get_common_data('nfs_url', 'nfsroot') if 'download_action' in self.data and 'nfsrootfs' in self.data['download_action']: substitutions['{NFSROOTFS}'] = self.get_common_data('file', 'nfsroot') substitutions['{NFS_SERVER_IP}'] = ip_addr elif nfs_url: substitutions['{NFSROOTFS}'] = nfs_url substitutions['{NFS_SERVER_IP}'] = self.get_common_data('nfs_url', 'serverip') substitutions['{ROOT}'] = self.get_common_data('uuid', 'root') # UUID label, not a file substitutions['{ROOT_PART}'] = self.get_common_data('uuid', 'boot_part') self.data.setdefault('u-boot', {}) self.data['u-boot']['commands'] = substitute(self.commands, substitutions) self.logger.debug("Parsed boot commands: %s", '; '.join(self.data['u-boot']['commands'])) return connection
def test_overlay_action(self): # pylint: disable=too-many-locals parameters = { 'device_type': 'd02', 'job_name': 'grub-standard-ramdisk', 'job_timeout': '15m', 'action_timeout': '5m', 'priority': 'medium', 'output_dir': mkdtemp(), 'actions': { 'boot': { 'method': 'grub', 'commands': 'ramdisk', 'prompts': ['linaro-test', 'root@debian:~#'] }, 'deploy': { 'ramdisk': 'initrd.gz', 'kernel': 'zImage', 'dtb': 'broken.dtb' } } } device = NewDevice(os.path.join(os.path.dirname(__file__), '../devices/d02-01.yaml')) job = Job(4212, parameters, None) job.device = device pipeline = Pipeline(job=job, parameters=parameters['actions']['boot']) job.pipeline = pipeline overlay = BootloaderCommandOverlay() pipeline.add_action(overlay) ip_addr = dispatcher_ip(None) parsed = [] kernel = parameters['actions']['deploy']['kernel'] ramdisk = parameters['actions']['deploy']['ramdisk'] dtb = parameters['actions']['deploy']['dtb'] substitution_dictionary = { '{SERVER_IP}': ip_addr, # the addresses need to be hexadecimal '{RAMDISK}': ramdisk, '{KERNEL}': kernel, '{DTB}': dtb } params = device['actions']['boot']['methods'] commands = params['grub']['ramdisk']['commands'] self.assertIn('net_bootp', commands) self.assertIn("linux (tftp,{SERVER_IP})/{KERNEL} console=ttyS0,115200 earlycon=uart8250,mmio32,0x80300000 root=/dev/ram0 ip=dhcp", commands) self.assertIn('initrd (tftp,{SERVER_IP})/{RAMDISK}', commands) self.assertIn('devicetree (tftp,{SERVER_IP})/{DTB}', commands) params['grub']['ramdisk']['commands'] = substitute(params['grub']['ramdisk']['commands'], substitution_dictionary) substituted_commands = params['grub']['ramdisk']['commands'] self.assertIs(type(substituted_commands), list) self.assertIn('net_bootp', substituted_commands) self.assertNotIn("linux (tftp,{SERVER_IP})/{KERNEL} console=ttyS0,115200 earlycon=uart8250,mmio32,0x80300000 root=/dev/ram0 ip=dhcp", substituted_commands) self.assertIn("linux (tftp,%s)/%s console=ttyS0,115200 earlycon=uart8250,mmio32,0x80300000 root=/dev/ram0 ip=dhcp" % (ip_addr, kernel), substituted_commands) self.assertNotIn('initrd (tftp,{SERVER_IP})/{RAMDISK}', parsed) self.assertNotIn('devicetree (tftp,{SERVER_IP})/{DTB}', parsed)
def test_overlay_action(self): # pylint: disable=too-many-locals parameters = { 'device_type': 'x86', 'job_name': 'ipxe-pipeline', 'job_timeout': '15m', 'action_timeout': '5m', 'priority': 'medium', 'output_dir': mkdtemp(), 'actions': { 'boot': { 'method': 'ipxe', 'commands': 'ramdisk', 'prompts': ['linaro-test', 'root@debian:~#'] }, 'deploy': { 'ramdisk': 'initrd.gz', 'kernel': 'zImage', } } } device = NewDevice( os.path.join(os.path.dirname(__file__), '../devices/x86-01.yaml')) job = Job(4212, parameters, None) job.device = device pipeline = Pipeline(job=job, parameters=parameters['actions']['boot']) job.pipeline = pipeline overlay = BootloaderCommandOverlay() pipeline.add_action(overlay) ip_addr = dispatcher_ip(None) kernel = parameters['actions']['deploy']['kernel'] ramdisk = parameters['actions']['deploy']['ramdisk'] substitution_dictionary = { '{SERVER_IP}': ip_addr, '{RAMDISK}': ramdisk, '{KERNEL}': kernel, '{LAVA_MAC}': "00:00:00:00:00:00" } params = device['actions']['boot']['methods'] params['ipxe']['ramdisk']['commands'] = substitute( params['ipxe']['ramdisk']['commands'], substitution_dictionary) commands = params['ipxe']['ramdisk']['commands'] self.assertIs(type(commands), list) self.assertIn("dhcp net0", commands) self.assertIn( "set console console=ttyS0,115200n8 lava_mac=00:00:00:00:00:00", commands) self.assertIn("set extraargs init=/sbin/init ip=dhcp", commands) self.assertNotIn( "kernel tftp://{SERVER_IP}/{KERNEL} ${extraargs} ${console}", commands) self.assertNotIn("initrd tftp://{SERVER_IP}/{RAMDISK}", commands) self.assertIn("boot", commands)
def run(self, connection, args=None): """ Read data from the download action and replace in context Use common data for all values passed into the substitutions so that multiple actions can use the same code. """ # Multiple deployments would overwrite the value if parsed in the validate step. # FIXME: implement isolation for repeated steps. connection = super(BootloaderCommandOverlay, self).run(connection, args) try: ip_addr = dispatcher_ip() except InfrastructureError as exc: raise RuntimeError("Unable to get dispatcher IP address: %s" % exc) substitutions = { '{SERVER_IP}': ip_addr } substitutions['{RAMDISK}'] = self.get_common_data('file', 'ramdisk') substitutions['{KERNEL}'] = self.get_common_data('file', 'kernel') substitutions['{LAVA_MAC}'] = self.lava_mac nfs_url = self.get_common_data('nfs_url', 'nfsroot') if 'download_action' in self.data and 'nfsrootfs' in self.data['download_action']: substitutions['{NFSROOTFS}'] = self.get_common_data('file', 'nfsroot') substitutions['{NFS_SERVER_IP}'] = ip_addr elif nfs_url: substitutions['{NFSROOTFS}'] = nfs_url substitutions['{NFS_SERVER_IP}'] = self.get_common_data('nfs_url', 'serverip') substitutions['{ROOT}'] = self.get_common_data('uuid', 'root') # UUID label, not a file substitutions['{ROOT_PART}'] = self.get_common_data('uuid', 'boot_part') if self.use_bootscript: script = "/script.ipxe" bootscript = self.get_common_data('tftp', 'tftp_dir') + script bootscripturi = "tftp://%s/%s" % (ip_addr, os.path.dirname(substitutions['{KERNEL}']) + script) write_bootscript(substitute(self.commands, substitutions), bootscript) bootscript_commands = ['dhcp net0', "chain %s" % bootscripturi] self.data[self.type]['commands'] = bootscript_commands else: self.data[self.type]['commands'] = substitute(self.commands, substitutions) self.logger.debug("Parsed boot commands: %s", '; '.join(self.data[self.type]['commands'])) return connection
def test_substitutions(self): sub_command = [ '/usr/bin/qemu-system-x86_64', '-nographic', '-enable-kvm', '-cpu host', '-net nic,model=virtio,macaddr=52:54:00:12:34:58 -net user', '-m 2048', ' -drive format=raw,file={emptyimage} ', '-boot c' ] substitutions = {'{emptyimage}': '/tmp/tmp.00000/hd.img'} sub_command = substitute(sub_command, substitutions) self.assertNotIn('{emptyimage}', sub_command) self.assertNotIn('/tmp/tmp.00000/hd.img', sub_command) self.assertIn('/tmp/tmp.00000/hd.img', ' '.join(sub_command))
def test_overlay_action(self): # pylint: disable=too-many-locals parameters = { 'device_type': 'x86', 'job_name': 'ipxe-pipeline', 'job_timeout': '15m', 'action_timeout': '5m', 'priority': 'medium', 'output_dir': mkdtemp(), 'actions': { 'boot': { 'method': 'ipxe', 'commands': 'ramdisk', 'prompts': ['linaro-test', 'root@debian:~#'] }, 'deploy': { 'ramdisk': 'initrd.gz', 'kernel': 'zImage', } } } device = NewDevice(os.path.join(os.path.dirname(__file__), '../devices/x86-01.yaml')) job = Job(4212, None, parameters) job.device = device pipeline = Pipeline(job=job, parameters=parameters['actions']['boot']) job.set_pipeline(pipeline) overlay = BootloaderCommandOverlay() pipeline.add_action(overlay) try: ip_addr = dispatcher_ip() except InfrastructureError as exc: raise RuntimeError("Unable to get dispatcher IP address: %s" % exc) parsed = [] kernel = parameters['actions']['deploy']['kernel'] ramdisk = parameters['actions']['deploy']['ramdisk'] substitution_dictionary = { '{SERVER_IP}': ip_addr, '{RAMDISK}': ramdisk, '{KERNEL}': kernel, '{LAVA_MAC}': "00:00:00:00:00:00" } params = device['actions']['boot']['methods'] params['ipxe']['ramdisk']['commands'] = substitute(params['ipxe']['ramdisk']['commands'], substitution_dictionary) commands = params['ipxe']['ramdisk']['commands'] self.assertIs(type(commands), list) self.assertIn("dhcp net0", commands) self.assertIn("set console console=ttyS0,115200n8 lava_mac=00:00:00:00:00:00", commands) self.assertIn("set extraargs init=/sbin/init ip=dhcp", commands) self.assertNotIn("kernel tftp://{SERVER_IP}/{KERNEL} ${extraargs} ${console}", commands) self.assertNotIn("initrd tftp://{SERVER_IP}/{RAMDISK}", commands) self.assertIn("boot", commands)
def run(self, connection, args=None): """ Retrieve the decompressed image from the dispatcher by calling the tool specified by the test writer, from within the test image of the first deployment, using the device to write directly to the secondary media, without needing to cache on the device. """ connection = super(DDAction, self).run(connection, args) if 'file' not in self.data['download_action']['image']: self.logger.debug("Skipping %s - nothing downloaded") return connection decompressed_image = os.path.basename(self.data['download_action']['image']['file']) try: device_path = os.path.realpath( "/dev/disk/by-id/%s" % self.boot_params[self.parameters['device']]['uuid']) except OSError: raise JobError("Unable to find disk by id %s" % self.boot_params[self.parameters['device']]['uuid']) suffix = "%s/%s" % ("tmp", self.data['storage-deploy'].get('suffix', '')) # As the test writer can use any tool we cannot predict where the # download URL will be positioned in the download command. # Providing the download URL as a substitution option gets round this download_url = "http://%s/%s/%s" % ( dispatcher_ip(), suffix, decompressed_image ) substitutions = { '{DOWNLOAD_URL}': download_url } download_options = substitute([self.parameters['download']['options']], substitutions)[0] download_cmd = "%s %s" % ( self.parameters['download']['tool'], download_options ) dd_cmd = "dd of='%s' bs=4M" % device_path # busybox dd does not support other flags # We must ensure that the secondary media deployment has completed before handing over # the connection. Echoing the SECONDARY_DEPLOYMENT_MSG after the deployment means we # always have a constant string to match against prompt_string = connection.prompt_str connection.prompt_str = SECONDARY_DEPLOYMENT_MSG self.logger.debug("Changing prompt to %s", connection.prompt_str) connection.sendline("%s | %s ; echo %s" % (download_cmd, dd_cmd, SECONDARY_DEPLOYMENT_MSG)) self.wait(connection) if not self.valid: self.logger.error(self.errors) # set prompt back connection.prompt_str = prompt_string self.logger.debug("Changing prompt to %s", connection.prompt_str) return connection
def run(self, connection, args=None): """ Read data from the download action and replace in context Use common data for all values passed into the substitutions so that multiple actions can use the same code. """ # Multiple deployments would overwrite the value if parsed in the validate step. # FIXME: implement isolation for repeated steps. connection = super(UBootCommandOverlay, self).run(connection, args) try: ip_addr = dispatcher_ip() except InfrastructureError as exc: raise RuntimeError("Unable to get dispatcher IP address: %s" % exc) substitutions = {'{SERVER_IP}': ip_addr} kernel_addr = self.job.device['parameters'][ self.parameters['type']]['kernel'] dtb_addr = self.job.device['parameters'][ self.parameters['type']]['dtb'] ramdisk_addr = self.job.device['parameters'][ self.parameters['type']]['ramdisk'] substitutions['{KERNEL_ADDR}'] = kernel_addr substitutions['{DTB_ADDR}'] = dtb_addr substitutions['{RAMDISK_ADDR}'] = ramdisk_addr if not self.get_common_data('tftp', 'ramdisk') and not self.get_common_data( 'file', 'ramdisk'): ramdisk_addr = '-' substitutions['{BOOTX}'] = "%s %s %s %s" % ( self.parameters['type'], kernel_addr, ramdisk_addr, dtb_addr) substitutions['{RAMDISK}'] = self.get_common_data('file', 'ramdisk') substitutions['{KERNEL}'] = self.get_common_data('file', 'kernel') substitutions['{DTB}'] = self.get_common_data('file', 'dtb') if 'download_action' in self.data and 'nfsrootfs' in self.data[ 'download_action']: substitutions['{NFSROOTFS}'] = self.get_common_data( 'file', 'nfsroot') substitutions['{ROOT}'] = self.get_common_data( 'uuid', 'root') # UUID label, not a file substitutions['{ROOT_PART}'] = self.get_common_data( 'uuid', 'boot_part') self.data['u-boot']['commands'] = substitute(self.commands, substitutions) self.logger.debug("Parsed boot commands: %s" % '; '.join(self.data['u-boot']['commands'])) return connection
def run(self, connection, args=None): """ Retrieve the decompressed image from the dispatcher by calling the tool specified by the test writer, from within the test image of the first deployment, using the device to write directly to the secondary media, without needing to cache on the device. """ connection = super(DDAction, self).run(connection, args) if 'file' not in self.data['download_action']['image']: self.logger.debug("Skipping %s - nothing downloaded") return connection decompressed_image = os.path.basename( self.data['download_action']['image']['file']) try: device_path = os.path.realpath( "/dev/disk/by-id/%s" % self.boot_params[self.parameters['device']]['uuid']) except OSError: raise JobError("Unable to find disk by id %s" % self.boot_params[self.parameters['device']]['uuid']) suffix = "%s/%s" % ("tmp", self.data['storage-deploy'].get( 'suffix', '')) # As the test writer can use any tool we cannot predict where the # download URL will be positioned in the download command. # Providing the download URL as a substitution option gets round this download_url = "http://%s/%s/%s" % (dispatcher_ip(), suffix, decompressed_image) substitutions = {'{DOWNLOAD_URL}': download_url} download_options = substitute([self.parameters['download']['options']], substitutions)[0] download_cmd = "%s %s" % (self.parameters['download']['tool'], download_options) dd_cmd = "dd of='%s' bs=4M" % device_path # busybox dd does not support other flags # We must ensure that the secondary media deployment has completed before handing over # the connection. Echoing the SECONDARY_DEPLOYMENT_MSG after the deployment means we # always have a constant string to match against prompt_string = connection.prompt_str connection.prompt_str = SECONDARY_DEPLOYMENT_MSG self.logger.debug("Changing prompt to %s" % connection.prompt_str) connection.sendline("%s | %s ; echo %s" % (download_cmd, dd_cmd, SECONDARY_DEPLOYMENT_MSG)) self.wait(connection) if not self.valid: self.logger.error(self.errors) # set prompt back connection.prompt_str = prompt_string self.logger.debug("Changing prompt to %s" % connection.prompt_str) return connection
def run(self, connection, args=None): connection = super(UefiSubstituteCommands, self).run(connection, args) try: ip_addr = dispatcher_ip() except InfrastructureError as exc: raise RuntimeError("Unable to get dispatcher IP address: %s" % exc) substitution_dictionary = { '{SERVER_IP}': ip_addr, '{RAMDISK}': self.get_common_data('file', 'ramdisk'), '{KERNEL}': self.get_common_data('file', 'kernel'), '{DTB}': self.get_common_data('file', 'dtb'), 'TEST_MENU_NAME': "LAVA %s test image" % self.parameters['commands'] } if 'download_action' in self.data and 'nfsrootfs' in self.data['download_action']: substitution_dictionary['{NFSROOTFS}'] = self.get_common_data('file', 'nfsroot') for item in self.items: if 'enter' in item['select']: item['select']['enter'] = substitute([item['select']['enter']], substitution_dictionary)[0] if 'items' in item['select']: # items is already a list, so pass without wrapping in [] item['select']['items'] = substitute(item['select']['items'], substitution_dictionary) return connection
def test_substitutions(self): sub_command = [ "/usr/bin/qemu-system-x86_64", "-nographic", "-enable-kvm", "-cpu host", "-net nic,model=virtio,macaddr=52:54:00:12:34:58 -net user", "-m 2048", " -drive format=raw,file={emptyimage} ", "-boot c", ] substitutions = {"{emptyimage}": "/tmp/tmp.00000/hd.img"} sub_command = substitute(sub_command, substitutions) self.assertNotIn("{emptyimage}", sub_command) self.assertNotIn("/tmp/tmp.00000/hd.img", sub_command) self.assertIn("/tmp/tmp.00000/hd.img", " ".join(sub_command))
def run(self, connection, args=None): if self.parameters["deployment_data"].get('installer_extra_cmd', None): if self.parameters.get('os', None) == "debian_installer": add_late_command( self.data['download_action']['preseed']['file'], self.parameters["deployment_data"]["installer_extra_cmd"]) if self.parameters.get('os', None) == "centos_installer": substitutions = {} substitutions['{OVERLAY_URL}'] = 'tftp://' + dispatcher_ip( ) + '/' + self.get_common_data('file', 'overlay') post_command = substitute([ self.parameters["deployment_data"]["installer_extra_cmd"] ], substitutions) add_to_kickstart( self.data['download_action']['preseed']['file'], post_command[0])
def validate(self): super(FlashDFUAction, self).validate() try: boot = self.job.device['actions']['boot']['methods']['dfu'] dfu_binary = which(boot['parameters']['command']) self.base_command = [dfu_binary] self.base_command.extend(boot['parameters'].get('options', [])) if self.job.device['board_id'] == '0000000000': self.errors = "board_id unset" if self.job.device['usb_vendor_id'] == '0000': self.errors = 'usb_vendor_id unset' if self.job.device['usb_product_id'] == '0000': self.errors = 'usb_product_id unset' self.usb_vendor_id = self.job.device['usb_vendor_id'] self.usb_product_id = self.job.device['usb_product_id'] self.board_id = self.job.device['board_id'] self.base_command.extend(['--serial', self.board_id]) self.base_command.extend([ '--device', '%s:%s' % (self.usb_vendor_id, self.usb_product_id) ]) except AttributeError as exc: raise ConfigurationError(exc) except (KeyError, TypeError): self.errors = "Invalid parameters for %s" % self.name substitutions = {} namespace = self.parameters['namespace'] for action in self.data[namespace]['download-action'].keys(): dfu_full_command = [] image_arg = self.data[namespace]['download-action'][action].get( 'image_arg', None) action_arg = self.data[namespace]['download-action'][action].get( 'file', None) if not image_arg or not action_arg: self.errors = "Missing image_arg for %s. " % action continue if not isinstance(image_arg, str): self.errors = "image_arg is not a string (try quoting it)" continue substitutions["{%s}" % action] = action_arg dfu_full_command.extend(self.base_command) dfu_full_command.extend(substitute([image_arg], substitutions)) self.exec_list.append(dfu_full_command) if len(self.exec_list) < 1: self.errors = "No DFU command to execute"
def run(self, connection, args=None): """ Read data from the download action and replace in context Use common data for all values passed into the substitutions so that multiple actions can use the same code. """ # Multiple deployments would overwrite the value if parsed in the validate step. # FIXME: implement isolation for repeated steps. connection = super(BootloaderCommandOverlay, self).run(connection, args) try: ip_addr = dispatcher_ip() except InfrastructureError as exc: raise RuntimeError("Unable to get dispatcher IP address: %s" % exc) substitutions = {'{SERVER_IP}': ip_addr} substitutions['{PRESEED_CONFIG}'] = self.get_common_data( 'file', 'preseed') substitutions['{PRESEED_LOCAL}'] = self.get_common_data( 'file', 'preseed_local') substitutions['{DTB}'] = self.get_common_data('file', 'dtb') substitutions['{RAMDISK}'] = self.get_common_data('file', 'ramdisk') substitutions['{KERNEL}'] = self.get_common_data('file', 'kernel') nfs_url = self.get_common_data('nfs_url', 'nfsroot') if 'download_action' in self.data and 'nfsrootfs' in self.data[ 'download_action']: substitutions['{NFSROOTFS}'] = self.get_common_data( 'file', 'nfsroot') substitutions['{NFS_SERVER_IP}'] = ip_addr elif nfs_url: substitutions['{NFSROOTFS}'] = nfs_url substitutions['{NFS_SERVER_IP}'] = self.get_common_data( 'nfs_url', 'serverip') substitutions['{ROOT}'] = self.get_common_data( 'uuid', 'root') # UUID label, not a file substitutions['{ROOT_PART}'] = self.get_common_data( 'uuid', 'boot_part') self.data[self.type]['commands'] = substitute(self.commands, substitutions) self.logger.debug("Parsed boot commands: %s" % '; '.join(self.data[self.type]['commands'])) return connection
def validate(self): super(CallQemuAction, self).validate() if self.parameters[ 'method'] == 'qemu' and 'prompts' not in self.parameters: self.errors = "Unable to identify boot prompts from job definition." try: boot = self.job.device['actions']['boot']['methods']['qemu'] if 'parameters' not in boot or 'command' not in boot['parameters']: self.errors = "Invalid device configuration - missing parameters" elif not boot['parameters']['command']: self.errors = "No QEMU binary command found - missing context." qemu_binary = which(boot['parameters']['command']) self.sub_command = [qemu_binary] self.sub_command.extend(boot['parameters'].get('options', [])) self.sub_command.extend( ['%s' % item for item in boot['parameters'].get('extra', [])]) except AttributeError as exc: self.errors = "Unable to parse device options: %s %s" % ( exc, self.job.device['actions']['boot']['methods']['qemu']) except (KeyError, TypeError): self.errors = "Invalid parameters for %s" % self.name substitutions = {} commands = [] for action in self.data['download_action'].keys(): if action == 'offset' or action == 'available_loops' or action == 'uefi': continue image_arg = self.data['download_action'][action].get( 'image_arg', None) action_arg = self.data['download_action'][action].get('file', None) if not image_arg or not action_arg: self.errors = "Missing image_arg for %s. " % action continue substitutions["{%s}" % action] = action_arg commands.append(image_arg) self.sub_command.extend(substitute(commands, substitutions)) if not self.sub_command: self.errors = "No QEMU command to execute" uefi_dir = self.get_common_data('image', 'uefi_dir') if uefi_dir: self.sub_command.extend(['-L', uefi_dir, '-monitor', 'none'])
def validate(self): super(MonitorPyOCDAction, self).validate() boot = self.job.device['actions']['boot']['methods']['pyocd'] pyocd_binary = boot['parameters']['command'] self.errors = infrastructure_error(pyocd_binary) self.sub_command = ['flock -o /var/lock/lava-pyocd.lck', pyocd_binary] self.sub_command.extend(boot['parameters'].get('options', [])) if self.job.device['board_id'] == '0000000000': self.errors = "board_id unset" substitutions = {} commands = ['-b ' + self.job.device['board_id']] for action in self.data['download_action'].keys(): image_arg = self.data['download_action'][action].get('image_arg', None) action_arg = self.data['download_action'][action].get('file', None) if not image_arg or not action_arg: self.errors = "Missing image_arg for %s. " % action continue substitutions["{%s}" % action] = action_arg commands.append(image_arg) self.sub_command.extend(substitute(commands, substitutions)) if not self.sub_command: self.errors = "No PyOCD command to execute"
def validate(self): super(CallQemuAction, self).validate() if self.parameters['method'] == 'qemu' and 'prompts' not in self.parameters: self.errors = "Unable to identify boot prompts from job definition." try: boot = self.job.device['actions']['boot']['methods']['qemu'] if 'parameters' not in boot or 'command' not in boot['parameters']: self.errors = "Invalid device configuration - missing parameters" elif not boot['parameters']['command']: self.errors = "No QEMU binary command found - missing context." qemu_binary = which(boot['parameters']['command']) self.sub_command = [qemu_binary] self.sub_command.extend(boot['parameters'].get('options', [])) self.sub_command.extend( ['%s' % item for item in boot['parameters'].get('extra', [])]) except AttributeError as exc: self.errors = "Unable to parse device options: %s %s" % ( exc, self.job.device['actions']['boot']['methods']['qemu']) except (KeyError, TypeError): self.errors = "Invalid parameters for %s" % self.name substitutions = {} commands = [] for action in self.data['download_action'].keys(): if action == 'offset' or action == 'available_loops' or action == 'uefi': continue image_arg = self.data['download_action'][action].get('image_arg', None) action_arg = self.data['download_action'][action].get('file', None) if not image_arg or not action_arg: self.errors = "Missing image_arg for %s. " % action continue substitutions["{%s}" % action] = action_arg commands.append(image_arg) self.sub_command.extend(substitute(commands, substitutions)) if not self.sub_command: self.errors = "No QEMU command to execute" uefi_dir = self.get_common_data('image', 'uefi_dir') if uefi_dir: self.sub_command.extend(['-L', uefi_dir, '-monitor', 'none'])
def validate(self): super(MonitorPyOCDAction, self).validate() boot = self.job.device['actions']['boot']['methods']['pyocd'] pyocd_binary = boot['parameters']['command'] self.errors = infrastructure_error(pyocd_binary) self.sub_command = ['flock -o /var/lock/lava-pyocd.lck', pyocd_binary] self.sub_command.extend(boot['parameters'].get('options', [])) if self.job.device['board_id'] == '0000000000': self.errors = "board_id unset" substitutions = {} commands = ['-b ' + self.job.device['board_id']] for action in self.data['download_action'].keys(): image_arg = self.data['download_action'][action].get( 'image_arg', None) action_arg = self.data['download_action'][action].get('file', None) if not image_arg or not action_arg: self.errors = "Missing image_arg for %s. " % action continue substitutions["{%s}" % action] = action_arg commands.append(image_arg) self.sub_command.extend(substitute(commands, substitutions)) if not self.sub_command: self.errors = "No PyOCD command to execute"
def run(self, connection, max_end_time, args=None): """ CommandRunner expects a pexpect.spawn connection which is the return value of target.device.power_on executed by boot in the old dispatcher. In the new pipeline, the pexpect.spawn is a ShellCommand and the connection is a ShellSession. CommandRunner inside the ShellSession turns the ShellCommand into a runner which the ShellSession uses via ShellSession.run() to run commands issued *after* the device has booted. pexpect.spawn is one of the raw_connection objects for a Connection class. """ # initialise the first Connection object, a command line shell into the running QEMU. guest = self.get_namespace_data(action='apply-overlay-guest', label='guest', key='filename') # check for NFS if 'qemu-nfs' in self.methods and self.parameters.get('media', None) == 'nfs': self.logger.debug("Adding NFS arguments to kernel command line.") root_dir = self.get_namespace_data(action='extract-rootfs', label='file', key='nfsroot') self.substitutions["{NFSROOTFS}"] = root_dir params = self.methods['qemu-nfs']['parameters']['append'] # console=ttyAMA0 root=/dev/nfs nfsroot=10.3.2.1:/var/lib/lava/dispatcher/tmp/dirname,tcp,hard,intr ip=dhcp append = [ 'console=%s' % params['console'], 'root=/dev/nfs', '%s rw' % substitute([params['nfsrootargs']], self.substitutions)[0], "%s" % params['ipargs'] ] self.sub_command.append('--append') self.sub_command.append('"%s"' % ' '.join(append)) elif guest: self.logger.info("Extending command line for qcow2 test overlay") # interface is ide by default in qemu interface = self.job.device['actions']['deploy']['methods'][ 'image']['parameters']['guest'].get('interface', 'ide') self.sub_command.append( '-drive format=qcow2,file=%s,media=disk,if=%s' % (os.path.realpath(guest), interface)) # push the mount operation to the test shell pre-command to be run # before the test shell tries to execute. shell_precommand_list = [] mountpoint = self.get_namespace_data(action='test', label='results', key='lava_test_results_dir') uuid = '/dev/disk/by-uuid/%s' % self.get_namespace_data( action='apply-overlay-guest', label='guest', key='UUID') shell_precommand_list.append('mkdir %s' % mountpoint) # prepare_guestfs always uses ext2 shell_precommand_list.append('mount %s -t ext2 %s' % (uuid, mountpoint)) # debug line to show the effect of the mount operation # also allows time for kernel messages from the mount operation to be processed. shell_precommand_list.append('ls -la %s/bin/lava-test-runner' % mountpoint) self.set_namespace_data(action='test', label='lava-test-shell', key='pre-command-list', value=shell_precommand_list) self.logger.info("Boot command: %s", ' '.join(self.sub_command)) shell = ShellCommand(' '.join(self.sub_command), self.timeout, logger=self.logger) if shell.exitstatus: raise JobError( "%s command exited %d: %s" % (self.sub_command, shell.exitstatus, shell.readlines())) self.logger.debug("started a shell command") shell_connection = ShellSession(self.job, shell) shell_connection = super(CallQemuAction, self).run(shell_connection, max_end_time, args) self.set_namespace_data(action='shared', label='shared', key='connection', value=shell_connection) return shell_connection
def test_qemu_nfs(self): self.assertIsNotNone(self.job) description_ref = self.pipeline_reference('qemu-nfs.yaml') self.assertEqual(description_ref, self.job.pipeline.describe(False)) boot = [ action for action in self.job.pipeline.actions if action.name == 'boot-image-retry' ][0] qemu = [ action for action in boot.internal_pipeline.actions if action.name == 'boot-qemu-image' ][0] execute = [ action for action in qemu.internal_pipeline.actions if action.name == 'execute-qemu' ][0] self.job.validate() self.assertNotEqual([], [ line for line in execute.sub_command if line.startswith('-kernel') ]) self.assertEqual( 1, len([ line for line in execute.sub_command if line.startswith('-kernel') ])) self.assertIn('vmlinuz', [ line for line in execute.sub_command if line.startswith('-kernel') ][0]) self.assertNotEqual([], [ line for line in execute.sub_command if line.startswith('-initrd') ]) self.assertEqual( 1, len([ line for line in execute.sub_command if line.startswith('-initrd') ])) self.assertIn('initrd.img', [ line for line in execute.sub_command if line.startswith('-initrd') ][0]) self.assertEqual( [], [line for line in execute.sub_command if '/dev/nfs' in line]) self.assertEqual( [], [line for line in execute.sub_command if 'nfsroot' in line]) args = execute.methods['qemu-nfs']['parameters']['append'][ 'nfsrootargs'] self.assertIn('{NFS_SERVER_IP}', args) self.assertIn('{NFSROOTFS}', args) substitutions = execute.substitutions substitutions["{NFSROOTFS}"] = 'root_dir' params = execute.methods['qemu-nfs']['parameters']['append'] # console=ttyAMA0 root=/dev/nfs nfsroot=10.3.2.1:/var/lib/lava/dispatcher/tmp/dirname,tcp,hard,intr ip=dhcp append = [ 'console=%s' % params['console'], 'root=/dev/nfs', '%s' % substitute([params['nfsrootargs']], substitutions)[0], "%s" % params['ipargs'] ] execute.sub_command.append('--append') execute.sub_command.append('"%s"' % ' '.join(append)) kernel_cmdline = ' '.join(execute.sub_command) self.assertIn('console=ttyAMA0', kernel_cmdline) self.assertIn('/dev/nfs', kernel_cmdline) self.assertIn('root_dir,tcp,hard,intr', kernel_cmdline) self.assertIn('smp', kernel_cmdline) self.assertIn('cortex-a57', kernel_cmdline)
def test_overlay_action(self): # pylint: disable=too-many-locals parameters = { 'device_type': 'beaglebone-black', 'job_name': 'uboot-pipeline', 'job_timeout': '15m', 'action_timeout': '5m', 'priority': 'medium', 'output_dir': mkdtemp(), 'actions': { 'boot': { 'method': 'u-boot', 'commands': 'ramdisk', 'type': 'bootz', 'prompts': ['linaro-test', 'root@debian:~#'] }, 'deploy': { 'ramdisk': 'initrd.gz', 'kernel': 'zImage', 'dtb': 'broken.dtb' } } } device = NewDevice(os.path.join(os.path.dirname(__file__), '../devices/bbb-01.yaml')) job = Job(4212, None, parameters) job.device = device pipeline = Pipeline(job=job, parameters=parameters['actions']['boot']) job.set_pipeline(pipeline) overlay = UBootCommandOverlay() pipeline.add_action(overlay) try: ip_addr = dispatcher_ip() except InfrastructureError as exc: raise RuntimeError("Unable to get dispatcher IP address: %s" % exc) parsed = [] kernel_addr = job.device['parameters'][overlay.parameters['type']]['ramdisk'] ramdisk_addr = job.device['parameters'][overlay.parameters['type']]['ramdisk'] dtb_addr = job.device['parameters'][overlay.parameters['type']]['dtb'] kernel = parameters['actions']['deploy']['kernel'] ramdisk = parameters['actions']['deploy']['ramdisk'] dtb = parameters['actions']['deploy']['dtb'] substitution_dictionary = { '{SERVER_IP}': ip_addr, # the addresses need to be hexadecimal '{KERNEL_ADDR}': kernel_addr, '{DTB_ADDR}': dtb_addr, '{RAMDISK_ADDR}': ramdisk_addr, '{BOOTX}': "%s %s %s %s" % ( overlay.parameters['type'], kernel_addr, ramdisk_addr, dtb_addr), '{RAMDISK}': ramdisk, '{KERNEL}': kernel, '{DTB}': dtb } params = device['actions']['boot']['methods'] params['u-boot']['ramdisk']['commands'] = substitute(params['u-boot']['ramdisk']['commands'], substitution_dictionary) commands = params['u-boot']['ramdisk']['commands'] self.assertIs(type(commands), list) self.assertIn("setenv loadkernel 'tftp ${kernel_addr_r} zImage'", commands) self.assertIn("setenv loadinitrd 'tftp ${initrd_addr_r} initrd.gz; setenv initrd_size ${filesize}'", commands) self.assertIn("setenv loadfdt 'tftp ${fdt_addr_r} broken.dtb'", commands) self.assertNotIn("setenv kernel_addr_r '{KERNEL_ADDR}'", commands) self.assertNotIn("setenv initrd_addr_r '{RAMDISK_ADDR}'", commands) self.assertNotIn("setenv fdt_addr_r '{DTB_ADDR}'", commands) for line in params['u-boot']['ramdisk']['commands']: line = line.replace('{SERVER_IP}', ip_addr) # the addresses need to be hexadecimal line = line.replace('{KERNEL_ADDR}', kernel_addr) line = line.replace('{DTB_ADDR}', dtb_addr) line = line.replace('{RAMDISK_ADDR}', ramdisk_addr) line = line.replace('{BOOTX}', "%s %s %s %s" % ( overlay.parameters['type'], kernel_addr, ramdisk_addr, dtb_addr)) line = line.replace('{RAMDISK}', ramdisk) line = line.replace('{KERNEL}', kernel) line = line.replace('{DTB}', dtb) parsed.append(line) self.assertIn("setenv loadkernel 'tftp ${kernel_addr_r} zImage'", parsed) self.assertIn("setenv loadinitrd 'tftp ${initrd_addr_r} initrd.gz; setenv initrd_size ${filesize}'", parsed) self.assertIn("setenv loadfdt 'tftp ${fdt_addr_r} broken.dtb'", parsed) self.assertNotIn("setenv kernel_addr_r '{KERNEL_ADDR}'", parsed) self.assertNotIn("setenv initrd_addr_r '{RAMDISK_ADDR}'", parsed) self.assertNotIn("setenv fdt_addr_r '{DTB_ADDR}'", parsed)
def run(self, connection, max_end_time, args=None): """ Read data from the download action and replace in context Use common data for all values passed into the substitutions so that multiple actions can use the same code. """ # Multiple deployments would overwrite the value if parsed in the validate step. # FIXME: implement isolation for repeated steps. connection = super(BootloaderCommandOverlay, self).run(connection, max_end_time, args) ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) substitutions = { '{SERVER_IP}': ip_addr, '{PRESEED_CONFIG}': self.get_namespace_data(action='download-action', label='file', key='preseed'), '{PRESEED_LOCAL}': self.get_namespace_data(action='compress-ramdisk', label='file', key='preseed_local'), '{DTB}': self.get_namespace_data(action='download-action', label='file', key='dtb'), '{RAMDISK}': self.get_namespace_data(action='compress-ramdisk', label='file', key='ramdisk'), '{KERNEL}': self.get_namespace_data(action='download-action', label='file', key='kernel'), '{LAVA_MAC}': self.lava_mac } self.bootcommand = self.get_namespace_data( action='uboot-prepare-kernel', label='bootcommand', key='bootcommand') if not self.bootcommand: if 'type' in self.parameters: self.logger.warning( "Using type from the boot action as the boot-command. " "Declaring a kernel type in the deploy is preferred.") self.bootcommand = self.parameters['type'] prepared_kernel = self.get_namespace_data(action='prepare-kernel', label='file', key='kernel') if prepared_kernel: self.logger.info("Using kernel file from prepare-kernel: %s", prepared_kernel) substitutions['{KERNEL}'] = prepared_kernel if self.bootcommand: kernel_addr = self.job.device['parameters'][ self.bootcommand]['kernel'] dtb_addr = self.job.device['parameters'][self.bootcommand]['dtb'] ramdisk_addr = self.job.device['parameters'][ self.bootcommand]['ramdisk'] if not self.get_namespace_data(action='tftp-deploy', label='tftp', key='ramdisk') \ and not self.get_namespace_data(action='download-action', label='file', key='ramdisk'): ramdisk_addr = '-' add_header = self.job.device['actions']['deploy'][ 'parameters'].get('add_header', None) if self.method == 'u-boot' and not add_header == "u-boot": self.logger.debug( "No u-boot header, not passing ramdisk to bootX cmd") ramdisk_addr = '-' substitutions['{BOOTX}'] = "%s %s %s %s" % ( self.bootcommand, kernel_addr, ramdisk_addr, dtb_addr) substitutions['{KERNEL_ADDR}'] = kernel_addr substitutions['{DTB_ADDR}'] = dtb_addr substitutions['{RAMDISK_ADDR}'] = ramdisk_addr self.results = { 'kernel_addr': kernel_addr, 'dtb_addr': dtb_addr, 'ramdisk_addr': ramdisk_addr } nfs_address = self.get_namespace_data(action='persistent-nfs-overlay', label='nfs_address', key='nfsroot') nfs_root = self.get_namespace_data(action='download-action', label='file', key='nfsrootfs') if nfs_root: substitutions['{NFSROOTFS}'] = self.get_namespace_data( action='extract-rootfs', label='file', key='nfsroot') substitutions['{NFS_SERVER_IP}'] = ip_addr elif nfs_address: substitutions['{NFSROOTFS}'] = nfs_address substitutions['{NFS_SERVER_IP}'] = self.get_namespace_data( action='persistent-nfs-overlay', label='nfs_address', key='serverip') substitutions['{ROOT}'] = self.get_namespace_data( action='uboot-from-media', label='uuid', key='root') # UUID label, not a file substitutions['{ROOT_PART}'] = self.get_namespace_data( action='uboot-from-media', label='uuid', key='boot_part') if self.use_bootscript: script = "/script.ipxe" bootscript = self.get_namespace_data( action='tftp-deploy', label='tftp', key='tftp_dir') + script bootscripturi = "tftp://%s/%s" % ( ip_addr, os.path.dirname(substitutions['{KERNEL}']) + script) write_bootscript(substitute(self.commands, substitutions), bootscript) bootscript_commands = ['dhcp net0', "chain %s" % bootscripturi] self.set_namespace_data(action=self.name, label=self.method, key='commands', value=bootscript_commands) self.logger.info("Parsed boot commands: %s", '; '.join(bootscript_commands)) return connection subs = substitute(self.commands, substitutions) self.set_namespace_data(action='bootloader-overlay', label=self.method, key='commands', value=subs) self.logger.info("Parsed boot commands: %s", '; '.join(subs)) return connection
def run(self, connection, max_end_time, args=None): # pylint: disable=too-many-locals """ Retrieve the decompressed image from the dispatcher by calling the tool specified by the test writer, from within the test image of the first deployment, using the device to write directly to the secondary media, without needing to cache on the device. """ connection = super(DDAction, self).run(connection, max_end_time, args) d_file = self.get_namespace_data(action='download-action', label='image', key='file') if not d_file: self.logger.debug("Skipping %s - nothing downloaded") return connection decompressed_image = os.path.basename(d_file) try: device_path = os.path.realpath( "/dev/disk/by-id/%s" % self.boot_params[self.parameters['device']]['uuid']) except OSError: raise JobError("Unable to find disk by id %s" % self.boot_params[self.parameters['device']]['uuid']) storage_suffix = self.get_namespace_data(action='storage-deploy', label='storage', key='suffix') if not storage_suffix: storage_suffix = '' suffix = "%s/%s" % ("tmp", storage_suffix) # As the test writer can use any tool we cannot predict where the # download URL will be positioned in the download command. # Providing the download URL as a substitution option gets round this ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) download_url = "http://%s/%s/%s" % ( ip_addr, suffix, decompressed_image ) substitutions = { '{DOWNLOAD_URL}': download_url } download_options = substitute([self.parameters['download']['options']], substitutions)[0] download_cmd = "%s %s" % ( self.parameters['download']['tool'], download_options ) dd_cmd = "dd of='%s' bs=4M" % device_path # busybox dd does not support other flags # set prompt to download prompt to ensure that the secondary deployment has started prompt_string = connection.prompt_str connection.prompt_str = self.parameters['download']['prompt'] self.logger.debug("Changing prompt to %s", connection.prompt_str) connection.sendline("%s | %s ; echo %s" % (download_cmd, dd_cmd, SECONDARY_DEPLOYMENT_MSG)) self.wait(connection) if not self.valid: self.logger.error(self.errors) # We must ensure that the secondary media deployment has completed before handing over # the connection. Echoing the SECONDARY_DEPLOYMENT_MSG after the deployment means we # always have a constant string to match against connection.prompt_str = SECONDARY_DEPLOYMENT_MSG self.logger.debug("Changing prompt to %s", connection.prompt_str) self.wait(connection) # set prompt back once secondary deployment is complete connection.prompt_str = prompt_string self.logger.debug("Changing prompt to %s", connection.prompt_str) self.set_namespace_data(action='shared', label='shared', key='connection', value=connection) return connection
def test_substitutions(self): """ Test substitution of secondary media values into u-boot commands Unlike most u-boot calls, removable knows in advance all the values it needs to substitute into the boot commands for the secondary deployment as these are fixed by the device config and the image details from the job submission. """ job_parser = JobParser() cubie = NewDevice( os.path.join(os.path.dirname(__file__), '../devices/cubie1.yaml')) sample_job_file = os.path.join( os.path.dirname(__file__), 'sample_jobs/cubietruck-removable.yaml') with open(sample_job_file) as sample_job_data: job = job_parser.parse(sample_job_data, cubie, 4212, None, None, None, output_dir='/tmp/') job.validate() boot_params = [ methods for methods in job.parameters['actions'] if 'boot' in methods.keys() ][1]['boot'] self.assertIn('ramdisk', boot_params) self.assertIn('kernel', boot_params) self.assertIn('dtb', boot_params) self.assertIn('root_uuid', boot_params) self.assertIn('boot_part', boot_params) self.assertIn('type', boot_params) self.assertGreater(len(job.pipeline.actions), 1) self.assertIsNotNone(job.pipeline.actions[1].internal_pipeline) u_boot_action = [ action for action in job.pipeline.actions if action.name == 'uboot-action' ][1].internal_pipeline.actions[2] self.assertIsNotNone(u_boot_action.get_common_data('u-boot', 'device')) self.assertEqual(u_boot_action.name, "uboot-overlay") methods = cubie['actions']['boot']['methods'] self.assertIn('u-boot', methods) self.assertIn('usb', methods['u-boot']) self.assertIn('commands', methods['u-boot']['usb']) commands_list = methods['u-boot']['usb']['commands'] device_id = u_boot_action.get_common_data('u-boot', 'device') substitutions = { '{BOOTX}': "%s %s %s %s" % ( u_boot_action.parameters['type'], cubie['parameters'][ u_boot_action.parameters['type']]['kernel'], cubie['parameters'][u_boot_action.parameters['type']] ['ramdisk'], cubie['parameters'][u_boot_action.parameters['type']]['dtb'], ), '{RAMDISK}': boot_params['ramdisk'], '{KERNEL}': boot_params['kernel'], '{DTB}': boot_params['dtb'], '{ROOT}': boot_params['root_uuid'], '{ROOT_PART}': "%s:%s" % (cubie['parameters']['media']['usb'][device_id]['device_id'], u_boot_action.parameters['boot_part']) } self.assertEqual('bootz 0x42000000 0x43300000 0x43000000', substitutions['{BOOTX}']) self.assertEqual('/boot/initrd.img-3.16.0-4-armmp-lpae.u-boot', substitutions['{RAMDISK}']) commands = substitute(commands_list, substitutions) self.assertEqual(commands, [ 'usb start', 'usb info', 'setenv autoload no', "setenv initrd_high '0xffffffff'", "setenv fdt_high '0xffffffff'", 'setenv initrd_addr_r ${ramdisk_addr_r}', "setenv loadkernel 'load usb 0:1 ${kernel_addr_r} /boot/vmlinuz-3.16.0-4-armmp-lpae'", "setenv loadinitrd 'load usb 0:1 ${initrd_addr_r} /boot/initrd.img-3.16.0-4-armmp-lpae.u-boot; setenv initrd_size ${filesize}'", "setenv loadfdt 'load usb 0:1 ${fdt_addr_r} /boot/dtb-3.16.0-4-armmp-lpae'", "setenv bootargs 'console=ttyS0,115200n8 root=UUID=159d17cc-697c-4125-95a0-a3775e1deabe ip=dhcp'", "setenv bootcmd 'run loadkernel; run loadinitrd; run loadfdt; bootz 0x42000000 0x43300000 0x43000000'", 'boot' ])
def test_overlay_action(self): # pylint: disable=too-many-locals parameters = { 'device_type': 'beaglebone-black', 'job_name': 'uboot-pipeline', 'job_timeout': '15m', 'action_timeout': '5m', 'priority': 'medium', 'output_dir': mkdtemp(), 'actions': { 'boot': { 'method': 'u-boot', 'commands': 'ramdisk', 'type': 'bootz', 'prompts': ['linaro-test', 'root@debian:~#'] }, 'deploy': { 'ramdisk': 'initrd.gz', 'kernel': 'zImage', 'dtb': 'broken.dtb' } } } device = NewDevice( os.path.join(os.path.dirname(__file__), '../devices/bbb-01.yaml')) job = Job(4212, parameters, None) job.device = device pipeline = Pipeline(job=job, parameters=parameters['actions']['boot']) job.pipeline = pipeline overlay = BootloaderCommandOverlay() pipeline.add_action(overlay) ip_addr = dispatcher_ip(None) parsed = [] kernel_addr = job.device['parameters'][ overlay.parameters['type']]['ramdisk'] ramdisk_addr = job.device['parameters'][ overlay.parameters['type']]['ramdisk'] dtb_addr = job.device['parameters'][overlay.parameters['type']]['dtb'] kernel = parameters['actions']['deploy']['kernel'] ramdisk = parameters['actions']['deploy']['ramdisk'] dtb = parameters['actions']['deploy']['dtb'] substitution_dictionary = { '{SERVER_IP}': ip_addr, # the addresses need to be hexadecimal '{KERNEL_ADDR}': kernel_addr, '{DTB_ADDR}': dtb_addr, '{RAMDISK_ADDR}': ramdisk_addr, '{BOOTX}': "%s %s %s %s" % (overlay.parameters['type'], kernel_addr, ramdisk_addr, dtb_addr), '{RAMDISK}': ramdisk, '{KERNEL}': kernel, '{DTB}': dtb } params = device['actions']['boot']['methods'] params['u-boot']['ramdisk']['commands'] = substitute( params['u-boot']['ramdisk']['commands'], substitution_dictionary) commands = params['u-boot']['ramdisk']['commands'] self.assertIs(type(commands), list) self.assertIn("setenv loadkernel 'tftp ${kernel_addr_r} zImage'", commands) self.assertIn( "setenv loadinitrd 'tftp ${initrd_addr_r} initrd.gz; setenv initrd_size ${filesize}'", commands) self.assertIn("setenv loadfdt 'tftp ${fdt_addr_r} broken.dtb'", commands) self.assertNotIn("setenv kernel_addr_r '{KERNEL_ADDR}'", commands) self.assertNotIn("setenv initrd_addr_r '{RAMDISK_ADDR}'", commands) self.assertNotIn("setenv fdt_addr_r '{DTB_ADDR}'", commands) for line in params['u-boot']['ramdisk']['commands']: line = line.replace('{SERVER_IP}', ip_addr) # the addresses need to be hexadecimal line = line.replace('{KERNEL_ADDR}', kernel_addr) line = line.replace('{DTB_ADDR}', dtb_addr) line = line.replace('{RAMDISK_ADDR}', ramdisk_addr) line = line.replace( '{BOOTX}', "%s %s %s %s" % (overlay.parameters['type'], kernel_addr, ramdisk_addr, dtb_addr)) line = line.replace('{RAMDISK}', ramdisk) line = line.replace('{KERNEL}', kernel) line = line.replace('{DTB}', dtb) parsed.append(line) self.assertIn("setenv loadkernel 'tftp ${kernel_addr_r} zImage'", parsed) self.assertIn( "setenv loadinitrd 'tftp ${initrd_addr_r} initrd.gz; setenv initrd_size ${filesize}'", parsed) self.assertIn("setenv loadfdt 'tftp ${fdt_addr_r} broken.dtb'", parsed) self.assertNotIn("setenv kernel_addr_r '{KERNEL_ADDR}'", parsed) self.assertNotIn("setenv initrd_addr_r '{RAMDISK_ADDR}'", parsed) self.assertNotIn("setenv fdt_addr_r '{DTB_ADDR}'", parsed)
def test_overlay_action(self): # pylint: disable=too-many-locals parameters = { "device_type": "beaglebone-black", "job_name": "uboot-pipeline", "job_timeout": "15m", "action_timeout": "5m", "priority": "medium", "output_dir": mkdtemp(), "actions": { "boot": {"method": "u-boot", "commands": "ramdisk", "type": "bootz"}, "deploy": {"ramdisk": "initrd.gz", "kernel": "zImage", "dtb": "broken.dtb"}, }, } device = NewDevice(os.path.join(os.path.dirname(__file__), "../devices/bbb-01.yaml")) job = Job(4212, None, parameters) job.device = device pipeline = Pipeline(job=job, parameters=parameters["actions"]["boot"]) job.set_pipeline(pipeline) overlay = UBootCommandOverlay() pipeline.add_action(overlay) try: ip_addr = dispatcher_ip() except InfrastructureError as exc: raise RuntimeError("Unable to get dispatcher IP address: %s" % exc) parsed = [] kernel_addr = job.device["parameters"][overlay.parameters["type"]]["ramdisk"] ramdisk_addr = job.device["parameters"][overlay.parameters["type"]]["ramdisk"] dtb_addr = job.device["parameters"][overlay.parameters["type"]]["dtb"] kernel = parameters["actions"]["deploy"]["kernel"] ramdisk = parameters["actions"]["deploy"]["ramdisk"] dtb = parameters["actions"]["deploy"]["dtb"] substitution_dictionary = { "{SERVER_IP}": ip_addr, # the addresses need to be hexadecimal "{KERNEL_ADDR}": kernel_addr, "{DTB_ADDR}": dtb_addr, "{RAMDISK_ADDR}": ramdisk_addr, "{BOOTX}": "%s %s %s %s" % (overlay.parameters["type"], kernel_addr, ramdisk_addr, dtb_addr), "{RAMDISK}": ramdisk, "{KERNEL}": kernel, "{DTB}": dtb, } params = device["actions"]["boot"]["methods"] params["u-boot"]["ramdisk"]["commands"] = substitute( params["u-boot"]["ramdisk"]["commands"], substitution_dictionary ) commands = params["u-boot"]["ramdisk"]["commands"] self.assertIs(type(commands), list) self.assertIn("setenv loadkernel 'tftp ${kernel_addr_r} zImage'", commands) self.assertIn("setenv loadinitrd 'tftp ${initrd_addr_r} initrd.gz; setenv initrd_size ${filesize}'", commands) self.assertIn("setenv loadfdt 'tftp ${fdt_addr_r} broken.dtb'", commands) self.assertNotIn("setenv kernel_addr_r '{KERNEL_ADDR}'", commands) self.assertNotIn("setenv initrd_addr_r '{RAMDISK_ADDR}'", commands) self.assertNotIn("setenv fdt_addr_r '{DTB_ADDR}'", commands) for line in params["u-boot"]["ramdisk"]["commands"]: line = line.replace("{SERVER_IP}", ip_addr) # the addresses need to be hexadecimal line = line.replace("{KERNEL_ADDR}", kernel_addr) line = line.replace("{DTB_ADDR}", dtb_addr) line = line.replace("{RAMDISK_ADDR}", ramdisk_addr) line = line.replace( "{BOOTX}", "%s %s %s %s" % (overlay.parameters["type"], kernel_addr, ramdisk_addr, dtb_addr) ) line = line.replace("{RAMDISK}", ramdisk) line = line.replace("{KERNEL}", kernel) line = line.replace("{DTB}", dtb) parsed.append(line) self.assertIn("setenv loadkernel 'tftp ${kernel_addr_r} zImage'", parsed) self.assertIn("setenv loadinitrd 'tftp ${initrd_addr_r} initrd.gz; setenv initrd_size ${filesize}'", parsed) self.assertIn("setenv loadfdt 'tftp ${fdt_addr_r} broken.dtb'", parsed) self.assertNotIn("setenv kernel_addr_r '{KERNEL_ADDR}'", parsed) self.assertNotIn("setenv initrd_addr_r '{RAMDISK_ADDR}'", parsed) self.assertNotIn("setenv fdt_addr_r '{DTB_ADDR}'", parsed)
def test_substitutions(self): """ Test substitution of secondary media values into u-boot commands Unlike most u-boot calls, removable knows in advance all the values it needs to substitute into the boot commands for the secondary deployment as these are fixed by the device config and the image details from the job submission. """ job_parser = JobParser() cubie = NewDevice(os.path.join(os.path.dirname(__file__), "../devices/cubie1.yaml")) sample_job_file = os.path.join(os.path.dirname(__file__), "sample_jobs/cubietruck-removable.yaml") sample_job_data = open(sample_job_file) job = job_parser.parse(sample_job_data, cubie, 4212, None, output_dir="/tmp/") job.validate() boot_params = [methods for methods in job.parameters["actions"] if "boot" in methods.keys()][0]["boot"] self.assertIn("ramdisk", boot_params) self.assertIn("kernel", boot_params) self.assertIn("dtb", boot_params) self.assertIn("root_uuid", boot_params) self.assertIn("boot_part", boot_params) self.assertIn("type", boot_params) self.assertGreater(len(job.pipeline.actions), 1) self.assertIsNotNone(job.pipeline.actions[1].internal_pipeline) u_boot_action = job.pipeline.actions[1].internal_pipeline.actions[1] self.assertIsNotNone(u_boot_action.get_common_data("u-boot", "device")) self.assertEqual(u_boot_action.name, "uboot-overlay") methods = cubie["actions"]["boot"]["methods"] self.assertIn("u-boot", methods) self.assertIn("usb", methods["u-boot"]) self.assertIn("commands", methods["u-boot"]["usb"]) commands_list = methods["u-boot"]["usb"]["commands"] device_id = u_boot_action.get_common_data("u-boot", "device") substitutions = { "{BOOTX}": "%s %s %s %s" % ( u_boot_action.parameters["type"], cubie["parameters"][u_boot_action.parameters["type"]]["kernel"], cubie["parameters"][u_boot_action.parameters["type"]]["ramdisk"], cubie["parameters"][u_boot_action.parameters["type"]]["dtb"], ), "{RAMDISK}": boot_params["ramdisk"], "{KERNEL}": boot_params["kernel"], "{DTB}": boot_params["dtb"], "{ROOT}": boot_params["root_uuid"], "{ROOT_PART}": "%s:%s" % (cubie["parameters"]["media"]["usb"][device_id]["device_id"], u_boot_action.parameters["boot_part"]), } self.assertEqual("bootz 0x42000000 0x43300000 0x43000000", substitutions["{BOOTX}"]) self.assertEqual("/boot/initrd.img-3.16.0-4-armmp-lpae.u-boot", substitutions["{RAMDISK}"]) commands = substitute(commands_list, substitutions) self.assertEqual( commands, [ "usb start", "usb info", "setenv autoload no", "setenv initrd_high '0xffffffff'", "setenv fdt_high '0xffffffff'", "setenv initrd_addr_r ${ramdisk_addr_r}", "setenv loadkernel 'load usb 0:1 ${kernel_addr_r} /boot/vmlinuz-3.16.0-4-armmp-lpae'", "setenv loadinitrd 'load usb 0:1 ${initrd_addr_r} /boot/initrd.img-3.16.0-4-armmp-lpae.u-boot; setenv initrd_size ${filesize}'", "setenv loadfdt 'load usb 0:1 ${fdt_addr_r} /boot/dtb-3.16.0-4-armmp-lpae'", "setenv bootargs 'console=ttyS0,115200n8 root=UUID=159d17cc-697c-4125-95a0-a3775e1deabe ip=dhcp'", "setenv bootcmd 'run loadkernel; run loadinitrd; run loadfdt; bootz 0x42000000 0x43300000 0x43000000'", "boot", ], )
def test_substitutions(self): """ Test substitution of secondary media values into u-boot commands Unlike most u-boot calls, removable knows in advance all the values it needs to substitute into the boot commands for the secondary deployment as these are fixed by the device config and the image details from the job submission. """ job_parser = JobParser() cubie = NewDevice(os.path.join(os.path.dirname(__file__), '../devices/cubie1.yaml')) sample_job_file = os.path.join(os.path.dirname(__file__), 'sample_jobs/cubietruck-removable.yaml') with open(sample_job_file) as sample_job_data: job = job_parser.parse(sample_job_data, cubie, 4212, None, None, None, output_dir='/tmp/') job.validate() boot_params = [ methods for methods in job.parameters['actions'] if 'boot' in methods.keys()][1]['boot'] self.assertIn('ramdisk', boot_params) self.assertIn('kernel', boot_params) self.assertIn('dtb', boot_params) self.assertIn('root_uuid', boot_params) self.assertIn('boot_part', boot_params) self.assertIn('type', boot_params) self.assertGreater(len(job.pipeline.actions), 1) self.assertIsNotNone(job.pipeline.actions[1].internal_pipeline) u_boot_action = [action for action in job.pipeline.actions if action.name == 'uboot-action'][1].internal_pipeline.actions[2] self.assertIsNotNone(u_boot_action.get_common_data('u-boot', 'device')) self.assertEqual(u_boot_action.name, "uboot-overlay") methods = cubie['actions']['boot']['methods'] self.assertIn('u-boot', methods) self.assertIn('usb', methods['u-boot']) self.assertIn('commands', methods['u-boot']['usb']) commands_list = methods['u-boot']['usb']['commands'] device_id = u_boot_action.get_common_data('u-boot', 'device') substitutions = { '{BOOTX}': "%s %s %s %s" % ( u_boot_action.parameters['type'], cubie['parameters'][u_boot_action.parameters['type']]['kernel'], cubie['parameters'][u_boot_action.parameters['type']]['ramdisk'], cubie['parameters'][u_boot_action.parameters['type']]['dtb'],), '{RAMDISK}': boot_params['ramdisk'], '{KERNEL}': boot_params['kernel'], '{DTB}': boot_params['dtb'], '{ROOT}': boot_params['root_uuid'], '{ROOT_PART}': "%s:%s" % ( cubie['parameters']['media']['usb'][device_id]['device_id'], u_boot_action.parameters['boot_part'] ) } self.assertEqual('bootz 0x42000000 0x43300000 0x43000000', substitutions['{BOOTX}']) self.assertEqual('/boot/initrd.img-3.16.0-4-armmp-lpae.u-boot', substitutions['{RAMDISK}']) commands = substitute(commands_list, substitutions) self.assertEqual( commands, [ 'usb start', 'usb info', 'setenv autoload no', "setenv initrd_high '0xffffffff'", "setenv fdt_high '0xffffffff'", 'setenv initrd_addr_r ${ramdisk_addr_r}', "setenv loadkernel 'load usb 0:1 ${kernel_addr_r} /boot/vmlinuz-3.16.0-4-armmp-lpae'", "setenv loadinitrd 'load usb 0:1 ${initrd_addr_r} /boot/initrd.img-3.16.0-4-armmp-lpae.u-boot; setenv initrd_size ${filesize}'", "setenv loadfdt 'load usb 0:1 ${fdt_addr_r} /boot/dtb-3.16.0-4-armmp-lpae'", "setenv bootargs 'console=ttyS0,115200n8 root=UUID=159d17cc-697c-4125-95a0-a3775e1deabe ip=dhcp'", "setenv bootcmd 'run loadkernel; run loadinitrd; run loadfdt; bootz 0x42000000 0x43300000 0x43000000'", 'boot' ] )
def validate(self): super(CallQemuAction, self).validate() # 'arch' must be defined in job definition context. try: if self.job.parameters['context']['arch'] not in \ self.job.device['available_architectures']: self.errors = "Non existing architecture specified in context arch parameter. Please check the device configuration for available options." return except KeyError: self.errors = "Arch parameter must be set in the context section. Please check the device configuration for available architectures." return if self.job.parameters['context']['arch'] in ['amd64', 'x86_64']: self.logger.info( "qemu-system-x86, installed at version: %s" % debian_package_version(pkg='qemu-system-x86', split=False)) if self.job.parameters['context']['arch'] in [ 'arm64', 'arm', 'armhf', 'aarch64' ]: self.logger.info( "qemu-system-arm, installed at version: %s" % debian_package_version(pkg='qemu-system-arm', split=False)) if self.parameters['method'] in ['qemu', 'qemu-nfs']: if 'prompts' not in self.parameters: if self.test_has_shell(self.parameters): self.errors = "Unable to identify boot prompts from job definition." self.methods = self.job.device['actions']['boot']['methods'] method = self.parameters['method'] boot = self.methods[ 'qemu'] if 'qemu' in self.methods else self.methods['qemu-nfs'] try: if 'parameters' not in boot or 'command' not in boot['parameters']: self.errors = "Invalid device configuration - missing parameters" elif not boot['parameters']['command']: self.errors = "No QEMU binary command found - missing context." qemu_binary = which(boot['parameters']['command']) self.sub_command = [qemu_binary] self.sub_command.extend(boot['parameters'].get('options', [])) self.sub_command.extend( ['%s' % item for item in boot['parameters'].get('extra', [])]) except AttributeError as exc: self.errors = "Unable to parse device options: %s %s" % ( exc, self.job.device['actions']['boot']['methods'][method]) except (KeyError, TypeError): self.errors = "Invalid parameters for %s" % self.name namespace = self.parameters['namespace'] for label in self.data[namespace]['download-action'].keys(): if label in ['offset', 'available_loops', 'uefi', 'nfsrootfs']: continue image_arg = self.get_namespace_data(action='download-action', label=label, key='image_arg') action_arg = self.get_namespace_data(action='download-action', label=label, key='file') if not image_arg or not action_arg: self.errors = "Missing image_arg for %s. " % label continue self.substitutions["{%s}" % label] = action_arg self.commands.append(image_arg) self.substitutions["{NFS_SERVER_IP}"] = dispatcher_ip( self.job.parameters['dispatcher']) self.sub_command.extend(substitute(self.commands, self.substitutions)) if not self.sub_command: self.errors = "No QEMU command to execute" uefi_dir = self.get_namespace_data(action='deployimages', label='image', key='uefi_dir') if uefi_dir: self.sub_command.extend(['-L', uefi_dir, '-monitor', 'none']) # Check for enable-kvm command line option in device configuration. if method not in self.job.device['actions']['boot']['methods']: self.errors = "Unknown boot method '%s'" % method return options = self.job.device['actions']['boot']['methods'][method][ 'parameters']['options'] if "-enable-kvm" in options: # Check if the worker has kvm enabled. if not os.path.exists(SYS_CLASS_KVM): self.errors = "Device configuration contains -enable-kvm option but kvm module is not enabled."