storage: Don't leave thread stranded on error

Move thread callback outside the StorageVolume class, so we are
forced to explicitly pass in every bit it may act on. Ensure we
always cancel and clean up the thread
This commit is contained in:
Cole Robinson 2019-07-17 12:57:38 -04:00
parent 7148727a05
commit ef46af706a

View File

@ -419,6 +419,31 @@ class StoragePool(_StorageObject):
return pool return pool
def _progress_thread(volname, pool, meter, event):
vol = None
if not meter:
return
while True:
try:
if not vol:
vol = pool.storageVolLookupByName(volname)
vol.info() # pragma: no cover
break # pragma: no cover
except Exception:
if event.wait(.2):
break
if vol is None:
log.debug("Couldn't lookup storage volume in prog thread.")
return
while True: # pragma: no cover
ignore, ignore, alloc = vol.info()
meter.update(alloc)
if event.wait(1):
break
class StorageVolume(_StorageObject): class StorageVolume(_StorageObject):
""" """
@ -479,8 +504,6 @@ class StorageVolume(_StorageObject):
self._pool_xml = None self._pool_xml = None
self._reflink = False self._reflink = False
self._install_finished = threading.Event()
###################### ######################
# Non XML properties # # Non XML properties #
@ -642,13 +665,6 @@ class StorageVolume(_StorageObject):
log.debug("Creating storage volume '%s' with xml:\n%s", log.debug("Creating storage volume '%s' with xml:\n%s",
self.name, xml) self.name, xml)
t = threading.Thread(target=self._progress_thread,
name="Checking storage allocation",
args=(meter,))
t.setDaemon(True)
meter = progress.ensure_meter(meter)
cloneflags = 0 cloneflags = 0
createflags = 0 createflags = 0
if (self.format == "qcow2" and if (self.format == "qcow2" and
@ -664,8 +680,14 @@ class StorageVolume(_StorageObject):
cloneflags |= getattr(libvirt, cloneflags |= getattr(libvirt,
"VIR_STORAGE_VOL_CREATE_REFLINK", 1) "VIR_STORAGE_VOL_CREATE_REFLINK", 1)
event = threading.Event()
meter = progress.ensure_meter(meter)
t = threading.Thread(target=_progress_thread,
name="Checking storage allocation",
args=(self.name, self.pool, meter, event))
t.setDaemon(True)
try: try:
self._install_finished.clear()
t.start() t.start()
meter.start(size=self.capacity, meter.start(size=self.capacity,
text=_("Allocating '%s'") % self.name) text=_("Allocating '%s'") % self.name)
@ -681,8 +703,6 @@ class StorageVolume(_StorageObject):
log.debug("Using vol create flags=%s", createflags) log.debug("Using vol create flags=%s", createflags)
vol = self.pool.createXML(xml, createflags) vol = self.pool.createXML(xml, createflags)
self._install_finished.set()
t.join()
meter.end(self.capacity) meter.end(self.capacity)
log.debug("Storage volume '%s' install complete.", self.name) log.debug("Storage volume '%s' install complete.", self.name)
return vol return vol
@ -690,32 +710,9 @@ class StorageVolume(_StorageObject):
log.debug("Error creating storage volume", exc_info=True) log.debug("Error creating storage volume", exc_info=True)
raise RuntimeError("Couldn't create storage volume " raise RuntimeError("Couldn't create storage volume "
"'%s': '%s'" % (self.name, str(e))) "'%s': '%s'" % (self.name, str(e)))
finally:
def _progress_thread(self, meter): event.set()
vol = None t.join()
if not meter:
return
while True:
try:
if not vol:
vol = self.pool.storageVolLookupByName(self.name)
vol.info() # pragma: no cover
break # pragma: no cover
except Exception:
if self._install_finished.wait(.2):
break
if vol is None:
log.debug("Couldn't lookup storage volume in prog thread.")
return
while True: # pragma: no cover
ignore, ignore, alloc = vol.info()
meter.update(alloc)
if self._install_finished.wait(1):
break
def is_size_conflict(self): def is_size_conflict(self):
""" """