The attached patch implements storage volume allocation progress
reporting for file volumes.
Currently, the volume creation process looks like:
- Grab the pool lock
- Fully allocated the volume
- 'define' the volume (so it shows up in 'virsh vol-list', etc)
- Lookup the volume object to return
- Drop the pool lock
The new sequence is:
- Grab the pool lock
- 'define' the volume (even though nothing is on disk yet)
- Drop the pool lock
- Allocate the volume as needed
- Regrab the pool lock
- Lookup the volume object to return
- Drop the pool lock (again)
Since the volume is 'defined', the user can fetch an object reference in
a separate thread, and continually poll the 'info' command for up to
date numbers. This also has the benefit of dropping the pool lock during
the potentially lengthy allocation, as currently 'virsh pool-list' etc.
will block while any volume is being allocated.
Non file volumes maintain existing behavior.
I tried to make the implementation resistant to user error: say if the
pool is deactivated or deleted while the volume is being allocated. The
creation process may bail out, but I couldn't produce any bad errors
(crashing).
There are a few other small fixes in this patch:
- Refresh volume info when doing volume dumpxml
- Update volume capacity when doing a refresh
I've also attached an ugly python script that can test this. Presuming
you have a pool named 'default', running
python sparse.py --vol-create-info
Will launch an allocation, print vol.info in a loop.
Feedback appreciated.
Thanks,
Cole
import libvirt
import threading
import time
import sys
import optparse
poolname = "default"
volname = "nonsparsetest"
testvol = "testvol"
uri = "qemu:///system"
testvolxml = """
<volume>
<name>%s</name>
<capacity>1048576000</capacity>
<allocation>0</allocation>
<target>
<format type='raw'/>
</target>
</volume>
""" % testvol
volxml = """
<volume>
<name>%s</name>
<capacity>1048576000</capacity>
<allocation>800000000</allocation>
<target>
<format type='raw'/>
</target>
</volume>
""" % volname
failvol = """
<volume>
<name>%s</name>
<capacity>1048576000</capacity>
<allocation>1048576000</allocation>
<target>
<format type='bochs'/>
</target>
</volume>
""" % volname
poolxml = """
<pool type='dir'>
<name>default</name>
<target>
<path>/var/lib/libvirt/images</path>
</target>
</pool>
"""
# Helper functions
def exception_wrapper(cmd, args):
try:
cmd(*args)
except Exception, e:
print str(e)
def make_test_vol():
pool = get_pool()
pool.createXML(testvolxml, 0)
def del_vol(name=volname):
pool = get_pool()
vol = pool.storageVolLookupByName(name)
vol.delete(0)
def del_pool():
pool = get_pool()
pool.destroy()
pool.undefine()
def define_pool():
conn = libvirt.open(uri)
pool = conn.storagePoolDefineXML(poolxml, 0)
pool.create(0)
pool.setAutostart(True)
def allocate_thread(xml=volxml):
try:
del_vol()
except:
pass
pool = get_pool()
print "creating vol in thread"
vol = pool.createXML(xml, 0)
print "creating vol complete."
def info_thread(vol):
for i in range(0, 40):
time.sleep(.5)
print vol.info()
def get_pool(name=poolname):
conn = libvirt.open(uri)
return conn.storagePoolLookupByName(name)
def get_vol(name=volname):
pool = get_pool()
return pool.storageVolLookupByName(name)
def cmd_vol_create(xml=volxml):
exception_wrapper(define_pool, ())
pool = get_pool()
print pool.listVolumes()
t = threading.Thread(target=allocate_thread, name="Allocating",
args=(xml,))
t.start()
time.sleep(5)
print "\nRefreshing pool and dumping list"
pool.refresh(0)
print pool.listVolumes()
def cmd_vol_fail():
cmd_vol_create(failvol)
def cmd_vol_poll():
cmd_vol_create()
vol = get_vol()
t = threading.Thread(target=info_thread, name="Getting info",
args=(vol,))
t.start()
def main():
import optparse
parser = optparse.OptionParser()
parser.add_option("", "--vol-create",
action="store_true",
dest="vol_create",
help="Create a volume nonsparse volume that should "
"succeed")
parser.add_option("", "--vol-create-info",
action="store_true",
dest="vol_create_info",
help="Create a volume nonsparse volume that should "
"succeed, and list info in a loop")
parser.add_option("", "--vol-create-fail",
action="store_true",
dest="vol_fail",
help="Create a volume that will fail at the allocate "
"stage")
options, ignore = parser.parse_args()
if options.vol_create:
cmd_vol_create()
elif options.vol_create_info:
cmd_vol_poll()
elif options.vol_fail:
cmd_vol_fail()
else:
parser.print_help()
sys.exit(1)
if __name__ == "__main__":
main()