def copy_mdadm_conf(mdadm_conf, target): if not mdadm_conf: LOG.warn("mdadm config must be specified, not copying") return LOG.info("copying mdadm.conf into target") shutil.copy(mdadm_conf, os.path.sep.join([target, 'etc/mdadm/mdadm.conf']))
def dpkg_reconfigure(packages, target=None): # For any packages that are already installed, but have preseed data # we populate the debconf database, but the filesystem configuration # would be preferred on a subsequent dpkg-reconfigure. # so, what we have to do is "know" information about certain packages # to unconfigure them. unhandled = [] to_config = [] for pkg in packages: if pkg in CONFIG_CLEANERS: LOG.debug("unconfiguring %s", pkg) CONFIG_CLEANERS[pkg](target) to_config.append(pkg) else: unhandled.append(pkg) if len(unhandled): LOG.warn( "The following packages were installed and preseeded, " "but cannot be unconfigured: %s", unhandled) if len(to_config): util.subp(['dpkg-reconfigure', '--frontend=noninteractive'] + list(to_config), data=None, target=target, capture=True)
def copy_iscsi_conf(nodes_dir, target): if not nodes_dir: LOG.warn("nodes directory must be specified, not copying") return LOG.info("copying iscsi nodes database into target") shutil.copytree(nodes_dir, os.path.sep.join([target, 'etc/iscsi/nodes']))
def disconnect_target_disks(target_root_path=None): target_nodes_path = paths.target_path(target_root_path, '/etc/iscsi/nodes') fails = [] if os.path.isdir(target_nodes_path): for target in os.listdir(target_nodes_path): if target not in iscsiadm_sessions(): LOG.debug('iscsi target %s not active, skipping', target) continue # conn is "host,port,lun" for conn in os.listdir( os.path.sep.join([target_nodes_path, target])): host, port, _ = conn.split(',') try: util.subp(['sync']) iscsiadm_logout(target, '%s:%s' % (host, port)) except util.ProcessExecutionError as e: fails.append(target) LOG.warn("Unable to logout of iSCSI target %s: %s", target, e) else: LOG.warning('Skipping disconnect: failed to find iscsi nodes path: %s', target_nodes_path) if fails: raise RuntimeError( "Unable to logout of iSCSI targets: %s" % ', '.join(fails))
def copy_dname_rules(rules_d, target): if not rules_d: LOG.warn("no udev rules directory to copy") return target_rules_dir = util.target_path(target, "etc/udev/rules.d") for rule in os.listdir(rules_d): target_file = os.path.join(target_rules_dir, rule) shutil.copy(os.path.join(rules_d, rule), target_file)
def disconnect(self): if self.target not in iscsiadm_sessions(): LOG.warning('Iscsi target %s not in active iscsi sessions', self.target) return try: util.subp(['sync']) iscsiadm_logout(self.target, self.portal) except util.ProcessExecutionError as e: LOG.warn("Unable to logout of iSCSI target %s from portal %s: %s", self.target, self.portal, e)
def _legacy_detect_multipath(target_mountpoint=None): """ Detect if the operating system has been installed to a multipath device. """ # The obvious way to detect multipath is to use multipath utility which is # provided by the multipath-tools package. Unfortunately, multipath-tools # package is not available in all ephemeral images hence we can't use it. # Another reasonable way to detect multipath is to look for two (or more) # devices with the same World Wide Name (WWN) which can be fetched using # scsi_id utility. This way doesn't work as well because WWNs are not # unique in some cases which leads to false positives which may prevent # system from booting (see LP: #1463046 for details). # Taking into account all the issues mentioned above, curent implementation # detects multipath by looking for a filesystem with the same UUID # as the target device. It relies on the fact that all alternative routes # to the same disk observe identical partition information including UUID. # There are some issues with this approach as well though. We won't detect # multipath disk if it doesn't any filesystems. Good news is that # target disk will always have a filesystem because curtin creates them # while installing the system. rescan_block_devices() binfo = blkid(cache=False) LOG.debug("legacy_detect_multipath found blkid info: %s", binfo) # get_devices_for_mp may return multiple devices by design. It is not yet # implemented but it should return multiple devices when installer creates # separate disk partitions for / and /boot. We need to do UUID-based # multipath detection against each of target devices. target_devs = get_devices_for_mp(target_mountpoint) LOG.debug("target_devs: %s" % target_devs) for devpath, data in binfo.items(): # We need to figure out UUID of the target device first if devpath not in target_devs: continue # This entry contains information about one of target devices target_uuid = data.get('UUID') # UUID-based multipath detection won't work if target partition # doesn't have UUID assigned if not target_uuid: LOG.warn("Target partition %s doesn't have UUID assigned", devpath) continue LOG.debug("%s: %s" % (devpath, data.get('UUID', ""))) # Iterating over available devices to see if any other device # has the same UUID as the target device. If such device exists # we probably installed the system to the multipath device. for other_devpath, other_data in binfo.items(): if ((other_data.get('UUID') == target_uuid) and (other_devpath != devpath)): return True # No other devices have the same UUID as the target devices. # We probably installed the system to the non-multipath device. return False
def system_upgrade_main(args): # curtin system-upgrade [--target=/] if args.target is None: args.target = "/" exit_code = 0 try: distro.system_upgrade(target=args.target, allow_daemons=args.allow_daemons) except util.ProcessExecutionError as e: LOG.warn("system upgrade failed: %s" % e) exit_code = e.exit_code sys.exit(exit_code)
def mirror_to_placeholder(tmpl, mirror, placeholder): """ mirror_to_placeholder replace the specified mirror in a template with a placeholder string Checks for existance of the expected mirror and warns if not found """ if mirror not in tmpl: if mirror.endswith("/") and mirror[:-1] in tmpl: LOG.debug( "mirror_to_placeholder: '%s' did not exist in tmpl, " "did without a trailing /. Accomodating.", mirror) mirror = mirror[:-1] else: LOG.warn("Expected mirror '%s' not found in: %s", mirror, tmpl) return tmpl.replace(mirror, placeholder)
def system_install_pkgs_main(args): # curtin system-install [--target=/] [pkg, [pkg...]] if args.target is None: args.target = "/" exit_code = 0 try: util.install_packages(pkglist=args.packages, target=args.target, allow_daemons=args.allow_daemons) except util.ProcessExecutionError as e: LOG.warn("system install failed for %s: %s" % (args.packages, e)) exit_code = e.exit_code sys.exit(exit_code)
def get_scsi_wwid(device, replace_whitespace=False): """ Issue a call to scsi_id utility to get WWID of the device. """ cmd = ['/lib/udev/scsi_id', '--whitelisted', '--device=%s' % device] if replace_whitespace: cmd.append('--replace-whitespace') try: (out, err) = util.subp(cmd, capture=True) LOG.debug("scsi_id output raw:\n%s\nerror:\n%s", out, err) scsi_wwid = out.rstrip('\n') return scsi_wwid except util.ProcessExecutionError as e: LOG.warn("Failed to get WWID: %s", e) return None
def handle_physical(self, command): ''' command = { 'type': 'physical', 'mac_address': 'c0:d6:9f:2c:e8:80', 'name': 'eth0', 'subnets': [ {'type': 'dhcp4'} ] } ''' required_keys = [ 'name', ] if not self.valid_command(command, required_keys): LOG.warn('Skipping Invalid command: %s', command) LOG.debug(self.dump_network_state()) return interfaces = self.network_state.get('interfaces') iface = interfaces.get(command['name'], {}) for param, val in command.get('params', {}).items(): iface.update({param: val}) # convert subnet ipv6 netmask to cidr as needed subnets = command.get('subnets') if subnets: for subnet in subnets: if subnet['type'] == 'static': if 'netmask' in subnet and ':' in subnet['address']: subnet['netmask'] = mask2cidr(subnet['netmask']) for route in subnet.get('routes', []): if 'netmask' in route: route['netmask'] = mask2cidr(route['netmask']) iface.update({ 'name': command.get('name'), 'type': command.get('type'), 'mac_address': command.get('mac_address'), 'inet': 'inet', 'mode': 'manual', 'mtu': command.get('mtu'), 'address': None, 'gateway': None, 'subnets': subnets, }) self.network_state['interfaces'].update({command.get('name'): iface}) self.dump_network_state()
def copy_install_log(logfile, target, log_target_path): """Copy curtin install log file to target system""" basemsg = 'Cannot copy curtin install log "%s" to target.' % logfile if not logfile: LOG.warn(basemsg) return if not os.path.isfile(logfile): LOG.warn(basemsg + " file does not exist.") return LOG.debug('Copying curtin install log from %s to target/%s', logfile, log_target_path) util.write_file( filename=util.target_path(target, log_target_path), content=util.load_file(logfile, decode=False), mode=0o400, omode="wb")
def migrate_proxy_settings(cfg): """Move the legacy proxy setting 'http_proxy' into cfg['proxy'].""" proxy = cfg.get('proxy', {}) if not isinstance(proxy, dict): raise ValueError("'proxy' in config is not a dictionary: %s" % proxy) if 'http_proxy' in cfg: hp = cfg['http_proxy'] if hp: if proxy.get('http_proxy', hp) != hp: LOG.warn("legacy http_proxy setting (%s) differs from " "proxy/http_proxy (%s), using %s", hp, proxy['http_proxy'], proxy['http_proxy']) else: LOG.debug("legacy 'http_proxy' migrated to proxy/http_proxy") proxy['http_proxy'] = hp del cfg['http_proxy'] cfg['proxy'] = proxy
def handle_route(self, command): required_keys = [ 'destination', ] if not self.valid_command(command, required_keys): LOG.warn('Skipping Invalid command: %s', command) LOG.warn(self.dump_network_state()) return routes = self.network_state.get('routes') network, cidr = command['destination'].split("/") netmask = cidr2mask(int(cidr)) route = { 'network': network, 'netmask': netmask, 'gateway': command.get('gateway'), 'metric': command.get('metric'), } routes.append(route)
def stop_all_unused_multipath_devices(): """ Stop all unused multipath devices. """ multipath = util.which('multipath') # Command multipath is not available only when multipath-tools package # is not installed. Nothing needs to be done in this case because system # doesn't create multipath devices without this package installed and we # have nothing to stop. if not multipath: return # Command multipath -F flushes all unused multipath device maps cmd = [multipath, '-F'] try: # unless multipath cleared *everything* it will exit with 1 util.subp(cmd, rcs=[0, 1]) except util.ProcessExecutionError as e: LOG.warn("Failed to stop multipath devices: %s", e)
def apply_power_state(pstate): """ power_state: delay: 5 mode: poweroff message: Bye Bye """ cmd = load_power_state(pstate) if not cmd: return LOG.info("powering off with %s", cmd) fid = os.fork() if fid == 0: try: util.subp(cmd) os._exit(0) except Exception as e: LOG.warn("%s returned non-zero: %s" % (cmd, e)) os._exit(1) return
def rescan_block_devices(devices=None, warn_on_fail=True): """ run 'blockdev --rereadpt' for all block devices not currently mounted """ if not devices: unused = get_unused_blockdev_info() devices = [] for devname, data in unused.items(): if data.get('RM') == "1": continue if data.get('RO') != "0" or data.get('TYPE') != "disk": continue devices.append(data['device_path']) if not devices: LOG.debug("no devices found to rescan") return # blockdev needs /dev/ parameters, convert if needed cmd = ['blockdev', '--rereadpt'] + [ dev if dev.startswith('/dev/') else sysfs_to_devpath(dev) for dev in devices ] try: util.subp(cmd, capture=True) except util.ProcessExecutionError as e: if warn_on_fail: # FIXME: its less than ideal to swallow this error, but until # we fix LP: #1489521 we kind of need to. LOG.warn( "Error rescanning devices, possibly known issue LP: #1489521") # Reformatting the exception output so as to not trigger # vmtest scanning for Unexepected errors in install logfile LOG.warn("cmd: %s\nstdout:%s\nstderr:%s\nexit_code:%s", e.cmd, e.stdout, e.stderr, e.exit_code) udevadm_settle() return
def handle_nameserver(self, command): required_keys = [ 'address', ] if not self.valid_command(command, required_keys): LOG.warn('Skipping Invalid command: %s', command) LOG.warn(self.dump_network_state()) return dns = self.network_state.get('dns') if 'address' in command: addrs = command['address'] if not type(addrs) == list: addrs = [addrs] for addr in addrs: dns['nameservers'].append(addr) if 'search' in command: paths = command['search'] if not isinstance(paths, list): paths = [paths] for path in paths: dns['search'].append(path)
def ensure_disk_connected(rfc4173, write_config=True): global _ISCSI_DISKS iscsi_disk = _ISCSI_DISKS.get(rfc4173) if not iscsi_disk: iscsi_disk = IscsiDisk(rfc4173) try: iscsi_disk.connect() except util.ProcessExecutionError: LOG.error('Unable to connect to iSCSI disk (%s)' % rfc4173) # what should we do in this case? raise if write_config: save_iscsi_config(iscsi_disk) _ISCSI_DISKS.update({rfc4173: iscsi_disk}) # this is just a sanity check that the disk is actually present and # the above did what we expected if not os.path.exists(iscsi_disk.devdisk_path): LOG.warn('Unable to find iSCSI disk for target (%s) by path (%s)', iscsi_disk.target, iscsi_disk.devdisk_path) return iscsi_disk
def setup_zipl(cfg, target): if platform.machine() != 's390x': return # assuming that below gives the "/" rootfs target_dev = block.get_devices_for_mp(target)[0] root_arg = None # not mapped rootfs, use UUID if 'mapper' in target_dev: root_arg = target_dev else: uuid = block.get_volume_uuid(target_dev) if uuid: root_arg = "UUID=%s" % uuid if not root_arg: msg = "Failed to identify root= for %s at %s." % (target, target_dev) LOG.warn(msg) raise ValueError(msg) zipl_conf = """ # This has been modified by the MAAS curtin installer [defaultboot] default=ubuntu [ubuntu] target = /boot image = /boot/vmlinuz ramdisk = /boot/initrd.img parameters = root=%s """ % root_arg futil.write_files( files={"zipl_conf": { "path": "/etc/zipl.conf", "content": zipl_conf }}, base_dir=target)
def run(self): for cmdname in sorted(self.commands.keys()): cmd = self.commands[cmdname] if not cmd: continue cur_res = events.ReportEventStack( name=cmdname, description="running '%s'" % ' '.join(cmd), parent=self.reportstack, level="DEBUG") env = self.env.copy() env['CURTIN_REPORTSTACK'] = cur_res.fullname shell = not isinstance(cmd, list) with util.LogTimer(LOG.debug, cmdname): with cur_res: try: sp = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, shell=shell) except OSError as e: LOG.warn("%s command failed", cmdname) raise util.ProcessExecutionError(cmd=cmd, reason=e) output = b"" while True: data = sp.stdout.read(1) if not data and sp.poll() is not None: break self.write(data) output += data rc = sp.returncode if rc != 0: LOG.warn("%s command failed", cmdname) raise util.ProcessExecutionError( stdout=output, stderr="", exit_code=rc, cmd=cmd)
def rename_apt_lists(new_mirrors, target=None): """rename_apt_lists - rename apt lists to preserve old cache data""" default_mirrors = get_default_mirrors(distro.get_architecture(target)) pre = paths.target_path(target, APT_LISTS) for (name, omirror) in default_mirrors.items(): nmirror = new_mirrors.get(name) if not nmirror: continue oprefix = pre + os.path.sep + mirrorurl_to_apt_fileprefix(omirror) nprefix = pre + os.path.sep + mirrorurl_to_apt_fileprefix(nmirror) if oprefix == nprefix: continue olen = len(oprefix) for filename in glob.glob("%s_*" % oprefix): newname = "%s%s" % (nprefix, filename[olen:]) LOG.debug("Renaming apt list %s to %s", filename, newname) try: os.rename(filename, newname) except OSError: # since this is a best effort task, warn with but don't fail LOG.warn("Failed to rename apt list:", exc_info=True)
def copy_crypttab(crypttab, target): if not crypttab: LOG.warn("crypttab config must be specified, not copying") return shutil.copy(crypttab, os.path.sep.join([target, 'etc/crypttab']))
def copy_interfaces(interfaces, target): if not interfaces: LOG.warn("no interfaces file to copy!") return eni = os.path.sep.join([target, 'etc/network/interfaces']) shutil.copy(interfaces, eni)
def setup_grub(cfg, target): # target is the path to the mounted filesystem # FIXME: these methods need moving to curtin.block # and using them from there rather than commands.block_meta from curtin.commands.block_meta import (extract_storage_ordered_dict, get_path_to_storage_volume) grubcfg = cfg.get('grub', {}) # copy legacy top level name if 'grub_install_devices' in cfg and 'install_devices' not in grubcfg: grubcfg['install_devices'] = cfg['grub_install_devices'] LOG.debug("setup grub on target %s", target) # if there is storage config, look for devices tagged with 'grub_device' storage_cfg_odict = None try: storage_cfg_odict = extract_storage_ordered_dict(cfg) except ValueError: pass if storage_cfg_odict: storage_grub_devices = [] for item_id, item in storage_cfg_odict.items(): if not item.get('grub_device'): continue LOG.debug("checking: %s", item) storage_grub_devices.append( get_path_to_storage_volume(item_id, storage_cfg_odict)) if len(storage_grub_devices) > 0: grubcfg['install_devices'] = storage_grub_devices LOG.debug("install_devices: %s", grubcfg.get('install_devices')) if 'install_devices' in grubcfg: instdevs = grubcfg.get('install_devices') if isinstance(instdevs, str): instdevs = [instdevs] if instdevs is None: LOG.debug("grub installation disabled by config") else: # If there were no install_devices found then we try to do the right # thing. That right thing is basically installing on all block # devices that are mounted. On powerpc, though it means finding PrEP # partitions. devs = block.get_devices_for_mp(target) blockdevs = set() for maybepart in devs: try: (blockdev, part) = block.get_blockdev_for_partition(maybepart) blockdevs.add(blockdev) except ValueError: # if there is no syspath for this device such as a lvm # or raid device, then a ValueError is raised here. LOG.debug("failed to find block device for %s", maybepart) if platform.machine().startswith("ppc64"): # assume we want partitions that are 4100 (PReP). The snippet here # just prints the partition number partitions of that type. shnip = textwrap.dedent(""" export LANG=C; for d in "$@"; do sgdisk "$d" --print | awk '$6 == prep { print d $1 }' "d=$d" prep=4100 done """) try: out, err = util.subp(['sh', '-c', shnip, '--'] + list(blockdevs), capture=True) instdevs = str(out).splitlines() if not instdevs: LOG.warn("No power grub target partitions found!") instdevs = None except util.ProcessExecutionError as e: LOG.warn("Failed to find power grub partitions: %s", e) instdevs = None else: instdevs = list(blockdevs) # UEFI requires grub-efi-{arch}. If a signed version of that package # exists then it will be installed. if util.is_uefi_bootable(): arch = util.get_architecture() pkgs = ['grub-efi-%s' % arch] # Architecture might support a signed UEFI loader uefi_pkg_signed = 'grub-efi-%s-signed' % arch if util.has_pkg_available(uefi_pkg_signed): pkgs.append(uefi_pkg_signed) # AMD64 has shim-signed for SecureBoot support if arch == "amd64": pkgs.append("shim-signed") # Install the UEFI packages needed for the architecture util.install_packages(pkgs, target=target) env = os.environ.copy() replace_default = grubcfg.get('replace_linux_default', True) if str(replace_default).lower() in ("0", "false"): env['REPLACE_GRUB_LINUX_DEFAULT'] = "0" else: env['REPLACE_GRUB_LINUX_DEFAULT'] = "1" if instdevs: instdevs = [block.get_dev_name_entry(i)[1] for i in instdevs] else: instdevs = ["none"] if util.is_uefi_bootable() and grubcfg.get('update_nvram', True): uefi_remove_old_loaders(grubcfg, target) LOG.debug("installing grub to %s [replace_default=%s]", instdevs, replace_default) with util.ChrootableTarget(target): args = ['install-grub'] if util.is_uefi_bootable(): args.append("--uefi") if grubcfg.get('update_nvram', True): LOG.debug("GRUB UEFI enabling NVRAM updates") args.append("--update-nvram") else: LOG.debug("NOT enabling UEFI nvram updates") LOG.debug("Target system may not boot") args.append(target) # capture stdout and stderr joined. join_stdout_err = ['sh', '-c', 'exec "$0" "$@" 2>&1'] out, _err = util.subp(join_stdout_err + args + instdevs, env=env, capture=True) LOG.debug("%s\n%s\n", args, out) if util.is_uefi_bootable() and grubcfg.get('update_nvram', True): uefi_reorder_loaders(grubcfg, target)
def net_meta(args): # curtin net-meta --devices connected dhcp # curtin net-meta --devices configured dhcp # curtin net-meta --devices netboot dhcp # curtin net-meta --devices connected custom # if network-config hook exists in target, # we do not run the builtin if util.run_hook_if_exists(args.target, 'network-config'): sys.exit(0) state = util.load_command_environment() cfg = config.load_command_config(args, state) if cfg.get("network") is not None: args.mode = "custom" eni = "etc/network/interfaces" if args.mode == "auto": if not args.devices: args.devices = ["connected"] t_eni = None if args.target: t_eni = os.path.sep.join(( args.target, eni, )) if not os.path.isfile(t_eni): t_eni = None if t_eni: args.mode = "copy" else: args.mode = "dhcp" devices = [] if args.devices: for dev in args.devices: if dev in DEVNAME_ALIASES: devices += resolve_alias(dev) else: devices.append(dev) LOG.debug("net-meta mode is '%s'. devices=%s", args.mode, devices) output_network_config = os.environ.get("OUTPUT_NETWORK_CONFIG", "") if args.mode == "copy": if not args.target: raise argparse.ArgumentTypeError("mode 'copy' requires --target") t_eni = os.path.sep.join(( args.target, "etc/network/interfaces", )) with open(t_eni, "r") as fp: content = fp.read() LOG.warn( "net-meta mode is 'copy', static network interfaces files" "can be brittle. Copied interfaces: %s", content) target = args.output elif args.mode == "dhcp": target = output_network_config content = config.dump_config(interfaces_basic_dhcp(devices)) elif args.mode == 'custom': target = output_network_config content = config.dump_config(interfaces_custom(args)) else: raise Exception("Unexpected network config mode '%s'." % args.mode) if not target: raise Exception( "No target given for mode = '%s'. No where to write content: %s" % (args.mode, content)) LOG.debug("writing to file %s with network config: %s", target, content) if target == "-": sys.stdout.write(content) else: with open(target, "w") as fp: fp.write(content) sys.exit(0)
def detect_and_handle_multipath(cfg, target): DEFAULT_MULTIPATH_PACKAGES = ['multipath-tools-boot'] mpcfg = cfg.get('multipath', {}) mpmode = mpcfg.get('mode', 'auto') mppkgs = mpcfg.get('packages', DEFAULT_MULTIPATH_PACKAGES) mpbindings = mpcfg.get('overwrite_bindings', True) if isinstance(mppkgs, str): mppkgs = [mppkgs] if mpmode == 'disabled': return if mpmode == 'auto' and not block.detect_multipath(target): return LOG.info("Detected multipath devices. Installing support via %s", mppkgs) util.install_packages(mppkgs, target=target) replace_spaces = True try: # check in-target version pkg_ver = util.get_package_version('multipath-tools', target=target) LOG.debug("get_package_version:\n%s", pkg_ver) LOG.debug("multipath version is %s (major=%s minor=%s micro=%s)", pkg_ver['semantic_version'], pkg_ver['major'], pkg_ver['minor'], pkg_ver['micro']) # multipath-tools versions < 0.5.0 do _NOT_ want whitespace replaced # i.e. 0.4.X in Trusty. if pkg_ver['semantic_version'] < 500: replace_spaces = False except Exception as e: LOG.warn( "failed reading multipath-tools version, " "assuming it wants no spaces in wwids: %s", e) multipath_cfg_path = os.path.sep.join([target, '/etc/multipath.conf']) multipath_bind_path = os.path.sep.join([target, '/etc/multipath/bindings']) # We don't want to overwrite multipath.conf file provided by the image. if not os.path.isfile(multipath_cfg_path): # Without user_friendly_names option enabled system fails to boot # if any of the disks has spaces in its name. Package multipath-tools # has bug opened for this issue (LP: 1432062) but it was not fixed yet. multipath_cfg_content = '\n'.join([ '# This file was created by curtin while installing the system.', 'defaults {', ' user_friendly_names yes', '}', '' ]) util.write_file(multipath_cfg_path, content=multipath_cfg_content) if mpbindings or not os.path.isfile(multipath_bind_path): # we do assume that get_devices_for_mp()[0] is / target_dev = block.get_devices_for_mp(target)[0] wwid = block.get_scsi_wwid(target_dev, replace_whitespace=replace_spaces) blockdev, partno = block.get_blockdev_for_partition(target_dev) mpname = "mpath0" grub_dev = "/dev/mapper/" + mpname if partno is not None: grub_dev += "-part%s" % partno LOG.debug("configuring multipath install for root=%s wwid=%s", grub_dev, wwid) multipath_bind_content = '\n'.join([ '# This file was created by curtin while installing the system.', "%s %s" % (mpname, wwid), '# End of content generated by curtin.', '# Everything below is maintained by multipath subsystem.', '' ]) util.write_file(multipath_bind_path, content=multipath_bind_content) grub_cfg = os.path.sep.join( [target, '/etc/default/grub.d/50-curtin-multipath.cfg']) msg = '\n'.join([ '# Written by curtin for multipath device wwid "%s"' % wwid, 'GRUB_DEVICE=%s' % grub_dev, 'GRUB_DISABLE_LINUX_UUID=true', '' ]) util.write_file(grub_cfg, content=msg) else: LOG.warn("Not sure how this will boot") # Initrams needs to be updated to include /etc/multipath.cfg # and /etc/multipath/bindings files. update_initramfs(target, all_kernels=True)
def __init__(self, rfc4173): auth_m = None _rfc4173 = rfc4173 if not rfc4173.startswith('iscsi:'): raise ValueError('iSCSI specification (%s) did not start with ' 'iscsi:. iSCSI disks must be specified as ' 'iscsi:[user:password[:initiatoruser:'******'initiatorpassword]@]' 'host:proto:port:lun:targetname' % _rfc4173) rfc4173 = rfc4173[6:] if '@' in rfc4173: if rfc4173.count('@') != 1: raise ValueError('Only one @ symbol allowed in iSCSI disk ' 'specification (%s). iSCSI disks must be ' 'specified as' 'iscsi:[user:password[:initiatoruser:'******'initiatorpassword]@]' 'host:proto:port:lun:targetname' % _rfc4173) auth, target = rfc4173.split('@') auth_m = RFC4173_AUTH_REGEX.match(auth) if auth_m is None: raise ValueError('Invalid authentication specified for iSCSI ' 'disk (%s). iSCSI disks must be specified as ' 'iscsi:[user:password[:initiatoruser:'******'initiatorpassword]@]' 'host:proto:port:lun:targetname' % _rfc4173) else: target = rfc4173 target_m = RFC4173_TARGET_REGEX.match(target) if target_m is None: raise ValueError('Invalid target specified for iSCSI disk (%s). ' 'iSCSI disks must be specified as ' 'iscsi:[user:password[:initiatoruser:'******'initiatorpassword]@]' 'host:proto:port:lun:targetname' % _rfc4173) if target_m.group('proto') and target_m.group('proto') != '6': LOG.warn('Specified protocol for iSCSI (%s) is unsupported, ' 'assuming 6 (TCP)', target_m.group('proto')) if not target_m.group('host') or not target_m.group('targetname'): raise ValueError('Both host and targetname must be specified for ' 'iSCSI disks') if auth_m: self.user = auth_m.group('user') self.password = auth_m.group('password') self.iuser = auth_m.group('initiatoruser') self.ipassword = auth_m.group('initiatorpassword') else: self.user = None self.password = None self.iuser = None self.ipassword = None self.host = target_m.group('host') self.proto = '6' self.lun = int(target_m.group('lun')) if target_m.group('lun') else 0 self.target = target_m.group('targetname') try: self.port = int(target_m.group('port')) if target_m.group('port') \ else 3260 except ValueError: raise ValueError('Specified iSCSI port (%s) is not an integer' % target_m.group('port')) portal = '%s:%s' % (self.host, self.port) if self.host.startswith('[') and self.host.endswith(']'): self.host = self.host[1:-1] if not util.is_valid_ipv6_address(self.host): raise ValueError('Specified iSCSI IPv6 address (%s) is not ' 'valid' % self.host) portal = '[%s]:%s' % (self.host, self.port) assert_valid_iscsi_portal(portal) self.portal = portal
def copy_fstab(fstab, target): if not fstab: LOG.warn("fstab variable not in state, not copying fstab") return shutil.copy(fstab, os.path.sep.join([target, 'etc/fstab']))