diff --git a/src/virtManager/connection.py b/src/virtManager/connection.py index 6fc468e6..30cc4b2e 100644 --- a/src/virtManager/connection.py +++ b/src/virtManager/connection.py @@ -36,6 +36,7 @@ from virtManager.network import vmmNetwork from virtManager.storagepool import vmmStoragePool from virtManager.interface import vmmInterface from virtManager.netdev import vmmNetDevice +from virtManager.mediadev import vmmMediaDevice class vmmConnection(gobject.GObject): __gsignals__ = { @@ -148,6 +149,7 @@ class vmmConnection(gobject.GObject): self.optical_initialized = False self.optical_error = "" + self.optical_use_libvirt = False ################# # Init routines # @@ -205,7 +207,16 @@ class vmmConnection(gobject.GObject): logging.debug("Using HAL for netdev enumeration") def _init_optical(self): - if self.get_hal_helper(): + if self.is_nodedev_capable(): + try: + self.connect("nodedev-added", self._nodedev_optical_added) + self.connect("nodedev-removed", self._nodedev_optical_removed) + self.optical_use_libvirt = True + except Exception, e: + self.optical_error = _("Could build optical interface " + "list via libvirt: %s") % str(e) + + elif self.get_hal_helper(): hal_helper = self.get_hal_helper() if self.is_remote(): @@ -229,7 +240,10 @@ class vmmConnection(gobject.GObject): if self.optical_error: logging.debug(self.optical_error) else: - logging.debug("Using HAL for optical enumeration") + if self.optical_use_libvirt: + logging.debug("Using libvirt API for optical enumeration") + else: + logging.debug("Using HAL for optical enumeration") ######################## # General data getters # @@ -289,6 +303,9 @@ class vmmConnection(gobject.GObject): elif name == "optical-added": for dev in self.opticaldevs.values(): self.emit("optical-added", dev) + elif name == "nodedev-added": + for key in self.nodedevs.keys(): + self.emit("nodedev-added", self.get_uri(), key) return handle_id @@ -558,6 +575,8 @@ class vmmConnection(gobject.GObject): return self.pools[uuid] def get_interface(self, name): return self.interfaces[name] + def get_nodedev(self, name): + return self.nodedevs[name] def get_devices(self, devtype=None, devcap=None): retdevs = [] for vdev in self.nodedevs.values(): @@ -645,6 +664,13 @@ class vmmConnection(gobject.GObject): # Update listeners # #################### + def _remove_optical(self, key): + del(self.opticaldevs[key]) + self.emit("optical-removed", key) + def _add_optical(self, key, dev): + self.opticaldevs[key] = dev + self.emit("optical-added", dev) + def _haldev_removed(self, ignore, hal_path): # Physical net device for name, obj in self.netdevs.items(): @@ -653,10 +679,8 @@ class vmmConnection(gobject.GObject): return for key, obj in self.opticaldevs.items(): - if obj.get_key() == hal_path: - del(self.opticaldevs[key]) - print "optical-removed %s" % hal_path - self.emit("optical-removed", hal_path) + if key == hal_path: + self._remove_optical(key) def _netdev_added(self, ignore, netdev): name = netdev.get_name() @@ -670,8 +694,24 @@ class vmmConnection(gobject.GObject): if self.opticaldevs.has_key(key): return - self.opticaldevs[key] = dev - self.emit("optical-added", dev) + self._add_optical(key, dev) + + def _nodedev_optical_added(self, ignore1, ignore2, name): + if self.opticaldevs.has_key(name): + return + + vobj = self.get_nodedev(name) + mediadev = vmmMediaDevice.mediadev_from_nodedev(self, vobj) + if not mediadev: + return + + self._add_optical(name, mediadev) + + def _nodedev_optical_removed(self, ignore1, ignore2, name): + if not self.opticaldevs.has_key(name): + return + + self._remove_optical(name) ###################################### # Connection closing/opening methods # diff --git a/src/virtManager/halhelper.py b/src/virtManager/halhelper.py index ed59f70c..1ce907be 100644 --- a/src/virtManager/halhelper.py +++ b/src/virtManager/halhelper.py @@ -164,7 +164,8 @@ class vmmHalHelper(gobject.GObject): self.emit("device-removed", str(path)) def add_optical_dev(self, devpath, halpath, media_label, media_hal_path): - obj = vmmMediaDevice(devpath, halpath, media_label, media_hal_path) + obj = vmmMediaDevice(devpath, halpath, bool(media_label), + media_label, media_hal_path) obj.set_hal_media_signals(self) self.emit("optical-added", obj) diff --git a/src/virtManager/mediadev.py b/src/virtManager/mediadev.py index d263b8ae..ac49d325 100644 --- a/src/virtManager/mediadev.py +++ b/src/virtManager/mediadev.py @@ -19,6 +19,11 @@ # import gobject +import logging + +import virtinst + +MEDIA_TIMEOUT = 3 class vmmMediaDevice(gobject.GObject): __gsignals__ = { @@ -28,14 +33,40 @@ class vmmMediaDevice(gobject.GObject): gobject.TYPE_NONE, []), } - def __init__(self, path, key, media_label, media_key): + @staticmethod + def mediadev_from_nodedev(conn, nodedev): + if nodedev.device_type != "storage": + return None + + if nodedev.drive_type != "cdrom": + return None + + path = nodedev.block + key = nodedev.name + has_media = nodedev.media_available + media_label = None + media_key = None + + nodedev_obj = conn.vmm.nodeDeviceLookupByName(key) + obj = vmmMediaDevice(path, key, has_media, media_label, media_key, + nodedev_obj) + obj.enable_poll_for_media() + + return obj + + def __init__(self, path, key, has_media, media_label, media_key, + nodedev_obj = None): self.__gobject_init__() self.path = path self.key = key + self._has_media = has_media self.media_label = media_label self.media_key = media_key + self.nodedev_obj = nodedev_obj + self.poll_signal = None + def get_path(self): return self.path @@ -43,23 +74,27 @@ class vmmMediaDevice(gobject.GObject): return self.key def has_media(self): - return self.has_media - + return self._has_media def get_media_label(self): return self.media_label def get_media_key(self): return self.media_key - def set_media(self, media_label, media_key): + def set_media(self, has_media, media_label, media_key): + self._has_media = has_media self.media_label = media_label self.media_key = media_key def clear_media(self): - self.set_media(None, None) + self.set_media(None, None, None) def pretty_label(self): media_label = self.get_media_label() - if not media_label: + has_media = self.has_media() + if not has_media: media_label = _("No media present") + else: + media_label = _("Media Unknown") + return "%s (%s)" % (media_label, self.get_path()) @@ -75,7 +110,7 @@ class vmmMediaDevice(gobject.GObject): if devpath != self.get_path(): return - self.set_media(media_label, media_key) + self.set_media(True, media_label, media_key) self.emit("media-added") def hal_media_removed(self, ignore, media_hal_path): @@ -86,4 +121,46 @@ class vmmMediaDevice(gobject.GObject): self.emit("media-removed") + ######################################### + # Nodedev API polling for media updates # + ######################################### + def enable_poll_for_media(self): + if self.poll_signal: + return + + self.poll_signal = gobject.timeout_add(MEDIA_TIMEOUT * 1000, + self._poll_for_media) + + def disable_poll_for_media(self): + self.poll_signal = None + + def _poll_for_media(self): + if not self.poll_signal: + return False + + if not self.nodedev_obj: + return False + + try: + xml = self.nodedev_obj.XMLDesc(0) + except: + # Assume the device was removed + return False + + try: + vobj = virtinst.NodeDeviceParser.parse(xml) + has_media = vobj.media_available + except: + logging.exception("Node device CDROM polling failed") + return False + + if has_media != self.has_media(): + self.set_media(has_media, None, None) + if has_media: + self.emit("media-added") + else: + self.emit("media-removed") + + return True + gobject.type_register(vmmMediaDevice) diff --git a/src/virtManager/uihelpers.py b/src/virtManager/uihelpers.py index 1835b0b4..b77b2eac 100644 --- a/src/virtManager/uihelpers.py +++ b/src/virtManager/uihelpers.py @@ -32,7 +32,6 @@ OPTICAL_LABEL = 1 OPTICAL_IS_MEDIA_PRESENT = 2 OPTICAL_DEV_KEY = 3 OPTICAL_MEDIA_KEY = 4 -OPTICAL_MEDIADEV = 5 ############################################################## # Initialize an error object to use for validation functions # @@ -253,7 +252,7 @@ def generate_macaddr(conn): def init_optical_combo(widget, empty_sensitive=False): # [Device path, pretty label, has_media?, device key, media key, # vmmMediaDevice] - model = gtk.ListStore(str, str, bool, str, str, object) + model = gtk.ListStore(str, str, bool, str, str) widget.set_model(model) model.clear() @@ -276,11 +275,10 @@ def populate_optical_combo(conn, widget): return sigs -def set_row_from_object(row): - obj = row[OPTICAL_MEDIADEV] +def set_row_from_object(row, obj): row[OPTICAL_DEV_PATH] = obj.get_path() row[OPTICAL_LABEL] = obj.pretty_label() - row[OPTICAL_IS_MEDIA_PRESENT] = bool(obj.get_media_label()) + row[OPTICAL_IS_MEDIA_PRESENT] = obj.has_media() row[OPTICAL_DEV_KEY] = obj.get_key() row[OPTICAL_MEDIA_KEY] = obj.get_media_key() @@ -310,10 +308,12 @@ def optical_added(ignore_helper, newobj, widget): newobj.connect("media-removed", optical_media_changed, widget) # Brand new device - row = [None, None, None, None, None, newobj] - set_row_from_object(row) + row = [None, None, None, None, None] + set_row_from_object(row, newobj) model.append(row) + optical_set_default_selection(widget) + def optical_media_changed(newobj, widget): model = widget.get_model() active = widget.get_active() @@ -324,7 +324,7 @@ def optical_media_changed(newobj, widget): # selection, select the new media. for row in model: if row[OPTICAL_DEV_PATH] == newobj.get_path(): - set_row_from_object(row) + set_row_from_object(row, newobj) has_media = row[OPTICAL_IS_MEDIA_PRESENT] if has_media and active == -1: @@ -334,6 +334,8 @@ def optical_media_changed(newobj, widget): idx = idx + 1 + optical_set_default_selection(widget) + def optical_set_default_selection(widget): # Set the first active cdrom device as selected, otherwise none model = widget.get_model()