예제 #1
0
    def generate_autoinstall(self, profile=None, system=None) -> str:
        """
        This is an internal method for generating an autoinstall config/script. Please use the
        ``generate_autoinstall_for_*`` methods. If you insist on using this mehtod please only supply a profile or a
        system, not both.

        :param profile: The profile to use for generating the autoinstall config/script.
        :param system: The system to use for generating the autoinstall config/script. If both arguments are given,
                       this wins.
        :return: The autoinstall script or configuration file as a string.
        """
        obj = system
        obj_type = "system"
        if system is None:
            obj = profile
            obj_type = "profile"

        meta = utils.blender(self.api, False, obj)
        autoinstall_rel_path = meta["autoinstall"]

        if not autoinstall_rel_path:
            return "# automatic installation file value missing or invalid at %s %s" % (obj_type, obj.name)

        # get parent distro
        distro = profile.get_conceptual_parent()
        if system is not None:
            distro = system.get_conceptual_parent().get_conceptual_parent()

        # make autoinstall_meta metavariable available at top level
        autoinstall_meta = meta["autoinstall_meta"]
        del meta["autoinstall_meta"]
        meta.update(autoinstall_meta)

        # add package repositories metadata to autoinstall metavariables
        if distro.breed == "redhat":
            meta["yum_repo_stanza"] = self.generate_repo_stanza(obj, (system is None))
            meta["yum_config_stanza"] = self.generate_config_stanza(obj, (system is None))
        # FIXME: implement something similar to zypper (SUSE based distros) and apt (Debian based distros)

        meta["kernel_options"] = utils.dict_to_string(meta["kernel_options"])
        if "kernel_options_post" in meta:
            meta["kernel_options_post"] = utils.dict_to_string(meta["kernel_options_post"])

        # add install_source_directory metavariable to autoinstall metavariables if distro is based on Debian
        if distro.breed in ["debian", "ubuntu"] and "tree" in meta:
            urlparts = urllib.parse.urlsplit(meta["tree"])
            meta["install_source_directory"] = urlparts[2]

        try:
            autoinstall_path = "%s/%s" % (self.settings.autoinstall_templates_dir, autoinstall_rel_path)
            raw_data = utils.read_file_contents(autoinstall_path)

            data = self.templar.render(raw_data, meta, None)

            return data
        except FileNotFoundError:
            error_msg = "automatic installation file %s not found at %s" \
                        % (meta["autoinstall"], self.settings.autoinstall_templates_dir)
            self.api.logger.warning(error_msg)
            return "# %s" % error_msg
예제 #2
0
    def generate_autoinstall(self, profile=None, system=None):

        obj = system
        obj_type = "system"
        if system is None:
            obj = profile
            obj_type = "profile"

        meta = utils.blender(self.api, False, obj)
        autoinstall_rel_path = meta["autoinstall"]

        if not autoinstall_rel_path:
            return "# automatic installation file value missing or invalid at %s %s" % (
                obj_type, obj.name)

        # get parent distro
        distro = profile.get_conceptual_parent()
        if system is not None:
            distro = system.get_conceptual_parent().get_conceptual_parent()

        # make autoinstall_meta metavariable available at top level
        autoinstall_meta = meta["autoinstall_meta"]
        del meta["autoinstall_meta"]
        meta.update(autoinstall_meta)

        # add package repositories metadata to autoinstall metavariables
        if distro.breed == "redhat":
            meta["yum_repo_stanza"] = self.generate_repo_stanza(
                obj, (system is None))
            meta["yum_config_stanza"] = self.generate_config_stanza(
                obj, (system is None))
        # FIXME: implement something similar to zypper (SUSE based distros) and apt
        #        (Debian based distros)

        meta["kernel_options"] = utils.dict_to_string(meta["kernel_options"])
        if "kernel_options_post" in meta:
            meta["kernel_options_post"] = utils.dict_to_string(
                meta["kernel_options_post"])

        # add install_source_directory metavariable to autoinstall metavariables
        # if distro is based on Debian
        if distro.breed in ["debian", "ubuntu"] and "tree" in meta:
            urlparts = urllib.parse.urlsplit(meta["tree"])
            meta["install_source_directory"] = urlparts[2]

        try:
            autoinstall_path = "%s/%s" % (
                self.settings.autoinstall_templates_dir, autoinstall_rel_path)
            raw_data = utils.read_file_contents(autoinstall_path,
                                                self.api.logger)

            data = self.templar.render(raw_data, meta, None, obj)

            return data
        except FileNotFoundException:
            error_msg = "automatic installation file %s not found at %s" % (
                meta["autoinstall"], self.settings.autoinstall_templates_dir)
            self.api.logger.warning(error_msg)
            return "# %s" % error_msg
