def run(self, connection, max_end_time, args=None): connection = super(UefiSubstituteCommands, self).run(connection, max_end_time, args) ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) substitution_dictionary = { '{SERVER_IP}': ip_addr, '{RAMDISK}': self.get_namespace_data(action='compress-ramdisk', label='file', key='ramdisk'), '{KERNEL}': self.get_namespace_data(action='download-action', label='file', key='kernel'), '{DTB}': self.get_namespace_data(action='download-action', label='file', key='dtb'), 'TEST_MENU_NAME': "LAVA %s test image" % self.parameters['commands'] } nfs_address = self.get_namespace_data(action='persistent-nfs-overlay', label='nfs_address', key='nfsroot') nfs_root = self.get_namespace_data(action='download-action', label='file', key='nfsrootfs') if nfs_root: substitution_dictionary['{NFSROOTFS}'] = self.get_namespace_data(action='extract-rootfs', label='file', key='nfsroot') substitution_dictionary['{NFS_SERVER_IP}'] = ip_addr elif nfs_address: substitution_dictionary['{NFSROOTFS}'] = nfs_address substitution_dictionary['{NFS_SERVER_IP}'] = self.get_namespace_data( action='persistent-nfs-overlay', label='nfs_address', key='serverip') for item in self.items: if 'enter' in item['select']: item['select']['enter'] = substitute([item['select']['enter']], substitution_dictionary)[0] if 'items' in item['select']: # items is already a list, so pass without wrapping in [] item['select']['items'] = substitute(item['select']['items'], substitution_dictionary) return connection
def run(self, connection, max_end_time, args=None): # substitutions substitutions = {'{emptyimage}': self.get_namespace_data(action='prepare-empty-image', label='prepare-empty-image', key='output')} sub_command = self.get_namespace_data(action='prepare-qemu-commands', label='prepare-qemu-commands', key='sub_command') sub_command = substitute(sub_command, substitutions) command_line = ' '.join(sub_command) commands = [] # get the download args in run() image_arg = self.get_namespace_data(action='download-action', label='iso', key='image_arg') action_arg = self.get_namespace_data(action='download-action', label='iso', key='file') substitutions["{%s}" % 'iso'] = action_arg commands.append(image_arg) command_line += ' '.join(substitute(commands, substitutions)) preseed_file = self.get_namespace_data(action='download-action', label='file', key='preseed') if not preseed_file: raise JobError("Unable to identify downloaded preseed filename.") substitutions = {'{preseed}': preseed_file} append_args = self.get_namespace_data(action='prepare-qemu-commands', label='prepare-qemu-commands', key='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_namespace_data( action='prepare-qemu-commands', label='prepare-qemu-commands', key='prompts') shell_connection = super(IsoCommandLine, self).run(shell_connection, max_end_time, args) return shell_connection
def run(self, connection, max_end_time): connection = super().run(connection, max_end_time) # Create the cmdline file, this is not set by any bootloader command ip_addr = dispatcher_ip(self.job.parameters["dispatcher"]) kernel_path = self.get_namespace_data( action="download-action", label="kernel", key="file" ) cmdline_path = os.path.join(os.path.dirname(kernel_path), "cmdline") 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" ), "{NFS_SERVER_IP}": ip_addr, } elif nfs_address: substitutions = { "{NFSROOTFS}": nfs_address, "{NFS_SERVER_IP}": self.get_namespace_data( action="persistent-nfs-overlay", label="nfs_address", key="serverip" ), } else: substitutions = {} cmdline = substitute([self.cmdline], substitutions)[0] with open(cmdline_path, "w") as cmdline_file: cmdline_file.write(cmdline) # Substitute {CMDLINE} with the cmdline file TFTP path kernel_tftp = self.get_namespace_data( action="download-action", label="file", key="kernel" ) cmdline_tftp = os.path.join(os.path.dirname(kernel_tftp), "cmdline") fit_tftp = self.get_namespace_data( action="prepare-fit", label="file", key="fit" ) substitutions = {"{CMDLINE}": cmdline_tftp, "{FIT}": fit_tftp} commands = self.get_namespace_data( action="bootloader-overlay", label=self.method, key="commands" ) commands = substitute(commands, substitutions) self.set_namespace_data( action="bootloader-overlay", label=self.method, key="commands", value=commands, ) self.logger.info("Parsed boot commands: %s", "; ".join(commands)) return connection
def run(self, connection, max_end_time): connection = super().run(connection, max_end_time) # Build the substitutions dictionary substitutions = {} paths = set() for action in self.get_namespace_keys("download-action"): filename = self.get_namespace_data(action="download-action", label=action, key="file") if filename is None: self.logger.warning( "Empty value for action='download-action' label='%s' key='file'", action, ) continue substitutions["{%s}" % action.upper()] = filename paths.add(os.path.dirname(filename)) # If needed, prepend with docker if self.container is None: cmd = self.gdb else: cmd = "docker run --rm -it --name lava-%s-%s" % ( self.job.job_id, self.level, ) for path in paths: cmd += " --volume %s:%s" % (path, path) for device in self.devices: cmd += " --device %s:%s:rw" % (device, device) cmd += " %s %s" % (self.container, self.gdb) for arg in substitute(self.arguments, substitutions): cmd += " " + arg # Start gdb self.logger.info("Starting gdb: %s", cmd) shell = ShellCommand(cmd, self.timeout, logger=self.logger) gdb = ShellSession(self.job, shell) gdb.prompt_str = "\\(gdb\\) " self.gdb_connection = gdb self.gdb_connection.wait() # Send all gdb commands for cmd in substitute(self.commands, substitutions): self.gdb_connection.sendline(cmd) self.gdb_connection.wait() # "continue" is send last if self.wait_before_continue: self.logger.debug("Sleeping %ss before sending 'continue'", self.wait_before_continue) time.sleep(self.wait_before_continue) self.gdb_connection.sendline("continue") return connection
def run(self, connection, max_end_time): # substitutions substitutions = { "{emptyimage}": self.get_namespace_data(action="prepare-empty-image", label="prepare-empty-image", key="output") } sub_command = self.get_namespace_data( action="prepare-qemu-commands", label="prepare-qemu-commands", key="sub_command", ) sub_command = substitute(sub_command, substitutions) command_line = " ".join(sub_command) commands = [] # get the download args in run() image_arg = self.get_namespace_data(action="download-action", label="iso", key="image_arg") action_arg = self.get_namespace_data(action="download-action", label="iso", key="file") substitutions["{%s}" % "iso"] = action_arg commands.append(image_arg) command_line += " ".join(substitute(commands, substitutions)) preseed_file = self.get_namespace_data(action="download-action", label="file", key="preseed") if not preseed_file: raise JobError("Unable to identify downloaded preseed filename.") substitutions = {"{preseed}": preseed_file} append_args = self.get_namespace_data(action="prepare-qemu-commands", label="prepare-qemu-commands", key="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_namespace_data( action="prepare-qemu-commands", label="prepare-qemu-commands", key="prompts") shell_connection = super().run(shell_connection, max_end_time) return shell_connection
def run(self, connection, max_end_time, args=None): # substitutions substitutions = { '{emptyimage}': self.get_namespace_data(action='prepare-empty-image', label='prepare-empty-image', key='output') } sub_command = self.get_namespace_data(action='prepare-qemu-commands', label='prepare-qemu-commands', key='sub_command') sub_command = substitute(sub_command, substitutions) command_line = ' '.join(sub_command) commands = [] # get the download args in run() image_arg = self.get_namespace_data(action='download-action', label='iso', key='image_arg') action_arg = self.get_namespace_data(action='download-action', label='iso', key='file') substitutions["{%s}" % 'iso'] = action_arg commands.append(image_arg) command_line += ' '.join(substitute(commands, substitutions)) preseed_file = self.get_namespace_data(action='download-action', label='file', key='preseed') if not preseed_file: raise JobError("Unable to identify downloaded preseed filename.") substitutions = {'{preseed}': preseed_file} append_args = self.get_namespace_data(action='prepare-qemu-commands', label='prepare-qemu-commands', key='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_namespace_data( action='prepare-qemu-commands', label='prepare-qemu-commands', key='prompts') shell_connection = super(IsoCommandLine, self).run(shell_connection, max_end_time, args) return shell_connection
def test_uefi_job(self): self.assertIsNotNone(self.job) self.job.validate() uefi_menu = [action for action in self.job.pipeline.actions if action.name == 'uefi-menu-action'][0] selector = [action for action in uefi_menu.internal_pipeline.actions if action.name == 'uefi-menu-selector'][0] self.assertEqual( selector.selector.prompt, "Start:" ) self.assertIsInstance(selector.items, list) description_ref = self.pipeline_reference('mustang-uefi.yaml') self.assertEqual(description_ref, self.job.pipeline.describe(False)) # just dummy strings substitution_dictionary = { '{SERVER_IP}': '10.4.0.1', '{RAMDISK}': None, '{KERNEL}': 'uImage', '{DTB}': 'mustang.dtb', '{NFSROOTFS}': 'tmp/tmp21dfed/', '{TEST_MENU_NAME}': 'LAVA NFS Test Image' } for block in selector.items: if 'select' in block: if 'enter' in block['select']: block['select']['enter'] = substitute([block['select']['enter']], substitution_dictionary) if 'items' in block['select']: block['select']['items'] = substitute(block['select']['items'], substitution_dictionary) count = 0 check_block = [ {'items': ['Boot Manager'], 'wait': 'Choice:'}, {'items': ['Remove Boot Device Entry'], 'fallback': 'Return to Main Menu', 'wait': 'Delete entry'}, {'items': ['LAVA NFS Test Image'], 'wait': 'Choice:'}, {'items': ['Add Boot Device Entry'], 'wait': 'Select the Boot Device:'}, {'items': ['TFTP on MAC Address: 00:01:73:69:5A:EF'], 'wait': 'Get the IP address from DHCP:'}, {'enter': ['y'], 'wait': 'Get the TFTP server IP address:'}, {'enter': ['10.4.0.1'], 'wait': 'File path of the EFI Application or the kernel :'}, {'enter': ['uImage'], 'wait': 'Is an EFI Application?'}, {'enter': ['n'], 'wait': 'Boot Type:'}, {'enter': ['f'], 'wait': 'Add an initrd:'}, {'enter': ['n'], 'wait': 'Get the IP address from DHCP:'}, {'enter': ['y'], 'wait': 'Get the TFTP server IP address:'}, {'enter': ['10.4.0.1'], 'wait': 'File path of the FDT :'}, {'enter': ['mustang.dtb'], 'wait': 'Arguments to pass to the binary:'}, {'enter': ['console=ttyS0,115200 earlyprintk=uart8250-32bit,0x1c020000 debug root=/dev/nfs rw ' 'nfsroot=10.4.0.1:tmp/tmp21dfed/,tcp,hard,intr ip=dhcp'], 'wait': 'Description for this new Entry:'}, {'enter': ['LAVA NFS Test Image'], 'wait': 'Choice:'}, {'items': ['Return to main menu'], 'wait': 'Start:'}, {'items': ['LAVA NFS Test Image']}, ] for item in selector.items: self.assertEqual( item['select'], check_block[count]) count += 1
def run(self, connection, max_end_time, args=None): connection = super(DepthchargeCommandOverlay, self).run( connection, max_end_time, args) # Create the cmdline file, this is not set by any bootloader command ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) kernel_path = self.get_namespace_data( action='download-action', label='kernel', key='file') cmdline_path = os.path.join(os.path.dirname(kernel_path), 'cmdline') 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'), '{NFS_SERVER_IP}': ip_addr, } elif nfs_address: substitutions = { '{NFSROOTFS}': nfs_address, '{NFS_SERVER_IP}': self.get_namespace_data( action='persistent-nfs-overlay', label='nfs_address', key='serverip'), } else: substitutions = {} cmdline = substitute([self.cmdline], substitutions)[0] with open(cmdline_path, "w") as cmdline_file: cmdline_file.write(cmdline) # Substitute {CMDLINE} with the cmdline file TFTP path kernel_tftp = self.get_namespace_data( action='download-action', label='file', key='kernel') cmdline_tftp = os.path.join(os.path.dirname(kernel_tftp), 'cmdline') fit_tftp = self.get_namespace_data( action='prepare-fit', label='file', key='fit') substitutions = { '{CMDLINE}': cmdline_tftp, '{FIT}': fit_tftp, } commands = self.get_namespace_data( action='bootloader-overlay', label=self.method, key='commands') commands = substitute(commands, substitutions) self.set_namespace_data( action='bootloader-overlay', label=self.method, key='commands', value=commands) self.logger.info("Parsed boot commands: %s", '; '.join(commands)) return connection
def run(self, connection, max_end_time): connection = super().run(connection, max_end_time) # Create the cmdline file, this is not set by any bootloader command ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) kernel_path = self.get_namespace_data( action='download-action', label='kernel', key='file') cmdline_path = os.path.join(os.path.dirname(kernel_path), 'cmdline') 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'), '{NFS_SERVER_IP}': ip_addr, } elif nfs_address: substitutions = { '{NFSROOTFS}': nfs_address, '{NFS_SERVER_IP}': self.get_namespace_data( action='persistent-nfs-overlay', label='nfs_address', key='serverip'), } else: substitutions = {} cmdline = substitute([self.cmdline], substitutions)[0] with open(cmdline_path, "w") as cmdline_file: cmdline_file.write(cmdline) # Substitute {CMDLINE} with the cmdline file TFTP path kernel_tftp = self.get_namespace_data( action='download-action', label='file', key='kernel') cmdline_tftp = os.path.join(os.path.dirname(kernel_tftp), 'cmdline') fit_tftp = self.get_namespace_data( action='prepare-fit', label='file', key='fit') substitutions = { '{CMDLINE}': cmdline_tftp, '{FIT}': fit_tftp, } commands = self.get_namespace_data( action='bootloader-overlay', label=self.method, key='commands') commands = substitute(commands, substitutions) self.set_namespace_data( action='bootloader-overlay', label=self.method, key='commands', value=commands) self.logger.info("Parsed boot commands: %s", '; '.join(commands)) return connection
def run(self, connection, max_end_time): connection = super().run(connection, max_end_time) 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}"] = dispatcher_ip( self.job.parameters["dispatcher"], "nfs") elif nfs_address: substitution_dictionary["{NFSROOTFS}"] = nfs_address substitution_dictionary[ "{NFS_SERVER_IP}"] = self.get_namespace_data( action="persistent-nfs-overlay", label="nfs_address", key="serverip") for item in self.items: if "enter" in item["select"]: item["select"]["enter"] = substitute( [item["select"]["enter"]], substitution_dictionary)[0] if "items" in item["select"]: # items is already a list, so pass without wrapping in [] item["select"]["items"] = substitute(item["select"]["items"], substitution_dictionary) return connection
def run(self, connection, max_end_time, args=None): connection = super(UefiSubstituteCommands, self).run(connection, max_end_time, args) ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) substitution_dictionary = { '{SERVER_IP}': ip_addr, '{RAMDISK}': self.get_namespace_data(action='compress-ramdisk', label='file', key='ramdisk'), '{KERNEL}': self.get_namespace_data(action='download-action', label='file', key='kernel'), '{DTB}': self.get_namespace_data(action='download-action', label='file', key='dtb'), 'TEST_MENU_NAME': "LAVA %s test image" % self.parameters['commands'] } nfs_address = self.get_namespace_data(action='persistent-nfs-overlay', label='nfs_address', key='nfsroot') nfs_root = self.get_namespace_data(action='download-action', label='file', key='nfsrootfs') if nfs_root: substitution_dictionary['{NFSROOTFS}'] = self.get_namespace_data( action='extract-rootfs', label='file', key='nfsroot') substitution_dictionary['{NFS_SERVER_IP}'] = ip_addr elif nfs_address: substitution_dictionary['{NFSROOTFS}'] = nfs_address substitution_dictionary[ '{NFS_SERVER_IP}'] = self.get_namespace_data( action='persistent-nfs-overlay', label='nfs_address', key='serverip') for item in self.items: if 'enter' in item['select']: item['select']['enter'] = substitute( [item['select']['enter']], substitution_dictionary)[0] if 'items' in item['select']: # items is already a list, so pass without wrapping in [] item['select']['items'] = substitute(item['select']['items'], substitution_dictionary) return connection
def test_overlay_noramdisk(self, which_mock): parameters = { "dispatcher": {}, # fake dispatcher parameter. Normally added by parser "device_type": "beaglebone-black", "job_name": "uboot-pipeline", "job_timeout": "15m", "action_timeout": "5m", "priority": "medium", "actions": { "boot": { "namespace": "common", "method": "u-boot", "commands": "ramdisk", "prompts": ["linaro-test", "root@debian:~#"], }, "deploy": { "namespace": "common", "ramdisk": {"url": ""}, "kernel": {"url": "zImage", "type": "zimage"}, "dtb": {"url": "broken.dtb"}, }, }, } data = yaml_safe_load(Factory().create_device("bbb-01.jinja2")[0]) device = NewDevice(data) ip_addr = dispatcher_ip(None) parsed = [] kernel_addr = "0x83000000" ramdisk_addr = "0x83000000" dtb_addr = "0x88000000" kernel = parameters["actions"]["deploy"]["kernel"]["url"] ramdisk = parameters["actions"]["deploy"]["ramdisk"]["url"] dtb = parameters["actions"]["deploy"]["dtb"]["url"] 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" % ("bootz", 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, drop=True ) commands = params["u-boot"]["ramdisk"]["commands"] self.assertIs(type(commands), list) self.assertIn("tftp 0x83000000 zImage", commands) self.assertNotIn("tftp 0x83000000 {RAMDISK}", commands) self.assertNotIn("tftp 0x83000000 ", commands) self.assertIn("setenv initrd_size ${filesize}", commands) self.assertIn("tftp 0x88000000 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)
def validate(self): super(FlashPyOCDAction, self).validate() boot = self.job.device['actions']['boot']['methods']['pyocd'] pyocd_binary = boot['parameters']['command'] which(pyocd_binary) self.base_command = [pyocd_binary] self.base_command.extend(boot['parameters'].get('options', [])) if self.job.device['board_id'] == '0000000000': self.errors = "board_id unset" substitutions = {} self.base_command.extend(['--board', self.job.device['board_id']]) namespace = self.parameters['namespace'] for action in self.data[namespace]['download-action'].keys(): pyocd_full_command = [] image_arg = self.get_namespace_data(action='download-action', label=action, key='image_arg') action_arg = self.get_namespace_data(action='download-action', label=action, key='file') if image_arg: if not isinstance(image_arg, str): self.errors = "image_arg is not a string (try quoting it)" continue substitutions["{%s}" % action] = action_arg pyocd_full_command.extend(self.base_command) pyocd_full_command.extend(substitute([image_arg], substitutions)) self.exec_list.append(pyocd_full_command) else: pyocd_full_command.extend(self.base_command) pyocd_full_command.extend([action_arg]) self.exec_list.append(pyocd_full_command) if len(self.exec_list) < 1: self.errors = "No PyOCD command to execute"
def validate(self): super().validate() boot = self.job.device["actions"]["boot"]["methods"]["pyocd"] pyocd_binary = boot["parameters"]["command"] binary = which(pyocd_binary) self.logger.info(binary_version(binary, "--version")) self.base_command = [pyocd_binary] self.base_command.extend(boot["parameters"].get("options", [])) if self.job.device["board_id"] == "0000000000": self.errors = "[PYOCD] board_id unset" substitutions = {} self.base_command.extend(["--board", self.job.device["board_id"]]) for action in self.get_namespace_keys("download-action"): 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 not self.exec_list: self.errors = "No PyOCD command to execute"
def run(self, connection, max_end_time, args=None): if 'deployment_data' not in self.parameters: return connection if self.parameters["deployment_data"].get('installer_extra_cmd', None): if self.parameters.get('os', None) == "debian_installer": add_late_command( self.get_namespace_data(action='download-action', label='preseed', key='file'), self.parameters["deployment_data"]["installer_extra_cmd"]) if self.parameters.get('os', None) == "centos_installer": ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) overlay = self.get_namespace_data(action='download-action', label='file', key='overlay') substitutions = { '{OVERLAY_URL}': 'tftp://' + ip_addr + '/' + overlay } post_command = substitute([ self.parameters["deployment_data"]["installer_extra_cmd"] ], substitutions) add_to_kickstart( self.get_namespace_data(action='download-action', label='preseed', key='file'), post_command[0]) return connection
def validate(self): super().validate() boot = self.job.device['actions']['boot']['methods']['pyocd'] pyocd_binary = boot['parameters']['command'] which(pyocd_binary) self.base_command = [pyocd_binary] self.base_command.extend(boot['parameters'].get('options', [])) if self.job.device['board_id'] == '0000000000': self.errors = "[PYOCD] board_id unset" substitutions = {} self.base_command.extend(['--board', self.job.device['board_id']]) for action in self.get_namespace_keys('download-action'): pyocd_full_command = [] image_arg = self.get_namespace_data(action='download-action', label=action, key='image_arg') action_arg = self.get_namespace_data(action='download-action', label=action, key='file') if image_arg: if not isinstance(image_arg, str): self.errors = "image_arg is not a string (try quoting it)" continue substitutions["{%s}" % action] = action_arg pyocd_full_command.extend(self.base_command) pyocd_full_command.extend( substitute([image_arg], substitutions)) self.exec_list.append(pyocd_full_command) else: pyocd_full_command.extend(self.base_command) pyocd_full_command.extend([action_arg]) self.exec_list.append(pyocd_full_command) if len(self.exec_list) < 1: self.errors = "No PyOCD command to execute"
def run(self, connection, max_end_time, args=None): connection = super(FlasherAction, self).run(connection, max_end_time, args) # Substitute in the device commands substitutions = {} for key in [key for key in self.parameters['images'].keys() if key != "yaml_line"]: filename = self.get_namespace_data(action='download-action', label=key, key='file') filename = filename[len(self.path) + 1:] substitutions["{%s}" % key.upper()] = filename # Add power commands substitutions["{HARD_RESET_COMMAND}"] = self.job.device.hard_reset_command substitutions["{SOFT_RESET_COMMAND}"] = self.job.device.soft_reset_command substitutions["{PRE_OS_COMMAND}"] = self.job.device.pre_os_command if substitutions["{PRE_OS_COMMAND}"] is None: substitutions["{PRE_OS_COMMAND}"] = '' substitutions["{PRE_POWER_COMMAND}"] = self.job.device.pre_power_command if substitutions["{PRE_POWER_COMMAND}"] is None: substitutions["{PRE_POWER_COMMAND}"] = '' substitutions["{POWER_ON_COMMAND}"] = self.job.device.power_command substitutions["{POWER_OFF_COMMAND}"] = self.job.device.get('commands', {}).get('power_off', '') # Add some device configuration substitutions["{DEVICE_INFO}"] = yaml.dump(self.job.device.get("device_info", [])) substitutions["{STATIC_INFO}"] = yaml.dump(self.job.device.get("static_info", [])) # Run the commands for cmd in self.commands: cmds = substitute(cmd.split(" "), substitutions) if not self.run_command(cmds, allow_fail=False, allow_silent=True, cwd=self.path): raise JobError("Unable to flash the device") return connection
def run(self, connection, max_end_time): if "deployment_data" not in self.parameters: return connection if self.parameters["deployment_data"].get("installer_extra_cmd"): if self.parameters.get("os") == "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") == "centos_installer": ip_addr = dispatcher_ip(self.job.parameters["dispatcher"]) overlay = self.get_namespace_data(action="download-action", label="file", key="overlay") substitutions = { "{OVERLAY_URL}": "tftp://" + ip_addr + "/" + overlay } post_command = substitute( [ self.parameters["deployment_data"] ["installer_extra_cmd"] ], substitutions, ) add_to_kickstart( self.get_namespace_data(action="download-action", label="preseed", key="file"), post_command[0], ) return connection
def run(self, connection, max_end_time, args=None): """ CommandRunner expects a pexpect.spawn connection which is the return value of target.device.power_on executed by boot in the old dispatcher. In the new pipeline, the pexpect.spawn is a ShellCommand and the connection is a ShellSession. CommandRunner inside the ShellSession turns the ShellCommand into a runner which the ShellSession uses via ShellSession.run() to run commands issued *after* the device has booted. pexpect.spawn is one of the raw_connection objects for a Connection class. """ # initialise the first Connection object, a command line shell into the running QEMU. guest = self.get_namespace_data(action='apply-overlay-guest', label='guest', key='filename') # check for NFS if 'qemu-nfs' in self.methods and self.parameters.get('media', None) == 'nfs': self.logger.debug("Adding NFS arguments to kernel command line.") root_dir = self.get_namespace_data(action='extract-rootfs', label='file', key='nfsroot') self.substitutions["{NFSROOTFS}"] = root_dir params = self.methods['qemu-nfs']['parameters']['append'] # console=ttyAMA0 root=/dev/nfs nfsroot=10.3.2.1:/var/lib/lava/dispatcher/tmp/dirname,tcp,hard,intr ip=dhcp append = [ 'console=%s' % params['console'], 'root=/dev/nfs', '%s rw' % substitute([params['nfsrootargs']], self.substitutions)[0], "%s" % params['ipargs'] ] self.sub_command.append('--append') self.sub_command.append('"%s"' % ' '.join(append)) elif guest: self.logger.info("Extending command line for qcow2 test overlay") # interface is ide by default in qemu interface = self.job.device['actions']['deploy']['methods']['image']['parameters']['guest'].get('interface', 'ide') self.sub_command.append('-drive format=qcow2,file=%s,media=disk,if=%s' % (os.path.realpath(guest), interface)) # push the mount operation to the test shell pre-command to be run # before the test shell tries to execute. shell_precommand_list = [] mountpoint = self.get_namespace_data(action='test', label='results', key='lava_test_results_dir') uuid = '/dev/disk/by-uuid/%s' % self.get_namespace_data(action='apply-overlay-guest', label='guest', key='UUID') shell_precommand_list.append('mkdir %s' % mountpoint) # prepare_guestfs always uses ext2 shell_precommand_list.append('mount %s -t ext2 %s' % (uuid, mountpoint)) # debug line to show the effect of the mount operation # also allows time for kernel messages from the mount operation to be processed. shell_precommand_list.append('ls -la %s/bin/lava-test-runner' % mountpoint) self.set_namespace_data(action='test', label='lava-test-shell', key='pre-command-list', value=shell_precommand_list) self.logger.info("Boot command: %s", ' '.join(self.sub_command)) shell = ShellCommand(' '.join(self.sub_command), self.timeout, logger=self.logger) if shell.exitstatus: raise JobError("%s command exited %d: %s" % (self.sub_command, shell.exitstatus, shell.readlines())) self.logger.debug("started a shell command") shell_connection = ShellSession(self.job, shell) shell_connection = super(CallQemuAction, self).run(shell_connection, max_end_time, args) self.set_namespace_data(action='shared', label='shared', key='connection', value=shell_connection) return shell_connection
def test_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', 'actions': { 'boot': { 'method': 'grub', 'commands': 'ramdisk', 'prompts': ['linaro-test', 'root@debian:~#'] }, 'deploy': { 'ramdisk': 'initrd.gz', 'kernel': 'zImage', 'dtb': 'broken.dtb' } } } (rendered, _) = self.factory.create_device('d02-01.jinja2') device = NewDevice(yaml.load(rendered)) job = Job(4212, parameters, None) job.device = device pipeline = Pipeline(job=job, parameters=parameters['actions']['boot']) job.pipeline = pipeline overlay = BootloaderCommandOverlay() pipeline.add_action(overlay) ip_addr = dispatcher_ip(None) parsed = [] kernel = parameters['actions']['deploy']['kernel'] ramdisk = parameters['actions']['deploy']['ramdisk'] dtb = parameters['actions']['deploy']['dtb'] substitution_dictionary = { '{SERVER_IP}': ip_addr, # the addresses need to be hexadecimal '{RAMDISK}': ramdisk, '{KERNEL}': kernel, '{DTB}': dtb } params = device['actions']['boot']['methods'] commands = params['grub']['ramdisk']['commands'] self.assertIn('net_bootp', commands) self.assertIn("linux (tftp,{SERVER_IP})/{KERNEL} console=ttyS0,115200 earlycon=uart8250,mmio32,0x80300000 root=/dev/ram0 ip=dhcp", commands) self.assertIn('initrd (tftp,{SERVER_IP})/{RAMDISK}', commands) self.assertIn('devicetree (tftp,{SERVER_IP})/{DTB}', commands) params['grub']['ramdisk']['commands'] = substitute(params['grub']['ramdisk']['commands'], substitution_dictionary) substituted_commands = params['grub']['ramdisk']['commands'] self.assertIs(type(substituted_commands), list) self.assertIn('net_bootp', substituted_commands) self.assertNotIn("linux (tftp,{SERVER_IP})/{KERNEL} console=ttyS0,115200 earlycon=uart8250,mmio32,0x80300000 root=/dev/ram0 ip=dhcp", substituted_commands) self.assertIn("linux (tftp,%s)/%s console=ttyS0,115200 earlycon=uart8250,mmio32,0x80300000 root=/dev/ram0 ip=dhcp" % (ip_addr, kernel), substituted_commands) self.assertNotIn('initrd (tftp,{SERVER_IP})/{RAMDISK}', parsed) self.assertNotIn('devicetree (tftp,{SERVER_IP})/{DTB}', parsed)
def test_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'} # nosec unit test support. sub_command = substitute(sub_command, substitutions) self.assertNotIn('{emptyimage}', sub_command) self.assertNotIn('/tmp/tmp.00000/hd.img', sub_command) # nosec unit test support. self.assertIn('/tmp/tmp.00000/hd.img', ' '.join(sub_command)) # nosec unit test support.
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', 'actions': { 'boot': { 'method': 'grub', 'commands': 'ramdisk', 'prompts': ['linaro-test', 'root@debian:~#'] }, 'deploy': { 'ramdisk': 'initrd.gz', 'kernel': 'zImage', 'dtb': 'broken.dtb' } } } device = NewDevice(os.path.join(os.path.dirname(__file__), '../devices/d02-01.yaml')) job = Job(4212, parameters, None) job.device = device pipeline = Pipeline(job=job, parameters=parameters['actions']['boot']) job.pipeline = pipeline overlay = BootloaderCommandOverlay() pipeline.add_action(overlay) ip_addr = dispatcher_ip(None) parsed = [] kernel = parameters['actions']['deploy']['kernel'] ramdisk = parameters['actions']['deploy']['ramdisk'] dtb = parameters['actions']['deploy']['dtb'] substitution_dictionary = { '{SERVER_IP}': ip_addr, # the addresses need to be hexadecimal '{RAMDISK}': ramdisk, '{KERNEL}': kernel, '{DTB}': dtb } params = device['actions']['boot']['methods'] commands = params['grub']['ramdisk']['commands'] self.assertIn('net_bootp', commands) self.assertIn("linux (tftp,{SERVER_IP})/{KERNEL} console=ttyS0,115200 earlycon=uart8250,mmio32,0x80300000 root=/dev/ram0 ip=dhcp", commands) self.assertIn('initrd (tftp,{SERVER_IP})/{RAMDISK}', commands) self.assertIn('devicetree (tftp,{SERVER_IP})/{DTB}', commands) params['grub']['ramdisk']['commands'] = substitute(params['grub']['ramdisk']['commands'], substitution_dictionary) substituted_commands = params['grub']['ramdisk']['commands'] self.assertIs(type(substituted_commands), list) self.assertIn('net_bootp', substituted_commands) self.assertNotIn("linux (tftp,{SERVER_IP})/{KERNEL} console=ttyS0,115200 earlycon=uart8250,mmio32,0x80300000 root=/dev/ram0 ip=dhcp", substituted_commands) self.assertIn("linux (tftp,%s)/%s console=ttyS0,115200 earlycon=uart8250,mmio32,0x80300000 root=/dev/ram0 ip=dhcp" % (ip_addr, kernel), substituted_commands) self.assertNotIn('initrd (tftp,{SERVER_IP})/{RAMDISK}', parsed) self.assertNotIn('devicetree (tftp,{SERVER_IP})/{DTB}', parsed)
def test_overlay_action(self): # pylint: disable=too-many-locals parameters = { "device_type": "x86", "job_name": "ipxe-pipeline", "job_timeout": "15m", "action_timeout": "5m", "priority": "medium", "actions": { "boot": { "method": "ipxe", "commands": "ramdisk", "prompts": ["linaro-test", "root@debian:~#"], }, "deploy": { "ramdisk": "initrd.gz", "kernel": "zImage" }, }, } (rendered, _) = self.factory.create_device("x86-01.jinja2") device = NewDevice(yaml.safe_load(rendered)) 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 ip=dhcp", commands) self.assertNotIn( "kernel tftp://{SERVER_IP}/{KERNEL} ${extraargs} ${console}", commands) self.assertNotIn("initrd tftp://{SERVER_IP}/{RAMDISK}", commands) self.assertIn("boot", commands)
def 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', 'actions': { 'boot': { 'method': 'ipxe', 'commands': 'ramdisk', 'prompts': ['linaro-test', 'root@debian:~#'] }, 'deploy': { 'ramdisk': 'initrd.gz', 'kernel': 'zImage', } } } (rendered, _) = self.factory.create_device('x86-01.jinja2') device = NewDevice(yaml.safe_load(rendered)) 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 ip=dhcp", commands) self.assertNotIn( "kernel tftp://{SERVER_IP}/{KERNEL} ${extraargs} ${console}", commands) self.assertNotIn("initrd tftp://{SERVER_IP}/{RAMDISK}", commands) self.assertIn("boot", commands)
def validate(self): super().validate() boot = self.job.device["actions"]["boot"]["methods"]["pyocd"] pyocd_binary = boot["parameters"]["command"] binary = which(pyocd_binary) self.logger.info(binary_version(binary, "--version")) self.base_command = [pyocd_binary] self.base_command.extend(boot["parameters"].get("options", [])) if self.job.device["board_id"] == "0000000000": self.errors = "[PYOCD] board_id unset" substitutions = {} # '--uid' should be used with 'pyocd flash' for connecting to # a specific board. 'pyocd flash --board' doesn't work for # selecting board, and the option has been removed since # version v0.32.0. # '--board' should be used for 'pyocd-flashtool' as '--uid' # isn't available for 'pyocd-flashtool'. # Different boards require (or work better) with a particular # version of pyocd. Due to this, we need to maintain support # for both 'pyocd flash' and 'pyocd-flashtool' for the foreseeable # future. connecting_option = "--uid" if pyocd_binary.startswith("pyocd-flashtool"): connecting_option = "--board" self.base_command.extend([connecting_option, self.job.device["board_id"]]) for action in self.get_namespace_keys("download-action"): 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 not self.exec_list: self.errors = "No PyOCD command to execute" pre_os_command = self.job.device.pre_os_command if pre_os_command: self.exec_list.append(pre_os_command.split(" "))
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', 'actions': { 'boot': { 'method': 'ipxe', 'commands': 'ramdisk', 'prompts': ['linaro-test', 'root@debian:~#'] }, 'deploy': { 'ramdisk': 'initrd.gz', 'kernel': 'zImage', } } } device = NewDevice(os.path.join(os.path.dirname(__file__), '../devices/x86-01.yaml')) job = Job(4212, parameters, None) job.device = device pipeline = Pipeline(job=job, parameters=parameters['actions']['boot']) job.pipeline = pipeline overlay = BootloaderCommandOverlay() pipeline.add_action(overlay) ip_addr = dispatcher_ip(None) kernel = parameters['actions']['deploy']['kernel'] ramdisk = parameters['actions']['deploy']['ramdisk'] substitution_dictionary = { '{SERVER_IP}': ip_addr, '{RAMDISK}': ramdisk, '{KERNEL}': kernel, '{LAVA_MAC}': "00:00:00:00:00:00" } params = device['actions']['boot']['methods'] params['ipxe']['ramdisk']['commands'] = substitute(params['ipxe']['ramdisk']['commands'], substitution_dictionary) commands = params['ipxe']['ramdisk']['commands'] self.assertIs(type(commands), list) self.assertIn("dhcp net0", commands) self.assertIn("set console console=ttyS0,115200n8 lava_mac=00:00:00:00:00:00", commands) self.assertIn("set extraargs init=/sbin/init ip=dhcp", commands) self.assertNotIn("kernel tftp://{SERVER_IP}/{KERNEL} ${extraargs} ${console}", commands) self.assertNotIn("initrd tftp://{SERVER_IP}/{RAMDISK}", commands) self.assertIn("boot", commands)
def run(self, connection, max_end_time, args=None): if 'deployment_data' not in self.parameters: return connection if self.parameters["deployment_data"].get('installer_extra_cmd', None): if self.parameters.get('os', None) == "debian_installer": add_late_command(self.get_namespace_data(action='download-action', label='preseed', key='file'), self.parameters["deployment_data"]["installer_extra_cmd"]) if self.parameters.get('os', None) == "centos_installer": ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) overlay = self.get_namespace_data( action='download-action', label='file', key='overlay') substitutions = { '{OVERLAY_URL}': 'tftp://' + ip_addr + '/' + overlay } post_command = substitute([self.parameters["deployment_data"]["installer_extra_cmd"]], substitutions) add_to_kickstart(self.get_namespace_data(action='download-action', label='preseed', key='file'), post_command[0]) return connection
def validate(self): super().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 = "[FLASH_DFU] board_id unset" if self.job.device['usb_vendor_id'] == '0000': self.errors = '[FLASH_DFU] usb_vendor_id unset' if self.job.device['usb_product_id'] == '0000': self.errors = '[FLASH_DFU] 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 = {} for action in self.get_namespace_keys('download-action'): dfu_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 not image_arg or not action_arg: self.errors = "Missing image_arg for %s. " % action continue if not isinstance(image_arg, str): self.errors = "image_arg is not a string (try quoting it)" continue substitutions["{%s}" % action] = action_arg dfu_full_command.extend(self.base_command) dfu_full_command.extend(substitute([image_arg], substitutions)) self.exec_list.append(dfu_full_command) if len(self.exec_list) < 1: self.errors = "No DFU command to execute"
def validate(self): super().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 = "[FLASH_DFU] board_id unset" if self.job.device["usb_vendor_id"] == "0000": self.errors = "[FLASH_DFU] usb_vendor_id unset" if self.job.device["usb_product_id"] == "0000": self.errors = "[FLASH_DFU] 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 = {} for action in self.get_namespace_keys("download-action"): dfu_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 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 not self.exec_list: self.errors = "No DFU command to execute"
def run(self, connection, max_end_time, args=None): connection = super(FlasherAction, self).run(connection, max_end_time, args) # Substitute in the device commands substitutions = {} for key in [ key for key in self.parameters['images'].keys() if key != "yaml_line" ]: filename = self.get_namespace_data(action='download-action', label=key, key='file') filename = filename[len(self.path) + 1:] substitutions["{%s}" % key.upper()] = filename # Add power commands substitutions[ "{HARD_RESET_COMMAND}"] = self.job.device.hard_reset_command substitutions[ "{SOFT_RESET_COMMAND}"] = self.job.device.soft_reset_command substitutions["{PRE_OS_COMMAND}"] = self.job.device.pre_os_command if substitutions["{PRE_OS_COMMAND}"] is None: substitutions["{PRE_OS_COMMAND}"] = '' substitutions[ "{PRE_POWER_COMMAND}"] = self.job.device.pre_power_command if substitutions["{PRE_POWER_COMMAND}"] is None: substitutions["{PRE_POWER_COMMAND}"] = '' substitutions["{POWER_ON_COMMAND}"] = self.job.device.power_command substitutions["{POWER_OFF_COMMAND}"] = self.job.device.get( 'commands', {}).get('power_off', '') # Add some device configuration substitutions["{DEVICE_INFO}"] = yaml.dump( self.job.device.get("device_info", [])) substitutions["{STATIC_INFO}"] = yaml.dump( self.job.device.get("static_info", [])) # Run the commands for cmd in self.commands: cmds = substitute(cmd.split(" "), substitutions) if not self.run_command( cmds, allow_fail=False, allow_silent=True, cwd=self.path): raise JobError("Unable to flash the device") return connection
def test_substitute(): assert substitute(["hello", "world"], {}) == ["hello", "world"] assert substitute(["hello", "world"], {"hello": "strange"}) == ["strange", "world"] assert substitute(["hello", "world"], {"nice": "strange"}) == ["hello", "world"] assert substitute(["hello", "world"], {"nice": "strange"}, drop=True) == [ "hello", "world", ] assert substitute(["hello", "world"], {"hello": None}) == ["hello", "world"] assert substitute(["hello", "world"], {"hello": None}, drop=True) == ["world"] assert substitute(["hello", "world"], {"hello": None}, drop=True, drop_line=False) == ["world"]
def run(self, connection, max_end_time): connection = super().run(connection, max_end_time) # Substitute in the device commands substitutions = {} for key in self.parameters["images"].keys(): filename = self.get_namespace_data(action="download-action", label=key, key="file") filename = filename[len(self.path) + 1:] substitutions["{%s}" % key.upper()] = filename # Add power commands substitutions["{HARD_RESET_COMMAND}"] = str( self.job.device.hard_reset_command) substitutions["{SOFT_RESET_COMMAND}"] = str( self.job.device.soft_reset_command) substitutions["{PRE_OS_COMMAND}"] = str(self.job.device.pre_os_command) if self.job.device.pre_os_command is None: substitutions["{PRE_OS_COMMAND}"] = "" substitutions["{PRE_POWER_COMMAND}"] = str( self.job.device.pre_power_command) if self.job.device.pre_power_command is None: substitutions["{PRE_POWER_COMMAND}"] = "" substitutions["{POWER_ON_COMMAND}"] = str( self.job.device.power_command) substitutions["{POWER_OFF_COMMAND}"] = str( self.job.device.get("commands", {}).get("power_off", "")) # Add some device configuration substitutions["{DEVICE_INFO}"] = yaml_safe_dump( self.job.device.get("device_info", [])) substitutions["{STATIC_INFO}"] = yaml_safe_dump( self.job.device.get("static_info", [])) # Run the commands for cmd in self.commands: cmds = substitute([cmd], substitutions) self.run_cmd(cmds[0], error_msg="Unable to flash the device", cwd=self.path) return connection
def test_qemu_nfs(self): self.assertIsNotNone(self.job) description_ref = self.pipeline_reference('qemu-nfs.yaml') self.assertEqual(description_ref, self.job.pipeline.describe(False)) boot = [action for action in self.job.pipeline.actions if action.name == 'boot-image-retry'][0] qemu = [action for action in boot.internal_pipeline.actions if action.name == 'boot-qemu-image'][0] execute = [action for action in qemu.internal_pipeline.actions if action.name == 'execute-qemu'][0] self.job.validate() self.assertNotEqual([], [line for line in execute.sub_command if line.startswith('-kernel')]) self.assertEqual(1, len([line for line in execute.sub_command if line.startswith('-kernel')])) self.assertIn('vmlinuz', [line for line in execute.sub_command if line.startswith('-kernel')][0]) self.assertNotEqual([], [line for line in execute.sub_command if line.startswith('-initrd')]) self.assertEqual(1, len([line for line in execute.sub_command if line.startswith('-initrd')])) self.assertIn('initrd.img', [line for line in execute.sub_command if line.startswith('-initrd')][0]) self.assertEqual([], [line for line in execute.sub_command if '/dev/nfs' in line]) self.assertEqual([], [line for line in execute.sub_command if 'nfsroot' in line]) args = execute.methods['qemu-nfs']['parameters']['append']['nfsrootargs'] self.assertIn('{NFS_SERVER_IP}', args) self.assertIn('{NFSROOTFS}', args) substitutions = execute.substitutions substitutions["{NFSROOTFS}"] = 'root_dir' params = execute.methods['qemu-nfs']['parameters']['append'] # console=ttyAMA0 root=/dev/nfs nfsroot=10.3.2.1:/var/lib/lava/dispatcher/tmp/dirname,tcp,hard,intr ip=dhcp append = [ 'console=%s' % params['console'], 'root=/dev/nfs', '%s' % substitute([params['nfsrootargs']], substitutions)[0], "%s" % params['ipargs'] ] execute.sub_command.append('--append') execute.sub_command.append('"%s"' % ' '.join(append)) kernel_cmdline = ' '.join(execute.sub_command) self.assertIn('console=ttyAMA0', kernel_cmdline) self.assertIn('/dev/nfs', kernel_cmdline) self.assertIn('root_dir,tcp,hard,intr', kernel_cmdline) self.assertIn('smp', kernel_cmdline) self.assertIn('cpu host', kernel_cmdline)
def test_qemu_nfs(self): self.assertIsNotNone(self.job) description_ref = self.pipeline_reference('qemu-nfs.yaml') self.assertEqual(description_ref, self.job.pipeline.describe(False)) boot = [action for action in self.job.pipeline.actions if action.name == 'boot-image-retry'][0] qemu = [action for action in boot.internal_pipeline.actions if action.name == 'boot-qemu-image'][0] execute = [action for action in qemu.internal_pipeline.actions if action.name == 'execute-qemu'][0] self.job.validate() self.assertNotEqual([], [line for line in execute.sub_command if line.startswith('-kernel')]) self.assertEqual(1, len([line for line in execute.sub_command if line.startswith('-kernel')])) self.assertIn('vmlinuz', [line for line in execute.sub_command if line.startswith('-kernel')][0]) self.assertNotEqual([], [line for line in execute.sub_command if line.startswith('-initrd')]) self.assertEqual(1, len([line for line in execute.sub_command if line.startswith('-initrd')])) self.assertIn('initrd.img', [line for line in execute.sub_command if line.startswith('-initrd')][0]) self.assertEqual([], [line for line in execute.sub_command if '/dev/nfs' in line]) self.assertEqual([], [line for line in execute.sub_command if 'nfsroot' in line]) args = execute.methods['qemu-nfs']['parameters']['append']['nfsrootargs'] self.assertIn('{NFS_SERVER_IP}', args) self.assertIn('{NFSROOTFS}', args) substitutions = execute.substitutions substitutions["{NFSROOTFS}"] = 'root_dir' params = execute.methods['qemu-nfs']['parameters']['append'] # console=ttyAMA0 root=/dev/nfs nfsroot=10.3.2.1:/var/lib/lava/dispatcher/tmp/dirname,tcp,hard,intr ip=dhcp append = [ 'console=%s' % params['console'], 'root=/dev/nfs', '%s' % substitute([params['nfsrootargs']], substitutions)[0], "%s" % params['ipargs'] ] execute.sub_command.append('--append') execute.sub_command.append('"%s"' % ' '.join(append)) kernel_cmdline = ' '.join(execute.sub_command) self.assertIn('console=ttyAMA0', kernel_cmdline) self.assertIn('/dev/nfs', kernel_cmdline) self.assertIn('root_dir,tcp,hard,intr', kernel_cmdline) self.assertIn('smp', kernel_cmdline) self.assertIn('cortex-a57', kernel_cmdline)
def validate(self): super(FlashDFUAction, self).validate() try: boot = self.job.device['actions']['boot']['methods']['dfu'] dfu_binary = which(boot['parameters']['command']) self.base_command = [dfu_binary] self.base_command.extend(boot['parameters'].get('options', [])) if self.job.device['board_id'] == '0000000000': self.errors = "board_id unset" if self.job.device['usb_vendor_id'] == '0000': self.errors = 'usb_vendor_id unset' if self.job.device['usb_product_id'] == '0000': self.errors = 'usb_product_id unset' self.usb_vendor_id = self.job.device['usb_vendor_id'] self.usb_product_id = self.job.device['usb_product_id'] self.board_id = self.job.device['board_id'] self.base_command.extend(['--serial', self.board_id]) self.base_command.extend(['--device', '%s:%s' % (self.usb_vendor_id, self.usb_product_id)]) except AttributeError as exc: raise ConfigurationError(exc) except (KeyError, TypeError): self.errors = "Invalid parameters for %s" % self.name substitutions = {} namespace = self.parameters['namespace'] for action in self.data[namespace]['download-action'].keys(): dfu_full_command = [] image_arg = self.data[namespace]['download-action'][action].get('image_arg', None) action_arg = self.data[namespace]['download-action'][action].get('file', None) if not image_arg or not action_arg: self.errors = "Missing image_arg for %s. " % action continue if not isinstance(image_arg, str): self.errors = "image_arg is not a string (try quoting it)" continue substitutions["{%s}" % action] = action_arg dfu_full_command.extend(self.base_command) dfu_full_command.extend(substitute([image_arg], substitutions)) self.exec_list.append(dfu_full_command) if len(self.exec_list) < 1: self.errors = "No DFU command to execute"
def 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" # nosec unit test support. } sub_command = substitute(sub_command, substitutions) self.assertNotIn("{emptyimage}", sub_command) self.assertNotIn( "/tmp/tmp.00000/hd.img", sub_command # nosec unit test support. ) self.assertIn( "/tmp/tmp.00000/hd.img", " ".join(sub_command) # nosec unit test support. )
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", "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_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, "") job.logger = DummyLogger() 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.assertNotIn('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] overlay = [action for action in u_boot_action.internal_pipeline.actions if action.name == 'bootloader-overlay'][0] self.assertIsNotNone(overlay.get_namespace_data(action='storage-deploy', label='u-boot', key='device')) 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_namespace_data(action='storage-deploy', label='u-boot', key='device') self.assertIsNotNone(device_id) kernel_type = u_boot_action.parameters['kernel_type'] bootcommand = map_kernel_uboot(kernel_type, device_params=cubie.get('parameters', None)) substitutions = { '{BOOTX}': "%s %s %s %s" % ( bootcommand, cubie['parameters'][bootcommand]['kernel'], cubie['parameters'][bootcommand]['ramdisk'], cubie['parameters'][bootcommand]['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_uefi_job(self, which_mock): 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", job=self.job) self.assertEqual(description_ref, self.job.pipeline.describe(False)) # just dummy strings substitution_dictionary = { "{SERVER_IP}": "10.4.0.1", "{RAMDISK}": None, "{KERNEL}": "uImage", "{DTB}": "mustang.dtb", "{NFSROOTFS}": "tmp/tmp21dfed/", "{TEST_MENU_NAME}": "LAVA NFS Test Image", } for block in selector.items: if "select" in block: if "enter" in block["select"]: block["select"]["enter"] = substitute( [block["select"]["enter"]], substitution_dictionary) if "items" in block["select"]: block["select"]["items"] = substitute( block["select"]["items"], substitution_dictionary) count = 0 check_block = [ { "items": ["Boot Manager"], "wait": "Choice:" }, { "items": ["Remove Boot Device Entry"], "fallback": "Return to Main Menu", "wait": "Delete entry", }, { "items": ["LAVA NFS Test Image"], "wait": "Choice:" }, { "items": ["Add Boot Device Entry"], "wait": "Select the Boot Device:" }, { "items": ["TFTP on MAC Address: 00:01:73:69:5A:EF"], "wait": "Get the IP address from DHCP:", }, { "enter": ["y"], "wait": "Get the TFTP server IP address:" }, { "enter": ["10.4.0.1"], "wait": "File path of the EFI Application or the kernel :", }, { "enter": ["uImage"], "wait": "Is an EFI Application?" }, { "enter": ["n"], "wait": "Boot Type:" }, { "enter": ["f"], "wait": "Add an initrd:" }, { "enter": ["n"], "wait": "Get the IP address from DHCP:" }, { "enter": ["y"], "wait": "Get the TFTP server IP address:" }, { "enter": ["10.4.0.1"], "wait": "File path of the FDT :" }, { "enter": ["mustang.dtb"], "wait": "Arguments to pass to the binary:" }, { "enter": [ "console=ttyS0,115200 earlyprintk=uart8250-32bit,0x1c020000 debug root=/dev/nfs rw " "nfsroot=10.4.0.1:tmp/tmp21dfed/,tcp,hard,intr ip=dhcp" ], "wait": "Description for this new Entry:", }, { "enter": ["LAVA NFS Test Image"], "wait": "Choice:" }, { "items": ["Return to main menu"], "wait": "Start:" }, { "items": ["LAVA NFS Test Image"] }, ] for item in selector.items: self.assertEqual(item["select"], check_block[count]) count += 1
def test_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', '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 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, '{DEVICE}': device_path } download_cmd = None download_params = self.parameters.get('download') if download_params: download_options = substitute([download_params['options']], substitutions)[0] download_cmd = ' '.join([download_params['tool'], download_options]) writer_params = self.parameters.get('writer') if writer_params: tool_options = substitute([writer_params['options']], substitutions)[0] tool_cmd = [writer_params['tool'], tool_options] else: tool_cmd = ["dd of='{}' bs=4M".format(device_path)] # busybox dd does not support other flags if self.tool_flags: tool_cmd.append(self.tool_flags) cmd = ' '.join(tool_cmd) cmd_line = ' '.join([download_cmd, '|', cmd]) if download_cmd else cmd # set prompt to either `download' or `writer' prompt to ensure that the # secondary deployment has started prompt_string = connection.prompt_str prompt_param = download_params or writer_params connection.prompt_str = prompt_param['prompt'] self.logger.debug("Changing prompt to %s", connection.prompt_str) connection.sendline(cmd_line) self.wait(connection) if not self.valid: self.logger.error(self.errors) # change prompt string to list of dd outputs connection.prompt_str = self.tool_prompts self.logger.debug("Changing prompt to %s", connection.prompt_str) self.wait(connection) # set prompt back once secondary deployment is complete connection.prompt_str = prompt_string self.logger.debug("Changing prompt to %s", connection.prompt_str) self.set_namespace_data(action='shared', label='shared', key='connection', value=connection) return connection
def test_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("cpu host", kernel_cmdline)
def test_substitutions(self, which_mock): """ 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() (rendered, _) = self.factory.create_device("cubie1.jinja2") cubie = NewDevice(yaml_safe_load(rendered)) 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, "") job.logger = DummyLogger() 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.assertNotIn("type", boot_params) self.assertGreater(len(job.pipeline.actions), 1) self.assertIsNotNone(job.pipeline.actions[1].pipeline) u_boot_action = [ action for action in job.pipeline.actions if action.name == "uboot-action" ][1] overlay = [ action for action in u_boot_action.pipeline.actions if action.name == "bootloader-overlay" ][0] self.assertIsNotNone( overlay.get_namespace_data(action="storage-deploy", label="u-boot", key="device")) 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_namespace_data(action="storage-deploy", label="u-boot", key="device") self.assertIsNotNone(device_id) kernel_type = u_boot_action.parameters["kernel_type"] bootcommand = map_kernel_uboot(kernel_type, device_params=cubie.get("parameters")) substitutions = { "{BOOTX}": "%s %s %s %s" % ( bootcommand, cubie["parameters"][bootcommand]["kernel"], cubie["parameters"][bootcommand]["ramdisk"], cubie["parameters"][bootcommand]["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) print(commands) self.assertEqual( commands, [ "usb start", "setenv autoload no", "load usb 0:0:1 {KERNEL_ADDR} /boot/vmlinuz-3.16.0-4-armmp-lpae", "load usb 0:0:1 {RAMDISK_ADDR} /boot/initrd.img-3.16.0-4-armmp-lpae.u-boot", "setenv initrd_size ${filesize}", "load usb 0:0:1 {DTB_ADDR} /boot/dtb-3.16.0-4-armmp-lpae", "console=ttyS0,115200n8 root=UUID=159d17cc-697c-4125-95a0-a3775e1deabe ip=dhcp", "bootz 0x42000000 0x43300000 0x43000000", ], )
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."
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', '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, which_mock): parameters = { "dispatcher": {}, # fake dispatcher parameter. Normally added by parser "device_type": "beaglebone-black", "job_name": "uboot-pipeline", "job_timeout": "15m", "action_timeout": "5m", "priority": "medium", "actions": { "boot": { "namespace": "common", "method": "u-boot", "commands": "ramdisk", "prompts": ["linaro-test", "root@debian:~#"], }, "deploy": { "namespace": "common", "ramdisk": { "url": "initrd.gz", "compression": "gz" }, "kernel": { "url": "zImage", "type": "zimage" }, "dtb": { "url": "broken.dtb" }, }, }, } data = yaml_safe_load(Factory().create_device("bbb-01.jinja2")[0]) device = NewDevice(data) job = Job(4212, parameters, None) job.device = device pipeline = Pipeline(job=job, parameters=parameters["actions"]["boot"]) job.pipeline = pipeline overlay = BootloaderCommandOverlay() connection = MagicMock() connection.timeout = MagicMock() pipeline.add_action(overlay) overlay.set_namespace_data( action="uboot-prepare-kernel", label="bootcommand", key="bootcommand", value="bootz", ) overlay.validate() overlay.run(connection, 100) ip_addr = dispatcher_ip(None) parsed = [] kernel_addr = job.device["parameters"][overlay.bootcommand]["ramdisk"] ramdisk_addr = job.device["parameters"][overlay.bootcommand]["ramdisk"] dtb_addr = job.device["parameters"][overlay.bootcommand]["dtb"] kernel = parameters["actions"]["deploy"]["kernel"]["url"] ramdisk = parameters["actions"]["deploy"]["ramdisk"]["url"] dtb = parameters["actions"]["deploy"]["dtb"]["url"] 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.bootcommand, 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("tftp 0x83000000 zImage", commands) self.assertIn("tftp 0x83000000 initrd.gz", commands) self.assertIn("setenv initrd_size ${filesize}", commands) self.assertIn("tftp 0x88000000 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.bootcommand, 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.assertNotIn("setenv kernel_addr_r '{KERNEL_ADDR}'", parsed) self.assertNotIn("setenv initrd_addr_r '{RAMDISK_ADDR}'", parsed) self.assertNotIn("setenv fdt_addr_r '{DTB_ADDR}'", parsed)
def run(self, connection, max_end_time, args=None): """ Read data from the download action and replace in context Use common data for all values passed into the substitutions so that multiple actions can use the same code. """ # Multiple deployments would overwrite the value if parsed in the validate step. # FIXME: implement isolation for repeated steps. connection = super(BootloaderCommandOverlay, self).run(connection, max_end_time, args) ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) self.ram_disk = self.get_namespace_data(action='compress-ramdisk', label='file', key='ramdisk') # most jobs substitute RAMDISK, so also use this for the initrd if self.get_namespace_data(action='nbd-deploy', label='nbd', key='initrd'): self.ram_disk = self.get_namespace_data(action='download-action', label='file', key='initrd') 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.ram_disk, '{INITRD}': self.ram_disk, '{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: self.logger.debug("%s", self.job.device['parameters']) 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') \ and not self.get_namespace_data(action='download-action', label='file', key='initrd'): 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 = '-' if self.get_namespace_data(action='download-action', label='file', key='initrd'): # no u-boot header, thus no embedded size, so we have to add it to the # boot cmd with colon after the ramdisk substitutions['{BOOTX}'] = "%s %s %s:%s %s" % ( self.bootcommand, kernel_addr, ramdisk_addr, '${initrd_size}', dtb_addr) else: 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') nbd_root = self.get_namespace_data(action='download-action', label='file', key='nbdroot') if nbd_root: substitutions['{NBDSERVERIP}'] = str(self.get_namespace_data(action='nbd-deploy', label='nbd', key='nbd_server_ip')) substitutions['{NBDSERVERPORT}'] = str(self.get_namespace_data(action='nbd-deploy', label='nbd', key='nbd_server_port')) substitutions['{ROOT}'] = self.get_namespace_data(action='bootloader-from-media', label='uuid', key='root') # UUID label, not a file substitutions['{ROOT_PART}'] = self.get_namespace_data(action='bootloader-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