create: Break out storage helpers for use in addhw

This commit is contained in:
Cole Robinson 2010-03-04 15:31:33 -05:00
parent d9759e6c9b
commit 4d4f34e596
4 changed files with 142 additions and 112 deletions

View File

@ -1139,6 +1139,13 @@ class vmmConnection(gobject.GObject):
logging.debug("Connection doesn't seem to support storage " logging.debug("Connection doesn't seem to support storage "
"APIs. Skipping all storage polling.") "APIs. Skipping all storage polling.")
else:
# Try to create the default storage pool
try:
util.build_default_pool(self.vmm)
except Exception, e:
logging.debug("Building default pool failed: %s" % str(e))
if not self.storage_capable: if not self.storage_capable:
return (stopPools, startPools, origPools, newPools, currentPools) return (stopPools, startPools, origPools, newPools, currentPools)
@ -1377,6 +1384,7 @@ class vmmConnection(gobject.GObject):
else: else:
# May be a new VM, we have no choice but # May be a new VM, we have no choice but
# to create the wrapper so we can see # to create the wrapper so we can see
# if its a previously inactive domain.
try: try:
vm = self.vmm.lookupByName(name) vm = self.vmm.lookupByName(name)
uuid = util.uuidstr(vm.UUID()) uuid = util.uuidstr(vm.UUID())
@ -1456,6 +1464,13 @@ class vmmConnection(gobject.GObject):
# Update VM states # Update VM states
for uuid in oldVMs: for uuid in oldVMs:
self.emit("vm-removed", self.uri, uuid) self.emit("vm-removed", self.uri, uuid)
# This forces the backing virDomain to be deleted and
# unreferenced. Not forcing this seems to cause refcount
# issues, and if the user creates another domain with the
# same name, libvirt will return the original UUID when
# requested, causing confusion.
oldVMs[uuid].release_handle()
for uuid in newVMs: for uuid in newVMs:
self.emit("vm-added", self.uri, uuid) self.emit("vm-added", self.uri, uuid)
for uuid in startVMs: for uuid in startVMs:

View File

