Exemple #1
0
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']))
Exemple #2
0
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)
Exemple #3
0
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']))
Exemple #4
0
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))
Exemple #5
0
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)
Exemple #6
0
    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)
Exemple #7
0
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
Exemple #8
0
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)
Exemple #9
0
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)
Exemple #10
0
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)
Exemple #11
0
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
Exemple #12
0
    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()
Exemple #13
0
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")
Exemple #14
0
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
Exemple #15
0
    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)
Exemple #16
0
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)
Exemple #17
0
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
Exemple #18
0
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
Exemple #19
0
    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)
Exemple #20
0
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
Exemple #21
0
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)
Exemple #22
0
    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)
Exemple #23
0
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)
Exemple #24
0
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']))
Exemple #25
0
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)
Exemple #26
0
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)
Exemple #27
0
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)
Exemple #28
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)
Exemple #29
0
    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
Exemple #30
0
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']))