コード例 #1
0
ファイル: apt_config.py プロジェクト: idryzhov/curtin
def apply_preserve_sources_list(target):
    # protect the just generated sources.list from cloud-init
    cloudfile = "/etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg"

    target_ver = distro.get_package_version('cloud-init', target=target)
    if not target_ver:
        LOG.info(
            "Attempt to read cloud-init version from target returned "
            "'%s', not writing preserve_sources_list config.", target_ver)
        return

    cfg = {'apt': {'preserve_sources_list': True}}
    if target_ver['major'] < 1:
        # anything cloud-init 0.X.X will get the old config key.
        cfg = {'apt_preserve_sources_list': True}

    try:
        util.write_file(paths.target_path(target, cloudfile),
                        config.dump_config(cfg),
                        mode=0o644)
        LOG.debug("Set preserve_sources_list to True in %s with: %s",
                  cloudfile, cfg)
    except IOError:
        LOG.exception(
            "Failed to protect /etc/apt/sources.list from cloud-init in '%s'",
            cloudfile)
        raise
コード例 #2
0
def ubuntu_core_curthooks(cfg, target=None):
    """ Ubuntu-Core 16 images cannot execute standard curthooks
        Instead we copy in any cloud-init configuration to
        the 'LABEL=writable' partition mounted at target.
    """

    ubuntu_core_target = os.path.join(target, "system-data")
    cc_target = os.path.join(ubuntu_core_target, 'etc/cloud/cloud.cfg.d')

    cloudconfig = cfg.get('cloudconfig', None)
    if cloudconfig:
        # remove cloud-init.disabled, if found
        cloudinit_disable = os.path.join(ubuntu_core_target,
                                         'etc/cloud/cloud-init.disabled')
        if os.path.exists(cloudinit_disable):
            util.del_file(cloudinit_disable)

        handle_cloudconfig(cloudconfig, base_dir=cc_target)

    netconfig = cfg.get('network', None)
    if netconfig:
        LOG.info('Writing network configuration')
        ubuntu_core_netconfig = os.path.join(cc_target,
                                             "50-curtin-networking.cfg")
        util.write_file(ubuntu_core_netconfig,
                        content=config.dump_config({'network': netconfig}))
コード例 #3
0
ファイル: network_state.py プロジェクト: mojodna/curtin
 def dump(self):
     state = {
         'version': self.version,
         'config': self.config,
         'network_state': self.network_state,
     }
     return curtin_config.dump_config(state)
コード例 #4
0
    def test_render_netconfig_passthrough(self, mock_writefile):
        netcfg = yaml.safe_load(self.config)
        pt_config = 'etc/cloud/cloud.cfg.d/50-curtin-networking.cfg'
        target_config = os.path.sep.join((self.target, pt_config), )

        net.render_netconfig_passthrough(self.target, netconfig=netcfg)

        content = config.dump_config(netcfg)
        mock_writefile.assert_called_with(target_config, content=content)
