Пример #1
0
    def run(self, target=None, initrd=None, background=False,  # noqa: C901
            paused=False, gdb=4123, dbg=False, virtio_nic=None, bridge=None,
            interface=None, dry_run=False, args=None, memory=64, cpu_sockets=1,
            cpu_cores=1):

        if target is None:
            raise KraftError('Target not set')

        elif target.binary is None:
            raise KraftError('Target has not been compiled')

        elif not os.path.exists(target.binary):
            raise KraftError('Could not find unikernel: %s' % target.binary)

        logger.debug("Running binary: %s" % target.binary)

        runner = target.platform.runner
        runner.use_debug = dbg
        runner.architecture = target.architecture.name

        if initrd:
            runner.add_initrd(initrd)

        if virtio_nic:
            runner.add_virtio_nic(virtio_nic)

        if bridge:
            runner.add_bridge(bridge)

        if interface:
            runner.add_interface(interface)

        if gdb:
            runner.open_gdb(gdb)

        if memory:
            runner.set_memory(memory)

        if cpu_sockets:
            runner.set_cpu_sockets(cpu_sockets)

        if cpu_cores:
            runner.set_cpu_cores(cpu_cores)

        runner.unikernel = target.binary
        runner.execute(
            extra_args=args,
            background=background,
            paused=paused,
            dry_run=dry_run,
        )
Пример #2
0
def validate_targets_section(config_file, config):
    if not isinstance(config, list):
        raise KraftError(
            "Top level object in '{}' needs to be an object not '{}'.".format(
                config_file.filename, type(config)))

    return config
Пример #3
0
def validate_top_level_string_or_list(config_file, config, section):
    if not isinstance(config, (six.string_types, list)) or config is None:
        raise KraftError(
            "Top level object in '{}' needs to be a string or array  not '{}'."
            .format(config_file.filename, type(config)))

    return config
Пример #4
0
def validate_unikraft_section(config_file, config):
    if not isinstance(config, (six.string_types, dict, int, float)):
        raise KraftError(
            "Top level object in '{}' needs to be an object not '{}'.".format(
                config_file.filename, type(config)))

    return config
Пример #5
0
def validate_top_level_string(config_file, config, section):
    if not isinstance(config, six.string_types):
        raise KraftError(
            "Top level object in '{}' needs to be a string not '{}'.".format(
                config_file.filename, type(config)))

    return config
Пример #6
0
def load_jsonschema(config_file):
    filename = os.path.join(
        get_schema_path(),
        "specification_v{0}.json".format(config_file.version))

    if not os.path.exists(filename):
        raise KraftError('Specification in "{}" is unsupported. {}'.format(
            filename, SPECIFICATION_EXPLANATION))

    with open(filename, "r") as fh:
        return json.load(fh)
Пример #7
0
    def version(self):
        if 'specification' not in self.config:
            return KRAFT_SPEC_LATEST

        version = str(self.config['specification'])

        version_pattern = re.compile(r"^[0-9]+(\.\d+)?$")
        if not version_pattern.match(version):
            raise KraftError('Specification "{}" in "{}" is invalid.'.format(
                version, self.filename))

        return SpecificationVersion(version)
Пример #8
0
def load_yaml(filename, encoding=None, binary=True):
    try:
        with io.open(filename, 'rb' if binary else 'r',
                     encoding=encoding) as fh:
            return yaml.safe_load(fh)
    except (IOError, yaml.YAMLError, UnicodeDecodeError) as e:
        if encoding is None:
            # Sometimes the user's locale sets an encoding that doesn't match
            # the YAML files. Im such cases, retry once with the "default"
            # UTF-8 encoding
            return load_yaml(filename, encoding='utf-8-sig', binary=False)
        error_name = getattr(e, '__module__', '') + '.' + e.__class__.__name__
        raise KraftError(u"{}: {}".format(error_name, e))
Пример #9
0
    def generate_bridge_name(self, prefix='virbr', max_tries=1024):
        suffix_i = 0
        new_name = None

        while suffix_i < max_tries:
            new_name = prefix + str(suffix_i)

            if not self.bridge_exists(new_name):
                return new_name

            suffix_i += 1

        raise KraftError("Max tries for bridge creation reached!")
