# # Copyright (C) 2006-2007 Red Hat, Inc. # Copyright (C) 2006 Hugh O. Brock # # 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 gtk import gtk.gdk import gtk.glade import libvirt import virtinst import os, sys import logging import dbus import re from virtManager.IPy import IP PAGE_INTRO = 0 PAGE_NAME = 1 PAGE_IPV4 = 2 PAGE_DHCP = 3 PAGE_FORWARDING = 4 PAGE_SUMMARY = 5 class vmmCreateNetwork(gobject.GObject): __gsignals__ = { "action-show-help": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, [str]), } def __init__(self, config, conn): self.__gobject_init__() self.config = config self.conn = conn self.window = gtk.glade.XML(config.get_glade_dir() + "/vmm-create-net.glade", "vmm-create-net", domain="virt-manager") self.topwin = self.window.get_widget("vmm-create-net") self.topwin.hide() self.window.signal_autoconnect({ "on_create_pages_switch_page" : self.page_changed, "on_create_cancel_clicked" : self.close, "on_vmm_create_delete_event" : self.close, "on_create_back_clicked" : self.back, "on_create_forward_clicked" : self.forward, "on_create_finish_clicked" : self.finish, "on_net_forward_toggled" : self.change_forward_type, "on_net_network_changed": self.change_network, "on_net_dhcp_start_changed": self.change_dhcp_start, "on_net_dhcp_end_changed": self.change_dhcp_end, "on_create_help_clicked": self.show_help, }) self.set_initial_state() def show(self): self.topwin.show() self.reset_state() self.topwin.present() def set_initial_state(self): notebook = self.window.get_widget("create-pages") notebook.set_show_tabs(False) #XXX I don't think I should have to go through and set a bunch of background colors # in code, but apparently I do... black = gtk.gdk.color_parse("#000") for num in range(PAGE_SUMMARY+1): name = "page" + str(num) + "-title" self.window.get_widget(name).modify_bg(gtk.STATE_NORMAL,black) # set up the list for the cd-path widget fw_list = self.window.get_widget("net-forward") # Fields are raw device path, volume label, flag indicating # whether volume is present or not, and HAL path fw_model = gtk.ListStore(str, bool, str) fw_list.set_model(fw_model) text = gtk.CellRendererText() fw_list.pack_start(text, True) fw_list.add_attribute(text, 'text', 0) fw_model.append([_("NAT to any physical device"), True, None]) for path in self.conn.list_net_device_paths(): net = self.conn.get_net_device(path) fw_model.append([_("NAT to physical device %s") % (net.get_name()), True, net.get_name()]) def reset_state(self): notebook = self.window.get_widget("create-pages") notebook.set_current_page(0) # Hide the "finish" button until the appropriate time self.window.get_widget("create-finish").hide() self.window.get_widget("create-forward").show() self.window.get_widget("create-back").set_sensitive(False) self.window.get_widget("net-name").set_text("") self.window.get_widget("net-network").set_text("192.168.100.0/24") self.window.get_widget("net-dhcp-start").set_text("") self.window.get_widget("net-dhcp-end").set_text("") self.window.get_widget("net-forward-none").set_active(True) def forward(self, ignore=None): notebook = self.window.get_widget("create-pages") if(self.validate(notebook.get_current_page()) != True): return notebook.next_page() def back(self, ignore=None): notebook = self.window.get_widget("create-pages") # do this always, since there's no "leaving a notebook page" event. self.window.get_widget("create-finish").hide() self.window.get_widget("create-forward").show() notebook.prev_page() def change_network(self, src): ip = self.get_config_ip4() green = gtk.gdk.color_parse("#c0ffc0") red = gtk.gdk.color_parse("#ffc0c0") black = gtk.gdk.color_parse("#000000") src.modify_text(gtk.STATE_NORMAL, black) if ip is None or ip.version() != 4: src.modify_base(gtk.STATE_NORMAL, red) self.window.get_widget("net-info-netmask").set_text("") self.window.get_widget("net-info-broadcast").set_text("") self.window.get_widget("net-info-gateway").set_text("") self.window.get_widget("net-info-size").set_text("") self.window.get_widget("net-info-type").set_text("") else: if ip.len() < 16 or ip.iptype() != "PRIVATE": src.modify_base(gtk.STATE_NORMAL, red) else: src.modify_base(gtk.STATE_NORMAL, green) self.window.get_widget("net-info-netmask").set_text(str(ip.netmask())) self.window.get_widget("net-info-broadcast").set_text(str(ip.broadcast())) if ip.len() <= 1: self.window.get_widget("net-info-gateway").set_text("") else: self.window.get_widget("net-info-gateway").set_text(str(ip[1])) self.window.get_widget("net-info-size").set_text(_("%d addresses") % (ip.len())) if ip.iptype() == "PUBLIC": self.window.get_widget("net-info-type").set_text(_("Public")) elif ip.iptype() == "PRIVATE": self.window.get_widget("net-info-type").set_text(_("Private")) elif ip.iptype() == "RESERVED": self.window.get_widget("net-info-type").set_text(_("Reserved")) else: self.window.get_widget("net-info-type").set_text(_("Other")) def change_dhcp_start(self, src): end = self.get_config_dhcp_start() self.change_dhcp(src, end) def change_dhcp_end(self, src): end = self.get_config_dhcp_end() self.change_dhcp(src, end) def change_dhcp(self, src, addr): ip = self.get_config_ip4() black = gtk.gdk.color_parse("#000000") src.modify_text(gtk.STATE_NORMAL, black) if addr is None or not ip.overlaps(addr): red = gtk.gdk.color_parse("#ffc0c0") src.modify_base(gtk.STATE_NORMAL, red) else: green = gtk.gdk.color_parse("#c0ffc0") src.modify_base(gtk.STATE_NORMAL, green) def change_forward_type(self, src): if self.window.get_widget("net-forward-none").get_active(): self.window.get_widget("net-forward").set_sensitive(False) else: self.window.get_widget("net-forward").set_sensitive(True) def get_config_name(self): return self.window.get_widget("net-name").get_text() def get_config_ip4(self): try: return IP(self.window.get_widget("net-network").get_text()) except: return None def get_config_dhcp_start(self): try: return IP(self.window.get_widget("net-dhcp-start").get_text()) except: return None def get_config_dhcp_end(self): try: return IP(self.window.get_widget("net-dhcp-end").get_text()) except: return None def get_config_forwarding(self): if self.window.get_widget("net-forward-none").get_active(): return [False, None] else: dev = self.window.get_widget("net-forward") model = dev.get_model() active = dev.get_active() name = model[active][2] return [True, name] def page_changed(self, notebook, page, page_number): # would you like some spaghetti with your salad, sir? if page_number == PAGE_INTRO: self.window.get_widget("create-back").set_sensitive(False) elif page_number == PAGE_NAME: name_widget = self.window.get_widget("net-name") name_widget.grab_focus() elif page_number == PAGE_IPV4: pass elif page_number == PAGE_DHCP: ip = self.get_config_ip4() start = int(ip.len() / 2) end = ip.len()-2 if self.window.get_widget("net-dhcp-start").get_text() == "": self.window.get_widget("net-dhcp-start").set_text(str(ip[start])) if self.window.get_widget("net-dhcp-end").get_text() == "": self.window.get_widget("net-dhcp-end").set_text(str(ip[end])) elif page_number == PAGE_FORWARDING: pass elif page_number == PAGE_SUMMARY: self.window.get_widget("summary-name").set_text(self.get_config_name()) ip = self.get_config_ip4() self.window.get_widget("summary-ip4-network").set_text(str(ip)) self.window.get_widget("summary-ip4-gateway").set_text(str(ip[1])) self.window.get_widget("summary-ip4-netmask").set_text(str(ip.netmask())) start = self.get_config_dhcp_start() end = self.get_config_dhcp_end() self.window.get_widget("summary-dhcp-start").set_text(str(start)) self.window.get_widget("summary-dhcp-end").set_text(str(end)) fw = self.get_config_forwarding() if fw[0]: if fw[1] is not None: self.window.get_widget("summary-forwarding").set_text(_("NAT to physical device %s") % (fw[1])) else: self.window.get_widget("summary-forwarding").set_text(_("NAT to any physical device")) else: self.window.get_widget("summary-forwarding").set_text(_("Isolated virtual network")) self.window.get_widget("create-forward").hide() self.window.get_widget("create-finish").show() def close(self, ignore1=None,ignore2=None): self.topwin.hide() return 1 def is_visible(self): if self.topwin.flags() & gtk.VISIBLE: return 1 return 0 def finish(self, ignore=None): name = self.get_config_name() ip = self.get_config_ip4() start = self.get_config_dhcp_start() end = self.get_config_dhcp_end() fw = self.get_config_forwarding() xml = "" + \ " %s\n" % name if fw[0]: if fw[1] is not None: xml += " \n" % fw[1] else: xml += " \n" xml += " \n" % (str(ip[1]), str(ip.netmask())) xml += " \n" xml += " \n" % (str(start), str(end)) xml += " \n" xml += " \n" xml += "\n" logging.debug("About to create network " + xml) self.conn.create_network(xml) self.close() def validate(self, page_num): if page_num == PAGE_NAME: name = self.window.get_widget("net-name").get_text() if len(name) > 50 or len(name) == 0: self._validation_error_box(_("Invalid Network Name"), \ _("Network name must be non-blank and less than 50 characters")) return False if re.match("^[a-zA-Z0-9_]*$", name) == None: self._validation_error_box(_("Invalid Network Name"), \ _("Network name may contain alphanumeric and '_' characters only")) return False elif page_num == PAGE_IPV4: ip = self.get_config_ip4() if ip is None: self._validation_error_box(_("Invalid Network Address"), \ _("The network address could not be understood")) return False if ip.version() != 4: self._validation_error_box(_("Invalid Network Address"), \ _("The network must be an IPv4 address")) return False if ip.len() < 16: self._validation_error_box(_("Invalid Network Address"), \ _("The network prefix must be at least /4 (16 addresses)")) return False if ip.iptype() != "PRIVATE": res = self._yes_no_box(_("Check Network Address"), \ _("The network should normally use a private IPv4 address. Use this non-private address anyway?")) if not res: return False elif page_num == PAGE_DHCP: ip = self.get_config_ip4() start = self.get_config_dhcp_start() end = self.get_config_dhcp_end() if start is None: self._validation_error_box(_("Invalid DHCP Address"), \ _("The DHCP start address could not be understood")) return False if end is None: self._validation_error_box(_("Invalid DHCP Address"), \ _("The DHCP end address could not be understood")) return False if not ip.overlaps(start): self._validation_error_box(_("Invalid DHCP Address"), \ _("The DHCP start address is not with the network %s") % (str(ip))) return False if not ip.overlaps(end): self._validation_error_box(_("Invalid DHCP Address"), \ _("The DHCP end address is not with the network %s") % (str(ip))) return False elif page_num == PAGE_FORWARDING: if self.window.get_widget("net-forward-dev").get_active(): dev = self.window.get_widget("net-forward") if dev.get_active() == -1: self._validation_error_box(_("Invalid forwarding mode"), \ _("Please select where the traffic should be forwarded")) return False # do this always, since there's no "leaving a notebook page" event. self.window.get_widget("create-back").set_sensitive(True) return True def _validation_error_box(self, text1, text2=None): message_box = gtk.MessageDialog(self.window.get_widget("vmm-create-net"), \ 0, \ gtk.MESSAGE_ERROR, \ gtk.BUTTONS_OK, \ text1) if text2 != None: message_box.format_secondary_text(text2) message_box.run() message_box.destroy() def _yes_no_box(self, text1, text2=None): message_box = gtk.MessageDialog(self.window.get_widget("vmm-create-net"), \ 0, \ gtk.MESSAGE_WARNING, \ gtk.BUTTONS_YES_NO, \ text1) if text2 != None: message_box.format_secondary_text(text2) if message_box.run()== gtk.RESPONSE_YES: res = True else: res = False message_box.destroy() return res def populate_opt_media(self, model): # get a list of optical devices with data discs in, for FV installs vollabel = {} volpath = {} # Track device add/removes so we can detect newly inserted CD media self.hal_iface.connect_to_signal("DeviceAdded", self._device_added) self.hal_iface.connect_to_signal("DeviceRemoved", self._device_removed) # Find info about all current present media for d in self.hal_iface.FindDeviceByCapability("volume"): vol = self.bus.get_object("org.freedesktop.Hal", d) volif = dbus.Interface(vol, "org.freedesktop.Hal.Device") if volif.GetPropertyBoolean("volume.is_disc") and \ volif.GetPropertyBoolean("volume.disc.has_data"): devnode = volif.GetProperty("block.device") label = volif.GetProperty("volume.label") if label == None or len(label) == 0: label = devnode vollabel[devnode] = label volpath[devnode] = d for d in self.hal_iface.FindDeviceByCapability("storage.cdrom"): dev = self.bus.get_object("org.freedesktop.Hal", d) devif = dbus.Interface(dev, "org.freedesktop.Hal.Device") devnode = devif.GetProperty("block.device") if vollabel.has_key(devnode): model.append([devnode, vollabel[devnode], True, volpath[devnode]]) else: model.append([devnode, _("No media present"), False, None]) def show_help(self, src): # help to show depends on the notebook page, yahoo page = self.window.get_widget("create-pages").get_current_page() if page == PAGE_INTRO: self.emit("action-show-help", "virt-manager-create-net-intro") elif page == PAGE_NAME: self.emit("action-show-help", "virt-manager-create-net-name") elif page == PAGE_IPV4: self.emit("action-show-help", "virt-manager-create-net-ipv4") elif page == PAGE_DHCP: self.emit("action-show-help", "virt-manager-create-net-dhcp") elif page == PAGE_FORWARDING: self.emit("action-show-help", "virt-manager-create-net-forwarding") elif page == PAGE_SUMMARY: self.emit("action-show-help", "virt-manager-create-net-sumary")