コード例 #5
0
    def test_curtin_error_copies_config_and_error_tarfile_defaults(self):
        """On curtin error, install error_tarfile is created with all logs.

        Curtin config, install log and error_tarfile are copied into target.
        """
        working_dir = self.tmp_path('working', _dir=self.new_root)
        ensure_dir(working_dir)
        target_dir = self.tmp_path('target', _dir=working_dir)
        write_file(self.logfile, 'old log')
        # Providing two dd images raises an error
        myargs = FakeArgs(
            config={'install': {'log_file': self.logfile}},
            source=['dd-raw:https://localhost/raw_images/centos-6-3.img',
                    'dd-raw:https://localhost/cant/provide/two/images.img'],
            reportstack=FakeReportStack())
        self.add_patch(
            'curtin.commands.collect_logs.create_log_tarfile', 'm_tar')
        self.add_patch(
            'curtin.commands.install.copy_install_log', 'm_copy_log')
        self.add_patch(
            'curtin.commands.install.tempfile.mkdtemp', 'm_mkdtemp')
        self.m_mkdtemp.return_value = working_dir
        with self.assertRaises(ValueError) as context_manager:
            install.cmd_install(myargs)
        self.assertEqual(
            'You may not use more than one disk image',
            str(context_manager.exception))
        expected_cfg = copy.deepcopy(install.CONFIG_BUILTIN)
        expected_cfg['install']['log_file'] = self.logfile
        expected_cfg['proxy'] = {}
        expected_cfg['sources'] = {
            '00_cmdline': {
                'type': 'dd-raw',
                'uri': 'https://localhost/raw_images/centos-6-3.img'},
            '01_cmdline': {
                'type': 'dd-raw',
                'uri': 'https://localhost/cant/provide/two/images.img'}}
        expected_cfg['write_files'] = {
            'curtin_install_cfg': {
                'owner': 'root:root', 'permissions': '0400',
                'path': '/root/curtin-install-cfg.yaml',
                'content': config.dump_config(expected_cfg)}}
        # Call create_log_tarfile to collect error logs.
        self.assertEqual(
            [mock.call('/var/log/curtin/curtin-error-logs.tar', expected_cfg)],
            self.m_tar.call_args_list)
        self.assertEqual(
            [mock.call(self.logfile, target_dir, '/root/curtin-install.log')],
            self.m_copy_log.call_args_list)
コード例 #6
0
def render_netconfig_passthrough(target, netconfig=None):
    """
    Extract original network config and pass it
    through to cloud-init in target
    """
    cc = 'etc/cloud/cloud.cfg.d/50-curtin-networking.cfg'
    if not isinstance(netconfig, dict):
        raise ValueError('Network config must be a dictionary')

    if 'network' not in netconfig:
        raise ValueError("Network config must contain the key 'network'")

    content = config.dump_config(netconfig)
    cc_passthrough = os.path.sep.join((target, cc,))
    LOG.info('Writing network config to %s: %s', cc, cc_passthrough)
    util.write_file(cc_passthrough, content=content)
コード例 #7
0
    def test_curthooks_net_config(self, mock_handle_cc, mock_del_file,
                                  mock_write_file):
        self.target = self.tmp_dir()
        cfg = {
            'network': {
                'version': '1',
                'config': [{'type': 'physical',
                            'name': 'eth0', 'subnets': [{'type': 'dhcp4'}]}]
            }
        }
        curthooks.ubuntu_core_curthooks(cfg, target=self.target)

        self.assertEqual(len(mock_del_file.call_args_list), 0)
        self.assertEqual(len(mock_handle_cc.call_args_list), 0)
        netcfg_path = os.path.join(self.target,
                                   'system-data',
                                   'etc/cloud/cloud.cfg.d',
                                   '50-curtin-networking.cfg')
        netcfg = config.dump_config({'network': cfg.get('network')})
        mock_write_file.assert_called_with(netcfg_path,
                                           content=netcfg)
        self.assertEqual(len(mock_del_file.call_args_list), 0)