@ -22,7 +22,7 @@ import gobject
import gtk import gtk
import gtk.glade import gtk.glade
import os, sys, statvfs import sys
import time import time
import traceback import traceback
import threading import threading
@ -80,7 +80,6 @@ class vmmCreate(gobject.GObject):
self.capsguest = None self.capsguest = None
self.capsdomain = None self.capsdomain = None
self.guest = None self.guest = None
self.usepool = False
self.storage_browser = None self.storage_browser = None
self.conn_signals = [] self.conn_signals = []
@ -100,7 +99,6 @@ class vmmCreate(gobject.GObject):
# Host space polling # Host space polling
self.host_storage_timer = None self.host_storage_timer = None
self.host_storage = None
# 'Configure before install' window # 'Configure before install' window
self.config_window = None self.config_window = None
@ -289,13 +287,9 @@ class vmmCreate(gobject.GObject):
hyperList.add_attribute(text, 'sensitive', 3) hyperList.add_attribute(text, 'sensitive', 3)
hyperList.set_model(hyperModel) hyperList.set_model(hyperModel)
# Sparse tooltip
sparse_info = self.window.get_widget("config-storage-nosparse-info") sparse_info = self.window.get_widget("config-storage-nosparse-info")
sparse_str = _("Fully allocating storage will take longer now, " uihelpers.set_sparse_tooltip(sparse_info)
"but the OS install phase will be quicker. \n\n"
"Skipping allocation can also cause space issues on "
"the host machine, if the maximum image size exceeds "
"available storage space.")
util.tooltip_wrapper(sparse_info, sparse_str)
def reset_state(self, urihint=None): def reset_state(self, urihint=None):
@ -348,9 +342,12 @@ class vmmCreate(gobject.GObject):
self.window.get_widget("config-cpus").set_value(1) self.window.get_widget("config-cpus").set_value(1)
# Storage # Storage
label_widget = self.window.get_widget("phys-hd-label")
if not self.host_storage_timer: if not self.host_storage_timer:
self.host_storage_timer = util.safe_timeout_add(3 * 1000, self.host_storage_timer = util.safe_timeout_add(3 * 1000,
self.host_space_tick) uihelpers.host_space_tick,
self.conn, self.config,
label_widget)
self.window.get_widget("enable-storage").set_active(True) self.window.get_widget("enable-storage").set_active(True)
self.window.get_widget("config-storage-create").set_active(True) self.window.get_widget("config-storage-create").set_active(True)
self.window.get_widget("config-storage-size").set_value(8) self.window.get_widget("config-storage-size").set_value(8)
@ -450,16 +447,6 @@ class vmmCreate(gobject.GObject):
util.tooltip_wrapper(method_local, local_tt) util.tooltip_wrapper(method_local, local_tt)
util.tooltip_wrapper(method_pxe, pxe_tt) util.tooltip_wrapper(method_pxe, pxe_tt)
# Attempt to create the default pool
self.usepool = False
try:
if is_storage_capable:
util.build_default_pool(self.conn.vmm)
self.usepool = True
except Exception, e:
logging.debug("Building default pool failed: %s" % str(e))
# Install local # Install local
iso_option = self.window.get_widget("install-local-iso") iso_option = self.window.get_widget("install-local-iso")
cdrom_option = self.window.get_widget("install-local-cdrom") cdrom_option = self.window.get_widget("install-local-cdrom")
@ -823,6 +810,14 @@ class vmmCreate(gobject.GObject):
return (media.strip(), extra.strip(), ks.strip()) return (media.strip(), extra.strip(), ks.strip())
def get_default_path(self, name):
# Don't generate a new path if the install failed
if self.failed_guest:
if len(self.failed_guest.disks) > 0:
return self.failed_guest.disks[0].path
return util.get_default_path(self.conn, self.config, name)
def is_default_storage(self): def is_default_storage(self):
return self.window.get_widget("config-storage-create").get_active() return self.window.get_widget("config-storage-create").get_active()
@ -838,96 +833,6 @@ class vmmCreate(gobject.GObject):
return (path, size, sparse) return (path, size, sparse)
def get_default_pool(self):
pool = None
for uuid in self.conn.list_pool_uuids():
p = self.conn.get_pool(uuid)
if p.get_name() == util.DEFAULT_POOL_NAME:
pool = p
if not pool:
raise RuntimeError(_("Did not find pool '%s'") %
util.DEFAULT_POOL_NAME)
return pool
def get_ideal_path_info(self, name):
pool = self.get_default_pool()
suffix = ".img"
return (pool.get_target_path(), name, suffix)
def get_ideal_path(self, name):
target, name, suffix = self.get_ideal_path_info(name)
return os.path.join(target, name) + suffix
def get_default_path(self, name):
path = ""
# Don't generate a new path if the install failed
if self.failed_guest:
if len(self.failed_guest.disks) > 0:
return self.failed_guest.disks[0].path
if not self.usepool:
# Use old generating method
d = self.config.get_default_image_dir(self.conn)
origf = os.path.join(d, name + ".img")
f = origf
n = 1
while os.path.exists(f) and n < 100:
f = os.path.join(d, self.get_config_name() +
"-" + str(n) + ".img")
n += 1
if os.path.exists(f):
f = origf
path = f
else:
pool = self.get_default_pool()
target, ignore, suffix = self.get_ideal_path_info(name)
path = virtinst.Storage.StorageVolume.find_free_name(name,
pool_object=pool.pool, suffix=suffix)
path = os.path.join(target, path)
return path
def host_disk_space(self, path=None):
if not path:
path = util.DEFAULT_POOL_PATH
avail = 0
if self.usepool:
# FIXME: make sure not inactive?
# FIXME: use a conn specific function after we send pool-added
pool = virtinst.util.lookup_pool_by_path(self.conn.vmm, path)
if pool:
pool.refresh(0)
avail = int(virtinst.util.get_xml_path(pool.XMLDesc(0),
"/pool/available"))
if not avail and not self.conn.is_remote():
vfs = os.statvfs(os.path.dirname(path))
avail = vfs[statvfs.F_FRSIZE] * vfs[statvfs.F_BAVAIL]
return float(avail / 1024.0 / 1024.0 / 1024.0)
def host_space_tick(self):
max_storage = self.host_disk_space()
if self.host_storage == max_storage:
return 1
self.host_storage = max_storage
hd_label = ("%s available in the default location" %
self.pretty_storage(max_storage))
hd_label = ("<span color='#484848'>%s</span>" % hd_label)
self.window.get_widget("phys-hd-label").set_markup(hd_label)
return 1
def get_config_network_info(self): def get_config_network_info(self):
netidx = self.window.get_widget("config-netdev").get_active() netidx = self.window.get_widget("config-netdev").get_active()
netinfo = self.window.get_widget("config-netdev").get_model()[netidx] netinfo = self.window.get_widget("config-netdev").get_model()[netidx]
@ -1374,7 +1279,8 @@ class vmmCreate(gobject.GObject):
if self.is_default_storage() and not revalidate: if self.is_default_storage() and not revalidate:
# See if the ideal disk path (/default/pool/vmname.img) # See if the ideal disk path (/default/pool/vmname.img)
# exists, and if unused, prompt the use for using it # exists, and if unused, prompt the use for using it
ideal = self.get_ideal_path(self.guest.name) ideal = util.get_ideal_path(self.conn, self.config,
self.guest.name)
do_exist = False do_exist = False
ret = True ret = True

View File