Пример #10
0
def handle_errors(errors, format_error_func, filename):
    """jsonschema returns an error tree full of information to explain what has
    gone wrong. Process each error and pull out relevant information and re-write
    helpful error messages that are relevant.
    """
    errors = list(sorted(errors, key=str))
    if not errors:
        return

    error_msg = '\n'.join(format_error_func(error) for error in errors)
    raise KraftError(
        "The Kraft file{file_msg} is invalid because:\n{error_msg}".format(
            file_msg=" '{}'".format(filename) if filename else "",
            error_msg=error_msg))
Пример #11
0
def validate_component_section(filename, config, section):
    """Validate the structure of a configuration section. This must be done
    before interpolation so it's separate from schema validation.
    """
    if not isinstance(config, dict):
        raise KraftError(
            "In file '{filename}', {section} must be a mapping, not "
            "{type}.".format(filename=filename,
                             section=section,
                             type=anglicize_json_type(
                                 python_type_to_yaml_type(config))))

    for key, value in config.items():
        if not isinstance(key, six.string_types):
            raise KraftError(
                "In file '{filename}', the {section} name {name} must be a "
                "quoted string, i.e. '{name}'.".format(filename=filename,
                                                       section=section,
                                                       name=key))

        # Turn a None type into a boolean, so that listing it returns as True
        if value is None:
            value = True
            config[key] = True

        if not isinstance(value,
                          (six.string_types, dict, bool, int, float, list)):
            raise KraftError(
                "In file '{filename}', {section} '{name}' must be a mapping not "
                "{type}.".format(filename=filename,
                                 section=section,
                                 name=key,
                                 type=anglicize_json_type(
                                     python_type_to_yaml_type(value))))

    return config
Пример #12
0
def kraft_configure(ctx,
                    env=None,
                    workdir=None,
                    target=None,
                    plat=None,
                    arch=None,
                    force_configure=False,
                    show_menuconfig=False,
                    options=[],
                    use_versions=[]):
    """
    Populates the local .config with the default values for the target
    application.
    """

    if workdir is None or os.path.exists(workdir) is False:
        raise ValueError("working directory is empty: %s" % workdir)

    logger.debug("Configuring %s..." % workdir)

    app = Application.from_workdir(
        workdir=workdir,
        force_init=force_configure,
        use_versions=use_versions,
    )
    if show_menuconfig:
        if sys.stdout.isatty():
            app.open_menuconfig()
            return
        else:
            raise KraftError("Cannot open menuconfig in non-TTY environment")

    if app.is_configured() and force_configure is False:
        if click.confirm(
                "%s is already configured, would you like to overwrite configuration?"
                % workdir):  # noqa
            force_configure = True
        else:
            raise CannotConfigureApplication(workdir)

    if len(app.config.targets.all()) == 1:
        target = app.config.targets.all()[0]

    elif len(app.binaries) == 1:
        target = app.binaries[0]

    else:
        for t in app.config.targets.all():
            # Did the user specific a target-name?
            if target is not None and target == t.name:
                target = t
                break

            # Did the user specify arch AND plat combo? Does it exist?
            elif arch == t.architecture.name \
                    and plat == t.platform.name:
                target = t
                break

    # The user did not specify something
    if target is None:
        binaries = []

        for t in app.binaries:
            binname = os.path.basename(t.binary)
            if t.name is not None:
                binname = "%s (%s)" % (binname, t.name)

            binaries.append(binname)

        # Prompt user for binary selection
        answers = inquirer.prompt([
            inquirer.List(
                'target',
                message="Which target would you like to configure?",
                choices=binaries,
            ),
        ])

        # Work backwards from binary name
        for t in app.binaries:
            if answers['target'] == os.path.basename(t.binary):
                target = t
                break

    app.configure(
        target=target,
        options=options,
        force_configure=force_configure,
    )

    app.save_yaml()
