Beispiel #1
0
    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
Beispiel #2
0
 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
Beispiel #3
0
 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
Beispiel #4
0
 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
Beispiel #5
0
 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
Beispiel #6
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(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
Beispiel #7
0
 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"
Beispiel #8
0
 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"
Beispiel #9
0
 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
Beispiel #10
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(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
Beispiel #11
0
 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"
Beispiel #12
0
 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"
Beispiel #13
0
 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"
Beispiel #14
0
    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])
Beispiel #16
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
Beispiel #17
0
    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)
Beispiel #18
0
    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)
Beispiel #19
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(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
Beispiel #20
0
 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))
Beispiel #21
0
    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)
Beispiel #22
0
    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
Beispiel #23
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 = '-'

        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
Beispiel #24
0
    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
Beispiel #25
0
 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
Beispiel #26
0
 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))
Beispiel #27
0
 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])
Beispiel #28
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"
Beispiel #29
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(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
Beispiel #30
0
 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'])
Beispiel #31
0
 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"
Beispiel #32
0
 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'])
Beispiel #33
0
 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
Beispiel #35
0
    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)
Beispiel #36
0
    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)
Beispiel #37
0
    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
Beispiel #38
0
    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
Beispiel #39
0
    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'
        ])
Beispiel #40
0
    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."