Пример #1
0
    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
Пример #3
0
    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")
Пример #4
0
    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