def generate_standalone_iso(self, imagesdir, isolinuxdir, distname, filesource, airgapped, profiles): """ Create bootable CD image to be used for handsoff CD installtions """ # Get the distro object for the requested distro # and then get all of its descendants (profiles/sub-profiles/systems) # with sort=True for profile/system heirarchy to allow menu indenting distro = self.api.find_distro(distname) if distro is None: utils.die(self.logger, "distro %s was not found, aborting" % distname) descendants = distro.get_descendants(sort=True) profiles = utils.input_string_or_list(profiles) if filesource is None: # Try to determine the source from the distro kernel path self.logger.debug("trying to locate source for distro") found_source = False (source_head, source_tail) = os.path.split(distro.kernel) while source_tail != '': if source_head == os.path.join(self.api.settings().webdir, "distro_mirror"): filesource = os.path.join(source_head, source_tail) found_source = True self.logger.debug("found source in %s" % filesource) break (source_head, source_tail) = os.path.split(source_head) # Can't find the source, raise an error if not found_source: utils.die( self.logger, "Error, no installation source found. When building a standalone ISO, you must specify a --source if the distro install tree is not hosted locally" ) self.logger.info("copying kernels and initrds for standalone distro") self.copy_boot_files(distro, isolinuxdir, None) self.logger.info("generating an isolinux.cfg") isolinuxcfg = os.path.join(isolinuxdir, "isolinux.cfg") cfg = open(isolinuxcfg, "w+") cfg.write(self.iso_template) if airgapped: repo_names_to_copy = {} for descendant in descendants: # if a list of profiles was given, skip any others and their systems if (profiles and ((descendant.COLLECTION_TYPE == 'profile' and descendant.name not in profiles) or (descendant.COLLECTION_TYPE == 'system' and descendant.profile not in profiles))): continue menu_indent = 0 if descendant.COLLECTION_TYPE == 'system': menu_indent = 4 data = utils.blender(self.api, False, descendant) cfg.write("\n") cfg.write("LABEL %s\n" % descendant.name) if menu_indent: cfg.write(" MENU INDENT %d\n" % menu_indent) cfg.write(" MENU LABEL %s\n" % descendant.name) cfg.write(" kernel %s\n" % os.path.basename(distro.kernel)) append_line = " append initrd=%s" % os.path.basename( distro.initrd) if distro.breed == "redhat": append_line += " ks=cdrom:/isolinux/%s.cfg" % descendant.name if distro.breed == "suse": append_line += " autoyast=file:///isolinux/%s.cfg install=cdrom:///" % descendant.name if "install" in data["kernel_options"]: del data["kernel_options"]["install"] if distro.breed in ["ubuntu", "debian"]: append_line += " auto-install/enable=true preseed/file=/cdrom/isolinux/%s.cfg" % descendant.name # add remaining kernel_options to append_line append_line += self.add_remaining_kopts(data["kernel_options"]) cfg.write(append_line) if descendant.COLLECTION_TYPE == 'profile': autoinstall_data = self.api.autoinstallgen.generate_autoinstall_for_profile( descendant.name) elif descendant.COLLECTION_TYPE == 'system': autoinstall_data = self.api.autoinstallgen.generate_autoinstall_for_system( descendant.name) if distro.breed == "redhat": cdregex = re.compile("^\s*url .*\n", re.IGNORECASE | re.MULTILINE) autoinstall_data = cdregex.sub("cdrom\n", autoinstall_data, count=1) if airgapped: descendant_repos = data['repos'] for repo_name in descendant_repos: repo_obj = self.api.find_repo(repo_name) error_fmt = (descendant.COLLECTION_TYPE + " " + descendant.name + " refers to repo " + repo_name + ", which %%s; cannot build airgapped ISO") if repo_obj is None: utils.die(self.logger, error_fmt % "does not exist") if not repo_obj.mirror_locally: utils.die( self.logger, error_fmt % "is not configured for local mirroring") # FIXME: don't hardcode mirrordir = os.path.join(self.settings.webdir, "repo_mirror", repo_obj.name) if not os.path.exists(mirrordir): utils.die( self.logger, error_fmt % "has a missing local mirror directory") repo_names_to_copy[repo_obj.name] = mirrordir # update the baseurl in autoinstall_data to use the cdrom copy of this repo reporegex = re.compile( "^(\s*repo --name=" + repo_obj.name + " --baseurl=).*", re.MULTILINE) autoinstall_data = reporegex.sub( r"\1" + "file:///mnt/source/repo_mirror/" + repo_obj.name, autoinstall_data) # rewrite any split-tree repos, such as in redhat, to use cdrom srcreporegex = re.compile( "^(\s*repo --name=\S+ --baseurl=).*/cobbler/ks_mirror/" + distro.name + "/?(.*)", re.MULTILINE) autoinstall_data = srcreporegex.sub( r"\1" + "file:///mnt/source" + r"\2", autoinstall_data) autoinstall_name = os.path.join(isolinuxdir, "%s.cfg" % descendant.name) autoinstall_file = open(autoinstall_name, "w+") autoinstall_file.write(autoinstall_data) autoinstall_file.close() self.logger.info("done writing config") cfg.write("\n") cfg.write("MENU END\n") cfg.close() if airgapped: # copy any repos found in profiles or systems to the iso build repodir = os.path.abspath( os.path.join(isolinuxdir, "..", "repo_mirror")) if not os.path.exists(repodir): os.makedirs(repodir) for repo_name in repo_names_to_copy: src = repo_names_to_copy[repo_name] dst = os.path.join(repodir, repo_name) self.logger.info(" - copying repo '" + repo_name + "' for airgapped ISO") ok = utils.rsync_files( src, dst, "--exclude=TRANS.TBL --exclude=cache/ --no-g", logger=self.logger, quiet=True) if not ok: utils.die(self.logger, "rsync of repo '" + repo_name + "' failed") # copy distro files last, since they take the most time cmd = "rsync -rlptgu --exclude=boot.cat --exclude=TRANS.TBL --exclude=isolinux/ %s/ %s/../" % ( filesource, isolinuxdir) self.logger.info("- copying distro %s files (%s)" % (distname, cmd)) rc = utils.subprocess_call(self.logger, cmd, shell=True) if rc: utils.die(self.logger, "rsync of distro files failed")
def run(self,pkgdir,mirror,mirror_name,network_root=None,kickstart_file=None,rsync_flags=None,arch=None,breed=None,os_version=None): self.pkgdir = pkgdir self.mirror = mirror self.mirror_name = mirror_name self.network_root = network_root self.kickstart_file = kickstart_file self.rsync_flags = rsync_flags self.arch = arch self.breed = breed self.os_version = os_version # some fixups for the XMLRPC interface, which does not use "None" if self.arch == "": self.arch = None if self.mirror == "": self.mirror = None if self.mirror_name == "": self.mirror_name = None if self.kickstart_file == "": self.kickstart_file = None if self.os_version == "": self.os_version = None if self.rsync_flags == "": self.rsync_flags = None if self.network_root == "": self.network_root = None if self.breed == None: self.set_breed_from_directory() if not self.breed: utils.die(self.logger,"import failed - could not determine breed of debian-based distro") # debug log stuff for testing #self.logger.info("self.pkgdir = %s" % str(self.pkgdir)) #self.logger.info("self.mirror = %s" % str(self.mirror)) #self.logger.info("self.mirror_name = %s" % str(self.mirror_name)) #self.logger.info("self.network_root = %s" % str(self.network_root)) #self.logger.info("self.kickstart_file = %s" % str(self.kickstart_file)) #self.logger.info("self.rsync_flags = %s" % str(self.rsync_flags)) #self.logger.info("self.arch = %s" % str(self.arch)) #self.logger.info("self.breed = %s" % str(self.breed)) #self.logger.info("self.os_version = %s" % str(self.os_version)) # both --import and --name are required arguments if self.mirror is None: utils.die(self.logger,"import failed. no --path specified") if self.mirror_name is None: utils.die(self.logger,"import failed. no --name specified") # if --arch is supplied, validate it to ensure it's valid if self.arch is not None and self.arch != "": self.arch = self.arch.lower() if self.arch in ( 'x86' , 'i486', 'i586', 'i686' ) : self.arch = "i386" if self.arch in ( 'amd64' ,) : self.arch = "x86_64" if self.arch not in self.get_valid_arches(): utils.die(self.logger,"arch must be one of: %s" % string.join(self.get_valid_arches(),", ")) # if we're going to do any copying, set where to put things # and then make sure nothing is already there. self.path = os.path.normpath( "%s/ks_mirror/%s" % (self.settings.webdir, self.mirror_name) ) self.rootdir = os.path.normpath( "%s/ks_mirror/%s" % (self.settings.webdir, self.mirror_name) ) if os.path.exists(self.path) and self.arch is None: # FIXME : Raise exception even when network_root is given ? utils.die(self.logger,"Something already exists at this import location (%s). You must specify --arch to avoid potentially overwriting existing files." % self.path) # import takes a --kickstart for forcing selection that can't be used in all circumstances if self.kickstart_file and not self.breed: utils.die(self.logger,"Kickstart file can only be specified when a specific breed is selected") if self.os_version and not self.breed: utils.die(self.logger,"OS version can only be specified when a specific breed is selected") if self.breed and self.breed.lower() not in self.get_valid_breeds(): utils.die(self.logger,"Supplied import breed is not supported by this module") # if --arch is supplied, make sure the user is not importing a path with a different # arch, which would just be silly. if self.arch: # append the arch path to the name if the arch is not already # found in the name. for x in self.get_valid_arches(): if self.path.lower().find(x) != -1: if self.arch != x : utils.die(self.logger,"Architecture found on pathname (%s) does not fit the one given in command line (%s)"%(x,self.arch)) break else: # FIXME : This is very likely removed later at get_proposed_name, and the guessed arch appended again self.path += ("-%s" % self.arch) # make the output path and mirror content but only if not specifying that a network # accessible support location already exists (this is --available-as on the command line) if self.network_root is None: # we need to mirror (copy) the files utils.mkdir(self.path) if self.mirror.startswith("http://") or self.mirror.startswith("ftp://") or self.mirror.startswith("nfs://"): # http mirrors are kind of primative. rsync is better. # that's why this isn't documented in the manpage and we don't support them. # TODO: how about adding recursive FTP as an option? utils.die(self.logger,"unsupported protocol") else: # good, we're going to use rsync.. # we don't use SSH for public mirrors and local files. # presence of user@host syntax means use SSH # kick off the rsync now if not utils.rsync_files(self.mirror, self.path, self.rsync_flags, self.logger): utils.die(self.logger, "failed to rsync the files") else: # rather than mirroring, we're going to assume the path is available # over http, ftp, and nfs, perhaps on an external filer. scanning still requires # --mirror is a filesystem path, but --available-as marks the network path if not os.path.exists(self.mirror): utils.die(self.logger, "path does not exist: %s" % self.mirror) # find the filesystem part of the path, after the server bits, as each distro # URL needs to be calculated relative to this. if not self.network_root.endswith("/"): self.network_root = self.network_root + "/" self.path = os.path.normpath( self.mirror ) valid_roots = [ "nfs://", "ftp://", "http://" ] for valid_root in valid_roots: if self.network_root.startswith(valid_root): break else: utils.die(self.logger, "Network root given to --available-as must be nfs://, ftp://, or http://") if self.network_root.startswith("nfs://"): try: (a,b,rest) = self.network_root.split(":",3) except: utils.die(self.logger, "Network root given to --available-as is missing a colon, please see the manpage example.") # now walk the filesystem looking for distributions that match certain patterns self.logger.info("adding distros") distros_added = [] # FIXME : search below self.path for isolinux configurations or known directories from TRY_LIST os.path.walk(self.path, self.distro_adder, distros_added) # find out if we can auto-create any repository records from the install tree if self.network_root is None: self.logger.info("associating repos") # FIXME: this automagic is not possible (yet) without mirroring self.repo_finder(distros_added) # find the most appropriate answer files for each profile object self.logger.info("associating kickstarts") self.kickstart_finder(distros_added) # ensure bootloaders are present self.api.pxegen.copy_bootloaders() return True
def generate_standalone_iso(self, imagesdir, isolinuxdir, distname, filesource, airgapped, profiles): """ Create bootable CD image to be used for handsoff CD installtions """ # Get the distro object for the requested distro # and then get all of its descendants (profiles/sub-profiles/systems) # with sort=True for profile/system heirarchy to allow menu indenting distro = self.api.find_distro(distname) if distro is None: utils.die(self.logger, "distro %s was not found, aborting" % distname) descendants = distro.get_descendants(sort=True) profiles = utils.input_string_or_list(profiles) if filesource is None: # Try to determine the source from the distro kernel path self.logger.debug("trying to locate source for distro") found_source = False (source_head, source_tail) = os.path.split(distro.kernel) while source_tail != '': if source_head == os.path.join(self.api.settings().webdir, "distro_mirror"): filesource = os.path.join(source_head, source_tail) found_source = True self.logger.debug("found source in %s" % filesource) break (source_head, source_tail) = os.path.split(source_head) # Can't find the source, raise an error if not found_source: utils.die(self.logger, "Error, no installation source found. When building a standalone ISO, you must specify a --source if the distro install tree is not hosted locally") self.logger.info("copying kernels and initrds for standalone distro") self.copy_boot_files(distro, isolinuxdir, None) self.logger.info("generating an isolinux.cfg") isolinuxcfg = os.path.join(isolinuxdir, "isolinux.cfg") cfg = open(isolinuxcfg, "w+") cfg.write(self.iso_template) if airgapped: repo_names_to_copy = {} for descendant in descendants: # if a list of profiles was given, skip any others and their systems if (profiles and ((descendant.COLLECTION_TYPE == 'profile' and descendant.name not in profiles) or (descendant.COLLECTION_TYPE == 'system' and descendant.profile not in profiles))): continue menu_indent = 0 if descendant.COLLECTION_TYPE == 'system': menu_indent = 4 data = utils.blender(self.api, False, descendant) cfg.write("\n") cfg.write("LABEL %s\n" % descendant.name) if menu_indent: cfg.write(" MENU INDENT %d\n" % menu_indent) cfg.write(" MENU LABEL %s\n" % descendant.name) cfg.write(" kernel %s\n" % os.path.basename(distro.kernel)) append_line = " append initrd=%s" % os.path.basename(distro.initrd) if distro.breed == "redhat": append_line += " ks=cdrom:/isolinux/%s.cfg" % descendant.name if distro.breed == "suse": append_line += " autoyast=file:///isolinux/%s.cfg install=cdrom:///" % descendant.name if "install" in data["kernel_options"]: del data["kernel_options"]["install"] if distro.breed in ["ubuntu", "debian"]: append_line += " auto-install/enable=true preseed/file=/cdrom/isolinux/%s.cfg" % descendant.name # add remaining kernel_options to append_line append_line += self.add_remaining_kopts(data["kernel_options"]) cfg.write(append_line) if descendant.COLLECTION_TYPE == 'profile': autoinstall_data = self.api.autoinstallgen.generate_autoinstall_for_profile(descendant.name) elif descendant.COLLECTION_TYPE == 'system': autoinstall_data = self.api.autoinstallgen.generate_autoinstall_for_system(descendant.name) if distro.breed == "redhat": cdregex = re.compile("^\s*url .*\n", re.IGNORECASE | re.MULTILINE) autoinstall_data = cdregex.sub("cdrom\n", autoinstall_data, count=1) if airgapped: descendant_repos = data['repos'] for repo_name in descendant_repos: repo_obj = self.api.find_repo(repo_name) error_fmt = (descendant.COLLECTION_TYPE + " " + descendant.name + " refers to repo " + repo_name + ", which %%s; cannot build airgapped ISO") if repo_obj is None: utils.die(self.logger, error_fmt % "does not exist") if not repo_obj.mirror_locally: utils.die(self.logger, error_fmt % "is not configured for local mirroring") # FIXME: don't hardcode mirrordir = os.path.join(self.settings.webdir, "repo_mirror", repo_obj.name) if not os.path.exists(mirrordir): utils.die(self.logger, error_fmt % "has a missing local mirror directory") repo_names_to_copy[repo_obj.name] = mirrordir # update the baseurl in autoinstall_data to use the cdrom copy of this repo reporegex = re.compile("^(\s*repo --name=" + repo_obj.name + " --baseurl=).*", re.MULTILINE) autoinstall_data = reporegex.sub(r"\1" + "file:///mnt/source/repo_mirror/" + repo_obj.name, autoinstall_data) # rewrite any split-tree repos, such as in redhat, to use cdrom srcreporegex = re.compile("^(\s*repo --name=\S+ --baseurl=).*/cobbler/ks_mirror/" + distro.name + "/?(.*)", re.MULTILINE) autoinstall_data = srcreporegex.sub(r"\1" + "file:///mnt/source" + r"\2", autoinstall_data) autoinstall_name = os.path.join(isolinuxdir, "%s.cfg" % descendant.name) autoinstall_file = open(autoinstall_name, "w+") autoinstall_file.write(autoinstall_data) autoinstall_file.close() self.logger.info("done writing config") cfg.write("\n") cfg.write("MENU END\n") cfg.close() if airgapped: # copy any repos found in profiles or systems to the iso build repodir = os.path.abspath(os.path.join(isolinuxdir, "..", "repo_mirror")) if not os.path.exists(repodir): os.makedirs(repodir) for repo_name in repo_names_to_copy: src = repo_names_to_copy[repo_name] dst = os.path.join(repodir, repo_name) self.logger.info(" - copying repo '" + repo_name + "' for airgapped ISO") ok = utils.rsync_files(src, dst, "--exclude=TRANS.TBL --exclude=cache/ --no-g", logger=self.logger, quiet=True) if not ok: utils.die(self.logger, "rsync of repo '" + repo_name + "' failed") # copy distro files last, since they take the most time cmd = "rsync -rlptgu --exclude=boot.cat --exclude=TRANS.TBL --exclude=isolinux/ %s/ %s/../" % (filesource, isolinuxdir) self.logger.info("- copying distro %s files (%s)" % (distname, cmd)) rc = utils.subprocess_call(self.logger, cmd, shell=True) if rc: utils.die(self.logger, "rsync of distro files failed")
def run(self, pkgdir, mirror, mirror_name, network_root=None, kickstart_file=None, rsync_flags=None, arch=None, breed=None, os_version=None): self.pkgdir = pkgdir self.mirror = mirror self.mirror_name = mirror_name self.network_root = network_root self.kickstart_file = kickstart_file self.rsync_flags = rsync_flags self.arch = arch self.breed = breed self.os_version = os_version # some fixups for the XMLRPC interface, which does not use "None" if self.arch == "": self.arch = None if self.mirror == "": self.mirror = None if self.mirror_name == "": self.mirror_name = None if self.kickstart_file == "": self.kickstart_file = None if self.os_version == "": self.os_version = None if self.rsync_flags == "": self.rsync_flags = None if self.network_root == "": self.network_root = None # If no breed was specified on the command line, figure it out if self.breed == None: self.breed = self.get_breed_from_directory() if not self.breed: utils.die( self.logger, "import failed - could not determine breed of debian-based distro" ) # debug log stuff for testing #self.logger.info("DEBUG: self.pkgdir = %s" % str(self.pkgdir)) #self.logger.info("DEBUG: self.mirror = %s" % str(self.mirror)) #self.logger.info("DEBUG: self.mirror_name = %s" % str(self.mirror_name)) #self.logger.info("DEBUG: self.network_root = %s" % str(self.network_root)) #self.logger.info("DEBUG: self.kickstart_file = %s" % str(self.kickstart_file)) #self.logger.info("DEBUG: self.rsync_flags = %s" % str(self.rsync_flags)) #self.logger.info("DEBUG: self.arch = %s" % str(self.arch)) #self.logger.info("DEBUG: self.breed = %s" % str(self.breed)) #self.logger.info("DEBUG: self.os_version = %s" % str(self.os_version)) # both --import and --name are required arguments if self.mirror is None: utils.die(self.logger, "import failed. no --path specified") if self.mirror_name is None: utils.die(self.logger, "import failed. no --name specified") # if --arch is supplied, validate it to ensure it's valid if self.arch is not None and self.arch != "": self.arch = self.arch.lower() if self.arch == "x86": # be consistent self.arch = "i386" if self.arch not in self.get_valid_arches(): utils.die( self.logger, "arch must be one of: %s" % string.join(self.get_valid_arches(), ", ")) # if we're going to do any copying, set where to put things # and then make sure nothing is already there. self.path = os.path.normpath("%s/ks_mirror/%s" % (self.settings.webdir, self.mirror_name)) if os.path.exists(self.path) and self.arch is None: # FIXME : Raise exception even when network_root is given ? utils.die( self.logger, "Something already exists at this import location (%s). You must specify --arch to avoid potentially overwriting existing files." % self.path) # import takes a --kickstart for forcing selection that can't be used in all circumstances if self.kickstart_file and not self.breed: utils.die( self.logger, "Kickstart file can only be specified when a specific breed is selected" ) if self.os_version and not self.breed: utils.die( self.logger, "OS version can only be specified when a specific breed is selected" ) if self.breed and self.breed.lower() not in self.get_valid_breeds(): utils.die(self.logger, "Supplied import breed is not supported by this module") # if --arch is supplied, make sure the user is not importing a path with a different # arch, which would just be silly. if self.arch: # append the arch path to the name if the arch is not already # found in the name. for x in self.get_valid_arches(): if self.path.lower().find(x) != -1: if self.arch != x: utils.die( self.logger, "Architecture found on pathname (%s) does not fit the one given in command line (%s)" % (x, self.arch)) break else: # FIXME : This is very likely removed later at get_proposed_name, and the guessed arch appended again self.path += ("-%s" % self.arch) # make the output path and mirror content but only if not specifying that a network # accessible support location already exists (this is --available-as on the command line) if self.network_root is None: # we need to mirror (copy) the files utils.mkdir(self.path) if self.mirror.startswith("http://") or self.mirror.startswith( "ftp://") or self.mirror.startswith("nfs://"): # http mirrors are kind of primative. rsync is better. # that's why this isn't documented in the manpage and we don't support them. # TODO: how about adding recursive FTP as an option? utils.die(self.logger, "unsupported protocol") else: # good, we're going to use rsync.. # we don't use SSH for public mirrors and local files. # presence of user@host syntax means use SSH # kick off the rsync now if not utils.rsync_files(self.mirror, self.path, self.rsync_flags, self.logger): utils.die(self.logger, "failed to rsync the files") else: # rather than mirroring, we're going to assume the path is available # over http, ftp, and nfs, perhaps on an external filer. scanning still requires # --mirror is a filesystem path, but --available-as marks the network path if not os.path.exists(self.mirror): utils.die(self.logger, "path does not exist: %s" % self.mirror) # find the filesystem part of the path, after the server bits, as each distro # URL needs to be calculated relative to this. if not self.network_root.endswith("/"): self.network_root = self.network_root + "/" self.path = os.path.normpath(self.mirror) valid_roots = ["nfs://", "ftp://", "http://"] for valid_root in valid_roots: if self.network_root.startswith(valid_root): break else: utils.die( self.logger, "Network root given to --available-as must be nfs://, ftp://, or http://" ) if self.network_root.startswith("nfs://"): try: (a, b, rest) = self.network_root.split(":", 3) except: utils.die( self.logger, "Network root given to --available-as is missing a colon, please see the manpage example." ) # now walk the filesystem looking for distributions that match certain patterns self.logger.info("adding distros") distros_added = [] # FIXME : search below self.path for isolinux configurations or known directories from TRY_LIST os.path.walk(self.path, self.distro_adder, distros_added) # find out if we can auto-create any repository records from the install tree if self.network_root is None: self.logger.info("associating repos") # FIXME: this automagic is not possible (yet) without mirroring self.repo_finder(distros_added) # find the most appropriate answer files for each profile object self.logger.info("associating kickstarts") self.kickstart_finder(distros_added) # ensure bootloaders are present self.api.pxegen.copy_bootloaders() return True