From 8c95e6d6f225c7bf056e77103c0c405104fbd81f Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 7 Aug 2008 17:37:16 -0400 Subject: [PATCH] Add vmmStorage{Pool,Vol}, poll for these devices in connection tick function. --- src/virtManager/connection.py | 85 +++++++++++++++++++ src/virtManager/storagepool.py | 145 +++++++++++++++++++++++++++++++++ src/virtManager/storagevol.py | 86 +++++++++++++++++++ 3 files changed, 316 insertions(+) create mode 100644 src/virtManager/storagepool.py create mode 100644 src/virtManager/storagevol.py diff --git a/src/virtManager/connection.py b/src/virtManager/connection.py index d36e6863..0f049b40 100644 --- a/src/virtManager/connection.py +++ b/src/virtManager/connection.py @@ -36,6 +36,7 @@ import virtinst from virtManager.domain import vmmDomain from virtManager.network import vmmNetwork from virtManager.netdev import vmmNetDevice +from virtManager.storagepool import vmmStoragePool LIBVIRT_POLICY_FILE = "/usr/share/PolicyKit/policy/libvirtd.policy" @@ -97,6 +98,14 @@ class vmmConnection(gobject.GObject): [str, str]), "net-stopped": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, [str, str]), + "pool-added": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + [str, str]), + "pool-removed": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + [str, str]), + "pool-started": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + [str, str]), + "pool-stopped": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + [str, str]), "netdev-added": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, [str]), "netdev-removed": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, @@ -134,6 +143,8 @@ class vmmConnection(gobject.GObject): self.state = self.STATE_DISCONNECTED self.vmm = None + # Connection Storage pools: UUID -> vmmStoragePool + self.pools = {} # Host network devices. name -> vmmNetDevice object self.netdevs = {} # Virtual networks UUUID -> vmmNetwork object @@ -339,6 +350,9 @@ class vmmConnection(gobject.GObject): def get_net_device(self, path): return self.netdevs[path] + def get_pool(self, uuid): + return self.pools[uuid] + def open(self): if self.state != self.STATE_DISCONNECTED: return @@ -513,6 +527,7 @@ class vmmConnection(gobject.GObject): #self.vmm.close() self.vmm = None self.nets = {} + self.pools = {} self.vms = {} self.activeUUIDs = [] self.record = [] @@ -528,6 +543,9 @@ class vmmConnection(gobject.GObject): def list_net_device_paths(self): return self.netdevs.keys() + def list_pool_uuids(self): + return self.pools.keys() + def get_host_info(self): return self.hostinfo @@ -662,6 +680,60 @@ class vmmConnection(gobject.GObject): return (startNets, stopNets, newNets, origNets, currentNets) + def _update_pools(self): + origPools = self.pools + currentPools = {} + startPools = [] + stopPools = [] + newPools = [] + newActivePoolNames = [] + newInactivePoolNames = [] + + try: + newActivePoolNames = self.vmm.listStoragePools() + except: + logging.warn("Unable to list active pools") + try: + newInactivePoolNames = self.vmm.listDefinedStoragePools() + except: + logging.warn("Unable to list inactive pools") + + for name in newActivePoolNames: + try: + pool = self.vmm.storagePoolLookupByName(name) + uuid = self.uuidstr(pool.UUID()) + if not origPools.has_key(uuid): + currentPools[uuid] = vmmStoragePool(self.config, self, + pool, uuid, True) + newPools.append(uuid) + startPools.append(uuid) + else: + currentPools[uuid] = origPools[uuid] + if not currentPools[uuid].is_active(): + currentPools[uuid].set_active(True) + startPools.append(uuid) + del origPools[uuid] + except libvirt.libvirtError: + logging.warn("Couldn't fetch active pool '%s'" % name) + + for name in newInactivePoolNames: + try: + pool = self.vmm.storagePoolLookupByName(name) + uuid = self.uuidstr(pool.UUID()) + if not origPools.has_key(uuid): + currentPools[uuid] = vmmStoragePool(self.config, self, + pool, uuid, False) + newPools.append(uuid) + else: + currentPools[uuid] = origPools[uuid] + if currentPools[uuid].is_active(): + currentPools[uuid].set_active(False) + stopPools.append(uuid) + del origPools[uuid] + except libvirt.libvirtError: + logging.warn("Couldn't fetch inactive pool '%s'" % name) + return (stopPools, startPools, origPools, newPools, currentPools) + def _update_vms(self): """returns lists of changed VM states""" @@ -778,6 +850,10 @@ class vmmConnection(gobject.GObject): (startNets, stopNets, newNets, oldNets, self.nets) = self._update_nets() + # Update pools + (stopPools, startPools, oldPools, + newPools, self.pools) = self._update_pools() + # Poll for changed/new/removed VMs (startVMs, newVMs, oldVMs, self.vms, self.activeUUIDs) = self._update_vms() @@ -801,6 +877,15 @@ class vmmConnection(gobject.GObject): for uuid in stopNets: self.emit("net-stopped", self.uri, uuid) + for uuid in oldPools: + self.emit("pool-removed", self.uri, uuid) + for uuid in newPools: + self.emit("pool-added", self.uri, uuid) + for uuid in startPools: + self.emit("pool-started", self.uri, uuid) + for uuid in stopPools: + self.emit("pool-stopped", self.uri, uuid) + # Finally, we sample each domain now = time() try: diff --git a/src/virtManager/storagepool.py b/src/virtManager/storagepool.py new file mode 100644 index 00000000..978ba0b1 --- /dev/null +++ b/src/virtManager/storagepool.py @@ -0,0 +1,145 @@ +# +# Copyright (C) 2008 Red Hat, Inc. +# Copyright (C) 2008 Cole Robinson +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA. +# + +import gobject +import libvirt +import libxml2 +import logging +import virtinst +import virtinst.util as util + +from virtManager.storagevol import vmmStorageVolume + +class vmmStoragePool(gobject.GObject): + __gsignals__ = { } + + def __init__(self, config, connection, pool, uuid, active): + self.__gobject_init__() + self.config = config + self.connection = connection + self.pool = pool # Libvirt pool object + self.uuid = uuid # String UUID + self.active = active # bool indicating if it is running + + self._volumes = {} # UUID->vmmStorageVolume mapping of the + # pools associated volumes + self._xml = None # xml cache + self._update_xml() + self.update_volumes() + + def set_active(self, state): + self.active = state + self._update_xml() + + def is_active(self): + return self.active + + def can_change_alloc(self): + type = self.get_type() + return (type in [virtinst.Storage.StoragePool.TYPE_LOGICAL]) + + def get_connection(self): + return self.connection + + def get_name(self): + return self.pool.name() + + def get_uuid(self): + return self.uuid + + def start(self): + self.pool.create(0) + self._update_xml() + + def stop(self): + self.pool.destroy() + self._update_xml() + + def delete(self): + self.pool.undefine() + del(self.pool) + + def _update_xml(self): + self._xml = self.pool.XMLDesc(0) + + def get_xml(self): + if self._xml is None: + self._update_xml() + return self._xml + + def set_autostart(self, value): + self.pool.setAutostart(value) + + def get_autostart(self): + return self.pool.autostart() + + def get_target_path(self): + return util.get_xml_path(self.get_xml(), "/pool/target/path") + + def get_allocation(self): + return long(util.get_xml_path(self.get_xml(), "/pool/allocation")) + def get_available(self): + return long(util.get_xml_path(self.get_xml(), "/pool/available")) + def get_capacity(self): + return long(util.get_xml_path(self.get_xml(), "/pool/capacity")) + + def get_pretty_allocation(self): + return self._prettyify(self.get_allocation()) + def get_pretty_available(self): + return self._prettyify(self.get_available()) + def get_pretty_capacity(self): + return self._prettyify(self.get_capacity()) + + def get_type(self): + return util.get_xml_path(self.get_xml(), "/pool/@type") + + def get_volumes(self): + self.update_volumes() + return self._volumes + + def get_volume(self, uuid): + return self._volumes[uuid] + + def update_volumes(self): + if not self.is_active(): + self._volumes = {} + return + + self.pool.refresh(0) + vols = self.pool.listVolumes() + new_vol_list = {} + + for volname in vols: + if self._volumes.has_key(volname): + new_vol_list[volname] = self._volumes[volname] + else: + new_vol_list[volname] = vmmStorageVolume(self.config, + self.connection, + self.pool.storageVolLookupByName(volname), + volname) + self._volumes = new_vol_list + + def _prettyify(self, val): + if val > (1024*1024*1024): + return "%2.2f GB" % (val/(1024.0*1024.0*1024.0)) + else: + return "%2.2f MB" % (val/(1024.0*1024.0)) + +gobject.type_register(vmmStoragePool) diff --git a/src/virtManager/storagevol.py b/src/virtManager/storagevol.py new file mode 100644 index 00000000..0008de03 --- /dev/null +++ b/src/virtManager/storagevol.py @@ -0,0 +1,86 @@ +# +# Copyright (C) 2008 Red Hat, Inc. +# Copyright (C) 2008 Cole Robinson +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA. +# + +import gobject +import libvirt +import libxml2 +import logging +import virtinst +import virtinst.util as util + +class vmmStorageVolume(gobject.GObject): + __gsignals__ = { } + + def __init__(self, config, connection, vol, name): + self.__gobject_init__() + self.config = config + self.connection = connection + self.vol = vol # Libvirt storage volume object + self.name = name + self._xml = None # Cache xml rather than repeated lookups + self._update_xml() + + def get_connection(self): + return self.connection + + def get_name(self): + return self.name + + def get_path(self): + return self.vol.path() + + def delete(self): + self.vol.undefine() + del(self.vol) + + def get_xml(self): + if self._xml is None: + self._update_xml() + return self._xml + + def get_target_path(self): + return util.get_xml_path(self.get_xml(),"/volume/target/path") + + def get_format(self): + return util.get_xml_path(self.get_xml(),"/volume/target/format/@type") + + def get_allocation(self): + return long(util.get_xml_path(self.get_xml(),"/volume/allocation")) + def get_capacity(self): + return long(util.get_xml_path(self.get_xml(),"/volume/capacity")) + + def get_pretty_capacity(self): + return self._prettyify(self.get_capacity()) + def get_pretty_allocation(self): + return self._prettyify(self.get_allocation()) + + def get_type(self): + return util.get_xml_path(self.get_xml(),"/volume/format/@type") + + def _update_xml(self): + self._xml = self.vol.XMLDesc(0) + + def _prettyify(self, val): + if val > (1024*1024*1024): + return "%2.2f GB" % (val/(1024.0*1024.0*1024.0)) + else: + return "%2.2f MB" % (val/(1024.0*1024.0)) + +gobject.type_register(vmmStorageVolume)