@ -20,12 +20,15 @@
import logging import logging
import traceback import traceback
import os, statvfs
import gtk import gtk
import virtinst
from virtinst import VirtualNetworkInterface from virtinst import VirtualNetworkInterface
from virtinst import VirtualDisk from virtinst import VirtualDisk
from virtManager import util
from virtManager.error import vmmErrorDialog from virtManager.error import vmmErrorDialog
OPTICAL_DEV_PATH = 0 OPTICAL_DEV_PATH = 0
@ -55,6 +58,51 @@ def set_error_parent(parent):
err_dial.set_parent(parent) err_dial.set_parent(parent)
err_dial = err_dial err_dial = err_dial
############################################################
# Helpers for shared storage UI between create/addhardware #
############################################################
def set_sparse_tooltip(widget):
sparse_str = _("Fully allocating storage will take longer now, "
"but the OS install phase will be quicker. \n\n"
"Skipping allocation can also cause space issues on "
"the host machine, if the maximum image size exceeds "
"available storage space.")
util.tooltip_wrapper(widget, sparse_str)
def host_disk_space(conn, config):
pool = util.get_default_pool(conn)
path = util.get_default_dir(conn, config)
avail = 0
if pool:
# FIXME: make sure not inactive?
# FIXME: use a conn specific function after we send pool-added
pool = virtinst.util.lookup_pool_by_path(conn.vmm, path)
if pool:
pool.refresh(0)
avail = int(virtinst.util.get_xml_path(pool.XMLDesc(0),
"/pool/available"))
elif not conn.is_remote():
vfs = os.statvfs(os.path.dirname(path))
avail = vfs[statvfs.F_FRSIZE] * vfs[statvfs.F_BAVAIL]
return float(avail / 1024.0 / 1024.0 / 1024.0)
def host_space_tick(conn, config, widget):
max_storage = host_disk_space(conn, config)
def pretty_storage(size):
return "%.1f Gb" % float(size)
hd_label = ("%s available in the default location" %
pretty_storage(max_storage))
hd_label = ("<span color='#484848'>%s</span>" % hd_label)
widget.set_markup(hd_label)
return 1
####################################################################### #######################################################################
# Widgets for listing network device options (in create, addhardware) # # Widgets for listing network device options (in create, addhardware) #

View File

@ -34,12 +34,15 @@ DEFAULT_POOL_NAME = "default"
DEFAULT_POOL_PATH = "/var/lib/libvirt/images" DEFAULT_POOL_PATH = "/var/lib/libvirt/images"
def build_default_pool(conn): def build_default_pool(conn):
"""Helper to build the 'default' storage pool""" """
Helper to build the 'default' storage pool
"""
# FIXME: This should use config.get_default_image_path ? # FIXME: This should use config.get_default_image_path ?
if not virtinst.util.is_storage_capable(conn): if not virtinst.util.is_storage_capable(conn):
# VirtualDisk will raise an error for us # VirtualDisk will raise an error for us
return return
pool = None pool = None
try: try:
pool = conn.storagePoolLookupByName(DEFAULT_POOL_NAME) pool = conn.storagePoolLookupByName(DEFAULT_POOL_NAME)
@ -61,6 +64,63 @@ def build_default_pool(conn):
raise RuntimeError(_("Couldn't create default storage pool '%s': %s") % raise RuntimeError(_("Couldn't create default storage pool '%s': %s") %
(DEFAULT_POOL_PATH, str(e))) (DEFAULT_POOL_PATH, str(e)))
def get_ideal_path_info(conn, config, name):
path = get_default_dir(conn, config)
suffix = ".img"
return (path, name, suffix)
def get_ideal_path(conn, config, name):
target, name, suffix = get_ideal_path_info(conn, config, name)
return os.path.join(target, name) + suffix
def get_default_pool(conn):
pool = None
for uuid in conn.list_pool_uuids():
p = conn.get_pool(uuid)
if p.get_name() == DEFAULT_POOL_NAME:
pool = p
return pool
def get_default_dir(conn, config):
pool = get_default_pool(conn)
if pool:
return pool.get_target_path()
else:
return config.get_default_image_dir(conn)
def get_default_path(conn, config, name):
pool = get_default_pool(conn)
default_dir = get_default_dir(conn, config)
if not pool:
# Use old generating method
origf = os.path.join(default_dir, name + ".img")
f = origf
n = 1
while os.path.exists(f) and n < 100:
f = os.path.join(default_dir, name +
"-" + str(n) + ".img")
n += 1
if os.path.exists(f):
f = origf
path = f
else:
target, ignore, suffix = get_ideal_path_info(conn, config, name)
path = virtinst.Storage.StorageVolume.find_free_name(name,
pool_object=pool.pool, suffix=suffix)
path = os.path.join(target, path)
return path
def tooltip_wrapper(obj, txt, func="set_tooltip_text"): def tooltip_wrapper(obj, txt, func="set_tooltip_text"):
# Catch & ignore errors - set_tooltip_* is in gtk >= 2.12 # Catch & ignore errors - set_tooltip_* is in gtk >= 2.12
# and we can easily work with lower versions # and we can easily work with lower versions
@ -334,3 +394,4 @@ def iface_in_use_by(conn, name):
use_str += iface.get_name() use_str += iface.get_name()
return use_str return use_str