def test_dispatcher_ip_nfs(): assert (dispatcher_ip( { "dispatcher_ip": "127.0.0.1", "dispatcher_nfs_ip": "192.168.1.2" }, "nfs") == "192.168.1.2") # Fall back to dispatcher_ip if dispatcher_nfs_ip is missing. assert dispatcher_ip({"dispatcher_ip": "127.0.0.1"}, "nfs") == "127.0.0.1"
def test_dispatcher_ip_http_port(): assert (dispatcher_ip( { "dispatcher_ip": "127.0.0.1", "dispatcher_http_ip": "192.168.1.2:8080" }, "http", ) == "192.168.1.2:8080") # Fall back to dispatcher_ip if dispatcher_http_ip is missing. assert dispatcher_ip({"dispatcher_ip": "127.0.0.1"}, "http") == "127.0.0.1"
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): 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): 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): if not self.parameters.get(self.param_key): # idempotency return connection connection = super().run(connection, max_end_time) if "prefix" in self.parameters[self.param_key]: prefix = self.parameters[self.param_key]["prefix"] self.logger.warning( "Adding '%s' prefix, any other content will not be visible.", prefix) # Grab the path already defined in super().run() and add the prefix root_dir = self.get_namespace_data(action="extract-rootfs", label="file", key=self.file_key) root_dir = os.path.join(root_dir, prefix) # sets the directory into which the overlay is unpacked and which # is used in the substitutions into the bootloader command string. self.set_namespace_data(action="extract-rootfs", label="file", key=self.file_key, value=root_dir) self.job.device["dynamic_data"][ "NFS_ROOTFS"] = self.get_namespace_data(action="extract-rootfs", label="file", key=self.file_key) self.job.device["dynamic_data"]["NFS_SERVER_IP"] = dispatcher_ip( self.job.parameters["dispatcher"]) return connection
def set_port(self, action): msg = {"data": {"nbd_server_port": 10809}} nbd_port = self.parameters["protocols"]["lava-xnbd"]["port"] if nbd_port == "auto": self.logger.debug("Get a port from pool") nbd_port = get_free_port(self.parameters["dispatcher"]) self.ports.append(nbd_port) msg["data"]["nbd_server_port"] = nbd_port action.set_namespace_data( "nbd-deploy", label="nbd", key="nbd_server_port", value=nbd_port, parameters=action.parameters, ) nbd_ip = dispatcher_ip(self.parameters["dispatcher"]) action.set_namespace_data( "nbd-deploy", label="nbd", key="nbd_server_ip", value=nbd_ip, parameters=action.parameters, ) self.logger.debug("Set_port %d", nbd_port) return msg["data"]
def validate(self): super(QemuCommandLine, self).validate() boot = self.job.device['actions']['boot']['methods']['qemu'] qemu_binary = which(boot['parameters']['command']) self.sub_command = [qemu_binary] self.sub_command.extend(boot['parameters'].get('options', [])) boot_opts = boot['parameters'].get('boot_options', None) if boot_opts: self.console = "console=%s" % boot_opts['console'] self.boot_order = "-boot %s" % boot_opts['boot_order'] if not qemu_binary or not self.console or not self.boot_order: self.errors = "Invalid parameters for %s" % self.name # create the preseed.cfg url # needs to be an IP address for DI, DNS is not available. # PRESEED_URL='http://10.15.0.32/tmp/d-i/jessie/preseed.cfg' ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) self.preseed_url = 'tftp://%s/' % ip_addr self.sub_command.append(' -drive format=raw,file={emptyimage} ') self.sub_command.append(self.boot_order) self.command_line = " -append '%s console=tty0 console=tty1 %s %s %s %s preseed/url=%s{preseed} --- %s ' " % ( self.parameters['deployment_data']['base'], self.parameters['deployment_data']['locale'], self.console, self.parameters['deployment_data']['keymaps'], self.parameters['deployment_data']['netcfg'], self.preseed_url, self.console) self.set_namespace_data(action=self.name, label=self.name, key='prompts', value=self.parameters['deployment_data']['prompts']) self.set_namespace_data(action=self.name, label=self.name, key='append', value=self.command_line)
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 run(self, connection, max_end_time, args=None): if 'deployment_data' not in self.parameters: return connection if self.parameters["deployment_data"].get('installer_extra_cmd', None): if self.parameters.get('os', None) == "debian_installer": add_late_command( self.get_namespace_data(action='download-action', label='preseed', key='file'), self.parameters["deployment_data"]["installer_extra_cmd"]) if self.parameters.get('os', None) == "centos_installer": ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) overlay = self.get_namespace_data(action='download-action', label='file', key='overlay') substitutions = { '{OVERLAY_URL}': 'tftp://' + ip_addr + '/' + overlay } post_command = substitute([ self.parameters["deployment_data"]["installer_extra_cmd"] ], substitutions) add_to_kickstart( self.get_namespace_data(action='download-action', label='preseed', key='file'), post_command[0]) return connection
def run(self, connection, max_end_time): connection = super().run(connection, max_end_time) if not connection: raise LAVABug("Cannot transfer overlay, no connection available.") overlay_full_path = self.get_namespace_data(action="compress-overlay", label="output", key="file") if not overlay_full_path: raise JobError("No overlay file identified for the transfer.") if not overlay_full_path.startswith(DISPATCHER_DOWNLOAD_DIR): raise ConfigurationError( "overlay should already be in DISPATCHER_DOWNLOAD_DIR") overlay_path = overlay_full_path[len(DISPATCHER_DOWNLOAD_DIR) + 1:] overlay = os.path.basename(overlay_path) connection.sendline("rm %s" % overlay) connection.wait() cmd = self.parameters["transfer_overlay"]["download_command"] ip_addr = dispatcher_ip(self.job.parameters["dispatcher"], "http") connection.sendline("%s http://%s/tmp/%s" % (cmd, ip_addr, overlay_path)) connection.wait() unpack = self.parameters["transfer_overlay"]["unpack_command"] connection.sendline(unpack + " " + overlay) connection.wait() 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 populate(self, parameters): self.tftp_dir = self.mkdtemp(override=tftpd_dir()) self.internal_pipeline = Pipeline(parent=self, job=self.job, parameters=parameters) self.set_namespace_data(action=self.name, label='tftp', key='tftp_dir', value=self.tftp_dir, parameters=parameters) for key in ['initrd', 'kernel', 'dtb', 'nbdroot']: if key in parameters: download = DownloaderAction(key, path=self.tftp_dir) download.max_retries = 3 # overridden by failure_retry in the parameters, if set. self.internal_pipeline.add_action(download) if key == 'initrd': self.set_namespace_data(action="tftp-deploy", label='tftp', key='ramdisk', value=True, parameters=parameters) self.set_namespace_data(action=self.name, label='nbd', key='initrd', value=True, parameters=parameters) # prepare overlay self.internal_pipeline.add_action(OverlayAction()) # setup values for protocol and later steps self.set_namespace_data(action=self.name, label='nbd', key='initrd', value=True, parameters=parameters) # store in parameters for protocol 'xnbd' to tear-down xnbd-server # and store in namespace for boot action # ip parameters['lava-xnbd'] = {} self.nbd_ip = dispatcher_ip(self.job.parameters['dispatcher']) parameters['lava-xnbd']['ip'] = self.nbd_ip self.set_namespace_data(action=self.name, label='nbd', key='nbd_server_ip', value=self.nbd_ip, parameters=parameters) # port self.nbd_port = get_free_port(self.job.parameters['dispatcher']) parameters['lava-xnbd']['port'] = self.nbd_port self.set_namespace_data(action=self.name, label='nbd', key='nbd_server_port', value=self.nbd_port, parameters=parameters) # handle XnbdAction next - bring-up xnbd-server self.internal_pipeline.add_action(XnbdAction())
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_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 target_extract(self, connection, dest): """ 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. """ d_file = self.get_namespace_data(action='download-action', label='rootfs', key='file') if not d_file: self.logger.debug("Skipping %s - nothing downloaded") return connection decompressed_image = os.path.basename(d_file) decompression_char = '' if decompressed_image.endswith('.gz') or decompressed_image.endswith('.tgz'): decompression_char = 'z' elif decompressed_image.endswith('.bz2'): decompression_char = 'j' elif decompressed_image.endswith('.tar'): decompression_char = '' else: raise JobError('bad file extension: %s' % tar_url) storage_suffix = self.get_namespace_data(action='qemudev-deploy', label='storage', key='suffix') if not storage_suffix: storage_suffix = '' suffix = "%s/%s" % ("tmp", storage_suffix) self.logger.debug("suffix : %s", 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 ) download_cmd = 'ping 8.8.8.8 -c 4 ;' +\ 'wget --no-check-certificate --no-proxy ' +\ '--connect-timeout=30 -S --progress=dot -e dotbytes=2M ' +\ '-O- %s' % (download_url) extract_cmd = 'tar --warning=no-timestamp --numeric-owner -C %s -x%sf -' % (dest, decompression_char) prompt_string = connection.prompt_str connection.prompt_str = 'written to stdout' self.logger.debug("Changing prompt to %s", connection.prompt_str) connection.sendline("%s | %s" % (download_cmd, extract_cmd)) self.wait(connection) if not self.valid: self.logger.error(self.errors) 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_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 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, 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_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() self.scripts_to_copy = sorted( glob.glob(os.path.join(self.lava_test_dir, "lava-*"))) lava_test_results_dir = self.get_constant("lava_test_results_dir", "posix") lava_test_results_dir = lava_test_results_dir % self.job.job_id self.set_namespace_data( action="test", label="results", key="lava_test_results_dir", value=lava_test_results_dir, ) lava_test_sh_cmd = self.get_constant("lava_test_sh_cmd", "posix") self.set_namespace_data( action="test", label="shared", key="lava_test_sh_cmd", value=lava_test_sh_cmd, ) # Add distro support scripts - only if deployment_data is set distro = self.parameters.get("deployment_data", {}).get("distro") if distro: distro_support_dir = "%s/distro/%s" % (self.lava_test_dir, distro) self.scripts_to_copy += sorted( glob.glob(os.path.join(distro_support_dir, "lava-*"))) if not self.scripts_to_copy: self.logger.debug("Skipping lava_test_shell support scripts.") if "parameters" in self.job.device: if "interfaces" in self.job.device["parameters"]: if "target" in self.job.device["parameters"]["interfaces"]: self.target_mac = self.job.device["parameters"][ "interfaces"]["target"].get("mac", "") self.target_ip = self.job.device["parameters"][ "interfaces"]["target"].get("ip", "") for device in self.job.device.get("static_info", []): if "probe_channel" in device and "probe_ip" in device: self.probe_channel = device["probe_channel"] self.probe_ip = device["probe_ip"] break self.dispatcher_ip = dispatcher_ip(self.job.parameters["dispatcher"])
def run(self, connection, max_end_time, args=None): connection = super(OverlayUnpack, self).run(connection, max_end_time, args) if not connection: raise LAVABug("Cannot transfer overlay, no connection available.") ip_addr = dispatcher_ip(self.job.parameters['dispatcher']) overlay_full_path = self.get_namespace_data(action='compress-overlay', label='output', key='file') if not overlay_full_path: raise JobError("No overlay file identified for the transfer.") if not overlay_full_path.startswith(DISPATCHER_DOWNLOAD_DIR): raise ConfigurationError("overlay should already be in DISPATCHER_DOWNLOAD_DIR") overlay_path = overlay_full_path[len(DISPATCHER_DOWNLOAD_DIR) + 1:] overlay = os.path.basename(overlay_path) dwnld = self.parameters['transfer_overlay']['download_command'] dwnld += " http://%s/tmp/%s" % (ip_addr, overlay_path) unpack = self.parameters['transfer_overlay']['unpack_command'] unpack += ' ' + overlay connection.sendline("rm %s; %s && %s" % (overlay, dwnld, unpack)) return connection
def run(self, connection, max_end_time): location = self.get_namespace_data(action="test", label="shared", key="location") docker_image = self.get_namespace_data(action="deploy-docker", label="image", key="name") # Build the command line # The docker image is safe to be included in the command line cmd = "docker" + self.remote + " run --rm --interactive --tty --hostname lava" cmd += " --name %s" % self.container if self.test_needs_overlay(self.parameters): overlay = self.get_namespace_data(action="test", label="results", key="lava_test_results_dir") if not self.remote: cmd += " --volume %s:%s" % ( os.path.join(location, overlay.strip("/")), overlay, ) else: cmd += ( ' --mount type=volume,volume-driver=local,dst=%s,volume-opt=type=nfs,volume-opt=device=:%s,"volume-opt=o=addr=%s"' % ( overlay, os.path.join(location, overlay.strip("/")), dispatcher_ip(self.job.parameters["dispatcher"]), )) cmd += self.extra_options cmd += " %s %s" % (docker_image, self.parameters["command"]) self.logger.debug("Boot command: %s", cmd) shell = ShellCommand(cmd, self.timeout, logger=self.logger) self.cleanup_required = True shell_connection = ShellSession(self.job, shell) shell_connection = super().run(shell_connection, max_end_time) self.set_namespace_data(action="shared", label="shared", key="connection", value=shell_connection) return shell_connection
def validate(self): super().validate() self.command = self.parameters.get("command", "/sbin/kexec") self.load_command = self.command[:] # local copy for idempotency if self.parameters.get("deploy", False): initrd_path = self.get_namespace_data(action="download-action", label="initrd", key="file") ip_addr = dispatcher_ip(self.job.parameters["dispatcher"], "http") self.append_deploy_cmd("kernel", ip_addr) self.append_deploy_cmd("initrd", ip_addr) self.append_deploy_cmd("dtb", ip_addr) self.logger.debug("deploy commands:") for cmd in self.deploy_commands: self.logger.info("- %s", cmd) # If on_panic is set, crash the kernel instead of calling "kexec -e" if self.parameters.get("on_panic", False): self.command = "echo c > /proc/sysrq-trigger" else: self.command += " -e" # If on_panic is set, use --load-panic instead of --load if "kernel" in self.parameters: if self.parameters.get("on_panic", False): self.load_command += " --load-panic %s" % self.parameters[ "kernel"] else: self.load_command += " --load %s" % self.parameters["kernel"] if "dtb" in self.parameters: self.load_command += " --dtb %s" % self.parameters["dtb"] if "initrd" in self.parameters: self.load_command += " --initrd %s" % self.parameters["initrd"] if "options" in self.parameters: for option in self.parameters["options"]: self.load_command += " %s" % option if self.load_command == "/sbin/kexec": self.errors = "Default kexec handler needs at least a kernel to pass to the --load command"
def validate(self): super().validate() boot = self.job.device["actions"]["boot"]["methods"]["qemu"] qemu_binary = which(boot["parameters"]["command"]) self.sub_command = [qemu_binary] self.sub_command.extend(boot["parameters"].get("options", [])) boot_opts = boot["parameters"].get("boot_options") if boot_opts: self.console = "console=%s" % boot_opts["console"] self.boot_order = "-boot %s" % boot_opts["boot_order"] if not qemu_binary or not self.console or not self.boot_order: self.errors = "Invalid parameters for %s" % self.name # create the preseed.cfg url # needs to be an IP address for DI, DNS is not available. # PRESEED_URL='http://10.15.0.32/tmp/d-i/jessie/preseed.cfg' ip_addr = dispatcher_ip(self.job.parameters["dispatcher"], "tftp") self.preseed_url = "tftp://%s/" % ip_addr self.sub_command.append(" -drive format=raw,file={emptyimage} ") self.sub_command.append(self.boot_order) self.command_line = ( " -append '%s console=tty0 console=tty1 %s %s %s %s preseed/url=%s{preseed} --- %s ' " % ( self.parameters["deployment_data"]["base"], self.parameters["deployment_data"]["locale"], self.console, self.parameters["deployment_data"]["keymaps"], self.parameters["deployment_data"]["netcfg"], self.preseed_url, self.console, )) self.set_namespace_data( action=self.name, label=self.name, key="prompts", value=self.parameters["deployment_data"]["prompts"], ) self.set_namespace_data(action=self.name, label=self.name, key="append", value=self.command_line)
def set_port(self, action): msg = {'data': {'nbd_server_port': 10809}} nbd_port = self.parameters['protocols']['lava-xnbd']['port'] if nbd_port == 'auto': self.logger.debug("Get a port from pool") nbd_port = get_free_port(self.parameters['dispatcher']) self.ports.append(nbd_port) msg['data']['nbd_server_port'] = nbd_port action.set_namespace_data('nbd-deploy', label='nbd', key='nbd_server_port', value=nbd_port, parameters=action.parameters) nbd_ip = dispatcher_ip(self.parameters['dispatcher']) action.set_namespace_data('nbd-deploy', label='nbd', key='nbd_server_ip', value=nbd_ip, parameters=action.parameters) self.logger.debug("Set_port %d", nbd_port) return msg['data']
def get_manipulated_command(self, cmd): if self.driver.is_container and self.driver.docker_options: ip_addr = dispatcher_ip(self.job.parameters["dispatcher"]) root_location = self.get_namespace_data(action="uuu-deploy", label="uuu-images", key="root_location") cmd = [ "mkdir", "-p", root_location, "&&", "mount", "-t", "nfs", "-o", "nolock", ip_addr + ":" + root_location, root_location, "&&", ] + cmd cmd = ["bash", "-c", " ".join(cmd)] return cmd
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): # 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): """ 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
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 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): """ 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().run(connection, max_end_time) 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"]) # 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"], "http") path = d_file[len(DISPATCHER_DOWNLOAD_DIR) + 1:] download_url = "http://%s/tmp/%s" % (ip_addr, path) 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 validate(self): super().validate() # 'arch' must be defined in job definition context. architecture = self.job.parameters["context"]["arch"] if "available_architectures" not in self.job.device: self.errors = "Device lacks list of available architectures." try: if architecture 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 architecture in ["amd64", "x86_64"]: ver_str = debian_package_version(pkg="qemu-system-x86", split=False) arch_str = debian_package_arch(pkg="qemu-system-x86") self.qemu_data = { "qemu_version": ver_str, "host_arch": arch_str, "job_arch": architecture, } self.logger.info( "qemu-system-x86, installed at version: %s, host architecture: %s", ver_str, arch_str, ) if architecture in ["arm64", "arm", "armhf", "aarch64"]: ver_str = debian_package_version(pkg="qemu-system-arm", split=False) arch_str = debian_package_arch(pkg="qemu-system-arm") self.qemu_data = { "qemu_version": ver_str, "host_arch": arch_str, "job_arch": architecture, } self.logger.info( "qemu-system-arm, installed at version: %s, host architecture: %s", ver_str, arch_str, ) 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 for label in self.get_namespace_keys("download-action"): 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."