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
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
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
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
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"]
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
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"]
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
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)