コード例 #8
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)
コード例 #9
0
ファイル: install.py プロジェクト: mojodna/curtin
def cmd_install(args):
    from .collect_logs import create_log_tarfile
    cfg = deepcopy(CONFIG_BUILTIN)
    config.merge_config(cfg, args.config)

    for source in args.source:
        src = util.sanitize_source(source)
        cfg['sources']["%02d_cmdline" % len(cfg['sources'])] = src

    LOG.info(INSTALL_START_MSG)
    LOG.debug('LANG=%s', os.environ.get('LANG'))
    LOG.debug("merged config: %s" % cfg)
    if not len(cfg.get('sources', [])):
        raise util.BadUsage("no sources provided to install")

    for i in cfg['sources']:
        # we default to tgz for old style sources config
        cfg['sources'][i] = util.sanitize_source(cfg['sources'][i])

    migrate_proxy_settings(cfg)
    for k in ('http_proxy', 'https_proxy', 'no_proxy'):
        if k in cfg['proxy']:
            os.environ[k] = cfg['proxy'][k]

    instcfg = cfg.get('install', {})
    logfile = instcfg.get('log_file')
    error_tarfile = instcfg.get('error_tarfile')
    post_files = instcfg.get('post_files', [logfile])

    # Generate curtin configuration dump and add to write_files unless
    # installation config disables dump
    yaml_dump_file = instcfg.get('save_install_config', SAVE_INSTALL_CONFIG)
    if yaml_dump_file:
        write_files = cfg.get('write_files', {})
        write_files['curtin_install_cfg'] = {
            'path': yaml_dump_file,
            'permissions': '0400',
            'owner': 'root:root',
            'content': config.dump_config(cfg)
        }
        cfg['write_files'] = write_files

    # Load reporter
    clear_install_log(logfile)
    legacy_reporter = load_reporter(cfg)
    legacy_reporter.files = post_files

    writeline_and_stdout(logfile, INSTALL_START_MSG)
    args.reportstack.post_files = post_files
    workingd = None
    try:
        workingd = WorkingDir(cfg)
        dd_images = util.get_dd_images(cfg.get('sources', {}))
        if len(dd_images) > 1:
            raise ValueError("You may not use more than one disk image")

        LOG.debug(workingd.env())
        env = os.environ.copy()
        env.update(workingd.env())

        for name in cfg.get('stages'):
            desc = STAGE_DESCRIPTIONS.get(name, "stage %s" % name)
            reportstack = events.ReportEventStack(
                "stage-%s" % name, description=desc,
                parent=args.reportstack)
            env['CURTIN_REPORTSTACK'] = reportstack.fullname

            with reportstack:
                commands_name = '%s_commands' % name
                with util.LogTimer(LOG.debug, 'stage_%s' % name):
                    stage = Stage(name, cfg.get(commands_name, {}), env,
                                  reportstack=reportstack, logfile=logfile)
                    stage.run()

        if apply_kexec(cfg.get('kexec'), workingd.target):
            cfg['power_state'] = {'mode': 'reboot', 'delay': 'now',
                                  'message': "'rebooting with kexec'"}

        writeline_and_stdout(logfile, INSTALL_PASS_MSG)
        legacy_reporter.report_success()
    except Exception as e:
        exp_msg = INSTALL_FAIL_MSG.format(exception=e)
        writeline(logfile, exp_msg)
        LOG.error(exp_msg)
        legacy_reporter.report_failure(exp_msg)
        if error_tarfile:
            create_log_tarfile(error_tarfile, cfg)
        raise e
    finally:
        log_target_path = instcfg.get('save_install_log', SAVE_INSTALL_LOG)
        if log_target_path and workingd:
            copy_install_log(logfile, workingd.target, log_target_path)

        if instcfg.get('unmount', "") == "disabled":
            LOG.info('Skipping unmount: config disabled target unmounting')
        elif workingd:
            # unmount everything (including iscsi disks)
            util.do_umount(workingd.target, recursive=True)

            # The open-iscsi service in the ephemeral environment handles
            # disconnecting active sessions.  On Artful release the systemd
            # unit file has conditionals that are not met at boot time and
            # results in open-iscsi service not being started; This breaks
            # shutdown on Artful releases.
            # Additionally, in release < Artful, if the storage configuration
            # is layered, like RAID over iscsi volumes, then disconnecting
            # iscsi sessions before stopping the raid device hangs.
            # As it turns out, letting the open-iscsi service take down the
            # session last is the cleanest way to handle all releases
            # regardless of what may be layered on top of the iscsi disks.
            #
            # Check if storage configuration has iscsi volumes and if so ensure
            # iscsi service is active before exiting install
            if iscsi.get_iscsi_disks_from_config(cfg):
                iscsi.restart_iscsi_service()

            shutil.rmtree(workingd.top)

    apply_power_state(cfg.get('power_state'))

    sys.exit(0)
コード例 #10
0
ファイル: network_state.py プロジェクト: mojodna/curtin
 def dump_network_state(self):
     return curtin_config.dump_config(self.network_state)