예제 #3
0
    def generate_autoinstall(self, profile=None, system=None):

        obj = system
        obj_type = "system"
        if system is None:
            obj = profile
            obj_type = "profile"

        meta = utils.blender(self.api, False, obj)
        autoinstall_rel_path = meta["autoinstall"]

        if not autoinstall_rel_path:
            return "# automatic installation file value missing or invalid at %s %s" % (obj_type, obj.name)

        # get parent distro
        distro = profile.get_conceptual_parent()
        if system is not None:
            distro = system.get_conceptual_parent().get_conceptual_parent()

        # make autoinstall_meta metavariable available at top level
        autoinstall_meta = meta["autoinstall_meta"]
        del meta["autoinstall_meta"]
        meta.update(autoinstall_meta)

        # add package repositories metadata to autoinstall metavariables
        if distro.breed == "redhat":
            meta["yum_repo_stanza"] = self.generate_repo_stanza(obj, (system is None))
            meta["yum_config_stanza"] = self.generate_config_stanza(obj, (system is None))
        # FIXME: implement something similar to zypper (SUSE based distros) and apt
        #        (Debian based distros)

        meta["kernel_options"] = utils.dict_to_string(meta["kernel_options"])
        if "kernel_options_post" in meta:
            meta["kernel_options_post"] = utils.dict_to_string(meta["kernel_options_post"])

        # add install_source_directory metavariable to autoinstall metavariables
        # if distro is based on Debian
        if distro.breed in ["debian", "ubuntu"] and "tree" in meta:
            urlparts = urlparse.urlsplit(meta["tree"])
            meta["install_source_directory"] = urlparts[2]

        try:
            autoinstall_path = "%s/%s" % (self.settings.autoinstall_templates_dir, autoinstall_rel_path)
            raw_data = utils.read_file_contents(autoinstall_path, self.api.logger)

            data = self.templar.render(raw_data, meta, None, obj)

            if distro.breed == "suse":
                # AutoYaST profile
                data = self.generate_autoyast(profile, system, data)

            return data
        except FileNotFoundException:
            error_msg = "automatic installation file %s not found at %s" % (meta["autoinstall"], self.settings.autoinstall_templates_dir)
            self.api.logger.warning(error_msg)
            return "# %s" % error_msg
예제 #4
0
def test_dict_to_string(testinput, expected_result):
    # Arrange
    # TODO: Generate more parameter combinations

    # Act
    result = utils.dict_to_string(testinput)

    # Assert
    assert expected_result == result
예제 #5
0
    def generate_kickstart(self, profile=None, system=None):

        obj = system
        if system is None:
            obj = profile

        meta = utils.blender(self.api, False, obj)
        kickstart_path = meta["kickstart"]

        if not kickstart_path:
            return "# kickstart is missing or invalid: %s" % meta["kickstart"]

        ksmeta = meta["ks_meta"]
        del meta["ks_meta"]
        meta.update(ksmeta)  # make available at top level
        meta["yum_repo_stanza"] = self.generate_repo_stanza(
            obj, (system is None))
        meta["yum_config_stanza"] = self.generate_config_stanza(
            obj, (system is None))
        meta["kernel_options"] = utils.dict_to_string(meta["kernel_options"])

        # add extra variables for other distro types
        if "tree" in meta:
            urlparts = urlparse.urlsplit(meta["tree"])
            meta["install_source_directory"] = urlparts[2]

        try:
            raw_data = utils.read_file_contents(kickstart_path,
                                                self.api.logger)
            if raw_data is None:
                return "# kickstart is sourced externally: %s" % meta[
                    "kickstart"]
            distro = profile.get_conceptual_parent()
            if system is not None:
                distro = system.get_conceptual_parent().get_conceptual_parent()

            data = self.templar.render(raw_data, meta, None, obj)

            if distro.breed == "suse":
                # AutoYaST profile
                data = self.generate_autoyast(profile, system, data)

            return data
        except FileNotFoundException:
            self.api.logger.warning("kickstart not found: %s" %
                                    meta["kickstart"])
            return "# kickstart not found: %s" % meta["kickstart"]
