def __setattr__(self, name, value): if name in DEFAULTS: try: if DEFAULTS[name][1] == "str": value = str(value) elif DEFAULTS[name][1] == "int": value = int(value) elif DEFAULTS[name][1] == "bool": if utils.input_boolean(value): value = 1 else: value = 0 elif DEFAULTS[name][1] == "float": value = float(value) elif DEFAULTS[name][1] == "list": value = utils.input_string_or_list(value) elif DEFAULTS[name][1] == "dict": value = utils.input_string_or_dict(value)[1] except: raise AttributeError self.__dict__[name] = value if not utils.update_settings_file(self.to_dict()): raise AttributeError return 0 else: raise AttributeError
def __getattr__(self, name): try: if name == "kernel_options": # backwards compatibility -- convert possible string value to dict (success, result) = utils.input_string_or_dict(self.__dict__[name], allow_multiples=False) self.__dict__[name] = result return result return self.__dict__[name] except: if name in DEFAULTS: lookup = DEFAULTS[name][0] self.__dict__[name] = lookup return lookup else: raise AttributeError
def write_templates(self, obj, write_file=False, path=None): """ A semi-generic function that will take an object with a template_files dict {source:destiation}, and generate a rendered file. The write_file option allows for generating of the rendered output without actually creating any files. The return value is a dict of the destination file names (after variable substitution is done) and the data in the file. """ self.logger.info("Writing template files for %s" % obj.name) results = {} try: templates = obj.template_files except: return results blended = utils.blender(self.api, False, obj) if obj.COLLECTION_TYPE == "distro": if re.search("esxi[56]", obj.os_version) is not None: realbootcfg = open(os.path.join(os.path.dirname(obj.kernel), 'boot.cfg')).read() bootmodules = re.findall(r'modules=(.*)', realbootcfg) for modules in bootmodules: blended['esx_modules'] = modules.replace('/', '') ksmeta = blended.get("ks_meta", {}) try: del blended["ks_meta"] except: pass blended.update(ksmeta) # make available at top level templates = blended.get("template_files", {}) try: del blended["template_files"] except: pass blended.update(templates) # make available at top level (success, templates) = utils.input_string_or_dict(templates) if not success: return results # FIXME: img_path and local_img_path should probably be moved # up into the blender function to ensure they're consistently # available to templates across the board if blended["distro_name"]: blended['img_path'] = os.path.join("/images", blended["distro_name"]) blended['local_img_path'] = os.path.join(utils.tftpboot_location(), "images", blended["distro_name"]) for template in templates.keys(): dest = templates[template] if dest is None: continue # Run the source and destination files through # templar first to allow for variables in the path template = self.templar.render(template, blended, None).strip() dest = os.path.normpath(self.templar.render(dest, blended, None).strip()) # Get the path for the destination output dest_dir = os.path.normpath(os.path.dirname(dest)) # If we're looking for a single template, skip if this ones # destination is not it. if path is not None and path != dest: continue # If we are writing output to a file, we allow files tobe # written into the tftpboot directory, otherwise force all # templated configs into the rendered directory to ensure that # a user granted cobbler privileges via sudo can't overwrite # arbitrary system files (This also makes cleanup easier). if os.path.isabs(dest_dir) and write_file: if dest_dir.find(utils.tftpboot_location()) != 0: raise CX(" warning: template destination (%s) is outside %s, skipping." % (dest_dir, utils.tftpboot_location())) continue elif write_file: dest_dir = os.path.join(self.settings.webdir, "rendered", dest_dir) dest = os.path.join(dest_dir, os.path.basename(dest)) if not os.path.exists(dest_dir): utils.mkdir(dest_dir) # Check for problems if not os.path.exists(template): raise CX("template source %s does not exist" % template) continue elif write_file and not os.path.isdir(dest_dir): raise CX("template destination (%s) is invalid" % dest_dir) continue elif write_file and os.path.exists(dest): raise CX("template destination (%s) already exists" % dest) continue elif write_file and os.path.isdir(dest): raise CX("template destination (%s) is a directory" % dest) continue elif template == "" or dest == "": raise CX("either the template source or destination was blank (unknown variable used?)" % dest) continue template_fh = open(template) template_data = template_fh.read() template_fh.close() buffer = self.templar.render(template_data, blended, None) results[dest] = buffer if write_file: self.logger.info("generating: %s" % dest) fd = open(dest, "w") fd.write(buffer) fd.close() return results
def write_pxe_file(self, filename, system, profile, distro, arch, image=None, include_header=True, metadata=None, format="pxe"): """ Write a configuration file for the boot loader(s). More system-specific configuration may come in later, if so that would appear inside the system object in api.py Can be used for different formats, "pxe" (default) and "grub". """ if arch is None: raise "missing arch" if image and not os.path.exists(image.file): return None # nfs:// URLs or something, can't use for TFTP if metadata is None: metadata = {} (rval, settings) = utils.input_string_or_dict(self.settings.to_dict()) if rval: for key in settings.keys(): metadata[key] = settings[key] # --- # just some random variables template = None buffer = "" # --- kickstart_path = None kernel_path = None initrd_path = None img_path = None if image is None: # not image based, it's something normalish img_path = os.path.join("/images", distro.name) if 'nexenta' == distro.breed: kernel_path = os.path.join("/images", distro.name, 'platform', 'i86pc', 'kernel', 'amd64', os.path.basename(distro.kernel)) initrd_path = os.path.join("/images", distro.name, 'platform', 'i86pc', 'amd64', os.path.basename(distro.initrd)) else: 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)) # Find the kickstart if we inherit from another profile if system: blended = utils.blender(self.api, True, system) else: blended = utils.blender(self.api, True, profile) kickstart_path = blended.get("kickstart", "") # update metadata with all known information # this allows for more powerful templating metadata.update(blended) else: # this is an image we are making available, not kernel+initrd if image.image_type == "direct": kernel_path = os.path.join("/images2", image.name) elif image.image_type == "memdisk": kernel_path = "/memdisk" initrd_path = os.path.join("/images2", image.name) else: # CD-ROM ISO or virt-clone image? We can't PXE boot it. kernel_path = None initrd_path = None if img_path is not None and "img_path" not in metadata: metadata["img_path"] = img_path if kernel_path is not None and "kernel_path" not in metadata: metadata["kernel_path"] = kernel_path if initrd_path is not None and "initrd_path" not in metadata: metadata["initrd_path"] = initrd_path # --- # choose a template if system: if format == "grub": if system.netboot_enabled: template = os.path.join(self.settings.boot_loader_conf_template_dir, "grubsystem.template") else: local = os.path.join(self.settings.boot_loader_conf_template_dir, "grublocal.template") if os.path.exists(local): template = local else: # pxe if system.netboot_enabled: template = os.path.join(self.settings.boot_loader_conf_template_dir, "pxesystem.template") if arch.startswith("ppc"): template = os.path.join(self.settings.boot_loader_conf_template_dir, "pxesystem_ppc.template") elif arch.startswith("arm"): template = os.path.join(self.settings.boot_loader_conf_template_dir, "pxesystem_arm.template") elif distro and distro.os_version.startswith("esxi"): # ESXi uses a very different pxe method, using more files than # a standard kickstart and different options - so giving it a dedicated # PXE template makes more sense than shoe-horning it into the existing # templates template = os.path.join(self.settings.boot_loader_conf_template_dir, "pxesystem_esxi.template") else: # local booting on ppc requires removing the system-specific dhcpd.conf filename if arch is not None and arch.startswith("ppc"): # Disable yaboot network booting for all interfaces on the system for (name, interface) in system.interfaces.iteritems(): filename = "%s" % utils.get_config_filename(system, interface=name).lower() # Remove symlink to the yaboot binary f3 = os.path.join(self.bootloc, "ppc", filename) if os.path.lexists(f3): utils.rmfile(f3) # Remove the interface-specific config file f3 = os.path.join(self.bootloc, "etc", filename) if os.path.lexists(f3): utils.rmfile(f3) # Yaboot/OF doesn't support booting locally once you've # booted off the network, so nothing left to do return None else: template = os.path.join(self.settings.boot_loader_conf_template_dir, "pxelocal.template") else: # not a system record, so this is a profile record or an image if arch.startswith("arm"): template = os.path.join(self.settings.boot_loader_conf_template_dir, "pxeprofile_arm.template") elif format == "grub": template = os.path.join(self.settings.boot_loader_conf_template_dir, "grubprofile.template") elif distro and distro.os_version.startswith("esxi"): # ESXi uses a very different pxe method, see comment above in the system section template = os.path.join(self.settings.boot_loader_conf_template_dir, "pxeprofile_esxi.template") elif 'nexenta' == format: template = os.path.join(self.settings.boot_loader_conf_template_dir, 'nexenta_profile.template') else: template = os.path.join(self.settings.boot_loader_conf_template_dir, "pxeprofile.template") if kernel_path is not None: metadata["kernel_path"] = kernel_path if initrd_path is not None: metadata["initrd_path"] = initrd_path # generate the kernel options and append line: kernel_options = self.build_kernel_options(system, profile, distro, image, arch, kickstart_path) metadata["kernel_options"] = kernel_options if distro and distro.os_version.startswith("esxi") and filename is not None: append_line = "BOOTIF=%s" % (os.path.basename(filename)) elif "initrd_path" in metadata and (not arch or arch not in ["ppc", "ppc64", "arm"]): append_line = "append initrd=%s" % (metadata["initrd_path"]) else: append_line = "append " append_line = "%s%s" % (append_line, kernel_options) if arch.startswith("ppc"): # remove the prefix "append" # TODO: this looks like it's removing more than append, really # not sure what's up here... append_line = append_line[7:] if distro and distro.os_version.startswith("xenserver620"): append_line = "%s" % (kernel_options) metadata["append_line"] = append_line # store variables for templating metadata["menu_label"] = "" if profile: if arch not in ["ppc", "ppc64"]: metadata["menu_label"] = "MENU LABEL %s" % profile.name metadata["profile_name"] = profile.name elif image: metadata["menu_label"] = "MENU LABEL %s" % image.name metadata["profile_name"] = image.name if system: metadata["system_name"] = system.name # get the template if kernel_path is not None: template_fh = open(template) template_data = template_fh.read() template_fh.close() else: # this is something we can't PXE boot template_data = "\n" # save file and/or return results, depending on how called. buffer = self.templar.render(template_data, metadata, None) if filename is not None: self.logger.info("generating: %s" % filename) fd = open(filename, "w") fd.write(buffer) fd.close() return buffer