diff --git a/man/virt-install.rst b/man/virt-install.rst index 963f95649..f75af6354 100644 --- a/man/virt-install.rst +++ b/man/virt-install.rst @@ -617,7 +617,7 @@ ftp://host/path An FTP server location containing an installable distribution image. ISO - Probe the ISO and extract files using 'isoinfo' + Extract files directly from the ISO path DIRECTORY Path to a local directory containing an installable distribution image. diff --git a/tests/test_cli.py b/tests/test_cli.py index 07b6b2bba..ca1a4d924 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -38,7 +38,7 @@ MEDIA_DIR = os.path.relpath(utils.DATADIR + "/fakemedia", utils.TOPDIR) UNATTENDED_DIR = XMLDIR + "/unattended" OLD_OSINFO = utils.has_old_osinfo() NO_OSINFO_UNATTEND = not unattended.OSInstallScript.have_new_libosinfo() -HAS_ISOINFO = shutil.which("isoinfo") +HAS_xorriso = shutil.which("xorriso") # We use this check as a surrogate for a released libosinfo with a bug # fix we need to get full test coverage @@ -99,9 +99,9 @@ def has_old_osinfo(): return "osinfo is too old" -def missing_isoinfo(): - if not HAS_ISOINFO: - return "isoinfo not installed" +def missing_xorriso(): + if not HAS_xorriso: + return "xorriso not installed" def no_osinfo_unattend_cb(): @@ -1001,8 +1001,8 @@ c.add_compare("--connect " + utils.URIs.kvm_session + " --disk size=8 --os-varia c.add_valid("--connect " + utils.URIs.kvm_session + " --install fedora21", prerun_check=has_old_osinfo) # hits some get_search_paths and media_upload code paths # misc KVM config tests -c.add_compare("--disk none --location %(ISO-NO-OS)s,kernel=frib.img,initrd=/frob.img", "location-manual-kernel", prerun_check=missing_isoinfo) # --location with an unknown ISO but manually specified kernel paths -c.add_compare("--disk %(EXISTIMG1)s --location %(ISOTREE)s --nonetworks", "location-iso", prerun_check=missing_isoinfo) # Using --location iso mounting +c.add_compare("--disk none --location %(ISO-NO-OS)s,kernel=frib.img,initrd=/frob.img", "location-manual-kernel", prerun_check=missing_xorriso) # --location with an unknown ISO but manually specified kernel paths +c.add_compare("--disk %(EXISTIMG1)s --location %(ISOTREE)s --nonetworks", "location-iso", prerun_check=missing_xorriso) # Using --location iso mounting c.add_compare("--disk %(EXISTIMG1)s --cdrom %(ISOLABEL)s", "cdrom-centos-label") # Using --cdrom with centos CD label, should use virtio etc. c.add_compare("--disk %(EXISTIMG1)s --install bootdev=network --os-variant rhel5.4 --cloud-init none", "kvm-rhel5") # RHEL5 defaults c.add_compare("--disk %(EXISTIMG1)s --install kernel=%(ISO-WIN7)s,initrd=%(ISOLABEL)s,kernel_args='foo bar' --os-variant rhel6.4 --unattended none", "kvm-rhel6") # RHEL6 defaults. ISO paths are just to point at existing files diff --git a/virt-manager.spec b/virt-manager.spec index d18eea321..03aff5bf2 100644 --- a/virt-manager.spec +++ b/virt-manager.spec @@ -72,8 +72,8 @@ Requires: python3-requests Requires: libosinfo >= 0.2.10 # Required for gobject-introspection infrastructure Requires: python3-gobject-base -# Required for pulling files from iso media with isoinfo -Requires: genisoimage +# Required for pulling files from iso media +Requires: xorriso %description common Common files used by the different virt-manager interfaces, as well as diff --git a/virtinst/install/urldetect.py b/virtinst/install/urldetect.py index a73b0bf1e..f5ed0270c 100644 --- a/virtinst/install/urldetect.py +++ b/virtinst/install/urldetect.py @@ -40,9 +40,9 @@ class _DistroCache(object): if path not in self._filecache: try: content = self._fetcher.acquireFileContent(path) - except ValueError: + except ValueError as e: content = None - log.debug("Failed to acquire file=%s", path) + log.debug("Failed to acquire file=%s: %s", path, e) self._filecache[path] = content return self._filecache[path] diff --git a/virtinst/install/urlfetcher.py b/virtinst/install/urlfetcher.py index 3cacab1a8..835c9e40e 100644 --- a/virtinst/install/urlfetcher.py +++ b/virtinst/install/urlfetcher.py @@ -26,7 +26,7 @@ class _ISOReader: def __init__(self, location): self._location = location - def grabFile(self, url): + def grabFile(self, url, scratchdir): raise NotImplementedError() def hasFile(self, url): raise NotImplementedError() @@ -43,20 +43,50 @@ class _ISOinfoReader(_ISOReader): def _make_file_list(self): cmd = ["isoinfo", "-J", "-i", self._location, "-f"] - log.debug("Running isoinfo: %s", cmd) + log.debug("Generating iso filelist: %s", cmd) output = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) return output.splitlines(False) - def grabFile(self, url): + def grabFile(self, url, scratchdir): + ignore = scratchdir cmd = ["isoinfo", "-J", "-i", self._location, "-x", url] - log.debug("Running isoinfo: %s", cmd) + log.debug("Extracting iso file: %s", cmd) return subprocess.check_output(cmd) def hasFile(self, url): return url.encode("ascii") in self._cache_file_list +class _XorrisoReader(_ISOReader): + def __init__(self, location): + super().__init__(location) + self._cache_file_list = self._make_file_list() + + def _make_file_list(self): + delim = "VIRTINST_BEGINLIST" + cmd = ["xorriso", "-indev", self._location, "-print", delim, "-find"] + + log.debug("Generating iso filelist: %s", cmd) + output = subprocess.check_output(cmd, + stderr=subprocess.DEVNULL, text=True) + return output.split(delim, 1)[1].strip().splitlines() + + def grabFile(self, url, scratchdir): + tmp = tempfile.NamedTemporaryFile( + prefix="virtinst-iso", suffix="-" + os.path.basename(url), + dir=scratchdir) + + cmd = ["xorriso", "-osirrox", "on", "-indev", self._location, + "-extract", url, tmp.name] + log.debug("Extracting iso file: %s", cmd) + subprocess.check_output(cmd) + return open(tmp.name, "rb").read() + + def hasFile(self, url): + return ("'.%s'" % url) in self._cache_file_list + + ########################### # Fetcher implementations # ########################### @@ -349,23 +379,17 @@ class _ISOURLFetcher(_URLFetcher): def _get_isoreader(self): if not self._isoreader: - self._isoreader = _ISOinfoReader(self.location) + self._isoreader = _XorrisoReader(self.location) return self._isoreader def _grabber(self, url): - """ - Use isoinfo to grab the file - """ if not self._hasFile(url): raise RuntimeError("iso doesn't have file=%s" % url) - output = self._get_isoreader().grabFile(url) + output = self._get_isoreader().grabFile(url, self.scratchdir) return io.BytesIO(output), len(output) def _hasFile(self, url): - """ - Use isoinfo to list and search for the file - """ return self._get_isoreader().hasFile(url)