예제 #6
0
    def generate_autoinstall(self, profile=None, system=None):

        obj = system
        obj_type = "system"
        if system is None:
            obj = profile
            obj_type = "profile"

        meta = utils.blender(self.api, False, obj)
        autoinstall_rel_path = meta["autoinstall"]

        if not autoinstall_rel_path:
            return "# automatic installation file value missing or invalid at %s %s" % (obj_type, obj.name)

        autoinstall_meta = meta["autoinstall_meta"]
        del meta["autoinstall_meta"]
        meta.update(autoinstall_meta)     # make available at top level
        meta["yum_repo_stanza"] = self.generate_repo_stanza(obj, (system is None))
        meta["yum_config_stanza"] = self.generate_config_stanza(obj, (system is None))
        meta["kernel_options"] = utils.dict_to_string(meta["kernel_options"])

        # add extra variables for other distro types
        if "tree" in meta:
            urlparts = urlparse.urlsplit(meta["tree"])
            meta["install_source_directory"] = urlparts[2]

        try:
            autoinstall_path = "%s/%s" % (self.settings.autoinstall_templates_dir, autoinstall_rel_path)
            raw_data = utils.read_file_contents(autoinstall_path, self.api.logger)
            distro = profile.get_conceptual_parent()
            if system is not None:
                distro = system.get_conceptual_parent().get_conceptual_parent()

            data = self.templar.render(raw_data, meta, None, obj)

            if distro.breed == "suse":
                # AutoYaST profile
                data = self.generate_autoyast(profile, system, data)

            return data
        except FileNotFoundException:
            error_msg = "automatic installation file %s not found at %s" % (meta["autoinstall"], self.settings.autoinstall_templates_dir)
            self.api.logger.warning(error_msg)
            return "# %s" % error_msg
예제 #7
0
    def generate_kickstart(self, profile=None, system=None):

        obj = system
        if system is None:
            obj = profile

        meta = utils.blender(self.api, False, obj)
        kickstart_path = meta["kickstart"]

        if not kickstart_path:
            return "# kickstart is missing or invalid: %s" % meta["kickstart"]

        ksmeta = meta["ks_meta"]
        del meta["ks_meta"]
        meta.update(ksmeta)     # make available at top level
        meta["yum_repo_stanza"] = self.generate_repo_stanza(obj, (system is None))
        meta["yum_config_stanza"] = self.generate_config_stanza(obj, (system is None))
        meta["kernel_options"] = utils.dict_to_string(meta["kernel_options"])

        # add extra variables for other distro types
        if "tree" in meta:
            urlparts = urlparse.urlsplit(meta["tree"])
            meta["install_source_directory"] = urlparts[2]

        try:
            raw_data = utils.read_file_contents(kickstart_path, self.api.logger)
            if raw_data is None:
                return "# kickstart is sourced externally: %s" % meta["kickstart"]
            distro = profile.get_conceptual_parent()
            if system is not None:
                distro = system.get_conceptual_parent().get_conceptual_parent()

            data = self.templar.render(raw_data, meta, None, obj)

            if distro.breed == "suse":
                # AutoYaST profile
                data = self.generate_autoyast(profile, system, data)

            return data
        except FileNotFoundException:
            self.api.logger.warning("kickstart not found: %s" % meta["kickstart"])
            return "# kickstart not found: %s" % meta["kickstart"]
