Example #1
0
    def calc_kernel_args(self, pd, replace_self=False):
        kickstart = self.safe_load(pd,'kickstart')
        options   = self.safe_load(pd,'kernel_options',default='')
        breed     = self.safe_load(pd,'breed')

        kextra    = ""
        if kickstart is not None and kickstart != "":
            if breed is not None and breed == "suse":
                kextra = "autoyast=" + kickstart
            else:
                kextra = "ks=" + kickstart 
        if options !="":
            kextra = kextra + " " + options
        # parser issues?  lang needs a trailing = and somehow doesn't have it.

        # convert the from-cobbler options back to a hash
        # so that we can override it in a way that works as intended
        hashv = utils.input_string_or_hash(kextra)

        if replace_self:
           hashv["ks"] = "file:ks.cfg"

        if self.kopts_override is not None:
           hash2 = utils.input_string_or_hash(self.kopts_override)
           hashv.update(hash2)
        options = ""
        for x in hashv.keys():
            if hashv[x] is None:
                options = options + "%s " % x
            else:
                options = options + "%s=%s " % (x, hashv[x])
        options = options.replace("lang ","lang= ")
        return options
Example #2
0
 def __setattr__(self,name,value):
     if DEFAULTS.has_key(name):
         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_hash(value)[1]
         except:
             raise AttributeError, "failed to set %s to %s" % (name,str(value))
         
         self.__dict__[name] = value
         if not utils.update_settings_file(self.to_datastruct()):
             raise AttributeError, "failed to save the settings file!"
             
         return 0
     else:
         raise AttributeError, name
Example #3
0
    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_hash(value)[1]
            except:
                raise AttributeError

            self.__dict__[name] = value
            if not utils.update_settings_file(self.to_datastruct()):
                raise AttributeError

            return 0
        else:
            raise AttributeError
Example #4
0
    def __setattr__(self, name, value):
        if DEFAULTS.has_key(name):
            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_hash(value)[1]
            except:
                raise AttributeError, "failed to set %s to %s" % (name,
                                                                  str(value))

            self.__dict__[name] = value
            utils.update_settings_file(name, value)
            return 0
        else:
            raise AttributeError, name
Example #5
0
    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_hash(value)[1]
            except:
                raise AttributeError

            self.__dict__[name] = value
            if not utils.update_settings_file(self.to_datastruct()):
                raise AttributeError

            return 0
        else:
            raise AttributeError
Example #6
0
 def set_params(self, params, inplace=False):
     (success, value) = utils.input_string_or_hash(params, allow_multiples=True)
     if not success:
         raise CX(_("invalid parameters"))
     else:
         if inplace:
             for key in value.keys():
                 if key.startswith("~"):
                     del self.params[key[1:]]
                 else:
                     self.params[key] = value[key]
         else:
             self.params = value
         return True
Example #7
0
 def set_params(self,params,inplace=False):
     (success, value) = utils.input_string_or_hash(params,allow_multiples=True)
     if not success:
         raise CX(_("invalid parameters"))
     else:
         if inplace:
             for key in value.keys():
                 if key.startswith("~"):
                     del self.params[key[1:]]
                 else:
                     self.params[key] = value[key]
         else:
             self.params = value
         return True
Example #8
0
 def __getattr__(self,name):
     if self._attributes.has_key(name):
         if name == "kernel_options":
             # backwards compatibility -- convert possible string value to hash
             (success, result) = utils.input_string_or_hash(self._attributes[name], " ",allow_multiples=False)
             self._attributes[name] = result
             return result
         return self._attributes[name]
     elif DEFAULTS.has_key(name):
         lookup = DEFAULTS[name]
         self._attributes[name] = lookup
         return lookup
     else:
         raise AttributeError, name
Example #9
0
 def set_environment(self,options,inplace=False):
     """
     Yum can take options from the environment.  This puts them there before
     each reposync.
     """
     (success, value) = utils.input_string_or_hash(options,allow_multiples=False)
     if not success:
         raise CX(_("invalid environment options"))
     else:
         if inplace:
             for key in value.keys():
                 self.environment[key] = value[key]
         else:
             self.environment = value
         return True
