The attached patch implements attaching serial and parallel devices via the 'Add Hardware' wizard in virt-manager. A couple screenshots: http://fedorapeople.org/~crobinso/virt-manager/vmm-add-char1.png http://fedorapeople.org/~crobinso/virt-manager/vmm-add-char2.png The options change quite a bit depending on which device type is selected: the associated fields and activated accordingly. The box on the right provides some documentation about the selected field. I snipped the actual UI diff from the patch since it is pretty large. Questions or comments appreciated. - Cole
# HG changeset patch # User Cole Robinson <crobinso@xxxxxxxxxx> # Date 1247164914 14400 # Node ID 6ba8a10d6f8f446464f2de4b566813c35c443c10 # Parent a39662e0da71d8108a767ca6f69f7d4ee3013d6d Support adding serial and parallel devices via 'Add Hardware' diff -r a39662e0da71 -r 6ba8a10d6f8f src/virtManager/addhardware.py --- a/src/virtManager/addhardware.py Thu Jul 09 14:35:55 2009 -0400 +++ b/src/virtManager/addhardware.py Thu Jul 09 14:41:54 2009 -0400 @@ -18,14 +18,17 @@ # MA 02110-1301 USA. # +import os +import logging +import traceback + import gobject import gtk import gtk.gdk import gtk.glade + import virtinst -import os -import logging -import traceback +from virtinst import VirtualCharDevice, VirtualDevice import virtManager.util as vmmutil from virtManager.asyncjob import vmmAsyncJob @@ -45,7 +48,18 @@ PAGE_GRAPHICS = 4 PAGE_SOUND = 5 PAGE_HOSTDEV = 6 -PAGE_SUMMARY = 7 +PAGE_CHAR = 7 +PAGE_SUMMARY = 8 + +char_widget_mappings = { + "source_path" : "char-path", + "source_mode" : "char-mode", + "source_host" : "char-host", + "source_port" : "char-port", + "bind_port": "char-bind-port", + "bind_host": "char-bind-host", + "protocol" : "char-use-telnet", +} class vmmAddHardware(gobject.GObject): __gsignals__ = { @@ -56,7 +70,6 @@ self.__gobject_init__() self.config = config self.vm = vm - self._dev = None self.window = gtk.glade.XML(config.get_glade_dir() + "/vmm-add-hardware.glade", "vmm-add-hardware", domain="virt-manager") self.topwin = self.window.get_widget("vmm-add-hardware") self.err = vmmErrorDialog(self.topwin, @@ -67,6 +80,8 @@ self.storage_browser = None self._browse_cb_id = None + self._dev = None + self.topwin.hide() self.window.signal_autoconnect({ "on_create_pages_switch_page" : self.page_changed, @@ -85,11 +100,42 @@ "on_graphics_port_auto_toggled": self.change_port_auto, "on_graphics_keymap_toggled": self.change_keymap, "on_host_device_type_changed": self.change_host_device_type, + "on_char_device_type_changed": self.change_char_device_type, "on_create_help_clicked": self.show_help, + + # Char dev info signals + "char_device_type_focus": (self.update_doc, "char_type"), + "char_path_focus_in": (self.update_doc, "source_path"), + "char_mode_changed": (self.update_doc_changed, "source_mode"), + "char_mode_focus" : (self.update_doc, "source_mode"), + "char_host_focus_in": (self.update_doc, "source_host"), + "char_bind_host_focus_in": (self.update_doc, "bind_host"), + "char_telnet_focus_in": (self.update_doc, "protocol"), }) self.set_initial_state() + def update_doc(self, ignore1, ignore2, param): + doc = self._build_doc_str(param) + self.window.get_widget("char-info").set_markup(doc) + + def update_doc_changed(self, ignore1, param): + # Wrapper for update_doc and 'changed' signal + self.update_doc(None, None, param) + + def _build_doc_str(self, param, docstr=None): + doc = "" + doctmpl = "<i>%s</i>" + + if docstr: + doc = doctmpl % (docstr) + elif self._dev: + devclass = self._dev.__class__ + if hasattr(devclass, param): + doc = doctmpl % (getattr(devclass, param).__doc__) + + return doc + def show(self): self.reset_state() self.topwin.show() @@ -195,6 +241,32 @@ host_dev_model.set_sort_column_id(0, gtk.SORT_ASCENDING) + char_devtype = self.window.get_widget("char-device-type") + # Type name, desc + char_devtype_model = gtk.ListStore(str, str) + char_devtype.set_model(char_devtype_model) + text = gtk.CellRendererText() + char_devtype.pack_start(text, True) + char_devtype.add_attribute(text, 'text', 1) + char_devtype_model.set_sort_column_id(0, gtk.SORT_ASCENDING) + for t in VirtualCharDevice.char_types: + desc = VirtualCharDevice.get_char_type_desc(t) + char_devtype_model.append([t, desc + " (%s)" % t]) + + char_mode = self.window.get_widget("char-mode") + # Mode name, desc + char_mode_model = gtk.ListStore(str, str) + char_mode.set_model(char_mode_model) + text = gtk.CellRendererText() + char_mode.pack_start(text, True) + char_mode.add_attribute(text, 'text', 1) + char_mode_model.set_sort_column_id(0, gtk.SORT_ASCENDING) + for t in VirtualCharDevice.char_modes: + desc = VirtualCharDevice.get_char_mode_desc(t) + char_mode_model.append([t, desc + " (%s)" % t]) + + self.window.get_widget("char-info-box").modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("grey")) + def reset_state(self): notebook = self.window.get_widget("create-pages") notebook.set_current_page(0) @@ -283,6 +355,17 @@ # Set available HW options + # Char parameters + self.window.get_widget("char-device-type").set_active(0) + self.window.get_widget("char-path").set_text("") + self.window.get_widget("char-host").set_text("127.0.0.1") + self.window.get_widget("char-port").get_adjustment().value = 4555 + self.window.get_widget("char-bind-host").set_text("127.0.0.1") + self.window.get_widget("char-bind-port").get_adjustment().value = 4556 + self.window.get_widget("char-use-telnet").set_active(False) + + # Available HW options + # FIXME: All of these needs to have better transparency. # All options should be listed, but disabled with a tooltip if # it can't be used. @@ -304,6 +387,10 @@ if self.vm.is_hvm(): model.append(["Sound", gtk.STOCK_MEDIA_PLAY, PAGE_SOUND]) + if self.vm.is_hvm(): + model.append(["Serial", gtk.STOCK_CONNECT, PAGE_CHAR]) + model.append(["Parallel", gtk.STOCK_CONNECT, PAGE_CHAR]) + if self.vm.get_connection().is_nodedev_capable(): model.append(["Physical Host Device", None, PAGE_HOSTDEV]) @@ -467,6 +554,11 @@ return devbox.get_model()[devbox.get_active()] def page_changed(self, notebook, page, page_number): + if page_number == PAGE_CHAR: + devtype = self.window.get_widget("char-device-type") + self.change_char_device_type(devtype) + self.set_page_char_type() + if page_number != PAGE_SUMMARY: return @@ -570,6 +662,35 @@ ] title = _("Sound") + elif hwpage == PAGE_CHAR: + mode = None + + info_list = [ + (_("Type:"), VirtualCharDevice.get_char_type_desc(self._dev.char_type)), + ] + + if hasattr(self._dev, "source_mode"): + mode = self._dev.source_mode.capitalize() + if hasattr(self._dev, "source_path"): + path = self._dev.source_path + label = "%sPath:" % (mode and mode + " " or "") + info_list.append((label, path)) + + if hasattr(self._dev, "source_host"): + host = "%s:%s" % (self._dev.source_host, self._dev.source_port) + label = "%sHost:" % (mode and mode + " " or "") + info_list.append((label, host)) + + if hasattr(self._dev, "bind_host"): + bind_host = "%s:%s" % (self._dev.bind_host, + self._dev.bind_port) + info_list.append(("Bind Host:", bind_host)) + if hasattr(self._dev, "protocol"): + proto = self._dev.protocol + info_list.append((_("Protocol:"), proto)) + + title = self.get_char_type().capitalize() + elif hwpage == PAGE_HOSTDEV: info_list = [ (_("Type:"), self.get_config_host_device_type_info()[0]), @@ -600,7 +721,8 @@ PAGE_INPUT: self.add_input, PAGE_GRAPHICS: self.add_graphics, PAGE_SOUND: self.add_sound, - PAGE_HOSTDEV: self.add_hostdev } + PAGE_HOSTDEV: self.add_hostdev, + PAGE_CHAR: self.add_device} try: func = func_dict[hw] @@ -660,7 +782,10 @@ else: return (error, details) - def add_device(self, xml): + def add_device(self, xml=None): + if not xml: + xml = self._dev.get_xml_config() + logging.debug("Adding device:\n" + xml) attach_err = False @@ -841,6 +966,42 @@ subtype, subcap) devbox.set_active(0) + def get_char_type(self): + hw_list = self.window.get_widget("hardware-type") + if hw_list.get_active() < 0: + label = "serial" + else: + label = hw_list.get_model()[hw_list.get_active()][0] + + if label.lower() == "parallel": + return VirtualDevice.VIRTUAL_DEV_PARALLEL + return VirtualDevice.VIRTUAL_DEV_SERIAL + + def set_page_char_type(self): + char_type = self.get_char_type().capitalize() + self.window.get_widget("char-title-label").set_markup( + """<span weight="heavy" size="xx-large" foreground="#FFF">%s Device</span>""" % char_type) + + def change_char_device_type(self, src): + self.update_doc(None, None, "char_type") + + chartype = self.get_char_type() + devtype = src.get_model()[src.get_active()][0] + conn = self.vm.get_connection().vmm + + self._dev = VirtualCharDevice.get_dev_instance(conn, + chartype, + devtype) + + for param_name, widget_name in char_widget_mappings.items(): + make_visible = hasattr(self._dev, param_name) + self.window.get_widget(widget_name).set_sensitive(make_visible) + + has_mode = hasattr(self._dev, "source_mode") + + if has_mode and self.window.get_widget("char-mode").get_active() == -1: + self.window.get_widget("char-mode").set_active(0) + def validate(self, page_num): if page_num == PAGE_INTRO: if self.get_config_hardware_type() == None: @@ -987,6 +1148,48 @@ return self.err.val_err(_("Host device parameter error", str(e))) + elif page_num == PAGE_CHAR: + chartype = self.get_char_type() + devbox = self.window.get_widget("char-device-type") + devtype = devbox.get_model()[devbox.get_active()][0] + conn = self.vm.get_connection().vmm + + devclass = VirtualCharDevice.get_dev_instance(conn, chartype, + devtype) + + source_path = self.window.get_widget("char-path").get_text() + source_host = self.window.get_widget("char-host").get_text() + bind_host = self.window.get_widget("char-bind-host").get_text() + source_port = self.window.get_widget("char-port").get_adjustment().value + bind_port = self.window.get_widget("char-bind-port").get_adjustment().value + + if self.window.get_widget("char-use-telnet").get_active(): + protocol = VirtualCharDevice.CHAR_PROTOCOL_TELNET + else: + protocol = VirtualCharDevice.CHAR_PROTOCOL_RAW + + value_mappings = { + "source_path" : source_path, + "source_host" : source_host, + "source_port" : source_port, + "bind_port": bind_port, + "bind_host": bind_host, + "protocol": protocol, + } + + try: + self._dev = devclass + + for param_name, val in value_mappings.items(): + if hasattr(self._dev, param_name): + setattr(self._dev, param_name, val) + + # Dump XML for sanity checking + self._dev.get_xml_config() + except Exception, e: + return self.err.val_err(_("%s device parameter error.") % + chartype.capitalize(), str(e)) + return True def populate_network_model(self, model):
_______________________________________________ et-mgmt-tools mailing list et-mgmt-tools@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/et-mgmt-tools