예제 #8
0
    def build_kernel_options(self, system, profile, distro, image, arch,
                             autoinstall_path):
        """
        Builds the full kernel options line.
        """

        management_interface = None
        if system is not None:
            blended = utils.blender(self.api, False, system)
            # find the first management interface
            try:
                for intf in list(system.interfaces.keys()):
                    if system.interfaces[intf]["management"]:
                        management_interface = intf
                        break
            except:
                # just skip this then
                pass
        elif profile is not None:
            blended = utils.blender(self.api, False, profile)
        else:
            blended = utils.blender(self.api, False, image)

        append_line = ""
        kopts = blended.get("kernel_options", dict())
        kopts = utils.revert_strip_none(kopts)

        # SUSE and other distro specific kernel additions or modificatins
        utils.kopts_overwrite(system, distro, kopts, self.settings)

        # since network needs to be configured again (it was already in netboot) when kernel boots
        # and we choose to do it dinamically, we need to set 'ksdevice' to one of
        # the interfaces' MAC addresses in ppc systems.
        # ksdevice=bootif is not useful in yaboot, as the "ipappend" line is a pxe feature.
        if system and arch and (arch == "ppc" or arch == "ppc64"):
            for intf in list(system.interfaces.keys()):
                # use first interface with defined IP and MAC, since these are required
                # fields in a DHCP entry
                mac_address = system.interfaces[intf]['mac_address']
                ip_address = system.interfaces[intf]['ip_address']
                if mac_address and ip_address:
                    kopts['BOOTIF'] = '01-' + mac_address
                    kopts['ksdevice'] = mac_address
                    break

        # support additional initrd= entries in kernel options.
        if "initrd" in kopts:
            append_line = ",%s" % kopts.pop("initrd")
        hkopts = utils.dict_to_string(kopts)
        append_line = "%s %s" % (append_line, hkopts)

        # automatic installation file path rewriting (get URLs for local files)
        if autoinstall_path:

            # FIXME: need to make shorter rewrite rules for these URLs

            try:
                ipaddress = socket.gethostbyname_ex(blended["http_server"])[2][0]
            except socket.gaierror:
                ipaddress = blended["http_server"]
            URL_REGEX = "[a-zA-Z]*://.*"
            local_autoinstall_file = not re.match(URL_REGEX, autoinstall_path)
            if local_autoinstall_file:
                if system is not None:
                    autoinstall_path = "http://%s/cblr/svc/op/autoinstall/system/%s" % (ipaddress, system.name)
                else:
                    autoinstall_path = "http://%s/cblr/svc/op/autoinstall/profile/%s" % (ipaddress, profile.name)

            if distro.breed is None or distro.breed == "redhat":

                append_line += " kssendmac"
                append_line = "%s ks=%s" % (append_line, autoinstall_path)
                gpxe = blended["enable_gpxe"]
                if gpxe:
                    append_line = append_line.replace('ksdevice=bootif', 'ksdevice=${net0/mac}')
            elif distro.breed == "suse":
                append_line = "%s autoyast=%s" % (append_line, autoinstall_path)
                if management_interface:
                    append_line += "netdevice=%s" % management_interface
            elif distro.breed == "debian" or distro.breed == "ubuntu":
                append_line = "%s auto-install/enable=true priority=critical netcfg/choose_interface=auto url=%s" % (append_line, autoinstall_path)
                if management_interface:
                    append_line += " netcfg/choose_interface=%s" % management_interface
            elif distro.breed == "freebsd":
                append_line = "%s ks=%s" % (append_line, autoinstall_path)

                # rework kernel options for debian distros
                translations = {'ksdevice': "interface", 'lang': "locale"}
                for k, v in list(translations.items()):
                    append_line = append_line.replace("%s=" % k, "%s=" % v)

                # interface=bootif causes a failure
                append_line = append_line.replace("interface=bootif", "")
            elif distro.breed == "vmware":
                if distro.os_version.find("esxi") != -1:
                    # ESXi is very picky, it's easier just to redo the
                    # entire append line here since
                    append_line = " ks=%s %s" % (autoinstall_path, hkopts)
                    # ESXi likes even fewer options, so we remove them too
                    append_line = append_line.replace("kssendmac", "")
                else:
                    append_line = "%s vmkopts=debugLogToSerial:1 mem=512M ks=%s" % \
                        (append_line, autoinstall_path)
                # interface=bootif causes a failure
                append_line = append_line.replace("ksdevice=bootif", "")
            elif distro.breed == "xen":
                if distro.os_version.find("xenserver620") != -1:
                    img_path = os.path.join("/images", distro.name)
                    append_line = "append %s/xen.gz dom0_max_vcpus=2 dom0_mem=752M com1=115200,8n1 console=com1,vga --- %s/vmlinuz xencons=hvc console=hvc0 console=tty0 install answerfile=%s --- %s/install.img" % (img_path, img_path, autoinstall_path, img_path)
                    return append_line
            elif distro.breed == "powerkvm":
                append_line += " kssendmac"
                append_line = "%s kvmp.inst.auto=%s" % (append_line, autoinstall_path)

        if distro is not None and (distro.breed in ["debian", "ubuntu"]):
            # Hostname is required as a parameter, the one in the preseed is
            # not respected, so calculate if we have one here.
            # We're trying: first part of FQDN in hostname field, then system
            # name, then profile name.
            # In Ubuntu, this is at least used for the volume group name when
            # using LVM.
            domain = "local.lan"
            if system is not None:
                if system.hostname is not None and system.hostname != "":
                    # If this is a FQDN, grab the first bit
                    hostname = system.hostname.split(".")[0]
                    _domain = system.hostname.split(".")[1:]
                    if _domain:
                        domain = ".".join(_domain)
                else:
                    hostname = system.name
            else:
                # ubuntu at the very least does not like having underscores
                # in the hostname.
                # FIXME: Really this should remove all characters that are
                # forbidden in hostnames
                hostname = profile.name.replace("_", "")

            # At least for debian deployments configured for DHCP networking
            # this values are not used, but specifying here avoids questions
            append_line = "%s hostname=%s" % (append_line, hostname)
            append_line = "%s domain=%s" % (append_line, domain)

            # A similar issue exists with suite name, as installer requires
            # the existence of "stable" in the dists directory
            append_line = "%s suite=%s" % (append_line, distro.os_version)

        # append necessary kernel args for arm architectures
        if arch is not None and arch.startswith("arm"):
            append_line = "%s fixrtc vram=48M omapfb.vram=0:24M" % append_line

        # do variable substitution on the append line
        # promote all of the autoinstall_meta variables
        if "autoinstall_meta" in blended:
            blended.update(blended["autoinstall_meta"])
        append_line = self.templar.render(append_line, utils.flatten(blended), None)

        # For now console=ttySx,BAUDRATE are only set for systems
        # This could get enhanced for profile/distro via utils.blender (inheritance)
        # This also is architecture specific. E.g: Some ARM consoles need: console=ttyAMAx,BAUDRATE
        # I guess we need a serial_kernel_dev = param, that can be set to "ttyAMA" if needed.
        if system:
            if (system.serial_device is not None) or (system.serial_baud_rate is not None):
                if system.serial_device:
                    serial_device = system.serial_device
                else:
                    serial_device = 0
                if system.serial_baud_rate:
                    serial_baud_rate = system.serial_baud_rate
                else:
                    serial_baud_rate = 115200

                append_line = "%s console=ttyS%s,%s" % (append_line, serial_device, serial_baud_rate)

        # FIXME - the append_line length limit is architecture specific
        if len(append_line) >= 1023:
            self.logger.warning("warning: kernel option length exceeds 1023")

        return append_line
