def _ata_erase(self, block_device): security_lines = self._get_ata_security_lines(block_device) # If secure erase isn't supported return False so erase_block_device # can try another mechanism. Below here, if secure erase is supported # but fails in some way, error out (operators of hardware that supports # secure erase presumably expect this to work). if 'supported' not in security_lines: return False if 'enabled' in security_lines: raise errors.BlockDeviceEraseError(('Block device {0} already has ' 'a security password set').format(block_device.name)) if 'not frozen' not in security_lines: raise errors.BlockDeviceEraseError(('Block device {0} is frozen ' 'and cannot be erased').format(block_device.name)) utils.execute('hdparm', '--user-master', 'u', '--security-set-pass', 'NULL', block_device.name) utils.execute('hdparm', '--user-master', 'u', '--security-erase', 'NULL', block_device.name) # Verify that security is now 'not enabled' security_lines = self._get_ata_security_lines(block_device) if 'not enabled' not in security_lines: raise errors.BlockDeviceEraseError(('An unknown error occurred ' 'erasing block device {0}').format(block_device.name)) return True
def _add_vlan_interface(interface, vlan, interfaces_list): vlan_name = interface + '.' + vlan # if any(x for x in interfaces_list if x.name == vlan_name): if any(x.name == vlan_name for x in interfaces_list): LOG.info("VLAN interface %s has already been added", vlan_name) return '' try: LOG.info('Adding VLAN interface %s', vlan_name) # Add the interface utils.execute('ip', 'link', 'add', 'link', interface, 'name', vlan_name, 'type', 'vlan', 'id', vlan, check_exit_code=[0, 2]) # Bring up interface utils.execute('ip', 'link', 'set', 'dev', vlan_name, 'up') except Exception as exc: LOG.warning( 'Exception when running ip commands to add VLAN ' 'interface: %s', exc) return '' return vlan_name
def _run_shutdown_command(self, command): """Run the shutdown or reboot command :param command: A string having the command to be run. :raises: InvalidCommandParamsError if the passed command is not equal to poweroff or reboot. :raises: SystemRebootError if the command errors out with an unsuccessful exit code. """ if command not in ('reboot', 'poweroff'): msg = (('Expected the command "poweroff" or "reboot" ' 'but received "%s".') % command) raise errors.InvalidCommandParamsError(msg) try: self.sync() except errors.CommandExecutionError as e: LOG.warning('Failed to sync file system buffers: % s', e) try: _, stderr = utils.execute(command, use_standard_locale=True, check_exit_code=[0]) if 'ignoring request.' in stderr: LOG.debug( '%s command failed with error %s, ' 'falling back to sysrq-trigger.', command, stderr) if command == 'poweroff': utils.execute("echo o > /proc/sysrq-trigger", shell=True) elif command == 'reboot': utils.execute("echo b > /proc/sysrq-trigger", shell=True) except processutils.ProcessExecutionError as e: raise errors.SystemRebootError(e.exit_code, e.stdout, e.stderr)
def test_no_exception_raised_for_execute(self, mock_exec): # Make sure we can call utils.execute() even though we didn't mock it. # We do mock ironic_lib.utils.execute() so we don't actually execute # anything. utils.execute("ls") utils.execute("echo") self.assertEqual(2, mock_exec.call_count)
def _get_vmedia_params(): """This method returns the parameters passed to the agent through virtual media floppy. """ vmedia_mount_point = "/vmedia_mnt" parameters_file = "parameters.txt" vmedia_device = _get_vmedia_device() if not vmedia_device: msg = "Unable to find virtual media device" raise errors.VirtualMediaBootError(msg) vmedia_device_file = os.path.join("/dev", vmedia_device) os.mkdir(vmedia_mount_point) try: stdout, stderr = utils.execute("mount", vmedia_device_file, vmedia_mount_point) except processutils.ProcessExecutionError as e: msg = ("Unable to mount virtual media device %(device)s: %(error)s" % { 'device': vmedia_device_file, 'error': e }) raise errors.VirtualMediaBootError(msg) parameters_file_path = os.path.join(vmedia_mount_point, parameters_file) params = _read_params_from_file(parameters_file_path) try: stdout, stderr = utils.execute("umount", vmedia_mount_point) except processutils.ProcessExecutionError as e: pass return params
def get_system_vendor_info(self): product_name = None serial_number = None manufacturer = None try: out, _e = utils.execute("dmidecode --type system", shell=True) except (processutils.ProcessExecutionError, OSError) as e: LOG.warning("Cannot get system vendor information: %s", e) else: for line in out.split('\n'): line_arr = line.split(':', 1) if len(line_arr) != 2: continue if line_arr[0].strip() == 'Product Name': product_name = line_arr[1].strip() elif line_arr[0].strip() == 'Serial Number': serial_number = line_arr[1].strip() elif line_arr[0].strip() == 'Manufacturer': manufacturer = line_arr[1].strip() try: out, _e = utils.execute("dmidecode -s chassis-asset-tag", shell=True) except (processutils.ProcessExecutionError, OSError) as e: LOG.warning("Cannot get system vendor asset tag: %s", e) else: out_list = out.strip().split('\n') asset_tag = out_list[len(out_list) - 1] return SystemVendorInfo(product_name=product_name, serial_number=serial_number, manufacturer=manufacturer, asset_tag=asset_tag)
def _run_shutdown_command(self, command): """Run the shutdown or reboot command :param command: A string having the command to be run. :raises: InvalidCommandParamsError if the passed command is not equal to poweroff or reboot. :raises: SystemRebootError if the command errors out with an unsuccessful exit code. """ if command not in ('reboot', 'poweroff'): msg = (('Expected the command "poweroff" or "reboot" ' 'but received "%s".') % command) raise errors.InvalidCommandParamsError(msg) try: self.sync() except errors.CommandExecutionError as e: LOG.warning('Failed to sync file system buffers: % s', e) try: _, stderr = utils.execute(command, use_standard_locale=True, check_exit_code=[0]) if 'ignoring request.' in stderr: LOG.debug('%s command failed with error %s, ' 'falling back to sysrq-trigger.', command, stderr) if command == 'poweroff': utils.execute("echo o > /proc/sysrq-trigger", shell=True) elif command == 'reboot': utils.execute("echo b > /proc/sysrq-trigger", shell=True) except processutils.ProcessExecutionError as e: raise errors.SystemRebootError(e.exit_code, e.stdout, e.stderr)
def _get_vmedia_params(): """This method returns the parameters passed to the agent through virtual media floppy. :returns: a partial dict of potential agent configuration parameters :raises: VirtualMediaBootError when it cannot find the virtual media device """ vmedia_mount_point = "/vmedia_mnt" parameters_file = "parameters.txt" vmedia_device = _get_vmedia_device() if not vmedia_device: msg = "Unable to find virtual media device" raise errors.VirtualMediaBootError(msg) vmedia_device_file = os.path.join("/dev", vmedia_device) os.mkdir(vmedia_mount_point) try: stdout, stderr = utils.execute("mount", vmedia_device_file, vmedia_mount_point) except processutils.ProcessExecutionError as e: msg = ("Unable to mount virtual media device %(device)s: %(error)s" % {'device': vmedia_device_file, 'error': e}) raise errors.VirtualMediaBootError(msg) parameters_file_path = os.path.join(vmedia_mount_point, parameters_file) params = _read_params_from_file(parameters_file_path) try: stdout, stderr = utils.execute("umount", vmedia_mount_point) except processutils.ProcessExecutionError as e: pass return params
def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition): """Executes efibootmgr and removes duplicate entries. :param valid_efi_bootloaders: the list of valid efi bootloaders :param device: the device to be used :param efi_partition: the efi partition on the device """ # Before updating let's get information about the bootorder LOG.debug("Getting information about boot order") utils.execute('efibootmgr') # NOTE(iurygregory): regex used to identify the Warning in the stderr after # we add the new entry. Example: # "efibootmgr: ** Warning ** : Boot0004 has same label ironic" duplicated_label = re.compile(r'^.*:\s\*\*.*\*\*\s:\s.*' r'Boot([0-9a-f-A-F]+)\s.*$') label_id = 1 for v_efi_bl_path in valid_efi_bootloaders: # Update the nvram using efibootmgr # https://linux.die.net/man/8/efibootmgr label = 'ironic' + str(label_id) LOG.debug("Adding loader %(path)s on partition %(part)s of device " " %(dev)s", {'path': v_efi_bl_path, 'part': efi_partition, 'dev': device}) cmd = utils.execute('efibootmgr', '-c', '-d', device, '-p', efi_partition, '-w', '-L', label, '-l', v_efi_bl_path) for line in cmd[1].split('\n'): match = duplicated_label.match(line) if match: boot_num = match.group(1) LOG.debug("Found bootnum %s matching label", boot_num) utils.execute('efibootmgr', '-b', boot_num, '-B') label_id += 1
def _validate_partitioning(device): """Validate the final partition table. Check if after writing the image to disk we have a valid partition table by trying to read it. This will fail if the disk is junk. """ try: # Ensure we re-read the partition table before we try to list # partitions utils.execute('partprobe', device, run_as_root=True, attempts=CONF.disk_utils.partprobe_attempts) except (processutils.UnknownArgumentError, processutils.ProcessExecutionError, OSError) as e: LOG.warning("Unable to probe for partitions on device %(device)s " "after writing the image, the partitioning table may " "be broken. Error: %(error)s", {'device': device, 'error': e}) try: nparts = len(disk_utils.list_partitions(device)) except (processutils.UnknownArgumentError, processutils.ProcessExecutionError, OSError) as e: msg = ("Unable to find a valid partition table on the disk after " "writing the image. Error {}".format(e)) raise exception.InstanceDeployFailure(msg) # Check if there is at least one partition in the partition table after # deploy if not nparts: msg = ("No partitions found on the device {} after writing " "the image.".format(device)) raise exception.InstanceDeployFailure(msg)
def _shred_block_device(self, node, block_device): """Erase a block device using shred. :param node: Ironic node info. :param block_device: a BlockDevice object to be erased :returns: True if the erase succeeds, False if it fails for any reason """ info = node.get('driver_internal_info', {}) npasses = info.get('agent_erase_devices_iterations', 1) args = ('shred', '--force') if info.get('agent_erase_devices_zeroize', True): args += ('--zero', ) args += ('--verbose', '--iterations', str(npasses), block_device.name) try: utils.execute(*args) except (processutils.ProcessExecutionError, OSError) as e: msg = ("Erasing block device %(dev)s failed with error %(err)s ", {'dev': block_device.name, 'err': e}) LOG.error(msg) return False return True
def _ata_erase(self, block_device): security_lines = self._get_ata_security_lines(block_device) # If secure erase isn't supported return False so erase_block_device # can try another mechanism. Below here, if secure erase is supported # but fails in some way, error out (operators of hardware that supports # secure erase presumably expect this to work). if 'supported' not in security_lines: return False if 'enabled' in security_lines: raise errors.BlockDeviceEraseError( ('Block device {0} already has ' 'a security password set').format(block_device.name)) if 'not frozen' not in security_lines: raise errors.BlockDeviceEraseError( ('Block device {0} is frozen ' 'and cannot be erased').format(block_device.name)) utils.execute('hdparm', '--user-master', 'u', '--security-set-pass', 'NULL', block_device.name) utils.execute('hdparm', '--user-master', 'u', '--security-erase', 'NULL', block_device.name) # Verify that security is now 'not enabled' security_lines = self._get_ata_security_lines(block_device) if 'not enabled' not in security_lines: raise errors.BlockDeviceEraseError( ('An unknown error occurred ' 'erasing block device {0}').format(block_device.name)) return True
def remove_bootloader(self, node, ports): driver_info = node.get('driver_info', {}) LOG.info('Remove Bootloader called with %s' % driver_info) bootdisk = self.get_os_install_device() cmd = ['dd', 'if=/dev/zero', 'of=' + bootdisk, 'bs=1M', 'count=1'] utils.execute(*cmd, check_exit_code=[0]) return True
def setup_ipmi_credentials(resp): """Setup IPMI credentials, if requested. :param resp: JSON response from inspector. """ if not resp.get("ipmi_setup_credentials"): LOG.info("setting IPMI credentials was not requested") return user, password = resp["ipmi_username"], resp["ipmi_password"] LOG.debug("setting IPMI credentials: user %s", user) commands = [ ("user", "set", "name", "2", user), ("user", "set", "password", "2", password), ("user", "enable", "2"), ("channel", "setaccess", "1", "2", "link=on", "ipmi=on", "callin=on", "privilege=4"), ] for cmd in commands: try: utils.execute("ipmitool", *cmd) except processutils.ProcessExecutionError: LOG.exception("failed to update IPMI credentials") raise errors.InspectionError("failed to update IPMI credentials") LOG.info("successfully set IPMI credentials: user %s", user)
def get_bmc_address(self): # These modules are rarely loaded automatically utils.try_execute('modprobe', 'ipmi_msghandler') utils.try_execute('modprobe', 'ipmi_devintf') utils.try_execute('modprobe', 'ipmi_si') try: out, _e = utils.execute( "ipmitool lan print | grep -e 'IP Address [^S]' " "| awk '{ print $4 }'", shell=True) if out.strip() and out.strip() != '0.0.0.0': return out.strip() except (processutils.ProcessExecutionError, OSError) as e: # Not error, because it's normal in virtual environment LOG.warning("Cannot get BMC address: %s", e) for channel_id in range(1, 10): try: out, _e = utils.execute("ipmitool lan print " + str(channel_id) + "| grep -e 'IP Address [^S]' " "| awk '{ print $4 }'", shell=True) if out.strip() and out.strip() != '0.0.0.0': return out.strip() except (processutils.ProcessExecutionError, OSError) as e: # Not error, because it's normal in virtual environment LOG.warning("Cannot get BMC address: %s, channel_id: %d" % (e, channel_id)) return None
def setup_ipmi_credentials(resp): """Setup IPMI credentials, if requested. :param resp: JSON response from inspector. """ if not resp.get('ipmi_setup_credentials'): LOG.info('setting IPMI credentials was not requested') return user, password = resp['ipmi_username'], resp['ipmi_password'] LOG.debug('setting IPMI credentials: user %s', user) commands = [ ('user', 'set', 'name', '2', user), ('user', 'set', 'password', '2', password), ('user', 'enable', '2'), ('channel', 'setaccess', '1', '2', 'link=on', 'ipmi=on', 'callin=on', 'privilege=4'), ] for cmd in commands: try: utils.execute('ipmitool', *cmd) except processutils.ProcessExecutionError: LOG.exception('failed to update IPMI credentials') raise errors.InspectionError('failed to update IPMI credentials') LOG.info('successfully set IPMI credentials: user %s', user)
def main(): log.register_options(CONF) CONF(args=sys.argv[1:]) log.setup(CONF, 'virtmedia-netconfig') LOG.info("Starting virtmedia-netconfig!!") params = _read_params_from_file('/proc/cmdline') # If the node booted over virtual media, the parameters are passed # in a text file within the virtual media floppy. if params.get('boot_method') == 'vmedia': LOG.debug("Erasing old filesystems") utils.execute('/usr/bin/erase-oldfs.sh') LOG.info( "This node is booted with vmedia. Checking for available virtual media!!" ) wait_for_cd_device() check_cd_config() vmedia_params = _get_vmedia_params() params.update(vmedia_params) LOG.debug("vmedia parameters: %r", vmedia_params) os_net_config = params.get('os_net_config') LOG.info("virtmedia: os_net_config=%s" % os_net_config) if os_net_config: _configure_static_net(os_net_config)
def _mount_for_chroot(path): """Mount items for grub-mkconfig to succeed.""" LOG.debug('Mounting Linux standard partitions for bootloader ' 'configuration generation') for fs in BIND_MOUNTS: utils.execute('mount', '-o', 'bind', fs, path + fs) utils.execute('mount', '-t', 'sysfs', 'none', path + '/sys')
def test_check_exit_code_boolean(self): utils.execute('/usr/bin/env', 'false', check_exit_code=False) self.assertRaises(processutils.ProcessExecutionError, utils.execute, '/usr/bin/env', 'false', check_exit_code=True)
def check_cd_config(): """ This function checks for any extended 64K block in CD. If it is available it will extract the contents for the block. Loop mount the image for reading configuration parameters. """ inputiso = '/dev/sr0' outputtgz = '/tmp/cdconf.tgz' mode = os.stat(inputiso).st_mode if stat.S_ISBLK(mode): filesize = get_file_size(inputiso) skip = filesize / 2048 - 32 ironic_utils.dd(inputiso, outputtgz, 'bs=2k', 'skip=%d' % skip) # Check if tgz file is valid. try: utils.execute("/usr/bin/gzip", '-t', outputtgz) except processutils.ProcessExecutionError as err: if 'not in gzip format' in err.stderr: LOG.info('File is not gzip format skipping!!') sys.exit() LOG.info('Configuration file in gzip format proceeding for extraction') tar = tarfile.open(outputtgz) tar.extractall('/tmp/floppy') tar.close() dir_list = os.listdir('/tmp/floppy') for item in dir_list: if item.find('.img') != -1: os.mkdir('/tmp/floppy/mnt') utils.execute("mount", '-o', 'loop', '/tmp/floppy/%s' % item, '/tmp/floppy/mnt') time.sleep(1)
def test_no_retry_on_success(self): fd, tmpfilename = tempfile.mkstemp() _, tmpfilename2 = tempfile.mkstemp() try: fp = os.fdopen(fd, 'w+') fp.write('''#!/bin/sh # If we've already run, bail out. grep -q foo "$1" && exit 1 # Mark that we've run before. echo foo > "$1" # Check that stdin gets passed correctly. grep foo ''') fp.close() os.chmod(tmpfilename, 0o755) try: utils.execute(tmpfilename, tmpfilename2, process_input=b'foo', attempts=2) except OSError as e: if e.errno == errno.EACCES: self.skipTest("Permissions error detected. " "Are you running with a noexec /tmp?") else: raise finally: os.unlink(tmpfilename) os.unlink(tmpfilename2)
def update_warpdrive_firmware(self, node, ports): driver_info = node.get('driver_info', {}) LOG.info('Update Warpdrive called with %s' % driver_info) devices = self._list_lsi_devices() for device in devices: # Don't reflash the same firmware if device['version'] != LSI_FIRMWARE_VERSION: preflash_path = os.path.join(LSI_WARPDRIVE_DIR, LSI_FIRMWARE_PREFLASH) firmware_path = os.path.join(LSI_WARPDRIVE_DIR, LSI_FIRMWARE_PACKAGE) # note(JayF): New firmware requires us to flash a new firmware # flasher before flashing the update package precmd = [DDOEMCLI, '-c', device['id'], '-f', preflash_path] cmd = [DDOEMCLI, '-c', device['id'], '-updatepkg', firmware_path] with metrics.instrument_context( __name__, 'upgrade_warpdrive_firmware_preflash'): utils.execute(*precmd, check_exit_code=[0]) with metrics.instrument_context( __name__, 'upgrade_warpdrive_firmware_package'): utils.execute(*cmd, check_exit_code=[0]) else: LOG.info('Device %(id)s already version %(version)s, ' 'not upgrading.' % { 'id': device['id'], 'version': device['version'] })
def _detect_raid_card(): cmd = "%s list" % MEGACLI try: utils.execute(cmd, shell=True) # return true if cmd succeeded return True except Exception: return False
def _has_dracut(root): try: utils.execute('chroot %(path)s /bin/sh -c ' '"which dracut"' % {'path': root}, shell=True) except processutils.ProcessExecutionError: return False return True
def delete_configuration(self, node, ports): LOG.info('Begin to delete configuration') cmd = '%s -CfgLdDel -LAll -aAll' % MEGACLI report, _e = utils.execute(cmd, shell=True) cmd = '%s -CfgForeign -Clear -aAll' % MEGACLI report, _e = utils.execute(cmd, shell=True) return
def create_configuration(self, node, ports): target_raid_config = node.get('target_raid_config', {}).copy() target_raid_config_list = target_raid_config['logical_disks'] LOG.info('Begin to create configuration') ld_num = 0 for vdriver in target_raid_config_list: size = 'MAX' raid_level = None physical_disks = None controller = None if 'size_gb' in vdriver and vdriver['size_gb'] != 'MAX': size = str(vdriver['size_gb'] * 1024) if 'raid_level' in vdriver: raid_level = vdriver['raid_level'] if 'physical_disks' in vdriver: physical_disks = vdriver['physical_disks'] if 'controller' in vdriver: controller = vdriver['controller'] LOG.info(('Raid Configuration:[size:%s, ' 'raid_level:%s, p_disks:%s, controller:%s]'), size, raid_level, physical_disks, controller) disklist = " " for i in range(0, len(physical_disks)): if i == 0: disklist = physical_disks[i] else: disklist = disklist + "," + physical_disks[i] LOG.info('Raid disk list:[%s]', disklist) cmd = ('%s -CfgLdAdd ' % MEGACLI) + '-r' \ + raid_level + "[" + disklist + "] " + "-a" + controller report, _e = utils.execute(cmd, shell=True) ld_num1 = report.split('\n')[1].split()[-1] cmd1 = ('%s -LDInfo -L' % MEGACLI) + \ ld_num1 + ' -aAll | grep -i size' report1, _e = utils.execute(cmd1, shell=True) if report1.split('\n')[0].split(':')[-1].split()[-1] == 'GB': ld_size = float( report1.split('\n')[0].split(':')[-1].split()[-2]) else: ld_size = float( report1.split('\n')[0].split(':')[-1].split()[-2]) * 1024 target_raid_config['logical_disks'][ld_num]['size_gb'] = int( ld_size) ld_num += 1 if raid_level is not None and physical_disks \ is not None and controller is not None: LOG.info('Raid Configuration Command:%s', cmd) else: LOG.info(('Param Error,No Raid Configuration' ' Command being Created:%s'), cmd) return target_raid_config
def create_configuration(self, node, ports): target_raid_config = node.get('target_raid_config', {}).copy() target_raid_config_list = target_raid_config['logical_disks'] LOG.info('Begin to create configuration') ld_num = 0 for vdriver in target_raid_config_list: size = 'MAX' raid_level = None physical_disks = None controller = 1 if 'size_gb' in vdriver and vdriver['size_gb'] != 'MAX': size = str(vdriver['size_gb'] * 1024) if 'raid_level' in vdriver: raid_level = vdriver['raid_level'] if 'physical_disks' in vdriver: physical_disks = vdriver['physical_disks'] if 'controller' in vdriver: controller = vdriver['controller'] LOG.info('Raid Configuration:[size:%(size)s, ' 'raid_level:%(raid_level)s, ' 'physical_disks:%(p_disks)s, ' 'controller:%(controller)s]', {'size': size, 'raid_level': raid_level, 'p_disks': physical_disks, 'controller': controller}) disklist = " " for i in range(0, len(physical_disks)): if i == 0: disklist = physical_disks[i] else: disklist = disklist + " " + physical_disks[i] cmd = ('%s create ' % ARCCONF) + controller \ + ' LOGICALDRIVE ' + size + ' ' + raid_level \ + ' ' + disklist + ' noprompt' report, _e = utils.execute(cmd, shell=True) ld_num1 = report.split('\n')[2].split()[-1] report1, _e = utils.execute(('%s getconfig ' % ARCCONF + controller + ' ld ' + ld_num1 + '|grep -i size'), shell=True) ld_size = report1.split('\n')[1].split()[-2] target_raid_config['logical_disks'][ld_num]['size_gb'] = int( int(ld_size) / 1024) ld_num += 1 if raid_level is not None and physical_disks \ is not None and controller is not None: LOG.info('Raid Configuration Command:%s', cmd) LOG.info('System Reaction:%s', report) else: LOG.info( ('Param Error,No Raid Configuration' ' Command being Created:%s'), cmd) return target_raid_config
def test_can_mock_execute(self, mock_exec): # NOTE(jlvillal): We had discovered an issue where mocking wasn't # working because we had used a mock to block access to the execute # functions. This caused us to "mock a mock" and didn't work correctly. # We want to make sure that we can mock our execute functions even with # our "block execute" code. utils.execute("ls") utils.execute("echo") self.assertEqual(2, mock_exec.call_count)
def config_ipmi_info(sn): LOG.info('Config ipmi info...') if not CONF.arobot_callback_url: LOG.info('Config automation is disabled, skipping') return interval = 5 index = 1 while True: ipmi_conf = None try: ipmi_conf = call_arobot(sn) except Exception as e: LOG.info('Got exception %s', e) LOG.info('%s times', index) time.sleep(interval) index += 1 continue if ipmi_conf is not None and \ ipmi_conf.get('return_value') == 'NeedConf': LOG.info('Got ipmi conf OK! address %s, netmask %s, gateway %s', ipmi_conf.get('ipmi_address'), ipmi_conf.get('ipmi_netmask'), ipmi_conf.get('ipmi_gateway')) break elif ipmi_conf is not None and \ ipmi_conf.get('return_value') == 'Success': LOG.info('IPMI info already confed!') return LOG.info('%s times', index) time.sleep(interval) index += 1 commands = [ ('lan', 'set', '1', 'ipsrc', 'static'), ('lan', 'set', '1', 'ipaddr', ipmi_conf.get('ipmi_address')), ('lan', 'set', '1', 'netmask', ipmi_conf.get('ipmi_netmask')), ('lan', 'set', '1', 'defgw', 'ipaddr', ipmi_conf.get('ipmi_gateway')), # ('user', 'set', 'name', '5', 'inspur'), # ('user', 'set', 'password', '5', 'Czilpjhstcgx4Ru5'), # ('user', 'enable', '5'), # ('channel', 'setaccess', '1', '5', # 'link=on', 'ipmi=on', 'callin=on', 'privilege=4'), ] for cmd in commands: try: utils.execute('ipmitool', *cmd) except processutils.ProcessExecutionError: LOG.exception('failed to update IPMI ip/netmask/gw') raise errors.InspectionError('failed to update IPMI ip/netmask/gw') tell_arobot_ipmi(sn=sn) LOG.info('successfully set IPMI conf!')
def create_raid_device(index, logical_disk): """Create a raid device. :param index: the index of the resulting md device. :param logical_disk: the logical disk containing the devices used to crete the raid. :raise: errors.SoftwareRAIDError if not able to create the raid device or fails to re-add a device to a raid. """ md_device = '/dev/md%d' % index component_devices = [] for device in logical_disk['block_devices']: # The partition delimiter for all common harddrives (sd[a-z]+) part_delimiter = '' if 'nvme' in device: part_delimiter = 'p' component_devices.append(device + part_delimiter + str(index + RAID_PARTITION)) raid_level = logical_disk['raid_level'] # The schema check allows '1+0', but mdadm knows it as '10'. if raid_level == '1+0': raid_level = '10' try: LOG.debug("Creating md device %(dev)s on %(comp)s", { 'dev': md_device, 'comp': component_devices }) utils.execute('mdadm', '--create', md_device, '--force', '--run', '--metadata=1', '--level', raid_level, '--raid-devices', len(component_devices), *component_devices) except processutils.ProcessExecutionError as e: msg = "Failed to create md device {} on {}: {}".format( md_device, ' '.join(component_devices), e) raise errors.SoftwareRAIDError(msg) # check for missing devices and re-add them actual_components = _get_actual_component_devices(md_device) missing = set(component_devices) - set(actual_components) for dev in missing: try: LOG.warning( 'Found %(device)s to be missing from %(md)s ' '... re-adding!', { 'device': dev, 'md': md_device }) utils.execute('mdadm', '--add', md_device, dev, attempts=3, delay_on_retry=True) except processutils.ProcessExecutionError as e: msg = "Failed re-add {} to {}: {}".format(dev, md_device, e) raise errors.SoftwareRAIDError(msg)
def _get_partition(device, uuid): """Find the partition of a given device.""" LOG.debug("Find the partition %(uuid)s on device %(dev)s", { 'dev': device, 'uuid': uuid }) try: # Try to tell the kernel to re-read the partition table try: utils.execute('partx', '-u', device, attempts=3, delay_on_retry=True) utils.execute('udevadm', 'settle') except processutils.ProcessExecutionError: LOG.warning("Couldn't re-read the partition table " "on device %s", device) report = utils.execute('lsblk', '-PbioKNAME,UUID,TYPE', device)[0] for line in report.split('\n'): part = {} # Split into KEY=VAL pairs vals = shlex.split(line) for key, val in (v.split('=', 1) for v in vals): part[key] = val.strip() # Ignore non partition if part.get('TYPE') != 'part': continue if part.get('UUID') == uuid: LOG.debug("Partition %(uuid)s found on device " "%(dev)s", { 'uuid': uuid, 'dev': device }) return '/dev/' + part.get('KNAME') else: error_msg = ("No partition with UUID %(uuid)s found on " "device %(dev)s" % { 'uuid': uuid, 'dev': device }) LOG.error(error_msg) raise errors.DeviceNotFound(error_msg) except processutils.ProcessExecutionError as e: error_msg = ('Finding the partition with UUID %(uuid)s on ' 'device %(dev)s failed with %(err)s' % { 'uuid': uuid, 'dev': device, 'err': e }) LOG.error(error_msg) raise errors.CommandExecutionError(error_msg)
def _get_vmedia_params(): """This method returns the parameters passed through virtual media floppy. :returns: a partial dict of potential agent configuration parameters :raises: VirtualMediaBootError when it cannot find the virtual media device """ parameters_file = "parameters.txt" vmedia_device_file_lower_case = "/dev/disk/by-label/ir-vfd-dev" vmedia_device_file_upper_case = "/dev/disk/by-label/IR-VFD-DEV" if os.path.exists(vmedia_device_file_lower_case): vmedia_device_file = vmedia_device_file_lower_case elif os.path.exists(vmedia_device_file_upper_case): vmedia_device_file = vmedia_device_file_upper_case else: # TODO(rameshg87): This block of code is there only for compatibility # reasons (so that newer agent can work with older Ironic). Remove # this after Liberty release. vmedia_device = utils._get_vmedia_device() if not vmedia_device: msg = "Unable to find virtual media device" raise errors.VirtualMediaBootError(msg) vmedia_device_file = os.path.join("/dev", vmedia_device) vmedia_mount_point = tempfile.mkdtemp() try: try: stdout, stderr = utils.execute("mount", vmedia_device_file, vmedia_mount_point) except processutils.ProcessExecutionError as e: msg = ("Unable to mount virtual media device %(device)s: " "%(error)s" % { 'device': vmedia_device_file, 'error': e }) raise errors.VirtualMediaBootError(msg) parameters_file_path = os.path.join(vmedia_mount_point, parameters_file) params = _read_params_from_file(parameters_file_path, '\n') try: stdout, stderr = utils.execute("umount", vmedia_mount_point) except processutils.ProcessExecutionError as e: pass finally: try: shutil.rmtree(vmedia_mount_point) except Exception as e: pass return params
def _rescan_device(device): """Force the device to be rescanned :param device: device upon which to rescan and update kernel partition records. """ try: utils.execute('partx', '-u', device, attempts=3, delay_on_retry=True) utils.execute('udevadm', 'settle') except processutils.ProcessExecutionError: LOG.warning("Couldn't re-read the partition table " "on device %s", device)
def sync(self): """Flush file system buffers forcing changed blocks to disk. :raises: CommandExecutionError if flushing file system buffers fails. """ LOG.debug('Flushing file system buffers') try: utils.execute('sync') except processutils.ProcessExecutionError as e: error_msg = 'Flushing file system buffers failed. Error: %s' % e LOG.error(error_msg) raise errors.CommandExecutionError(error_msg)
def _udev_settle(): """Wait for the udev event queue to settle. Wait for the udev event queue to settle to make sure all devices are detected once the machine boots up. """ try: utils.execute('udevadm', 'settle') except processutils.ProcessExecutionError as e: LOG.warning('Something went wrong when waiting for udev ' 'to settle. Error: %s', e) return
def get_bios_given_nic_name(self, interface_name): """Collect the BIOS given NICs name. This function uses the biosdevname utility to collect the BIOS given name of network interfaces. The collected data is added to the network interface inventory with an extra field named ``biosdevname``. :param interface_name: list of names of node's interfaces. :return: the BIOS given NIC name of node's interfaces or default as None. """ global WARN_BIOSDEVNAME_NOT_FOUND try: stdout, _ = utils.execute('biosdevname', '-i', interface_name) return stdout.rstrip('\n') except OSError: if not WARN_BIOSDEVNAME_NOT_FOUND: LOG.warning("Executable 'biosdevname' not found") WARN_BIOSDEVNAME_NOT_FOUND = True except processutils.ProcessExecutionError as e: # NOTE(alezil) biosdevname returns 4 if running in a # virtual machine. if e.exit_code == 4: LOG.info('The system is a virtual machine, so biosdevname ' 'utility does not provide names for virtual NICs.') else: LOG.warning('Biosdevname returned exit code %s', e.exit_code)
def _write_configdrive_to_partition(configdrive, device): filename = _configdrive_location() if _configdrive_is_url(configdrive): _download_configdrive_to_file(configdrive, filename) else: _write_configdrive_to_file(configdrive, filename) # check configdrive size before writing it filesize = os.stat(filename).st_size if filesize > (64 * 1024 * 1024): raise errors.ConfigDriveTooLargeError(filename, filesize) starttime = time.time() script = _path_to_script('shell/copy_configdrive_to_disk.sh') command = ['/bin/bash', script, filename, device] LOG.info('copying configdrive to disk with command {0}'.format( ' '.join(command))) try: stdout, stderr = utils.execute(*command, check_exit_code=[0]) except processutils.ProcessExecutionError as e: raise errors.ConfigDriveWriteError(device, e.exit_code, e.stdout, e.stderr) totaltime = time.time() - starttime LOG.info('configdrive copied from {0} to {1} in {2} seconds'.format( configdrive, device, totaltime))
def _shred_block_device(self, block_device): """Erase a block device using shred. :param block_device: a BlockDevice object to be erased :returns: True if the erase succeeds, False if it fails for any reason """ try: utils.execute('shred', '--force', '--zero', '--verbose', '--iterations', '1', block_device.name) except (processutils.ProcessExecutionError, OSError) as e: msg = ("Erasing block device %(dev)s failed with error %(err)s ", {'dev': block_device.name, 'err': e}) LOG.error(msg) return False return True
def list_block_devices(self): """List all physical block devices The switches we use for lsblk: P for KEY="value" output, b for size output in bytes, d to exclude dependant devices (like md or dm devices), i to ensure ascii characters only, and o to specify the fields we need :return: A list of BlockDevices """ report = utils.execute('lsblk', '-PbdioKNAME,MODEL,SIZE,ROTA,TYPE', check_exit_code=[0])[0] lines = report.split('\n') devices = [] for line in lines: device = {} # Split into KEY=VAL pairs vals = shlex.split(line) for key, val in (v.split('=', 1) for v in vals): device[key] = val.strip() # Ignore non disk if device.get('TYPE') != 'disk': continue # Ensure all required keys are at least present, even if blank diff = set(['KNAME', 'MODEL', 'SIZE', 'ROTA']) - set(device.keys()) if diff: raise errors.BlockDeviceError( '%s must be returned by lsblk.' % diff) devices.append(BlockDevice(name='/dev/' + device['KNAME'], model=device['MODEL'], size=int(device['SIZE']), rotational=bool(int(device['ROTA'])))) return devices
def get_memory(self): # psutil returns a long, so we force it to an int if psutil.version_info[0] == 1: total = int(psutil.TOTAL_PHYMEM) elif psutil.version_info[0] == 2: total = int(psutil.phymem_usage().total) try: out, _e = utils.execute("dmidecode --type memory | grep Size", shell=True) except (processutils.ProcessExecutionError, OSError) as e: LOG.warning("Cannot get real physical memory size: %s", e) physical = None else: physical = 0 for line in out.strip().split('\n'): line = line.strip() if not line: continue try: value = line.split(None, 1)[1].strip() physical += int(UNIT_CONVERTER(value).to_base_units()) except Exception as exc: LOG.error('Cannot parse size expression %s: %s', line, exc) if not physical: LOG.warning('failed to get real physical RAM, dmidecode ' 'returned %s', out) return Memory(total=total, physical_mb=physical)
def _execute(cmd, error_msg, **kwargs): try: stdout, stderr = utils.execute(*cmd, **kwargs) except processutils.ProcessExecutionError as e: LOG.error(error_msg) raise errors.ISCSICommandError(error_msg, e.exit_code, e.stdout, e.stderr)
def collect_extra_hardware(data, failures): """Collect detailed inventory using 'hardware-detect' utility. Recognizes ipa-inspection-benchmarks with list of benchmarks (possible values are cpu, disk, mem) to run. No benchmarks are run by default, as they're pretty time-consuming. Puts collected data as JSON under 'data' key. Requires 'hardware' python package to be installed on the ramdisk in addition to the packages in requirements.txt. :param data: mutable data that we'll send to inspector :param failures: AccumulatedFailures object """ benchmarks = utils.get_agent_params().get("ipa-inspection-benchmarks", []) if benchmarks: benchmarks = ["--benchmark"] + benchmarks.split(",") try: out, err = utils.execute("hardware-detect", *benchmarks) except (processutils.ProcessExecutionError, OSError) as exc: failures.add("failed to run hardware-detect utility: %s", exc) return try: data["data"] = json.loads(out) except ValueError as exc: msg = "JSON returned from hardware-detect cannot be decoded: %s" failures.add(msg, exc)
def collect_logs(data, failures): """Collect journald logs from the ramdisk. As inspection runs before any nodes details are known, it's handy to have logs returned with data. This collector sends logs to inspector in format expected by the 'ramdisk_error' plugin: base64 encoded tar.gz. This collector should be installed last in the collector chain, otherwise it won't collect enough logs. This collector does not report failures. :param data: mutable data that we'll send to inspector :param failures: AccumulatedFailures object """ try: out, _e = utils.execute("journalctl", "--full", "--no-pager", "-b", "-n", "10000") except (processutils.ProcessExecutionError, OSError): LOG.warn("failed to get system journal") return journal = io.BytesIO(out.encode("utf-8")) with io.BytesIO() as fp: with tarfile.open(fileobj=fp, mode="w:gz") as tar: tarinfo = tarfile.TarInfo("journal") tarinfo.size = len(out) tar.addfile(tarinfo, journal) fp.seek(0) data["logs"] = base64.b64encode(fp.getvalue())
def collect_logs(data, failures): """Collect journald logs from the ramdisk. As inspection runs before any nodes details are known, it's handy to have logs returned with data. This collector sends logs to inspector in format expected by the 'ramdisk_error' plugin: base64 encoded tar.gz. This collector should be installed last in the collector chain, otherwise it won't collect enough logs. This collector does not report failures. :param data: mutable data that we'll send to inspector :param failures: AccumulatedFailures object """ try: out, _e = utils.execute('journalctl', '--full', '--no-pager', '-b', '-n', '10000', binary=True, log_stdout=False) except (processutils.ProcessExecutionError, OSError): LOG.warning('failed to get system journal') return journal = io.BytesIO(bytes(out)) with io.BytesIO() as fp: with tarfile.open(fileobj=fp, mode='w:gz') as tar: tarinfo = tarfile.TarInfo('journal') tarinfo.size = len(out) tarinfo.mtime = time.time() tar.addfile(tarinfo, journal) fp.seek(0) data['logs'] = base64.b64encode(fp.getvalue())
def get_cpus(self): lines = utils.execute('lscpu')[0] cpu_info = {k.strip().lower(): v.strip() for k, v in (line.split(':', 1) for line in lines.split('\n') if line.strip())} # Current CPU frequency can be different from maximum one on modern # processors freq = cpu_info.get('cpu max mhz', cpu_info.get('cpu mhz')) flags = [] out = utils.try_execute('grep', '-Em1', '^flags', '/proc/cpuinfo') if out: try: # Example output (much longer for a real system): # flags : fpu vme de pse flags = out[0].strip().split(':', 1)[1].strip().split() except (IndexError, ValueError): LOG.warning('Malformed CPU flags information: %s', out) else: LOG.warning('Failed to get CPU flags') return CPU(model_name=cpu_info.get('model name'), frequency=freq, # this includes hyperthreading cores count=int(cpu_info.get('cpu(s)')), architecture=cpu_info.get('architecture'), flags=flags)
def _get_partition(device, uuid): """Find the partition of a given device.""" LOG.debug("Find the partition %(uuid)s on device %(dev)s", {'dev': device, 'uuid': uuid}) try: # Try to tell the kernel to re-read the partition table try: utils.execute('partx', '-u', device, attempts=3, delay_on_retry=True) utils.execute('udevadm', 'settle') except processutils.ProcessExecutionError: LOG.warning("Couldn't re-read the partition table " "on device %s", device) lsblk = utils.execute('lsblk', '-PbioKNAME,UUID,PARTUUID,TYPE', device) report = lsblk[0] for line in report.split('\n'): part = {} # Split into KEY=VAL pairs vals = shlex.split(line) for key, val in (v.split('=', 1) for v in vals): part[key] = val.strip() # Ignore non partition if part.get('TYPE') != 'part': continue if part.get('UUID') == uuid: LOG.debug("Partition %(uuid)s found on device " "%(dev)s", {'uuid': uuid, 'dev': device}) return '/dev/' + part.get('KNAME') if part.get('PARTUUID') == uuid: LOG.debug("Partition %(uuid)s found on device " "%(dev)s", {'uuid': uuid, 'dev': device}) return '/dev/' + part.get('KNAME') else: error_msg = ("No partition with UUID %(uuid)s found on " "device %(dev)s" % {'uuid': uuid, 'dev': device}) LOG.error(error_msg) raise errors.DeviceNotFound(error_msg) except processutils.ProcessExecutionError as e: error_msg = ('Finding the partition with UUID %(uuid)s on ' 'device %(dev)s failed with %(err)s' % {'uuid': uuid, 'dev': device, 'err': e}) LOG.error(error_msg) raise errors.CommandExecutionError(error_msg)
def _run_shutdown_script(self, parameter): script = _path_to_script("shell/shutdown.sh") command = ["/bin/bash", script, parameter] # this should never return if successful try: stdout, stderr = utils.execute(*command, check_exit_code=[0]) except processutils.ProcessExecutionError as e: raise errors.SystemRebootError(e.exit_code, e.stdout, e.stderr)
def _execute(cmd, error_msg, check_exit_code=None): if check_exit_code is None: check_exit_code = [0] try: stdout, stderr = utils.execute(*cmd, check_exit_code=check_exit_code) except processutils.ProcessExecutionError as e: LOG.error(error_msg) raise errors.ISCSIError(error_msg, e.exit_code, e.stdout, e.stderr)
def run_image(self): script = _path_to_script('shell/reboot.sh') LOG.info('Rebooting system') command = ['/bin/bash', script] # this should never return if successful try: stdout, stderr = utils.execute(*command, check_exit_code=[0]) except processutils.ProcessExecutionError as e: raise errors.SystemRebootError(e.exit_code, e.stdout, e.stderr)
def _get_warpdrive_attributes(self, block_device): device = self._get_warpdrive_card(block_device) result = utils.execute(DDOEMCLI, '-c', device['id'], '-health') attributes = {} attrkey = None # note(JayF): What we really get here is SMART data for the 4 SSDs # behind the Warpdrive card. Split them up to parse separately. for idx, lines in enumerate(result[0].split('SSD Drive SMART')): # note(JayF): The first entry is headers, throw it away if idx == 0: continue it = iter(lines.split('\n')) for line in it: line = line.strip() if not line: continue if line.startswith('Data Slot #'): # note(JayF): Get the drive number and serial. The full # line is actually "SSD Drive SMART Data Slot #" but by # splitting on it above we lose the first three words. drivenum = line.split()[3][0] driveserial = line.split()[7] attrkey = drivenum + '_' + driveserial attributes[attrkey] = {} # note(JayF): Once we know what drive this is, skip past the # current metrics (which are only for a power cycle), to put # the iterator in the right place for starting to grab k:v # pairs below if line.startswith('-------------- Cumulative'): break for line in it: line = line.strip() # note(JayF): It doesn't make sense to store time in graphite if not line or line.endswith('(Hours:Minutes:Seconds)'): continue # note(JayF): This is the first line of the footer. If we get # here, we're done parsing. if line.startswith('Warranty Remaining'): break # note(JayF): Some of the metrics have units or notes in # parens, we look for these and adjust our rsplit accordingly if line.endswith('(degree C)'): key = line.rsplit(None, 3)[0].replace(" ", "") value = line.rsplit(None, 3)[1] elif (line.endswith('(Gigabytes)') or line.endswith('(%)')): key = line.rsplit(None, 2)[0].replace(" ", "") value = line.rsplit(None, 2)[1] else: key = line.rsplit(None, 1)[0].replace(" ", "") value = line.rsplit(None, 1)[1] # note(JayF): Ensure all characters are safe for graphite key = re.sub(r'[\(\)/\\]', '_', key) attributes[attrkey][key] = re.sub(r'[\(\)/\\]', '_', value) return attributes
def _check_for_iscsi(): """Connect iSCSI shared connected via iBFT or OF. iscsistart -f will print the iBFT or OF info. In case such connection exists, we would like to issue iscsistart -b to create a session to the target. - If no connection is detected we simply return. """ try: utils.execute('iscsistart', '-f') except (processutils.ProcessExecutionError, EnvironmentError) as e: LOG.debug("No iscsi connection detected. Skipping iscsi. " "Error: %s", e) return try: utils.execute('iscsistart', '-b') except processutils.ProcessExecutionError as e: LOG.warning("Something went wrong executing 'iscsistart -b' " "Error: %s", e)