urlfetcher: Bunch of small cleanups to the ImageFetcher classes

This commit is contained in:
Cole Robinson 2015-09-18 16:49:18 -04:00
parent ec166e2cc4
commit cda3e381e6

View File

@ -39,7 +39,7 @@ from .osdict import OSDB
# Backends for the various URL types we support (http, ftp, nfs, local) # # Backends for the various URL types we support (http, ftp, nfs, local) #
######################################################################### #########################################################################
class _ImageFetcher(object): class _URLFetcher(object):
""" """
This is a generic base class for fetching/extracting files from This is a generic base class for fetching/extracting files from
a media source, such as CD ISO, NFS server, or HTTP/FTP server a media source, such as CD ISO, NFS server, or HTTP/FTP server
@ -48,24 +48,33 @@ class _ImageFetcher(object):
self.location = location self.location = location
self.scratchdir = scratchdir self.scratchdir = scratchdir
self.meter = meter self.meter = meter
self.srcdir = None
self._srcdir = None
logging.debug("Using scratchdir=%s", scratchdir) logging.debug("Using scratchdir=%s", scratchdir)
def _make_path(self, filename): ####################
path = self.srcdir or self.location # Internal helpers #
####################
if filename: def _make_full_url(self, filename):
if not path.endswith("/"): """
path += "/" Generate a full fetchable URL from the passed filename, which
path += filename is relative to the self.location
"""
ret = self._srcdir or self.location
if not filename:
return ret
return path if not ret.endswith("/"):
ret += "/"
def saveTemp(self, fileobj, prefix): return ret + filename
if not os.path.exists(self.scratchdir):
os.makedirs(self.scratchdir, 0750)
def _saveTemp(self, fileobj, prefix):
"""
Save the fileobj contents to a temporary file, and return
the filename
"""
prefix = "virtinst-" + prefix prefix = "virtinst-" + prefix
if "VIRTINST_TEST_SUITE" in os.environ: if "VIRTINST_TEST_SUITE" in os.environ:
fn = os.path.join("/tmp", prefix) fn = os.path.join("/tmp", prefix)
@ -85,18 +94,38 @@ class _ImageFetcher(object):
os.close(fd) os.close(fd)
return fn return fn
##############
# Public API #
##############
def prepareLocation(self): def prepareLocation(self):
"""
Perform any necessary setup
"""
pass pass
def cleanupLocation(self): def cleanupLocation(self):
"""
Perform any necessary cleanup
"""
pass pass
def hasFile(self, filename):
"""
Return True if self.location has the passed filename
"""
raise NotImplementedError("Must be implemented in subclass")
def acquireFile(self, filename): def acquireFile(self, filename):
# URLGrabber works for all network and local cases """
Grab the passed filename from self.location and save it to
a temporary file, returning the temp filename
"""
f = None f = None
try: try:
path = self._make_path(filename) path = self._make_full_url(filename)
base = os.path.basename(filename) base = os.path.basename(filename)
logging.debug("Fetching URI: %s", path) logging.debug("Fetching URI: %s", path)
@ -108,7 +137,7 @@ class _ImageFetcher(object):
raise ValueError(_("Couldn't acquire file %s: %s") % raise ValueError(_("Couldn't acquire file %s: %s") %
(path, str(e))) (path, str(e)))
tmpname = self.saveTemp(f, prefix=base + ".") tmpname = self._saveTemp(f, prefix=base + ".")
logging.debug("Saved file to " + tmpname) logging.debug("Saved file to " + tmpname)
return tmpname return tmpname
finally: finally:
@ -116,89 +145,78 @@ class _ImageFetcher(object):
f.close() f.close()
def hasFile(self, src):
raise NotImplementedError("Must be implemented in subclass")
class _HTTPURLFetcher(_URLFetcher):
class _URIImageFetcher(_ImageFetcher):
"""
Base class for downloading from FTP / HTTP
"""
def hasFile(self, filename):
raise NotImplementedError
class _HTTPImageFetcher(_URIImageFetcher):
def hasFile(self, filename): def hasFile(self, filename):
url = self._make_full_url(filename)
try: try:
path = self._make_path(filename) request = urllib2.Request(url)
request = urllib2.Request(path)
request.get_method = lambda: "HEAD" request.get_method = lambda: "HEAD"
urllib2.urlopen(request) urllib2.urlopen(request)
except Exception, e: except Exception, e:
logging.debug("HTTP hasFile: didn't find %s: %s", path, str(e)) logging.debug("HTTP hasFile: didn't find %s: %s", url, str(e))
return False return False
return True return True
class _FTPImageFetcher(_URIImageFetcher): class _FTPURLFetcher(_URLFetcher):
ftp = None _ftp = None
def prepareLocation(self): def prepareLocation(self):
if self.ftp: if self._ftp:
return return
try: try:
url = urlparse.urlparse(self._make_path("")) urlret = urlparse.urlparse(self._make_full_url(""))
if not url[1]: self._ftp = ftplib.FTP(urlret[1])
raise ValueError(_("Invalid install location")) self._ftp.login()
self.ftp = ftplib.FTP(url[1])
self.ftp.login()
except Exception, e: except Exception, e:
raise ValueError(_("Opening URL %s failed: %s.") % raise ValueError(_("Opening URL %s failed: %s.") %
(self.location, str(e))) (self.location, str(e)))
def cleanupLocation(self): def cleanupLocation(self):
if not self.ftp: if not self._ftp:
return return
try: try:
self.ftp.quit() self._ftp.quit()
except: except:
logging.debug("Error quitting ftp connection", exc_info=True) logging.debug("Error quitting ftp connection", exc_info=True)
self.ftp = None self._ftp = None
def hasFile(self, filename): def hasFile(self, filename):
path = self._make_path(filename) url = self._make_full_url(filename)
url = urlparse.urlparse(path) urlret = urlparse.urlparse(url)
try: try:
try: try:
# If it's a file # If it's a file
self.ftp.size(url[2]) self._ftp.size(urlret[2])
except ftplib.all_errors: except ftplib.all_errors:
# If it's a dir # If it's a dir
self.ftp.cwd(url[2]) self._ftp.cwd(urlret[2])
except ftplib.all_errors, e: except ftplib.all_errors, e:
logging.debug("FTP hasFile: couldn't access %s: %s", logging.debug("FTP hasFile: couldn't access %s: %s",
path, str(e)) url, str(e))
return False return False
return True return True
class _LocalImageFetcher(_ImageFetcher): class _LocalURLFetcher(_URLFetcher):
"""
For grabbing files from a local directory
"""
def hasFile(self, filename): def hasFile(self, filename):
src = self._make_path(filename) url = self._make_full_url(filename)
if os.path.exists(src): ret = os.path.exists(url)
return True if not ret:
else: logging.debug("local hasFile: Couldn't find %s", url)
logging.debug("local hasFile: Couldn't find %s", src) return ret
return False
class _MountedImageFetcher(_LocalImageFetcher): class _MountedURLFetcher(_LocalURLFetcher):
""" """
Fetcher capable of extracting files from a NFS server Fetcher capable of extracting files from a NFS server
or loopback mounted file, or local CDROM device or loopback mounted file, or local CDROM device
@ -211,21 +229,21 @@ class _MountedImageFetcher(_LocalImageFetcher):
return return
if self._in_test_suite: if self._in_test_suite:
self.srcdir = os.environ["VIRTINST_TEST_URL_DIR"] self._srcdir = os.environ["VIRTINST_TEST_URL_DIR"]
else: else:
self.srcdir = tempfile.mkdtemp(prefix="virtinstmnt.", self._srcdir = tempfile.mkdtemp(prefix="virtinstmnt.",
dir=self.scratchdir) dir=self.scratchdir)
mountcmd = "/bin/mount" mountcmd = "/bin/mount"
logging.debug("Preparing mount at " + self.srcdir) logging.debug("Preparing mount at " + self._srcdir)
if self.location.startswith("nfs:"): if self.location.startswith("nfs:"):
cmd = [mountcmd, "-o", "ro", self.location[4:], self.srcdir] cmd = [mountcmd, "-o", "ro", self.location[4:], self._srcdir]
else: else:
if stat.S_ISBLK(os.stat(self.location)[stat.ST_MODE]): if stat.S_ISBLK(os.stat(self.location)[stat.ST_MODE]):
mountopt = "ro" mountopt = "ro"
else: else:
mountopt = "ro,loop" mountopt = "ro,loop"
cmd = [mountcmd, "-o", mountopt, self.location, self.srcdir] cmd = [mountcmd, "-o", mountopt, self.location, self._srcdir]
logging.debug("mount cmd: %s", cmd) logging.debug("mount cmd: %s", cmd)
if not self._in_test_suite: if not self._in_test_suite:
@ -241,36 +259,32 @@ class _MountedImageFetcher(_LocalImageFetcher):
if not self._mounted: if not self._mounted:
return return
logging.debug("Cleaning up mount at " + self.srcdir) logging.debug("Cleaning up mount at " + self._srcdir)
try: try:
if not self._in_test_suite: if not self._in_test_suite:
cmd = ["/bin/umount", self.srcdir] cmd = ["/bin/umount", self._srcdir]
subprocess.call(cmd) subprocess.call(cmd)
try: try:
os.rmdir(self.srcdir) os.rmdir(self._srcdir)
except: except:
pass pass
finally: finally:
self._mounted = False self._mounted = False
class _DirectImageFetcher(_LocalImageFetcher):
def prepareLocation(self):
self.srcdir = self.location
def fetcherForURI(uri, *args, **kwargs): def fetcherForURI(uri, *args, **kwargs):
if uri.startswith("http://") or uri.startswith("https://"): if uri.startswith("http://") or uri.startswith("https://"):
fclass = _HTTPImageFetcher fclass = _HTTPURLFetcher
elif uri.startswith("ftp://"): elif uri.startswith("ftp://"):
fclass = _FTPImageFetcher fclass = _FTPURLFetcher
elif uri.startswith("nfs:"): elif uri.startswith("nfs:"):
fclass = _MountedImageFetcher fclass = _MountedURLFetcher
elif os.path.isdir(uri):
# Pointing to a local tree
fclass = _LocalURLFetcher
else: else:
if os.path.isdir(uri): # Pointing to a path, like an .iso to mount
fclass = _DirectImageFetcher fclass = _MountedURLFetcher
else:
fclass = _MountedImageFetcher
return fclass(uri, *args, **kwargs) return fclass(uri, *args, **kwargs)