예제 #9
0
    def write_all_system_files(self, system, menu_items):

        profile = system.get_conceptual_parent()
        if profile is None:
            raise CX("system %(system)s references a missing profile %(profile)s" % {"system": system.name, "profile": system.profile})

        distro = profile.get_conceptual_parent()
        image_based = False
        image = None
        if distro is None:
            if profile.COLLECTION_TYPE == "profile":
                raise CX("profile %(profile)s references a missing distro %(distro)s" % {"profile": system.profile, "distro": profile.distro})
            else:
                image_based = True
                image = profile

        pxe_metadata = {'pxe_menu_items': menu_items}

        # hack: s390 generates files per system not per interface
        if not image_based and distro.arch.startswith("s390"):
            short_name = system.name.split('.')[0]
            s390_name = 'linux' + short_name[7:10]
            self.logger.info("Writing s390x pxe config for %s" % short_name)
            # Always write a system specific _conf and _parm file
            pxe_f = os.path.join(self.bootloc, "s390x", "s_%s" % s390_name)
            conf_f = "%s_conf" % pxe_f
            parm_f = "%s_parm" % pxe_f

            c_templ = os.path.join(self.settings.boot_loader_conf_template_dir, "s390x_conf.template")
            p_templ = os.path.join(self.settings.boot_loader_conf_template_dir, "s390x_parm.template")

            template_conf_f = open(c_templ)
            template_parm_f = open(p_templ)

            self.logger.info("Files: (conf,param) - (%s,%s)" % (conf_f, parm_f))
            self.logger.info("Template Files: (conf,param) - (%s,%s)" % (c_templ, p_templ))
            blended = utils.blender(self.api, True, system)
            self.templar.render(template_conf_f, blended, conf_f)
            # FIXME: profiles also need this data!
            # FIXME: the _conf and _parm files are limited to 80 characters in length
            # gather default kernel_options and default kernel_options_s390x
            kopts = blended.get("kernel_options", "")
            hkopts = shlex.split(utils.dict_to_string(kopts))
            blended["kernel_options"] = hkopts
            self.templar.render(template_parm_f, blended, parm_f)

            # Write system specific zPXE file
            if system.is_management_supported():
                kernel_path = os.path.join("/images", distro.name, os.path.basename(distro.kernel))
                initrd_path = os.path.join("/images", distro.name, os.path.basename(distro.initrd))
                with open(pxe_f, 'w') as out:
                    out.write(kernel_path + '\n' + initrd_path + '\n')
            else:
                # ensure the file doesn't exist
                utils.rmfile(pxe_f)
            return

        # generate one record for each described NIC ..
        for (name, interface) in list(system.interfaces.items()):

            pxe_name = system.get_config_filename(interface=name)
            grub_name = system.get_config_filename(interface=name, loader="grub")

            if pxe_name is not None:
                pxe_path = os.path.join(self.bootloc, "pxelinux.cfg", pxe_name)

            if grub_name is not None:
                grub_path = os.path.join(self.bootloc, "grub", "system", grub_name)

            if grub_path is None and pxe_path is None:
                self.logger.warning("invalid interface recorded for system (%s,%s)" % (system.name, name))
                continue

            if image_based:
                working_arch = image.arch
            else:
                working_arch = distro.arch

            if working_arch is None:
                raise CX("internal error, invalid arch supplied")

            # for tftp only ...
            if working_arch in ["i386", "x86", "x86_64", "arm", "aarch64", "ppc64le", "ppc64el", "standard"]:
                # ToDo: This is old, move this logic into item_system.get_config_filename()
                pass

            elif working_arch == "ppc" or working_arch == "ppc64":
                # Determine filename for system-specific bootloader config
                filename = "%s" % system.get_config_filename(interface=name).lower()
                # to inherit the distro and system's boot_loader values correctly
                blended_system = utils.blender(self.api, False, system)
                if blended_system["boot_loader"] == "pxelinux":
                    pass
                else:
                    pxe_path = os.path.join(self.bootloc, "etc", filename)
                    # Link to the yaboot binary
                    f3 = os.path.join(self.bootloc, "ppc", filename)
                    if os.path.lexists(f3):
                        utils.rmfile(f3)
                    os.symlink("../yaboot", f3)
            else:
                continue

            if system.is_management_supported():
                if not image_based:
                    if pxe_path:
                        self.write_pxe_file(pxe_path, system, profile, distro, working_arch, metadata=pxe_metadata)
                    if grub_path:
                        self.write_pxe_file(grub_path, system, profile, distro, working_arch, format="grub")
                        # Generate a link named after system to the mac file for easier lookup
                        link_path = os.path.join(self.bootloc, "grub", "system_link", system.name)
                        if os.path.exists(link_path):
                            utils.rmfile(link_path)
                        os.symlink(os.path.join("..", "system", grub_name), link_path)
                else:
                    self.write_pxe_file(pxe_path, system, None, None, working_arch, image=profile, metadata=pxe_metadata)
            else:
                # ensure the file doesn't exist
                utils.rmfile(pxe_path)
                if grub_path:
                    utils.rmfile(grub_path)