Пример #13
0
    def run(
            self,
            target=None,
            initrd=None,
            background=False,  # noqa: C901
            paused=False,
            gdb=4123,
            dbg=False,
            virtio_nic=None,
            bridge=None,
            interface=None,
            dry_run=False,
            args=None,
            memory=64,
            cpu_sockets=1,
            cpu_cores=1):

        if target is None:
            raise KraftError('Target not set')

        elif target.binary is None:
            raise KraftError('Target has not been compiled')

        elif not os.path.exists(target.binary):
            raise KraftError('Could not find unikernel: %s' % target.binary)

        logger.debug("Running binary: %s" % target.binary)

        runner = target.platform.runner
        runner.use_debug = dbg
        runner.architecture = target.architecture.name

        if initrd:
            runner.add_initrd(initrd)

        if virtio_nic:
            runner.add_virtio_nic(virtio_nic)

        if bridge:
            runner.add_bridge(bridge)

        if interface:
            runner.add_interface(interface)

        if gdb:
            runner.open_gdb(gdb)

        if isinstance(memory, int) and memory > 0:
            runner.set_memory(memory)

        if cpu_sockets:
            runner.set_cpu_sockets(cpu_sockets)

        if cpu_cores:
            runner.set_cpu_cores(cpu_cores)

        for volume in self.config.volumes.all():
            if volume.driver is VolumeDriver.VOL_9PFS:
                path = os.path.join(self.localdir, volume.name)

                if not os.path.exists(path):
                    tar = tarfile.open(volume.source)
                    tar.extractall(path)
                    tar.close()

                runner.add_virtio_9pfs(path)

        runner.unikernel = target.binary
        runner.execute(
            extra_args=args,
            background=background,
            paused=paused,
            dry_run=dry_run,
        )
Пример #14
0
    def configure(ctx,
                  self,
                  target=None,
                  arch=None,
                  plat=None,
                  options=[],
                  force_configure=False):
        """
        Configure a Unikraft application.
        """

        if not self.is_configured():
            self.init()

        if target is not None and isinstance(target, Target):
            arch = target.architecture
            plat = target.platform

        archs = list()
        plats = list()

        def match_arch(arch, target):
            if isinstance(arch, six.string_types) and \
                    arch == target.architecture.name:
                return target.architecture
            if isinstance(arch, Architecture) and \
                    arch.name == target.architecture.name:
                return arch
            return None

        def match_plat(plat, target):
            if isinstance(plat, six.string_types) and \
                    plat == target.platform.name:
                return target.platform
            if isinstance(plat, Platform) and \
                    plat.name == target.platform.name:
                return plat
            return None

        if len(self.config.targets.all()) == 1 \
                and target is None and arch is None and plat is None:
            target = self.config.targets.all()[0]
            archs.append(target.architecture)
            plats.append(target.platform)

        else:
            for t in self.config.targets.all():
                if match_arch(arch, t) is not None \
                        and match_plat(plat, t) is not None:
                    archs.append(t.architecture)
                    plats.append(t.platform)

        # Generate a dynamic .config to populate defconfig with based on
        # configure's parameterization.
        dotconfig = list()
        dotconfig.extend(self.config.unikraft.kconfig or [])

        for arch in archs:
            if not arch.is_downloaded:
                raise MissingComponent(arch)

            dotconfig.extend(arch.kconfig)
            dotconfig.append(arch.kconfig_enabled_flag)

        for plat in plats:
            if not plat.is_downloaded:
                raise MissingComponent(plat)

            dotconfig.extend(plat.kconfig)
            dotconfig.append(plat.kconfig_enabled_flag)

        for lib in self.config.libraries.all():
            if not lib.is_downloaded:
                raise MissingComponent(lib)

            dotconfig.extend(lib.kconfig)
            dotconfig.append(lib.kconfig_enabled_flag)

        # Add any additional confguration options, and overriding existing
        # configuraton options.
        for new_opt in options:
            o = new_opt.split('=')
            for exist_opt in dotconfig:
                e = exist_opt.split('=')
                if o[0] == e[0]:
                    dotconfig.remove(exist_opt)
                    break
            dotconfig.append(new_opt)

        # Create a temporary file with the kconfig written to it
        fd, path = tempfile.mkstemp()

        with os.fdopen(fd, 'w+') as tmp:
            logger.debug('Using the following defconfig:')
            for line in dotconfig:
                logger.debug(' > ' + line)
                tmp.write(line + '\n')

        return_code = 0

        try:
            return_code = self.make([('UK_DEFCONFIG=%s' % path), 'defconfig'])
        finally:
            os.remove(path)

        if return_code > 0:
            raise KraftError("Could not configure application")

        return True