diff --git a/src/virtManager/connection.py b/src/virtManager/connection.py index 27db578a9..a9409bd8d 100644 --- a/src/virtManager/connection.py +++ b/src/virtManager/connection.py @@ -1139,6 +1139,13 @@ class vmmConnection(gobject.GObject): logging.debug("Connection doesn't seem to support storage " "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: return (stopPools, startPools, origPools, newPools, currentPools) @@ -1377,6 +1384,7 @@ class vmmConnection(gobject.GObject): else: # May be a new VM, we have no choice but # to create the wrapper so we can see + # if its a previously inactive domain. try: vm = self.vmm.lookupByName(name) uuid = util.uuidstr(vm.UUID()) @@ -1456,6 +1464,13 @@ class vmmConnection(gobject.GObject): # Update VM states for uuid in oldVMs: 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: self.emit("vm-added", self.uri, uuid) for uuid in startVMs: diff --git a/src/virtManager/create.py b/src/virtManager/create.py index db5346d08..3701fcd98 100644 --- a/src/virtManager/create.py +++ b/src/virtManager/create.py @@ -22,7 +22,7 @@ import gobject import gtk import gtk.glade -import os, sys, statvfs +import sys import time import traceback import threading @@ -80,7 +80,6 @@ class vmmCreate(gobject.GObject): self.capsguest = None self.capsdomain = None self.guest = None - self.usepool = False self.storage_browser = None self.conn_signals = [] @@ -100,7 +99,6 @@ class vmmCreate(gobject.GObject): # Host space polling self.host_storage_timer = None - self.host_storage = None # 'Configure before install' window self.config_window = None @@ -289,13 +287,9 @@ class vmmCreate(gobject.GObject): hyperList.add_attribute(text, 'sensitive', 3) hyperList.set_model(hyperModel) + # Sparse tooltip sparse_info = self.window.get_widget("config-storage-nosparse-info") - 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(sparse_info, sparse_str) + uihelpers.set_sparse_tooltip(sparse_info) def reset_state(self, urihint=None): @@ -348,9 +342,12 @@ class vmmCreate(gobject.GObject): self.window.get_widget("config-cpus").set_value(1) # Storage + label_widget = self.window.get_widget("phys-hd-label") if not self.host_storage_timer: 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("config-storage-create").set_active(True) 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_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 iso_option = self.window.get_widget("install-local-iso") cdrom_option = self.window.get_widget("install-local-cdrom") @@ -823,6 +810,14 @@ class vmmCreate(gobject.GObject): 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): return self.window.get_widget("config-storage-create").get_active() @@ -838,96 +833,6 @@ class vmmCreate(gobject.GObject): 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 = ("%s" % hd_label) - self.window.get_widget("phys-hd-label").set_markup(hd_label) - - return 1 - def get_config_network_info(self): netidx = self.window.get_widget("config-netdev").get_active() 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: # See if the ideal disk path (/default/pool/vmname.img) # 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 ret = True diff --git a/src/virtManager/uihelpers.py b/src/virtManager/uihelpers.py index ebde68d34..24bf1915d 100644 --- a/src/virtManager/uihelpers.py +++ b/src/virtManager/uihelpers.py @@ -20,12 +20,15 @@ import logging import traceback +import os, statvfs import gtk +import virtinst from virtinst import VirtualNetworkInterface from virtinst import VirtualDisk +from virtManager import util from virtManager.error import vmmErrorDialog OPTICAL_DEV_PATH = 0 @@ -55,6 +58,51 @@ def set_error_parent(parent): err_dial.set_parent(parent) 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 = ("%s" % hd_label) + widget.set_markup(hd_label) + + return 1 + ####################################################################### # Widgets for listing network device options (in create, addhardware) # diff --git a/src/virtManager/util.py b/src/virtManager/util.py index 25b61ecf2..8bc190ac1 100644 --- a/src/virtManager/util.py +++ b/src/virtManager/util.py @@ -34,12 +34,15 @@ DEFAULT_POOL_NAME = "default" DEFAULT_POOL_PATH = "/var/lib/libvirt/images" 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 ? if not virtinst.util.is_storage_capable(conn): # VirtualDisk will raise an error for us return + pool = None try: 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") % (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"): # Catch & ignore errors - set_tooltip_* is in gtk >= 2.12 # and we can easily work with lower versions @@ -334,3 +394,4 @@ def iface_in_use_by(conn, name): use_str += iface.get_name() return use_str +