Example #10
0
 def set_yumopts(self,options,inplace=False):
     """
     Kernel options are a space delimited list,
     like 'a=b c=d e=f g h i=j' or a hash.
     """
     (success, value) = utils.input_string_or_hash(options,allow_multiples=False)
     if not success:
         raise CX(_("invalid yum options"))
     else:
         if inplace:
             for key in value.keys():
                 self.yumopts[key] = value[key]
         else:
             self.yumopts = value
         return True
Example #11
0
 def __getattr__(self, name):
     try:
         if name == "kernel_options":
             # backwards compatibility -- convert possible string value to hash
             (success, result) = utils.input_string_or_hash(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
Example #12
0
 def __getattr__(self,name):
     try:
         if name == "kernel_options":
             # backwards compatibility -- convert possible string value to hash
             (success, result) = utils.input_string_or_hash(self.__dict__[name], " ",allow_multiples=False)
             self.__dict__[name] = result
             return result
         return self.__dict__[name]
     except:
         if DEFAULTS.has_key(name):
             lookup = DEFAULTS[name][0]
             self.__dict__[name] = lookup
             return lookup
         else:
             raise AttributeError, name
Example #13
0
 def set_environment(self,options,inplace=False):
     """
     Yum can take options from the environment.  This puts them there before
     each reposync.
     """
     (success, value) = utils.input_string_or_hash(options,None,allow_multiples=False)
     if not success:
         raise CX(_("invalid environment options"))
     else:
         if inplace:
             for key in value.keys():
                 self.environment[key] = value[key]
         else:
             self.environment = value
         return True
Example #14
0
 def set_yumopts(self,options,inplace=False):
     """
     Kernel options are a space delimited list,
     like 'a=b c=d e=f g h i=j' or a hash.
     """
     (success, value) = utils.input_string_or_hash(options,None,allow_multiples=False)
     if not success:
         raise CX(_("invalid yum options"))
     else:
         if inplace:
             for key in value.keys():
                 self.yumopts[key] = value[key]
         else:
             self.yumopts = value
         return True
Example #15
0
 def set_fetchable_files(self, fetchable_files, inplace=False):
     """
     A comma seperated list of virt_name=path_to_template
     that should be fetchable via tftp or a webserver
     """
     (success, value) = utils.input_string_or_hash(fetchable_files, allow_multiples=False)
     if not success:
         return False
     else:
         if inplace:
             for key in value.keys():
                 if key.startswith("~"):
                     del self.fetchable_files[key[1:]]
                 else:
                     self.fetchable_files[key] = value[key]
         else:
             self.fetchable_files = value
         return True
Example #16
0
 def set_boot_files(self,boot_files,inplace=False):
     """
     A comma seperated list of req_name=source_file_path
     that should be fetchable via tftp 
     """
     (success, value) = utils.input_string_or_hash(boot_files,allow_multiples=inplace)
     if not success:
         return False
     else:
         if inplace:
             for key in value.keys():
                 if key.startswith("~"):
                     del self.boot_files[key[1:]]
                 else:
                     self.boot_files[key] = value[key]
         else:
             self.boot_files= value
         return True
Example #17
0
 def set_template_files(self,template_files,inplace=False):
     """
     A comma seperated list of source=destination templates
     that should be generated during a sync.
     """
     (success, value) = utils.input_string_or_hash(template_files,allow_multiples=False)
     if not success:
         return False
     else:
         if inplace:
             for key in value.keys():
                 if key.startswith("~"):
                     del self.template_files[key[1:]]
                 else:
                     self.template_files[key] = value[key]
         else:
             self.template_files = value
         return True
Example #18
0
 def set_fetchable_files(self,fetchable_files,inplace=False):
     """
     A comma seperated list of virt_name=path_to_template
     that should be fetchable via tftp or a webserver
     """
     (success, value) = utils.input_string_or_hash(fetchable_files,allow_multiples=inplace)
     if not success:
         return False
     else:
         if inplace:
             for key in value.keys():
                 if key.startswith("~"):
                     del self.fetchable_files[key[1:]]
                 else:
                     self.fetchable_files[key] = value[key]
         else:
             self.fetchable_files= value
         return True
Example #19
0
 def set_kernel_options_post(self,options,inplace=False):
     """
     Post kernel options are a space delimited list,
     like 'a=b c=d e=f g h i=j' or a hash.
     """
     (success, value) = utils.input_string_or_hash(options)
     if not success:
         raise CX(_("invalid post kernel options"))
     else:
         if inplace:
             for key in value.keys():
                 if key.startswith("~"):
                     del self.self.kernel_options_post[key[1:]]
                 else:
                     self.kernel_options_post[key] = value[key]
         else:
             self.kernel_options_post = value
         return True
Example #20
0
 def set_template_files(self,template_files,inplace=False):
     """
     A comma seperated list of source=destination templates
     that should be generated during a sync.
     """
     (success, value) = utils.input_string_or_hash(template_files,allow_multiples=inplace)
     if not success:
         return False
     else:
         if inplace:
             for key in value.keys():
                 if key.startswith("~"):
                     del self.template_files[key[1:]]
                 else:
                     self.template_files[key] = value[key]
         else:
             self.template_files = value
         return True
Example #21
0
 def set_kernel_options_post(self,options,inplace=False):
     """
     Post kernel options are a space delimited list,
     like 'a=b c=d e=f g h i=j' or a hash.
     """
     (success, value) = utils.input_string_or_hash(options,allow_multiples=inplace)
     if not success:
         raise CX(_("invalid post kernel options"))
     else:
         if inplace:
             for key in value.keys():
                 if key.startswith("~"):
                     del self.self.kernel_options_post[key[1:]]
                 else:
                     self.kernel_options_post[key] = value[key]
         else:
             self.kernel_options_post = value
         return True
Example #22
0
 def set_boot_files(self, boot_files, inplace=False):
     """
     A comma seperated list of req_name=source_file_path
     that should be fetchable via tftp
     """
     (success, value) = utils.input_string_or_hash(boot_files, allow_multiples=False)
     if not success:
         return False
     else:
         if inplace:
             for key in value.keys():
                 if key.startswith("~"):
                     del self.boot_files[key[1:]]
                 else:
                     self.boot_files[key] = value[key]
         else:
             self.boot_files = value
         return True
Example #23
0
 def set_ks_meta(self,options,inplace=False):
     """
     A comma delimited list of key value pairs, like 'a=b,c=d,e=f' or a hash.
     The meta tags are used as input to the templating system
     to preprocess kickstart files
     """
     (success, value) = utils.input_string_or_hash(options,allow_multiples=False)
     if not success:
         return False
     else:
         if inplace:
             for key in value.keys():
                 if key.startswith("~"):
                     del self.ks_meta[key[1:]]
                 else:
                     self.ks_meta[key] = value[key]
         else:
             self.ks_meta = value
         return True
Example #24
0
 def set_ks_meta(self,options,inplace=False):
     """
     A comma delimited list of key value pairs, like 'a=b,c=d,e=f' or a hash.
     The meta tags are used as input to the templating system
     to preprocess kickstart files
     """
     (success, value) = utils.input_string_or_hash(options,allow_multiples=inplace)
     if not success:
         return False
     else:
         if inplace:
             for key in value.keys():
                 if key.startswith("~"):
                     del self.ks_meta[key[1:]]
                 else:
                     self.ks_meta[key] = value[key]
         else:
             self.ks_meta = value
         return True
Example #25
0
    def __find_compare(self, from_search, from_obj):

        if isinstance(from_obj, basestring):
            # FIXME: fnmatch is only used for string to string comparisions
            # which should cover most major usage, if not, this deserves fixing
            if fnmatch.fnmatch(from_obj.lower(), from_search.lower()):
                return True
            else:
                return False

        else:
            if isinstance(from_search, basestring):
                if isinstance(from_obj, list):
                    from_search = utils.input_string_or_list(from_search)
                    for x in from_search:
                        if x not in from_obj:
                            return False
                    return True

                if isinstance(from_obj, dict):
                    (junk, from_search) = utils.input_string_or_hash(
                        from_search, allow_multiples=True)
                    for x in from_search.keys():
                        y = from_search[x]
                        if x not in from_obj:
                            return False
                        if not (y == from_obj[x]):  # XXX
                            return False
                    return True

                if isinstance(from_obj, bool):
                    if from_search.lower() in ["true", "1", "y", "yes"]:
                        inp = True
                    else:
                        inp = False
                    if inp == from_obj:
                        return True
                    return False

            raise CX(_("find cannot compare type: %s") % type(from_obj))
Example #26
0
    def __find_compare(self, from_search, from_obj):

        if isinstance(from_obj, basestring):
            # FIXME: fnmatch is only used for string to string comparisions
            # which should cover most major usage, if not, this deserves fixing
            if fnmatch.fnmatch(from_obj.lower(), from_search.lower()):
                return True
            else:
                return False    
        
        else:
            if isinstance(from_search, basestring):
                if type(from_obj) == type([]):
                    from_search = utils.input_string_or_list(from_search)
                    for x in from_search:
                        if x not in from_obj:
                            return False
                    return True            

                if type(from_obj) == type({}):
                    (junk, from_search) = utils.input_string_or_hash(from_search,allow_multiples=True)
                    for x in from_search.keys():
                        y = from_search[x]
                        if not from_obj.has_key(x):
                            return False
                        if not (y == from_obj[x]):
                            return False
                    return True

                if type(from_obj) == type(True):
                    if from_search.lower() in [ "true", "1", "y", "yes" ]:
                        inp = True
                    else:
                        inp = False
                    if inp == from_obj:
                        return True
                    return False
                
            raise CX(_("find cannot compare type: %s") % type(from_obj)) 
Example #27
0
    def write_templates(self,obj,write_file=False,path=None):
        """
        A semi-generic function that will take an object
        with a template_files hash {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 hash 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)

        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_hash(templates)

        if not success:
            return results

        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 not path is 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
            else:
                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
Example #28
0
    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

        NOTE: relevant to tftp and pseudo-PXE (s390) only

        ia64 is mostly the same as syslinux stuff, s390 is a bit
        short-circuited and simpler.  All of it goes through the
        templating engine, see the templates in /etc/cobbler for
        more details

        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_hash(self.settings.to_datastruct())
        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)
            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","")
            
        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 not metadata.has_key("img_path"):
            metadata["img_path"] = img_path
        if kernel_path is not None and not metadata.has_key("kernel_path"):
            metadata["kernel_path"] = kernel_path
        if initrd_path is not None and not metadata.has_key("initrd_path"):
            metadata["initrd_path"] = initrd_path

        # ---
        # choose a template
        if system:
            if format == "grub":
                template = os.path.join(self.settings.pxe_template_dir, "grubsystem.template")
            else: # pxe
                if system.netboot_enabled:
                    template = os.path.join(self.settings.pxe_template_dir,"pxesystem.template")

                    if arch.startswith("s390"):
                        template = os.path.join(self.settings.pxe_template_dir,"pxesystem_s390x.template")
                    elif arch == "ia64":
                        template = os.path.join(self.settings.pxe_template_dir,"pxesystem_ia64.template")
                    elif arch.startswith("ppc"):
                        template = os.path.join(self.settings.pxe_template_dir,"pxesystem_ppc.template")
                    elif arch.startswith("arm"):
                        template = os.path.join(self.settings.pxe_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.pxe_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
                    elif arch is not None and arch.startswith("s390"):
                        template = os.path.join(self.settings.pxe_template_dir,"pxelocal_s390x.template")
                    elif arch is not None and arch.startswith("ia64"):
                        template = os.path.join(self.settings.pxe_template_dir,"pxelocal_ia64.template")
                    else:
                        template = os.path.join(self.settings.pxe_template_dir,"pxelocal.template")
        else:
            # not a system record, so this is a profile record or an image
            if arch.startswith("s390"):
                template = os.path.join(self.settings.pxe_template_dir,"pxeprofile_s390x.template")
            if arch.startswith("arm"):
                template = os.path.join(self.settings.pxe_template_dir,"pxeprofile_arm.template")
            elif format == "grub":
                template = os.path.join(self.settings.pxe_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.pxe_template_dir,"pxeprofile_esxi.template")
            else:
                template = os.path.join(self.settings.pxe_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 metadata.has_key("initrd_path") and (not arch or arch not in ["ia64", "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") or arch.startswith("s390"):
            # 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:]
        metadata["append_line"] = append_line

        # store variables for templating
        metadata["menu_label"] = ""
        if profile:
            if not arch in [ "ia64", "ppc", "ppc64", "s390", "s390x" ]:
                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