Esempio n. 1
0
    def test_parse_iscsi_disk_from_config(self):
        """Test parsing iscsi volume path creates the same iscsi disk"""
        target = 'curtin-659d5f45-4f23-46cb-b826-f2937b896e09'
        iscsi_path = 'iscsi:10.245.168.20::20112:1:' + target
        cfg = {
            'storage': {
                'config': [{
                    'type': 'disk',
                    'id': 'iscsidev1',
                    'path': iscsi_path,
                    'name': 'iscsi_disk1',
                    'ptable': 'msdos',
                    'wipe': 'superblock'
                }]
            }
        }
        expected_iscsi_disk = iscsi.IscsiDisk(iscsi_path)
        iscsi_disk = iscsi.get_iscsi_disks_from_config(cfg).pop()
        # utilize IscsiDisk str method for equality check
        self.assertEqual(str(expected_iscsi_disk), str(iscsi_disk))

        # test with cfg.get('storage') since caller may already have
        # grabbed the 'storage' value from the curtin config
        iscsi_disk = iscsi.get_iscsi_disks_from_config(
            cfg.get('storage')).pop()
        # utilize IscsiDisk str method for equality check
        self.assertEqual(str(expected_iscsi_disk), str(iscsi_disk))
Esempio n. 2
0
    def test_parse_iscsi_disk_from_config_none(self):
        """Test parse_iscsi_disks handles no config"""
        expected_iscsi_disks = []
        iscsi_disks = iscsi.get_iscsi_disks_from_config({})
        self.assertEqual(expected_iscsi_disks, iscsi_disks)

        cfg = None
        iscsi_disks = iscsi.get_iscsi_disks_from_config(cfg)
        self.assertEqual(expected_iscsi_disks, iscsi_disks)
Esempio n. 3
0
    def test_parse_iscsi_disk_from_config_empty(self):
        """Test parse_iscsi_disks handles empty/invalid config"""
        expected_iscsi_disks = []
        iscsi_disks = iscsi.get_iscsi_disks_from_config({})
        self.assertEqual(expected_iscsi_disks, iscsi_disks)

        cfg = {'storage': {'config': []}}
        iscsi_disks = iscsi.get_iscsi_disks_from_config(cfg)
        self.assertEqual(expected_iscsi_disks, iscsi_disks)
Esempio n. 4
0
 def test_parse_iscsi_disk_from_config_invalid_iscsi(self):
     """Test parsing storage config with no iscsi disks included"""
     cfg = {
         'storage': {
             'config': [{
                 'type': 'disk',
                 'id': 'iscsidev2',
                 'path': 'iscsi:garbage',
                 'name': 'noob-city',
                 'ptable': 'msdos',
                 'wipe': 'superblock'
             }]
         }
     }
     with self.assertRaises(ValueError):
         iscsi.get_iscsi_disks_from_config(cfg)
Esempio n. 5
0
 def test_parse_iscsi_disk_from_config_no_iscsi(self):
     """Test parsing storage config with no iscsi disks included"""
     cfg = {
         'storage': {
             'config': [{
                 'type': 'disk',
                 'id': 'ssd1',
                 'path': 'dev/slash/foo1',
                 'name': 'the-fast-one',
                 'ptable': 'gpt',
                 'wipe': 'superblock'
             }]
         }
     }
     expected_iscsi_disks = []
     iscsi_disks = iscsi.get_iscsi_disks_from_config(cfg)
     self.assertEqual(expected_iscsi_disks, iscsi_disks)
Esempio n. 6
0
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)