merge heads

This commit is contained in:
Daniel P. Berrange 2008-04-03 17:31:05 -04:00
commit d559c56a26
27 changed files with 6698 additions and 8217 deletions

222
ChangeLog
View File

@ -1,5 +1,225 @@
2008-03-10 "Daniel P. Berrange <berrange@redhat.com>
* NEWS, configure.ac, virt-manager.spec.in:
Update for new release
[81d87628fd7b] [tip]
* src/virtManager/create.py:
Fix disk field visibility
[2ccd1c5ccd1a]
2008-03-09 "Daniel P. Berrange <berrange@redhat.com>
* src/virtManager/connection.py, src/virtManager/create.py, src/vmm-
create.glade, virt-manager.spec.in:
Control create wizard state based on capabilities info
[215779fe46c6]
2008-03-07 "Daniel P. Berrange <berrange@redhat.com>
* src/virtManager/console.py, src/vmm-console.glade:
Enable VNC window scaling
[9ef26db66b0b]
2008-03-06 "Cole Robinson <crobinso@redhat.com>
* src/virtManager/details.py:
Have max memory consider current mem allocation when altered
directly (Adam Stokes)
[1f1ee216fcef]
* src/virtManager/details.py, src/virtManager/domain.py, src/vmm-
details.glade:
Add autostart and boot device selection support.
[4770f435ab01]
2008-03-05 "Daniel P. Berrange <berrange@redhat.com>
* src/virtManager/connection.py:
Merge heads
[0b213f9406fd]
2008-03-04 "Cole Robinson <crobinso@redhat.com>
* src/virtManager/domain.py:
Actually fix cdrom connect/disconnect error reporting
[4d2416cd81ec]
* src/virtManager/host.py:
Don't allow adding virtual networks is we are disconnected.
[35372d4a6ffa]
2008-03-03 "Cole Robinson <crobinso@redhat.com>
* src/virtManager/connection.py:
Fix typo in creds scanning.
[7fcbb49d9add]
2008-02-28 "Cole Robinson <crobinso@redhat.com>
* src/virtManager/domain.py:
Raise cdrom connect/disconnect failures.
[191b7b0e2f21]
* src/virtManager/details.py:
Raise previously ignored exceptions in widget update function.
[49bba0b0389d]
2008-02-25 "Cole Robinson <crobinso@redhat.com>
* src/virt-manager.py.in:
Add custom libvirt error handler.
The handler will ignore all libvirt _errors_, as these should be
dealt with as exceptions. This will also prevent libvirt errors from
being printed to the console when an exception is deliberately
ignored. All other libvirt messages (warnings) will be logged.
[bc4b197cdc1f]
* src/virtManager/manager.py:
Fix delete button sensitivity if selected guest is shutdown.
[dc9c723e13ff]
2008-02-22 "Cole Robinson <crobinso@redhat.com>
* src/virtManager/domain.py:
Check vm maxmem directly before we set current mem value. (Thanks
Adam Stokes)
Prevents a race when we set maxmem and current mem to new values at
the same time. Maxmem isn't updated in time, so we check the vm info
directly when setting the current mem to get the most up to date
value.
[8a68171fe280]
2008-02-21 "Cole Robinson <crobinso@redhat.com>
* src/virtManager/addhardware.py:
Only offer SCSI disk option for xen hvm: qemu doesn't like sd*
disks.
[1eeb1546b21b]
2008-02-22 "Cole Robinson <crobinso@redhat.com>
* src/virtManager/connection.py:
Reuse virtinst.util.get_max_vcpus
[f46dcf034d35]
2008-03-05 "Daniel P. Berrange <berrange@redhat.com>
* src/virtManager/connection.py, src/virtManager/console.py,
src/virtManager/create.py, src/virtManager/createnet.py,
src/virtManager/opticalhelper.py:
Fix use of DBus objects to always go via an explicit interface
[43d82ba67ffa]
2008-02-23 "Daniel P. Berrange <berrange@redhat.com>
* src/virtManager/connection.py:
Force connection to readonly if non-root and local HV uri and no
policykit (Saori Fukuta)
[d1c6390bbea9]
* Merge heads
[01c1acc34b1c]
2008-02-21 "Daniel P. Berrange <berrange@redhat.com>
* src/virtManager/connection.py:
Skip bonding if /sys/class/net/bonding_masters doesn't exist
(Shigeki Sakamoto)
[5c86a029ee3a]
2008-02-23 "Daniel P. Berrange <berrange@redhat.com>
* src/virtManager/create.py, src/vmm-create.glade:
Allow choice between local CDROM & kernel/initrd network install for
fullvirt guests
[ea6903353938]
2008-02-18 "Daniel P. Berrange <berrange@redhat.com>
* Merge heads
[07ff9bffe54d]
* src/virtManager/connection.py:
Support bonding & VLAN devices for attaching guest NICs (S.Sakamoto)
[6a155cfe437c]
2008-02-16 "Daniel P. Berrange <berrange@redhat.com>
* src/virtManager/engine.py, src/virtManager/host.py:
Wire up hosts dialog about menu entry (Henry Zhang)
[4a5c3994af32]
2008-02-14 "Cole Robinson <crobinso@redhat.com>
* src/virtManager/addhardware.py, src/virtManager/create.py:
Auto add usermode nic during create and addhardware when
appropriate: everything was in place but it was broken.
[402283b6aa0a]
2008-02-03 "Daniel P. Berrange <berrange@redhat.com>
* src/virtManager/serialcon.py:
Use pty.slave_open for Solaris portability (Henry Zhang)
[a623336112b9]
2008-01-31 "Daniel P. Berrange <berrange@redhat.com>
* po/fr.po:
Refresh French translation from Gauthier Ancelin
[7bcfffba3a97]
* README, TODO, src/virt-manager.py.in, src/virtManager/connect.py,
src/virtManager/connection.py, src/vmm-open-connection.glade, virt-
manager.spec.in:
Added support for libvirt authentication
[1892867ca5c7]
2008-01-30 "Cole Robinson <crobinso@redhat.com>
* src/vmm-modify-file-storage.glade:
Remove unused vmm-modify-file-storage
[28067b47f82c]
* src/virtManager/addhardware.py:
Fix adding virtual blktap disk to pv guest via addhardware wizard.
rhbz 430926
[90dd30034ebc]
2008-01-16 "Cole Robinson <crobinso@redhat.com>
* src/virtManager/addhardware.py, src/virtManager/create.py:
Fix disk size sensitivity: opening and closing the file chooser
would make the disk size field sensitive even if nothing was
selected.
[384724cb14e0]
* src/virtManager/addhardware.py:
Adding a bridged network device was broken, adding the display name
and not the interface name. Fix similar to cset 597. Fixes bug
392881.
[dbd25721f588]
2008-01-15 "Daniel P. Berrange <berrange@redhat.com>
* AUTHORS, src/virtManager/console.py, src/virtManager/create.py,
src/virtManager/domain.py, src/virtManager/manager.py:
Pass libvirt connection username through to VNC console instead of
hardcoding root (Soren Hansen)
[49c55daf788d]
2008-01-10 "Daniel P. Berrange <berrange@redhat.com>
* .hgtags:
Added tag RELEASE-0.5.3-1 for changeset 49f8c16f0acc
[d99010f79088]
* ChangeLog:
Refresh changelog
[49f8c16f0acc] [RELEASE-0.5.3-1]
* po/bg.po, po/bn_IN.po, po/bs.po, po/ca.po, po/cs.po, po/da.po,
po/de.po, po/es.po, po/fi.po, po/fr.po, po/gu.po, po/hi.po,
po/hr.po, po/hu.po, po/is.po, po/it.po, po/ja.po, po/kn.po,
@ -8,7 +228,7 @@
po/ru.po, po/sr.po, po/sr@Latn.po, po/sv.po, po/ta.po, po/te.po,
po/tr.po, po/uk.po, po/virt-manager.pot, po/zh_CN.po, po/zh_TW.po:
Refresh po files
[c95c688762cf] [tip]
[c95c688762cf]
* configure.ac, virt-manager.spec.in:
Update in prep for new release

26
NEWS
View File

@ -1,6 +1,32 @@
Virtual Machine Manager News
============================
Release 0.5.4
-------------
This release focuses on minor feature enhancement and bug fixes. Using
the new GTK-VNC accelerated scaling support, the guest console window
can be smoothly resized to fill the screen. The SSH username is passed
through to the VNC console when tunnelling. Adding bridged network
devices is fixed. Support for all libvirt authentication methods is
enabled including Kerberos and PolicyKit. Solaris portability fix for
the text console. Support for detecting bonding and VLAN devices for
attaching guest NICs. Allow fullvirt guests to install off kernel and
initrd as well as existing CDROM methods. Fix invocation of DBus methods
to use an interface. Allow setting of autostart flag, and changing boot
device ordering. Control the new VM wizard based on declared hypervisor
capabilities.
Release 0.5.3
-------------
This is a bug fix release. The sizing of the VNC window is fixed for
screens where the physical size is less than the guest screen size.
The 'new vm' button is switched back to its old (more obvious style/
placement). Restore of VMs is working again for local connections. A
menu for sending special key sequences to the guest is added. Lots of
other misc bug fixes
Release 0.5.2
-------------

View File

@ -1,5 +1,5 @@
AC_INIT(virt-manager.spec.in)
AM_INIT_AUTOMAKE(virt-manager, 0.5.3)
AM_INIT_AUTOMAKE(virt-manager, 0.5.4)
ALL_LINGUAS="bg bn_IN bs ca cs da de es fi fr gu hi hr hu is it ja kn ko ml mr ms nb nl or pa pl pt_BR pt ro ru sr@Latn sr sv ta te tr uk zh_CN zh_TW"
IT_PROG_INTLTOOL([0.35.0], [no-xml])

View File

@ -23,6 +23,8 @@ import os
import os.path
import sys
import libvirt
import locale
import gettext
import logging
@ -125,6 +127,13 @@ def setup_logging():
rootLogger.addHandler(fileHandler)
logging.info("Application startup")
# Register libvirt handler
def libvirt_callback(ctx, err):
if err[3] != libvirt.VIR_ERR_ERROR:
# Don't log libvirt errors: global error handler will do that
logging.warn("Non-error from libvirt: '%s'" % err[2])
libvirt.registerErrorHandler(f=libvirt_callback, ctx=None)
# Log uncaught exceptions
def exception_log(type, val, tb):
import traceback

View File

@ -51,6 +51,8 @@ PAGE_INPUT = 3
PAGE_GRAPHICS = 4
PAGE_SUMMARY = 5
KEYBOARD_DIR = "/etc/sysconfig/keyboard"
class vmmAddHardware(gobject.GObject):
__gsignals__ = {
"action-show-help": (gobject.SIGNAL_RUN_FIRST,
@ -60,10 +62,13 @@ class vmmAddHardware(gobject.GObject):
self.__gobject_init__()
self.config = config
self.vm = vm
self._net = None
self._disk = None
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,
0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
_("Unexpected Error"),
_("An unexpected error occurred"))
self.topwin.hide()
self.window.signal_autoconnect({
"on_create_pages_switch_page" : self.page_changed,
@ -80,6 +85,7 @@ class vmmAddHardware(gobject.GObject):
"on_mac_address_clicked" : self.change_macaddr_use,
"on_graphics_type_changed": self.change_graphics_type,
"on_graphics_port_auto_toggled": self.change_port_auto,
"on_graphics_keymap_toggled": self.change_keymap,
"on_create_help_clicked": self.show_help,
})
@ -206,14 +212,17 @@ class vmmAddHardware(gobject.GObject):
self.window.get_widget("graphics-address").set_active(False)
self.window.get_widget("graphics-port-auto").set_active(True)
self.window.get_widget("graphics-password").set_text("")
self.window.get_widget("graphics-keymap").set_text("")
self.window.get_widget("graphics-keymap-chk").set_active(True)
model = self.window.get_widget("hardware-type").get_model()
model.clear()
model.append(["Storage device", gtk.STOCK_HARDDISK, PAGE_DISK])
# User mode networking only allows a single card for now
if (self.vm.get_connection().get_type().lower() == "qemu" and \
os.getuid() == 0) or \
self.vm.get_connection().get_type().lower() == "xen":
# Can't use shared or virtual networking as regular user
# Can only have one usermode network device
if (os.getuid() == 0 or
(self.vm.get_connection().get_type().lower() == "qemu" and
len(self.vm.get_network_devices()) == 0)):
model.append(["Network card", gtk.STOCK_NETWORK, PAGE_NETWORK])
# Can only customize HVM guests, no Xen PV
@ -222,14 +231,20 @@ class vmmAddHardware(gobject.GObject):
model.append(["Graphics device", gtk.STOCK_SELECT_COLOR, PAGE_GRAPHICS])
def forward(self, ignore=None):
notebook = self.window.get_widget("create-pages")
if(self.validate(notebook.get_current_page()) != True):
try:
if(self.validate(notebook.get_current_page()) != True):
return
except Exception, e:
self.err.show_err(_("Uncaught error validating hardware input: %s") % str(e),
"".join(traceback.format_exc()))
return
if notebook.get_current_page() == PAGE_INTRO:
notebook.set_current_page(self.get_config_hardware_type())
hwtype = self.get_config_hardware_type()
if notebook.get_current_page() == PAGE_INTRO and \
(hwtype != PAGE_NETWORK or os.getuid() == 0):
notebook.set_current_page(hwtype)
else:
notebook.set_current_page(PAGE_SUMMARY)
self.window.get_widget("create-finish").show()
@ -240,7 +255,11 @@ class vmmAddHardware(gobject.GObject):
notebook = self.window.get_widget("create-pages")
if notebook.get_current_page() == PAGE_SUMMARY:
notebook.set_current_page(self.get_config_hardware_type())
hwtype = self.get_config_hardware_type()
if hwtype == PAGE_NETWORK and os.getuid() != 0:
notebook.set_current_page(PAGE_INTRO)
else:
notebook.set_current_page(hwtype)
self.window.get_widget("create-finish").hide()
else:
notebook.set_current_page(PAGE_INTRO)
@ -302,6 +321,32 @@ class vmmAddHardware(gobject.GObject):
pw = self.window.get_widget("graphics-password")
return pw.get_text()
def get_config_keymap(self):
if self.window.get_widget("graphics-keymap").get_property("sensitive") \
and self.window.get_widget("graphics-keymap").get_text() != "":
return self.window.get_widget("graphics-keymap").get_text()
else:
# Set keymap to same as hosts
import keytable
keymap = None
try:
f = open(KEYBOARD_DIR, "r")
except IOError, e:
logging.debug('addhardware: Could not open "/etc/sysconfig/keyboard" ' + str(e))
else:
while 1:
s = f.readline()
if s == "":
break
if re.search("KEYTABLE", s) != None:
kt = s.split('"')[1]
if keytable.keytable.has_key(kt):
keymap = keytable.keytable[kt]
else:
logging.debug("addhardware: Didn't find keymap '%s' in keytable!" % kt)
f.close
return keymap
def get_config_network(self):
if os.getuid() != 0:
return ["user"]
@ -374,9 +419,9 @@ class vmmAddHardware(gobject.GObject):
self.window.get_widget("summary-graphics").show()
graphics = self.get_config_graphics()
if graphics == "vnc":
self.window.get_widget("summary-graphics-type").set_text("VNC server")
self.window.get_widget("summary-graphics-type").set_text(_("VNC server"))
else:
self.window.get_widget("summary-graphics-type").set_text("Local SDL window")
self.window.get_widget("summary-graphics-type").set_text(_("Local SDL window"))
if graphics == "vnc":
self.window.get_widget("summary-graphics-address").set_text(self.get_config_vnc_address())
if self.get_config_vnc_port() == -1:
@ -386,11 +431,17 @@ class vmmAddHardware(gobject.GObject):
if self.get_config_vnc_password() is not None and self.get_config_vnc_password() != "":
self.window.get_widget("summary-graphics-password").set_text(_("Yes"))
else:
self.window.get_widget("summary-graphics-password").set_text(_("Yes"))
self.window.get_widget("summary-graphics-password").set_text(_("No"))
if self.get_config_keymap() != "":
self.window.get_widget("summary-graphics-keymap").set_text(str(self.get_config_keymap()))
else:
self.window.get_widget("summary-graphics-keymap").set_text(_("Same as host"))
else:
self.window.get_widget("summary-graphics-address").set_text(_("N/A"))
self.window.get_widget("summary-graphics-port").set_text(_("N/A"))
self.window.get_widget("summary-graphics-password").set_text(_("N/A"))
self.window.get_widget("summary-graphics-keymap").set_text(_("N/A"))
def close(self, ignore1=None,ignore2=None):
self.topwin.hide()
@ -418,12 +469,7 @@ class vmmAddHardware(gobject.GObject):
self.add_graphics()
if self.install_error is not None:
dg = vmmErrorDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
self.install_error,
self.install_details)
dg.run()
dg.hide()
dg.destroy()
self.err.show_err(self.install_error, self.install_details)
self.topwin.set_sensitive(True)
self.topwin.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.TOP_LEFT_ARROW))
# Don't close becase we allow user to go back in wizard & correct
@ -436,8 +482,10 @@ class vmmAddHardware(gobject.GObject):
self.close()
def add_network(self):
self._net.setup(self.vm.get_connection().vmm)
self.add_device(self._net.get_xml_config())
if self._dev is None and os.getuid() != 0:
self._dev = virtinst.VirtualNetworkInterface(type="user")
self._dev.setup(self.vm.get_connection().vmm)
self.add_device(self._dev.get_xml_config())
def add_input(self):
input = self.get_config_input()
@ -445,24 +493,7 @@ class vmmAddHardware(gobject.GObject):
self.add_device(xml)
def add_graphics(self):
graphics = self.get_config_graphics()
if graphics == "vnc":
port = self.get_config_vnc_port()
pw = self.get_config_vnc_password()
addr = self.get_config_vnc_address()
if addr is None or addr == "":
if pw is None or pw == "":
xml = "<graphics type='vnc' port='%d'/>" % (port,)
else:
xml = "<graphics type='vnc' port='%d' passwd='%s'/>" % (port,pw)
else:
if pw is None or pw == "":
xml = "<graphics type='vnc' listen='%s' port='%d'/>" % (addr,port)
else:
xml = "<graphics type='vnc' listen='%s' port='%d' passwd='%s'/>" % (addr,port,pw)
else:
xml = "<graphics type='sdl'/>"
self.add_device(xml)
self.add_device(self._dev.get_xml_config())
def add_storage(self):
node, maxnode, device = self.get_config_disk_target()
@ -498,14 +529,14 @@ class vmmAddHardware(gobject.GObject):
self.install_details = details
return
progWin = vmmAsyncJob(self.config, self.do_file_allocate, [self._disk],
progWin = vmmAsyncJob(self.config, self.do_file_allocate, [self._dev],
title=_("Creating Storage File"),
text=_("Allocation of disk storage may take a few minutes " + \
"to complete."))
progWin.run()
if self.install_error == None:
self.add_device(self._disk.get_xml_config(node))
self.add_device(self._dev.get_xml_config(node))
def add_device(self, xml):
logging.debug("Adding device " + xml)
@ -623,12 +654,15 @@ class vmmAddHardware(gobject.GObject):
self.window.get_widget("graphics-port-auto").set_sensitive(True)
self.window.get_widget("graphics-address").set_sensitive(True)
self.window.get_widget("graphics-password").set_sensitive(True)
self.window.get_widget("graphics-keymap-chk").set_sensitive(True)
self.change_port_auto()
else:
self.window.get_widget("graphics-port").set_sensitive(False)
self.window.get_widget("graphics-port-auto").set_sensitive(False)
self.window.get_widget("graphics-address").set_sensitive(False)
self.window.get_widget("graphics-password").set_sensitive(False)
self.window.get_widget("graphics-keymap-chk").set_sensitive(False)
self.window.get_widget("graphics-keymap").set_sensitive(False)
def change_port_auto(self,ignore=None):
if self.window.get_widget("graphics-port-auto").get_active():
@ -636,23 +670,27 @@ class vmmAddHardware(gobject.GObject):
else:
self.window.get_widget("graphics-port").set_sensitive(True)
def change_keymap(self, ignore=None):
if self.window.get_widget("graphics-keymap-chk").get_active():
self.window.get_widget("graphics-keymap").set_sensitive(False)
else:
self.window.get_widget("graphics-keymap").set_sensitive(True)
def validate(self, page_num):
if page_num == PAGE_INTRO:
if self.get_config_hardware_type() == None:
self._validation_error_box(_("Hardware Type Required"), \
_("You must specify what type of hardware to add"))
return False
return self.err.val_err(_("Hardware Type Required"), \
_("You must specify what type of hardware to add"))
self._dev = None
elif page_num == PAGE_DISK:
path = self.get_config_disk_image()
if path == None or len(path) == 0:
self._validation_error_box(_("Storage Path Required"), \
_("You must specify a partition or a file for disk storage."))
return False
return self.err.val_err(_("Storage Path Required"), \
_("You must specify a partition or a file for disk storage."))
if self.window.get_widget("target-device").get_active() == -1:
self._validation_error_box(_("Target Device Required"),
_("You must select a target device for the disk"))
return False
return self.err.val_err(_("Target Device Required"),
_("You must select a target device for the disk"))
node, nodemax, device = self.get_config_disk_target()
if self.window.get_widget("storage-partition").get_active():
@ -665,8 +703,8 @@ class vmmAddHardware(gobject.GObject):
if not os.path.exists(path):
dir = os.path.dirname(os.path.abspath(path))
if not os.path.exists(dir):
self._validation_error_box(_("Storage Path Does not exist"),
_("The directory %s containing the disk image does not exist") % dir)
self.err.val_err(_("Storage Path Does not exist"),
_("The directory %s containing the disk image does not exist") % dir)
return False
else:
vfs = os.statvfs(dir)
@ -674,14 +712,12 @@ class vmmAddHardware(gobject.GObject):
need = size * 1024 * 1024
if need > avail:
if self.is_sparse_file():
res = self._yes_no_box(_("Not Enough Free Space"),
_("The filesystem will not have enough free space to fully allocate the sparse file when the guest is running. Use this path anyway?"))
if not res:
if not self.err.yes_no(_("Not Enough Free Space"),
_("The filesystem will not have enough free space to fully allocate the sparse file when the guest is running. Use this path anyway?")):
return False
else:
self._validation_error_box(_("Not Enough Free Space"),
_("There is not enough free space to create the disk"))
return False
return self.err.val_err(_("Not Enough Free Space"),
_("There is not enough free space to create the disk"))
# Build disk object
filesize = self.get_config_disk_size()
@ -692,120 +728,86 @@ class vmmAddHardware(gobject.GObject):
readonly=True
try:
self._disk = virtinst.VirtualDisk(self.get_config_disk_image(),
self._dev = virtinst.VirtualDisk(self.get_config_disk_image(),
filesize,
type = type,
sparse = self.is_sparse_file(),
readOnly=readonly,
device=device)
if self._disk.type == virtinst.VirtualDisk.TYPE_FILE and \
if self._dev.type == virtinst.VirtualDisk.TYPE_FILE and \
not self.vm.is_hvm() and virtinst.util.is_blktap_capable():
self._disk.driver_name = virtinst.VirtualDisk.DRIVER_TAP
self._dev.driver_name = virtinst.VirtualDisk.DRIVER_TAP
except ValueError, e:
self._validation_error_box(_("Invalid Storage Parameters"), \
str(e))
return False
if self._disk.is_conflict_disk(self.vm.get_connection().vmm) is True:
res = self._yes_no_box(_('Disk "%s" is already in use by another guest!' % self._disk), \
_("Do you really want to use the disk ?"))
return self.err.val_err(_("Invalid Storage Parameters"), str(e))
if self._dev.is_conflict_disk(self.vm.get_connection().vmm) is True:
res = self.err.yes_no(_('Disk "%s" is already in use by another guest!' % self._dev), \
_("Do you really want to use the disk ?"))
return res
elif page_num == PAGE_NETWORK:
net = self.get_config_network()
if self.window.get_widget("net-type-network").get_active():
if self.window.get_widget("net-network").get_active() == -1:
self._validation_error_box(_("Virtual Network Required"),
_("You must select one of the virtual networks"))
return False
return self.err.val_err(_("Virtual Network Required"),
_("You must select one of the virtual networks"))
else:
if self.window.get_widget("net-device").get_active() == -1:
self._validation_error_box(_("Physical Device Required"),
_("You must select one of the physical devices"))
return False
return self.err.val_err(_("Physical Device Required"),
_("You must select one of the physical devices"))
mac = self.get_config_macaddr()
if self.window.get_widget("mac-address").get_active():
if mac is None or len(mac) == 0:
self._validation_error_box(_("Invalid MAC address"), \
_("No MAC address was entered. Please enter a valid MAC address."))
return False
return self.err.val_err(_("Invalid MAC address"), \
_("No MAC address was entered. Please enter a valid MAC address."))
try:
self._net = virtinst.VirtualNetworkInterface(macaddr=mac)
self._dev = virtinst.VirtualNetworkInterface(macaddr=mac)
except ValueError, e:
self._validation_error_box(_("Invalid MAC address"), \
str(e))
return False
return self.err.val_err(_("Invalid MAC address"), str(e))
hostdevs = virtinst.util.get_host_network_devices()
for hostdev in hostdevs:
if mac.lower() == hostdev[4]:
return self._validation_error_box(_('MAC address "%s" is already in use by the host') % mac, \
_("Please enter a different MAC address or select no fixed MAC address"))
vms = []
for domains in self.vm.get_connection().vms.values():
vms.append(domains.vm)
# get inactive Domains
inactive_vm = []
names = self.vm.get_connection().vmm.listDefinedDomains()
for name in names:
vm = self.vm.get_connection().vmm.lookupByName(name)
inactive_vm.append(vm)
if (self._net.countMACaddr(vms) - self._net.countMACaddr(inactive_vm)) > 0:
return self._validation_error_box(_('MAC address "%s" is already in use by an active guest') % mac, \
_("Please enter a different MAC address or select no fixed MAC address"))
elif self._net.countMACaddr(inactive_vm) > 0:
return self._yes_no_box(_('MAC address "%s" is already in use by another inactive guest!') % mac, \
_("Do you really want to use the MAC address ?"))
try:
if net[0] == "bridge":
self._net = virtinst.VirtualNetworkInterface(macaddr=mac,
self._dev = virtinst.VirtualNetworkInterface(macaddr=mac,
type=net[0],
bridge=net[1])
elif net[0] == "network":
self._net = virtinst.VirtualNetworkInterface(macaddr=mac,
self._dev = virtinst.VirtualNetworkInterface(macaddr=mac,
type=net[0],
network=net[1])
else:
raise ValueError, _("Unsupported networking type") + net[0]
except ValueError, e:
self._validation_error_box(_("Invalid Network Parameter"), \
str(e))
return False
return self.err.val_err(_("Invalid Network Parameter"), \
str(e))
conflict = self._dev.is_conflict_net(self.vm.get_connection().vmm)
if conflict[0]:
return self.err.val_err(_("Mac address collision"),\
conflict[1])
elif conflict[1] is not None:
return self.err.yes_no(_("Mac address collision"),\
conflict[1] + " " + _("Are you sure you want to use this address?"))
elif page_num == PAGE_GRAPHICS:
graphics = self.get_config_graphics()
if graphics == "vnc":
type = virtinst.VirtualGraphics.TYPE_VNC
else:
type = virtinst.VirtualGraphics.TYPE_SDL
self._dev = virtinst.VirtualGraphics(type=type)
try:
self._dev.port = self.get_config_vnc_port()
self._dev.passwd = self.get_config_vnc_password()
self._dev.listen = self.get_config_vnc_address()
self._dev.keymap = self.get_config_keymap()
except ValueError, e:
self.err.val_err(_("Graphics device parameter error"), str(e))
return True
def _validation_error_box(self, text1, text2=None):
message_box = gtk.MessageDialog(self.window.get_widget("vmm-add-hardware"), \
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-add-hardware"), \
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_network_model(self, model):
model.clear()
for uuid in self.vm.get_connection().list_net_uuids():
@ -830,8 +832,8 @@ class vmmAddHardware(gobject.GObject):
model.append(["hd", 4, virtinst.VirtualDisk.DEVICE_DISK, gtk.STOCK_HARDDISK, "IDE disk"])
model.append(["hd", 4, virtinst.VirtualDisk.DEVICE_CDROM, gtk.STOCK_CDROM, "IDE cdrom"])
model.append(["fd", 2, virtinst.VirtualDisk.DEVICE_FLOPPY, gtk.STOCK_FLOPPY, "Floppy disk"])
model.append(["sd", 7, virtinst.VirtualDisk.DEVICE_DISK, gtk.STOCK_HARDDISK, "SCSI disk"])
if self.vm.get_connection().get_type().lower() == "xen":
model.append(["sd", 7, virtinst.VirtualDisk.DEVICE_DISK, gtk.STOCK_HARDDISK, "SCSI disk"])
model.append(["xvd", 26, virtinst.VirtualDisk.DEVICE_DISK, gtk.STOCK_HARDDISK, "Virtual disk"])
#model.append(["usb", virtinst.VirtualDisk.DEVICE_DISK, gtk.STOCK_HARDDISK, "USB disk"])
else:

View File

@ -22,6 +22,7 @@ import gobject
import logging
import virtinst
from virtManager.opticalhelper import vmmOpticalDriveHelper
from virtManager.error import vmmErrorDialog
class vmmChooseCD(gobject.GObject):
__gsignals__ = {"cdrom-chosen": (gobject.SIGNAL_RUN_FIRST,
@ -31,6 +32,10 @@ class vmmChooseCD(gobject.GObject):
def __init__(self, config, target):
self.__gobject_init__()
self.window = gtk.glade.XML(config.get_glade_dir() + "/vmm-choose-cd.glade", "vmm-choose-cd", domain="virt-manager")
self.err = vmmErrorDialog(self.window.get_widget("vmm-choose-cd"),
0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
_("Unexpected Error"),
_("An unexpected error occurred"))
self.config = config
self.window.get_widget("vmm-choose-cd").hide()
self.target = target
@ -87,16 +92,13 @@ class vmmChooseCD(gobject.GObject):
path = model.get_value(cd.get_active_iter(), 0)
if path == "" or path == None:
self._validation_error_box(_("Invalid Media Path"), \
_("A media path must be specified."))
return
return self.err.val_err(_("Invalid Media Path"), \
_("A media path must be specified."))
try:
disk = virtinst.VirtualDisk(path=path, device=virtinst.VirtualDisk.DEVICE_CDROM, readOnly=True)
except Exception, e:
self._validation_error_box(_("Invalid Media Path"), str(e))
return
return self.err.val_err(_("Invalid Media Path"), str(e))
self.emit("cdrom-chosen", disk.type, disk.path, self.target)
self.close()
@ -141,16 +143,5 @@ class vmmChooseCD(gobject.GObject):
else:
fcdialog.destroy()
return None
def _validation_error_box(self, text1, text2=None):
message_box = gtk.MessageDialog(self.window.get_widget("vmm-choosecd"), \
0, \
gtk.MESSAGE_ERROR, \
gtk.BUTTONS_OK, \
text1)
if text2 != None:
message_box.format_secondary_text(text2)
message_box.run()
message_box.destroy()
gobject.type_register(vmmChooseCD)

View File

@ -311,7 +311,7 @@ class vmmConfig:
# the url isn't already in the list, so add it
uris.insert(len(uris) - 1,uri)
self.conf.set_list(self.conf_dir + "/connections/uris", gconf.VALUE_STRING, uris)
def remove_connection(self, uri):
uris = self.conf.get_list(self.conf_dir + "/connections/uris", gconf.VALUE_STRING)
if uris == None:
@ -319,6 +319,31 @@ class vmmConfig:
if uris.count(uri) != 0:
uris.remove(uri)
self.conf.set_list(self.conf_dir + "/connections/uris", gconf.VALUE_STRING, uris)
if self.get_conn_autoconnect(uri):
uris = self.conf.get_list(self.conf_dir + \
"/connections/autoconnect",\
gconf.VALUE_STRING)
uris.remove(uri)
self.conf.set_list(self.conf_dir + "/connections/autoconnect", \
gconf.VALUE_STRING, uris)
def get_conn_autoconnect(self, uri):
uris = self.conf.get_list(self.conf_dir + "/connections/autoconnect",\
gconf.VALUE_STRING)
return ((uris is not None) and (uri in uris))
def toggle_conn_autoconnect(self, uri):
uris = self.conf.get_list(self.conf_dir + "/connections/autoconnect",\
gconf.VALUE_STRING)
if uris is None:
uris = []
if uri in uris:
uris.remove(uri)
else:
uris.append(uri)
self.conf.set_list(self.conf_dir + "/connections/autoconnect", \
gconf.VALUE_STRING, uris)
def get_media_urls(self):
return self.conf.get_list(self.conf_dir + "/urls/media", gconf.VALUE_STRING)

View File

@ -22,6 +22,7 @@ import gobject
import libvirt
import logging
import os, sys
import glob
import traceback
from time import time
import logging
@ -29,11 +30,15 @@ from socket import gethostbyaddr, gethostname
import dbus
import threading
import gtk
import string
import virtinst
from virtManager.domain import vmmDomain
from virtManager.network import vmmNetwork
from virtManager.netdev import vmmNetDevice
LIBVIRT_POLICY_FILE = "/usr/share/PolicyKit/policy/libvirtd.policy"
def get_local_hostname():
try:
(host, aliases, ipaddrs) = gethostbyaddr(gethostname())
@ -111,7 +116,6 @@ class vmmConnection(gobject.GObject):
def __init__(self, config, uri, readOnly = None):
self.__gobject_init__()
self.config = config
self.readOnly = readOnly
self.connectThread = None
self.connectError = None
@ -119,6 +123,11 @@ class vmmConnection(gobject.GObject):
if self.uri is None or self.uri.lower() == "xen":
self.uri = "xen:///"
self.readOnly = readOnly
if not self.is_remote() and os.getuid() != 0 and self.uri != "qemu:///session":
if not os.path.exists(LIBVIRT_POLICY_FILE):
self.readOnly = True
self.state = self.STATE_DISCONNECTED
self.vmm = None
@ -133,10 +142,9 @@ class vmmConnection(gobject.GObject):
# Resource utilization statistics
self.record = []
self.hostinfo = None
self.autoconnect = self.config.get_conn_autoconnect(self.get_uri())
self.detect_network_devices()
def detect_network_devices(self):
# Probe for network devices
try:
# Get a connection to the SYSTEM bus
self.bus = dbus.SystemBus()
@ -145,12 +153,24 @@ class vmmConnection(gobject.GObject):
self.hal_iface = dbus.Interface(hal_object, 'org.freedesktop.Hal.Manager')
# 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)
self.hal_iface.connect_to_signal("DeviceAdded", self._net_phys_device_added)
self.hal_iface.connect_to_signal("DeviceRemoved", self._net_phys_device_removed)
# Find info about all current present media
# find all bonding master devices and register them
# XXX bonding stuff is linux specific
bondMasters = self._net_get_bonding_masters()
if bondMasters is not None:
for bond in bondMasters:
sysfspath = "/sys/class/net/" + bond
mac = self._net_get_mac_address(bond, sysfspath)
self._net_device_added(bond, mac, sysfspath)
# Add any associated VLANs
self._net_tag_device_added(bond, sysfspath)
# Find info about all current present physical net devices
# This is OS portable...
for path in self.hal_iface.FindDeviceByCapability("net"):
self._device_added(path)
self._net_phys_device_added(path)
except:
(type, value, stacktrace) = sys.exc_info ()
logging.error("Unable to connect to HAL to list network devices: '%s'" + \
@ -159,56 +179,75 @@ class vmmConnection(gobject.GObject):
self.bus = None
self.hal_iface = None
def _device_added(self, path):
def _net_phys_device_added(self, path):
logging.debug("Got physical device %s" % path)
obj = self.bus.get_object("org.freedesktop.Hal", path)
if obj.QueryCapability("net"):
name = obj.GetPropertyString("net.interface")
mac = obj.GetPropertyString("net.address")
objif = dbus.Interface(obj, "org.freedesktop.Hal.Device")
if objif.QueryCapability("net"):
name = objif.GetPropertyString("net.interface")
# XXX ...but this is Linux specific again - patches welcomed
#sysfspath = objif.GetPropertyString("linux.sysfs_path")
# XXX hal gives back paths to /sys/devices/pci0000:00/0000:00:1e.0/0000:01:00.0/net/eth0
# which doesnt' work so well - we want this:
sysfspath = "/sys/class/net/" + name
# Now magic to determine if the device is part of a bridge
shared = False
bridge = None
try:
# XXX Linux specific - needs porting for other OS - patches
# welcomed...
sysfspath = obj.GetPropertyString("linux.sysfs_path")
# If running a device in bridged mode, there's a reasonable
# chance that the actual ethernet device has been renamed to
# something else. ethN -> pethN
psysfspath = sysfspath[0:len(sysfspath)-len(name)] + "p" + name
if os.path.exists(psysfspath):
logging.debug("Device %s named to p%s" % (name, name))
name = "p" + name
sysfspath = psysfspath
# If running a device in bridged mode, there's a reasonable
# chance that the actual ethernet device has been renamed to
# something else. ethN -> pethN
psysfspath = sysfspath[0:len(sysfspath)-len(name)] + "p" + name
if os.path.exists(psysfspath):
name = "p" + name
sysfspath = psysfspath
# Ignore devices that are slaves of a bond
if self._net_is_bonding_slave(name, sysfspath):
logging.debug("Skipping device %s in bonding slave" % name)
return
brportpath = os.path.join(sysfspath, "brport")
mac = objif.GetPropertyString("net.address")
if os.path.exists(brportpath):
shared = True
brlinkpath = os.path.join(brportpath, "bridge")
dest = os.readlink(brlinkpath)
(head,tail) = os.path.split(dest)
bridge = tail
except:
(type, value, stacktrace) = sys.exc_info ()
logging.error("Unable to determine if device is shared:" +
str(type) + " " + str(value) + "\n" + \
traceback.format_exc (stacktrace))
# Add the main NIC
self._net_device_added(name, mac, sysfspath)
if self.netdevs.has_key(path):
currDev = self.netdevs[path]
if currDev.get_info() == (name, mac, shared, bridge):
return
del self.netdevs[path]
dev = vmmNetDevice(self.config, self, name, mac, shared, bridge)
self.netdevs[path] = dev
self.emit("netdev-added", dev.get_name())
# Add any associated VLANs
self._net_tag_device_added(name, sysfspath)
def _device_removed(self, path):
if self.netdevs.has_key(path):
dev = self.netdevs[path]
def _net_tag_device_added(self, name, sysfspath):
logging.debug("Checking for VLANs on %s" % sysfspath)
for vlanpath in glob.glob(sysfspath + ".*"):
if os.path.exists(vlanpath):
logging.debug("Process VLAN %s" % vlanpath)
vlanmac = self._net_get_mac_address(name, vlanpath)
(ignore,vlanname) = os.path.split(vlanpath)
self._net_device_added(vlanname, vlanmac, vlanpath)
def _net_device_added(self, name, mac, sysfspath):
# Race conditions mean we can occassionally see device twice
if self.netdevs.has_key(name):
return
bridge = self._net_get_bridge_owner(name, sysfspath)
shared = False
if bridge is not None:
shared = True
logging.debug("Adding net device %s %s %s bridge %s" % (name, mac, sysfspath, str(bridge)))
dev = vmmNetDevice(self.config, self, name, mac, shared, bridge)
self.netdevs[name] = dev
self.emit("netdev-added", dev.get_name())
def _net_phys_device_removed(self, path):
obj = self.bus.get_object("org.freedesktop.Hal", path)
objif = dbus.Interface(obj, "org.freedesktop.Hal.Device")
if objif.QueryCapability("net"):
name = objif.GetPropertyString("net.interface")
if self.netdevs.has_key(name):
dev = self.netdevs[name]
self.emit("netdev-removed", dev.get_name())
del self.netdevs[path]
del self.netdevs[name]
def is_read_only(self):
return self.readOnly
@ -261,6 +300,9 @@ class vmmConnection(gobject.GObject):
pass
return "xen"
def get_capabilities(self):
return virtinst.CapabilitiesParser.parse(self.vmm.getCapabilities())
def is_remote(self):
try:
(scheme, username, netloc, path, query, fragment) = uri_split(self.uri)
@ -356,7 +398,7 @@ class vmmConnection(gobject.GObject):
return self._do_creds_polkit(creds[0][1])
for cred in creds:
if creds[0] == libvirt.VIR_CRED_EXTERNAL:
if cred[0] == libvirt.VIR_CRED_EXTERNAL:
return -1
return self._do_creds_dialog(creds)
@ -460,12 +502,19 @@ class vmmConnection(gobject.GObject):
def get_host_info(self):
return self.hostinfo
def get_max_vcpus(self):
try:
return self.vmm.getMaxVcpus(self.get_type())
except Exception, e:
logging.debug('Unable to get max vcpu')
return 32;
def get_max_vcpus(self, type=None):
return virtinst.util.get_max_vcpus(self.vmm, type)
def get_autoconnect(self):
# Use a local variable to cache autoconnect so we don't repeatedly
# have to poll gconf
return self.autoconnect
def toggle_autoconnect(self):
if self.is_remote():
return
self.config.toggle_conn_autoconnect(self.get_uri())
self.autoconnect = (not self.autoconnect)
def connect(self, name, callback):
handle_id = gobject.GObject.connect(self, name, callback)
@ -546,38 +595,38 @@ class vmmConnection(gobject.GObject):
except:
logging.warn("Unable to list inactive networks")
# check of net devices
newPaths = []
if self.hal_iface:
newPaths = self.hal_iface.FindDeviceByCapability("net")
for newPath in newPaths:
self._device_added(newPath)
for name in newActiveNetNames:
net = self.vmm.networkLookupByName(name)
uuid = self.uuidstr(net.UUID())
if not oldNets.has_key(uuid):
self.nets[uuid] = vmmNetwork(self.config, self, net, uuid, True)
newNets[uuid] = self.nets[uuid]
startNets[uuid] = newNets[uuid]
else:
self.nets[uuid] = oldNets[uuid]
if not self.nets[uuid].is_active():
self.nets[uuid].set_active(True)
startNets[uuid] = self.nets[uuid]
del oldNets[uuid]
try:
net = self.vmm.networkLookupByName(name)
uuid = self.uuidstr(net.UUID())
if not oldNets.has_key(uuid):
self.nets[uuid] = vmmNetwork(self.config, self, net, uuid, True)
newNets[uuid] = self.nets[uuid]
startNets[uuid] = newNets[uuid]
else:
self.nets[uuid] = oldNets[uuid]
if not self.nets[uuid].is_active():
self.nets[uuid].set_active(True)
startNets[uuid] = self.nets[uuid]
del oldNets[uuid]
except libvirt.libvirtError:
logging.warn("Couldn't fetch active network name '" + name + "'")
for name in newInactiveNetNames:
net = self.vmm.networkLookupByName(name)
uuid = self.uuidstr(net.UUID())
if not oldNets.has_key(uuid):
self.nets[uuid] = vmmNetwork(self.config, self, net, uuid, False)
newNets[uuid] = self.nets[uuid]
else:
self.nets[uuid] = oldNets[uuid]
if self.nets[uuid].is_active():
self.nets[uuid].set_active(False)
stopNets[uuid] = self.nets[uuid]
del oldNets[uuid]
try:
net = self.vmm.networkLookupByName(name)
uuid = self.uuidstr(net.UUID())
if not oldNets.has_key(uuid):
self.nets[uuid] = vmmNetwork(self.config, self, net, uuid, False)
newNets[uuid] = self.nets[uuid]
else:
self.nets[uuid] = oldNets[uuid]
if self.nets[uuid].is_active():
self.nets[uuid].set_active(False)
stopNets[uuid] = self.nets[uuid]
del oldNets[uuid]
except libvirt.libvirtError:
logging.warn("Couldn't fetch inactive network name '" + name + "'")
oldActiveIDs = {}
oldInactiveNames = {}
@ -596,7 +645,12 @@ class vmmConnection(gobject.GObject):
# Now we can clear the list of actives from the last time through
self.activeUUIDs = []
newActiveIDs = self.vmm.listDomainsID()
newActiveIDs = []
try:
newActiveIDs = self.vmm.listDomainsID()
except:
logging.warn("Unable to list active domains")
newInactiveNames = []
try:
newInactiveNames = self.vmm.listDefinedDomains()
@ -626,13 +680,16 @@ class vmmConnection(gobject.GObject):
# May be a new VM, we have no choice but
# to create the wrapper so we can see
# if its a previously inactive domain.
vm = self.vmm.lookupByID(id)
uuid = self.uuidstr(vm.UUID())
maybeNewUUIDs[uuid] = vm
# also add the new or newly started VM to the "started" list
startedUUIDs.append(uuid)
#print "Maybe new active " + str(maybeNewUUIDs[uuid].get_name()) + " " + uuid
self.activeUUIDs.append(uuid)
try:
vm = self.vmm.lookupByID(id)
uuid = self.uuidstr(vm.UUID())
maybeNewUUIDs[uuid] = vm
# also add the new or newly started VM to the "started" list
startedUUIDs.append(uuid)
#print "Maybe new active " + str(maybeNewUUIDs[uuid].get_name()) + " " + uuid
self.activeUUIDs.append(uuid)
except libvirt.libvirtError:
logging.debug("Couldn't fetch domain id " + str(id) + "; it probably went away")
# Filter out inactive domains which haven't changed
if newInactiveNames != None:
@ -711,7 +768,10 @@ class vmmConnection(gobject.GObject):
# Finally, we sample each domain
now = time()
self.hostinfo = self.vmm.getInfo()
try:
self.hostinfo = self.vmm.getInfo()
except:
logging.warn("Unable to get host information")
updateVMs = self.vms
if noStatsUpdate:
@ -847,5 +907,51 @@ class vmmConnection(gobject.GObject):
else:
return _("Unknown")
def _net_get_bridge_owner(self, name, sysfspath):
# Now magic to determine if the device is part of a bridge
brportpath = os.path.join(sysfspath, "brport")
try:
if os.path.exists(brportpath):
brlinkpath = os.path.join(brportpath, "bridge")
dest = os.readlink(brlinkpath)
(ignore,bridge) = os.path.split(dest)
return bridge
except:
(type, value, stacktrace) = sys.exc_info ()
logging.error("Unable to determine if device is shared:" +
str(type) + " " + str(value) + "\n" + \
traceback.format_exc (stacktrace))
return None
def _net_get_mac_address(self, name, sysfspath):
mac = None
addrpath = sysfspath + "/address"
if os.path.exists(addrpath):
df = open(addrpath, 'r')
mac = df.readline()
df.close()
return mac.strip(" \n\t")
def _net_get_bonding_masters(self):
masters = []
if os.path.exists("/sys/class/net/bonding_masters"):
f = open("/sys/class/net/bonding_masters")
while True:
rline = f.readline()
if not rline: break
if rline == "\x00": continue
rline = rline.strip("\n\t")
masters = rline[:-1].split(' ')
return masters
else:
return None
def _net_is_bonding_slave(self, name, sysfspath):
masterpath = sysfspath + "/master"
if os.path.exists(masterpath):
return True
return False
gobject.type_register(vmmConnection)

View File

@ -30,8 +30,6 @@ import gtkvnc
import os
import socket
from virtManager.error import vmmErrorDialog
PAGE_UNAVAILABLE = 0
PAGE_SCREENSHOT = 1
PAGE_AUTHENTICATE = 2
@ -75,16 +73,17 @@ class vmmConsole(gobject.GObject):
self.gtk_settings_accel = None
self.vncViewer = gtkvnc.Display()
self.window.get_widget("console-vnc-align").add(self.vncViewer)
self.window.get_widget("console-vnc-viewport").add(self.vncViewer)
self.vncViewer.realize()
self.vncTunnel = None
if self.config.get_console_keygrab() == 2:
self.vncViewer.set_keyboard_grab(True)
self.vncViewer.set_pointer_grab(True)
else:
self.vncViewer.set_keyboard_grab(False)
self.vncViewer.set_pointer_grab(False)
self.vncViewer.set_pointer_local(True)
self.vncViewer.set_pointer_grab(True)
if not topwin.is_composited():
self.vncViewer.set_scaling(True)
self.window.get_widget("menu-view-scale-display").set_active(True)
self.vncViewer.connect("vnc-pointer-grab", self.notify_grabbed)
self.vncViewer.connect("vnc-pointer-ungrab", self.notify_ungrabbed)
@ -147,6 +146,8 @@ class vmmConsole(gobject.GObject):
"on_menu_send_caf7_activate": self.send_key,
"on_menu_send_caf8_activate": self.send_key,
"on_menu_send_printscreen_activate": self.send_key,
"on_menu_view_scale_display_activate": self.scale_display,
})
self.vm.connect("status-changed", self.update_widget_states)
@ -165,8 +166,10 @@ class vmmConsole(gobject.GObject):
# widget it still seems to show scrollbars. So we do evil stuff here
def _force_resize(self, src, size):
w,h = src.get_size_request()
if w == -1 or h == -1:
return
self.window.get_widget("console-screenshot").set_size_request(w, h)
self.window.get_widget("console-vnc-scroll").set_size_request(w, h)
topw,toph = self.window.get_widget("vmm-console").size_request()
padx = topw-w
@ -178,6 +181,7 @@ class vmmConsole(gobject.GObject):
maxh = rooth - 100 - pady
self.window.get_widget("console-vnc-viewport").set_size_request(w, h)
self.window.get_widget("console-screenshot-viewport").set_size_request(w, h)
self.window.get_widget("console-vnc-scroll").set_size_request(w, h)
if w > maxw or h > maxh:
self.window.get_widget("console-vnc-scroll").set_policy(gtk.POLICY_ALWAYS, gtk.POLICY_ALWAYS)
self.window.get_widget("console-screenshot-scroll").set_policy(gtk.POLICY_ALWAYS, gtk.POLICY_ALWAYS)
@ -185,26 +189,10 @@ class vmmConsole(gobject.GObject):
self.window.get_widget("console-vnc-scroll").set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER)
self.window.get_widget("console-screenshot-scroll").set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER)
# Auto-increase the window size to fit the console - within reason
# though, cos we don't want a min window size greater than the screen
# the user has scrollbars anyway if they want it smaller / it can't fit
def autosize(self, src, size):
rootWidth = gtk.gdk.screen_width()
rootHeight = gtk.gdk.screen_height()
vncWidth, vncHeight = src.get_size_request()
if vncWidth > (rootWidth-200):
vncWidth = rootWidth - 200
if vncHeight > (rootHeight-200):
vncHeight = rootHeight - 200
self.window.get_widget("console-vnc-vp").set_size_request(vncWidth+2, vncHeight+2)
def send_key(self, src):
keys = None
if src.get_name() == "menu-send-cad":
keys = ["Control_L", "Alt_L", "Del"]
keys = ["Control_L", "Alt_L", "Delete"]
elif src.get_name() == "menu-send-cab":
keys = ["Control_L", "Alt_L", "BackSpace"]
elif src.get_name() == "menu-send-caf1":
@ -287,10 +275,14 @@ class vmmConsole(gobject.GObject):
def keygrab_changed(self, src, ignore1=None,ignore2=None,ignore3=None):
if self.config.get_console_keygrab() == 2:
self.vncViewer.set_keyboard_grab(True)
self.vncViewer.set_pointer_grab(True)
else:
self.vncViewer.set_keyboard_grab(False)
self.vncViewer.set_pointer_grab(False)
def scale_display(self, src):
if src.get_active():
self.vncViewer.set_scaling(True)
else:
self.vncViewer.set_scaling(False)
def toggle_fullscreen(self, src):
if src.get_active():
@ -669,6 +661,8 @@ class vmmConsole(gobject.GObject):
cr.show_text(overlay)
screenshot.set_from_pixmap(pixmap, None)
self.activate_screenshot_page()
elif self.window.get_widget("console-pages").get_current_page() == PAGE_SCREENSHOT:
pass
else:
if self.window.get_widget("console-pages").get_current_page() != PAGE_UNAVAILABLE:
self.vncViewer.close()

View File

@ -1,5 +1,5 @@
#
# Copyright (C) 2006 Red Hat, Inc.
# Copyright (C) 2006, 2008 Red Hat, Inc.
# Copyright (C) 2006 Hugh O. Brock <hbrock@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
@ -49,17 +49,22 @@ VM_INSTALL_FROM_CD = 2
VM_STORAGE_PARTITION = 1
VM_STORAGE_FILE = 2
VM_INST_LOCAL = 1
VM_INST_TREE = 2
VM_INST_PXE = 3
DEFAULT_STORAGE_FILE_SIZE = 500
PAGE_INTRO = 0
PAGE_NAME = 1
PAGE_TYPE = 2
PAGE_FVINST = 3
PAGE_PVINST = 4
PAGE_DISK = 5
PAGE_NETWORK = 6
PAGE_CPUMEM = 7
PAGE_SUMMARY = 8
PAGE_INST = 3
PAGE_INST_LOCAL = 4
PAGE_INST_TREE = 5
PAGE_DISK = 6
PAGE_NETWORK = 7
PAGE_CPUMEM = 8
PAGE_SUMMARY = 9
KEYBOARD_DIR = "/etc/sysconfig/keyboard"
@ -80,6 +85,10 @@ class vmmCreate(gobject.GObject):
self.connection = connection
self.window = gtk.glade.XML(config.get_glade_dir() + "/vmm-create.glade", "vmm-create", domain="virt-manager")
self.topwin = self.window.get_widget("vmm-create")
self.err = vmmErrorDialog(self.topwin,
0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
_("Unexpected Error"),
_("An unexpected error occurred"))
self.topwin.hide()
self.window.signal_autoconnect({
"on_create_pages_switch_page" : self.page_changed,
@ -103,6 +112,7 @@ class vmmCreate(gobject.GObject):
"on_create_help_clicked": self.show_help,
})
self.caps = self.connection.get_capabilities()
self.set_initial_state()
# Guest to fill in with values along the way
@ -139,15 +149,13 @@ class vmmCreate(gobject.GObject):
cd_list.add_attribute(text, 'sensitive', 2)
try:
self.optical_helper = vmmOpticalDriveHelper(self.window.get_widget("cd-path"))
self.optical_helper.populate_opt_media()
self.window.get_widget("media-physical").set_sensitive(True)
except Exception, e:
logging.error("Unable to create optical-helper widget: '%s'", e)
self.window.get_widget("media-physical").set_sensitive(False)
if os.getuid() != 0:
self.window.get_widget("media-physical").set_sensitive(False)
self.window.get_widget("storage-partition").set_sensitive(False)
self.window.get_widget("media-physical").set_sensitive(True)
self.window.get_widget("storage-partition").set_sensitive(True)
# set up the lists for the url widgets
media_url_list = self.window.get_widget("pv-media-url")
@ -196,18 +204,13 @@ class vmmCreate(gobject.GObject):
self.window.get_widget("create-host-memory").set_text(self.pretty_memory(memory))
self.window.get_widget("create-memory-max").set_range(50, memory/1024)
if self.connection.get_type() == "QEMU":
if os.uname()[4] == "x86_64":
self.window.get_widget("cpu-architecture").set_active(1)
else:
self.window.get_widget("cpu-architecture").set_active(0)
else:
self.window.get_widget("cpu-architecture").set_active(-1)
self.window.get_widget("cpu-architecture").set_sensitive(False)
self.window.get_widget("cpu-accelerate").set_sensitive(False)
self.change_virt_method()
archModel = gtk.ListStore(str)
archList = self.window.get_widget("cpu-architecture")
archList.set_model(archModel)
hyperModel = gtk.ListStore(str)
hyperList = self.window.get_widget("hypervisor")
hyperList.set_model(hyperModel)
def reset_state(self):
notebook = self.window.get_widget("create-pages")
@ -220,29 +223,34 @@ class vmmCreate(gobject.GObject):
# If we don't have full-virt support disable the choice, and
# display a message telling the user why it is not working
if self.connection.get_type().lower() == "qemu":
self.window.get_widget("virt-method-pv").set_sensitive(False)
self.window.get_widget("virt-method-fv").set_active(True)
has_pv = False
has_fv = False
for guest in self.caps.guests:
if guest.os_type in ["xen", "linux"]:
has_pv = True
elif guest.os_type == "hvm":
has_fv = True
self.window.get_widget("virt-method-pv").set_sensitive(has_pv)
self.window.get_widget("virt-method-fv").set_sensitive(has_fv)
# prioritize pv if the option is available?
self.window.get_widget("virt-method-fv").set_active(has_fv)
self.window.get_widget("virt-method-pv").set_active(has_pv)
self.change_virt_method() # repopulate arch and hypervisor lists
if has_fv:
self.window.get_widget("virt-method-fv-unsupported").hide()
self.window.get_widget("virt-method-fv-disabled").hide()
else:
self.window.get_widget("virt-method-pv").set_sensitive(True)
self.window.get_widget("virt-method-pv").set_active(True)
if virtinst.util.is_hvm_capable():
self.window.get_widget("virt-method-fv").set_sensitive(True)
self.window.get_widget("virt-method-fv-unsupported").hide()
self.window.get_widget("virt-method-fv-disabled").hide()
self.window.get_widget("virt-method-fv-unsupported").show()
flags = self.caps.host.features.names()
if "vmx" in flags or "svm" in flags:
self.window.get_widget("virt-method-fv-disabled").show()
else:
self.window.get_widget("virt-method-fv").set_sensitive(False)
flags = virtinst.util.get_cpu_flags()
if "vmx" in flags or "svm" in flags:
# Host has support, but disabled in bios
self.window.get_widget("virt-method-fv-unsupported").hide()
self.window.get_widget("virt-method-fv-disabled").show()
else:
# Host has no support
self.window.get_widget("virt-method-fv-unsupported").show()
self.window.get_widget("virt-method-fv-disabled").hide()
self.window.get_widget("virt-method-fv-disabled").hide()
self.change_media_type()
self.change_storage_type()
@ -251,10 +259,7 @@ class vmmCreate(gobject.GObject):
self.window.get_widget("create-vm-name").set_text("")
self.window.get_widget("media-iso-image").set_active(True)
self.window.get_widget("fv-iso-location").set_text("")
if os.getuid() == 0:
self.window.get_widget("storage-partition").set_active(True)
else:
self.window.get_widget("storage-file-backed").set_active(True)
self.window.get_widget("storage-file-backed").set_active(True)
self.window.get_widget("storage-partition-address").set_text("")
self.window.get_widget("storage-file-address").set_text("")
self.window.get_widget("storage-file-size").set_value(2000)
@ -292,14 +297,26 @@ class vmmCreate(gobject.GObject):
def forward(self, ignore=None):
notebook = self.window.get_widget("create-pages")
if(self.validate(notebook.get_current_page()) != True):
try:
if(self.validate(notebook.get_current_page()) != True):
return
except Exception, e:
self.err.show_err(_("Uncaught error validating input: %s") % str(e),
"".join(traceback.format_exc()))
return
if (notebook.get_current_page() == PAGE_TYPE and self.get_config_method() == VM_PARA_VIRT):
notebook.set_current_page(PAGE_PVINST)
elif (notebook.get_current_page() == PAGE_FVINST and self.get_config_method() == VM_FULLY_VIRT):
if notebook.get_current_page() == PAGE_INST:
if self.get_config_install_method() == VM_INST_LOCAL:
notebook.set_current_page(PAGE_INST_LOCAL)
elif self.get_config_install_method() == VM_INST_TREE:
notebook.set_current_page(PAGE_INST_TREE)
else:
# No config for PXE needed (yet)
notebook.set_current_page(PAGE_DISK)
elif notebook.get_current_page() in [PAGE_INST_TREE, PAGE_INST_LOCAL]:
notebook.set_current_page(PAGE_DISK)
elif (notebook.get_current_page() == PAGE_DISK and os.getuid() != 0):
elif notebook.get_current_page() == PAGE_DISK and self.connection.get_uri() == "qemu:///session":
# Skip network for non-root
notebook.set_current_page(PAGE_CPUMEM)
else:
notebook.next_page()
@ -309,11 +326,18 @@ class vmmCreate(gobject.GObject):
# 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()
if notebook.get_current_page() == PAGE_PVINST and self.get_config_method() == VM_PARA_VIRT:
notebook.set_current_page(PAGE_TYPE)
elif notebook.get_current_page() == PAGE_DISK and self.get_config_method() == VM_FULLY_VIRT:
notebook.set_current_page(PAGE_FVINST)
elif notebook.get_current_page() == PAGE_CPUMEM and os.getuid() != 0:
if notebook.get_current_page() in [PAGE_INST_TREE, PAGE_INST_LOCAL]:
notebook.set_current_page(PAGE_INST)
elif notebook.get_current_page() == PAGE_DISK:
if self.get_config_install_method() == VM_INST_LOCAL:
notebook.set_current_page(PAGE_INST_LOCAL)
elif self.get_config_install_method() == VM_INST_TREE:
notebook.set_current_page(PAGE_INST_TREE)
else:
# No config for PXE needed (yet)
notebook.set_current_page(PAGE_INST)
elif notebook.get_current_page() == PAGE_CPUMEM and self.connection.get_uri() == "qemu:///session":
# Skip network for non-root
notebook.set_current_page(PAGE_DISK)
else:
notebook.prev_page()
@ -330,31 +354,29 @@ class vmmCreate(gobject.GObject):
return VM_PARA_VIRT
def get_config_install_source(self):
if self.get_config_method() == VM_PARA_VIRT:
if self.get_config_install_method() == VM_INST_TREE:
widget = self.window.get_widget("pv-media-url")
url= widget.child.get_text()
# Add the URL to the list, if it's different
self.config.add_media_url(url)
self.populate_url_model(widget.get_model(), self.config.get_media_urls())
return url
else:
elif self.get_config_install_method() == VM_INST_LOCAL:
if self.window.get_widget("media-iso-image").get_active():
return self.window.get_widget("fv-iso-location").get_text()
elif self.window.get_widget("media-physical").get_active():
cd = self.window.get_widget("cd-path")
model = cd.get_model()
return model.get_value(cd.get_active_iter(), 0)
else:
return "PXE"
def get_config_installer(self, type):
if self.get_config_method() == VM_FULLY_VIRT and self.window.get_widget("media-network").get_active():
return virtinst.PXEInstaller(type = type)
return self.window.get_widget("cd-path").get_active_text()
else:
return virtinst.DistroInstaller(type = type)
return "PXE"
def get_config_installer(self, type, os_type):
if self.get_config_install_method() == VM_INST_PXE:
return virtinst.PXEInstaller(type = type, os_type = os_type)
else:
return virtinst.DistroInstaller(type = type, os_type = os_type)
def get_config_kickstart_source(self):
if self.get_config_method() == VM_PARA_VIRT:
if self.get_config_install_method() == VM_INST_TREE:
widget = self.window.get_widget("pv-ks-url")
url = widget.child.get_text()
self.config.add_kickstart_url(url)
@ -370,6 +392,8 @@ class vmmCreate(gobject.GObject):
return self.window.get_widget("storage-file-address").get_text()
def get_config_disk_size(self):
if not self.window.get_widget("storage-file-backed").get_active():
return None
if not self.window.get_widget("storage-file-size").get_editable():
return None
else:
@ -379,7 +403,7 @@ class vmmCreate(gobject.GObject):
return self.window.get_widget("kernel-params").get_text()
def get_config_network(self):
if os.getuid() != 0:
if self.connection.get_uri() == "qemu:///session":
return ["user"]
if self.window.get_widget("net-type-network").get_active():
@ -406,6 +430,14 @@ class vmmCreate(gobject.GObject):
def get_config_virtual_cpus(self):
return self.window.get_widget("create-vcpus").get_value()
def get_config_install_method(self):
if self.window.get_widget("method-local").get_active():
return VM_INST_LOCAL
elif self.window.get_widget("method-tree").get_active():
return VM_INST_TREE
else:
return VM_INST_PXE
def get_config_os_type(self):
type = self.window.get_widget("os-type")
if type.get_active_iter() != None:
@ -438,9 +470,19 @@ class vmmCreate(gobject.GObject):
name_widget.grab_focus()
elif page_number == PAGE_TYPE:
pass
elif page_number == PAGE_FVINST:
pass
elif page_number == PAGE_PVINST:
elif page_number == PAGE_INST:
if self.get_config_method() == VM_PARA_VIRT:
# Xen can't PXE or CDROM install :-(
self.window.get_widget("method-local").set_sensitive(False)
self.window.get_widget("method-pxe").set_sensitive(False)
self.window.get_widget("method-tree").set_active(True)
else:
self.window.get_widget("method-local").set_sensitive(True)
self.window.get_widget("method-pxe").set_sensitive(True)
elif page_number == PAGE_INST_TREE:
url_widget = self.window.get_widget("pv-media-url")
url_widget.grab_focus()
elif page_number == PAGE_INST_LOCAL:
url_widget = self.window.get_widget("pv-media-url")
url_widget.grab_focus()
elif page_number == PAGE_DISK:
@ -519,14 +561,25 @@ class vmmCreate(gobject.GObject):
try:
guest.uuid = virtinst.util.uuidToString(virtinst.util.randomUUID())
except ValueError, E:
self._validation_error_box(_("UUID Error"), str(e))
return self.err.val_err(_("UUID Error"), str(e))
# HACK: If usermode, and no nic is setup, use usermode networking
if self.connection.get_uri() == "qemu:///session":
try:
self._net = virtinst.VirtualNetworkInterface(type="user")
except ValueError, e:
return self.err.val_err(_("Failed to set up usermode networking"), str(e))
if self._disk is not None:
guest.disks = [self._disk]
else:
logging.debug('No guest disks found in install phase.')
if self._net is not None:
guest.nics = [self._net]
# set up the graphics to use SDL
else:
logging.debug('No guest nics found in install phase.')
# Set vnc display to use same keymap as host
import keytable
keymap = None
vncport = None
@ -543,8 +596,17 @@ class vmmCreate(gobject.GObject):
kt = s.split('"')[1]
if keytable.keytable.has_key(kt):
keymap = keytable.keytable[kt]
else:
logging.debug("Didn't find keymap '%s' in keytable!" % kt)
f.close
guest.graphics = (True, "vnc", vncport, keymap)
try:
guest._graphics_dev = virtinst.VirtualGraphics(type=virtinst.VirtualGraphics.TYPE_VNC,
port=vncport,
keymap=keymap)
except Exception, e:
self.err.show_err(_("Error setting up graphics device:") + str(e),
"".join(traceback.format_exc()))
return False
logging.debug("Creating a VM " + guest.name + \
"\n Type: " + guest.type + \
@ -577,12 +639,7 @@ class vmmCreate(gobject.GObject):
progWin.run()
if self.install_error != None:
dg = vmmErrorDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
self.install_error,
self.install_details)
dg.run()
dg.hide()
dg.destroy()
self.err.show_err(self.install_error, self.install_details)
self.topwin.set_sensitive(True)
self.topwin.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.TOP_LEFT_ARROW))
# Don't close becase we allow user to go back in wizard & correct
@ -687,7 +744,10 @@ class vmmCreate(gobject.GObject):
if file != None and len(file) > 0 and not(os.path.exists(file)):
self.window.get_widget("storage-file-size").set_sensitive(True)
self.window.get_widget("non-sparse").set_sensitive(True)
self.window.get_widget("storage-file-size").set_value(4000)
size = self.get_config_disk_size()
if size == None:
size = 4000
self.window.get_widget("storage-file-size").set_value(size)
else:
self.window.get_widget("storage-file-size").set_sensitive(False)
self.window.get_widget("non-sparse").set_sensitive(False)
@ -767,8 +827,7 @@ class vmmCreate(gobject.GObject):
try:
self._guest.name = name
except ValueError, e:
self._validation_error_box(_("Invalid System Name"), str(e))
return False
return self.err.val_err(_("Invalid System Name"), str(e))
elif page_num == PAGE_TYPE:
# Set up appropriate guest object dependent on selected type
@ -780,11 +839,36 @@ class vmmCreate(gobject.GObject):
self._guest = virtinst.FullVirtGuest(type=self.get_domain_type(),
arch=self.get_domain_arch(),
hypervisorURI=self.connection.get_uri())
self._guest.name = name # Transfer name over
elif page_num == PAGE_FVINST:
self._guest.installer = self.get_config_installer(self.get_domain_type())
elif page_num == PAGE_INST:
if self.get_config_method() == VM_PARA_VIRT:
os_type = "xen"
else:
os_type = "hvm"
self._guest.installer = self.get_config_installer(self.get_domain_type(), os_type)
try:
if self.get_config_os_type() is not None \
and self.get_config_os_type() != "generic":
logging.debug("OS Type: %s" % self.get_config_os_type())
self._guest.os_type = self.get_config_os_type()
except ValueError, e:
return self.err.val_err(_("Invalid FV OS Type"), str(e))
try:
if self.get_config_os_variant() is not None \
and self.get_config_os_type() != "generic":
logging.debug("OS Variant: %s" % self.get_config_os_variant())
self._guest.os_variant = self.get_config_os_variant()
except ValueError, e:
return self.err.val_err(_("Invalid FV OS Variant"), str(e))
elif page_num == PAGE_INST_LOCAL:
if self.get_config_method() == VM_PARA_VIRT:
os_type = "xen"
else:
os_type = "hvm"
self._guest.installer = self.get_config_installer(self.get_domain_type(), os_type)
if self.window.get_widget("media-iso-image").get_active():
@ -792,51 +876,28 @@ class vmmCreate(gobject.GObject):
try:
self._guest.cdrom = src
except ValueError, e:
self._validation_error_box(_("ISO Path Not Found"), str(e))
return False
elif self.window.get_widget("media-physical").get_active():
return self.err.val_err(_("ISO Path Not Found"), str(e))
else:
cdlist = self.window.get_widget("cd-path")
src = self.get_config_install_source()
try:
self._guest.cdrom = src
except ValueError, e:
self._validation_error_box(_("CD-ROM Path Error"), str(e))
return False
else:
pass # No checks for PXE
try:
if self.get_config_os_type() is not None \
and self.get_config_os_type() != "generic":
logging.debug("OS Type: %s" % self.get_config_os_type())
self._guest.os_type = self.get_config_os_type()
except ValueError, e:
self._validation_error_box(_("Invalid FV OS Type"), str(e))
return False
try:
if self.get_config_os_variant() is not None \
and self.get_config_os_type() != "generic":
logging.debug("OS Variant: %s" % self.get_config_os_variant())
self._guest.os_variant = self.get_config_os_variant()
except ValueError, e:
self._validation_error_box(_("Invalid FV OS Variant"), str(e))
return False
elif page_num == PAGE_PVINST:
return self.err.val_err(_("CD-ROM Path Error"), str(e))
elif page_num == PAGE_INST_TREE:
src = self.get_config_install_source()
try:
self._guest.location = src
except ValueError, e:
self._validation_error_box(_("Invalid Install URL"), str(e))
return False
return self.err.val_err(_("Invalid Install URL"), str(e))
ks = self.get_config_kickstart_source()
if ks is not None and len(ks) != 0:
if not (ks.startswith("http://") or ks.startswith("ftp://") \
or ks.startswith("nfs:")):
self._validation_error_box(_("Kickstart URL Error"), \
_("Kickstart location must be an NFS, HTTP or FTP source"))
return False
return self.err.val_err(_("Kickstart URL Error"), \
_("Kickstart location must be an NFS, HTTP or FTP source"))
else:
self._guest.extraargs = "ks=%s" % (ks,)
@ -852,9 +913,8 @@ class vmmCreate(gobject.GObject):
disk = self.get_config_disk_image()
if disk == None or len(disk) == 0:
self._validation_error_box(_("Storage Address Required"), \
_("You must specify a partition or a file for storage for the guest install"))
return False
return self.err.val_err(_("Storage Address Required"), \
_("You must specify a partition or a file for storage for the guest install"))
if not self.window.get_widget("storage-partition").get_active():
disk = self.get_config_disk_image()
@ -862,23 +922,21 @@ class vmmCreate(gobject.GObject):
if not os.path.exists(disk):
dir = os.path.dirname(os.path.abspath(disk))
if not os.path.exists(dir):
self._validation_error_box(_("Storage Path Does not exist"),
_("The directory %s containing the disk image does not exist") % dir)
return False
return self.err.val_err(_("Storage Path Does not exist"),
_("The directory %s containing the disk image does not exist") % dir)
else:
vfs = os.statvfs(dir)
avail = vfs[statvfs.F_FRSIZE] * vfs[statvfs.F_BAVAIL]
need = size * 1024 * 1024
if need > avail:
if self.is_sparse_file():
res = self._yes_no_box(_("Not Enough Free Space"),
_("The filesystem will not have enough free space to fully allocate the sparse file when the guest is running. Use this path anyway?"))
res = self.err.yes_no(_("Not Enough Free Space"),
_("The filesystem will not have enough free space to fully allocate the sparse file when the guest is running. Use this path anyway?"))
if not res:
return False
else:
self._validation_error_box(_("Not Enough Free Space"),
_("There is not enough free space to create the disk"))
return False
return self.err.val_err(_("Not Enough Free Space"),
_("There is not enough free space to create the disk"))
# Attempt to set disk
filesize = None
@ -907,41 +965,31 @@ class vmmCreate(gobject.GObject):
else:
self.non_sparse = False
except ValueError, e:
self._validation_error_box(_("Invalid Storage Address"), \
str(e))
return False
return self.err.val_err(_("Invalid Storage Address"), str(e))
if self._disk.is_conflict_disk(self.connection.vmm) is True:
res = self._yes_no_box(_('Disk "%s" is already in use by another guest!' % disk), _("Do you really want to use the disk ?"))
res = self.err.yes_no(_('Disk "%s" is already in use by another guest!' % disk), _("Do you really want to use the disk ?"))
return res
elif page_num == PAGE_NETWORK:
if self.window.get_widget("net-type-network").get_active():
if self.window.get_widget("net-network").get_active() == -1:
self._validation_error_box(_("Virtual Network Required"),
_("You must select one of the virtual networks"))
return False
return self.err.val_err(_("Virtual Network Required"),
_("You must select one of the virtual networks"))
else:
if self.window.get_widget("net-device").get_active() == -1:
self._validation_error_box(_("Physical Device Required"),
_("You must select one of the physical devices"))
return False
return self.err.val_err(_("Physical Device Required"),
_("You must select one of the physical devices"))
net = self.get_config_network()
if self.window.get_widget("mac-address").get_active():
mac = self.window.get_widget("create-mac-address").get_text()
if mac is None or len(mac) == 0:
self._validation_error_box(_("Invalid MAC address"), \
_("No MAC address was entered. Please enter a valid MAC address."))
return False
hostdevs = virtinst.util.get_host_network_devices()
for hostdev in hostdevs:
if mac.lower() == hostdev[4]:
return self._validation_error_box(_('MAC address "%s" is already in use by the host') % mac, \
_("Please enter a different MAC address or select no fixed MAC address"))
return self.err.val_err(_("Invalid MAC address"), \
_("No MAC address was entered. Please enter a valid MAC address."))
else:
mac = None
try:
@ -957,27 +1005,14 @@ class vmmCreate(gobject.GObject):
self._net = virtinst.VirtualNetworkInterface(macaddr=mac, \
type=net[0])
except ValueError, e:
self._validation_error_box(_("Network Parameter Error"), \
str(e))
return False
return self.err.val_err(_("Network Parameter Error"), str(e))
vms = []
for domains in self.connection.vms.values():
vms.append(domains.vm)
# get inactive Domains
inactive_vm = []
names = self.connection.vmm.listDefinedDomains()
for name in names:
vm = self.connection.vmm.lookupByName(name)
inactive_vm.append(vm)
if (self._net.countMACaddr(vms) - self._net.countMACaddr(inactive_vm)) > 0:
return self._validation_error_box(_('MAC address "%s" is already in use by a active guest') % mac, \
_("Please enter a different MAC address or select no fixed MAC address"))
elif self._net.countMACaddr(inactive_vm) > 0:
return self._yes_no_box(_('MAC address "%s" is already in use by another inactive guest!') % mac, \
_("Do you really want to use the MAC address ?"))
conflict = self._net.is_conflict_net(self.connection.vmm)
if conflict[0]:
return self.err.val_err(_("Mac address collision"), conflict[1])
elif conflict[1] is not None:
return self.err.yes_no(_("Mac address collision"),\
conflict[1] + " " + _("Are you sure you want to use this address?"))
elif page_num == PAGE_CPUMEM:
@ -985,54 +1020,22 @@ class vmmCreate(gobject.GObject):
try:
self._guest.vcpus = int(self.get_config_virtual_cpus())
except ValueError, e:
self._validation_error_box(_("VCPU Count Error"), \
str(e))
return False
return self.err.val_err(_("VCPU Count Error"), str(e))
# Set Memory
try:
self._guest.memory = int(self.get_config_initial_memory())
except ValueError, e:
self._validation_error_box(_("Memory Amount Error"), \
str(e))
return False
return self.err.val_err(_("Memory Amount Error"), str(e))
# Set Max Memory
try:
self._guest.maxmemory = int(self.get_config_maximum_memory())
except ValueError, e:
self._validation_error_box(_("Max Memory Amount Error"), \
str(e))
return False
return self.err.val_err(_("Max Memory Amount Error"), str(e))
# 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"), \
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):
#import pdb; pdb.set_trace()
message_box = gtk.MessageDialog(self.window.get_widget("vmm-create"), \
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_url_model(self, model, urls):
model.clear()
for url in urls:
@ -1086,29 +1089,31 @@ class vmmCreate(gobject.GObject):
def change_virt_method(self, ignore=None):
arch = self.window.get_widget("cpu-architecture")
if self.connection.get_type() != "QEMU" or self.window.get_widget("virt-method-pv").get_active():
arch.set_sensitive(False)
if self.get_config_method() == VM_PARA_VIRT:
nativeArch = self.repopulate_cpu_arch(arch.get_model(), ["xen", "linux"])
else:
arch.set_sensitive(True)
self.change_cpu_arch(arch)
nativeArch = self.repopulate_cpu_arch(arch.get_model(), ["hvm"])
arch.set_active(nativeArch)
self.change_cpu_arch()
def change_cpu_arch(self, src):
model = src.get_model()
active = src.get_active()
canAccel = False
if active != -1 and src.get_property("sensitive") and \
(virtinst.util.is_kvm_capable() or virtinst.util.is_kqemu_capable()):
if os.uname()[4] == "i686" and model[active][0] == "i686":
canAccel = True
elif os.uname()[4] == "x86_64" and model[active][0] in ("i686", "x86_64"):
canAccel = True
def change_cpu_arch(self, ignore=None):
hypervisor = self.window.get_widget("hypervisor")
arch = self.get_domain_arch()
self.window.get_widget("cpu-accelerate").set_sensitive(canAccel)
self.window.get_widget("cpu-accelerate").set_active(canAccel)
if arch is None:
hypervisor.set_active(-1)
hypervisor.set_sensitive(False)
return
hypervisor.set_sensitive(True)
if self.get_config_method() == VM_PARA_VIRT:
bestHyper = self.repopulate_hypervisor(hypervisor.get_model(), ["xen", "linux"], arch)
else:
bestHyper = self.repopulate_hypervisor(hypervisor.get_model(), ["hvm"], arch)
hypervisor.set_active(bestHyper)
def get_domain_arch(self):
if self.connection.get_type() != "QEMU":
return None
arch = self.window.get_widget("cpu-architecture")
if arch.get_active() == -1:
return None
@ -1127,15 +1132,41 @@ class vmmCreate(gobject.GObject):
return True
def get_domain_type(self):
if self.connection.get_type() == "QEMU":
if self.window.get_widget("cpu-accelerate").get_active():
if virtinst.util.is_kvm_capable():
return "kvm"
elif virtinst.util.is_kqemu_capable():
return "kqemu"
return "qemu"
else:
return "xen"
hypervisor = self.window.get_widget("hypervisor")
if hypervisor.get_active() == -1:
return None
return hypervisor.get_model()[hypervisor.get_active()][0]
def repopulate_cpu_arch(self, model, ostype):
model.clear()
i = 0
native = -1
for guest in self.caps.guests:
if guest.os_type not in ostype:
continue
model.append([guest.arch])
if guest.arch == self.caps.host.arch:
native = i
i = i + 1
return native
def repopulate_hypervisor(self, model, ostype, arch):
model.clear()
i = -1
for guest in self.caps.guests:
if guest.os_type not in ostype or guest.arch != arch:
continue
for domain in guest.domains:
model.append([domain.hypervisor_type])
i = i + 1
return i
def show_help(self, src):
# help to show depends on the notebook page, yahoo
@ -1146,10 +1177,12 @@ class vmmCreate(gobject.GObject):
self.emit("action-show-help", "virt-manager-system-name")
elif page == PAGE_TYPE:
self.emit("action-show-help", "virt-manager-virt-method")
elif page == PAGE_FVINST:
self.emit("action-show-help", "virt-manager-installation-media-full-virt")
elif page == PAGE_PVINST:
self.emit("action-show-help", "virt-manager-installation-media-paravirt")
elif page == PAGE_INST:
self.emit("action-show-help", "virt-manager-installation-media")
elif page == PAGE_INST_LOCAL:
self.emit("action-show-help", "virt-manager-installation-media-local")
elif page == PAGE_INST_TREE:
self.emit("action-show-help", "virt-manager-installation-media-tree")
elif page == PAGE_DISK:
self.emit("action-show-help", "virt-manager-storage-space")
elif page == PAGE_NETWORK:

View File

@ -28,8 +28,10 @@ import os, sys
import logging
import dbus
import re
import traceback
from virtManager.IPy import IP
from virtManager.error import vmmErrorDialog
PAGE_INTRO = 0
PAGE_NAME = 1
@ -50,6 +52,10 @@ class vmmCreateNetwork(gobject.GObject):
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.err = vmmErrorDialog(self.topwin,
0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
_("Unexpected Error"),
_("An unexpected error occurred"))
self.topwin.hide()
self.window.signal_autoconnect({
"on_create_pages_switch_page" : self.page_changed,
@ -295,41 +301,40 @@ class vmmCreateNetwork(gobject.GObject):
logging.debug("About to create network " + xml)
self.conn.create_network(xml)
try:
self.conn.create_network(xml)
except Exception, e:
self.err.show_err(_("Error creating virtual network: %s" % str(e)),
"".join(traceback.format_exc()))
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
return self.err.val_err(_("Invalid Network Name"), \
_("Network name must be non-blank and less than 50 characters"))
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
return self.err.val_err(_("Invalid Network Name"), \
_("Network name may contain alphanumeric and '_' characters only"))
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
return self.err.val_err(_("Invalid Network Address"), \
_("The network address could not be understood"))
if ip.version() != 4:
self._validation_error_box(_("Invalid Network Address"), \
_("The network must be an IPv4 address"))
return False
return self.err.val_err(_("Invalid Network Address"), \
_("The network must be an IPv4 address"))
if ip.len() < 16:
self._validation_error_box(_("Invalid Network Address"), \
_("The network prefix must be at least /4 (16 addresses)"))
return False
return self.err.val_err(_("Invalid Network Address"), \
_("The network prefix must be at least /4 (16 addresses)"))
if ip.iptype() != "PRIVATE":
res = self._yes_no_box(_("Check Network Address"), \
res = self.err.yes_no(_("Check Network Address"), \
_("The network should normally use a private IPv4 address. Use this non-private address anyway?"))
if not res:
return False
@ -339,60 +344,29 @@ class vmmCreateNetwork(gobject.GObject):
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
return self.err.val_err(_("Invalid DHCP Address"), \
_("The DHCP start address could not be understood"))
if end is None:
self._validation_error_box(_("Invalid DHCP Address"), \
_("The DHCP end address could not be understood"))
return False
return self.err.val_err(_("Invalid DHCP Address"), \
_("The DHCP end address could not be understood"))
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
return self.err.val_err(_("Invalid DHCP Address"), \
_("The DHCP start address is not with the network %s") % (str(ip)))
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
return self.err.val_err(_("Invalid DHCP Address"), \
_("The DHCP end address is not with the network %s") % (str(ip)))
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
return self.err.val_err(_("Invalid forwarding mode"), \
_("Please select where the traffic should be forwarded"))
# 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 = {}
@ -404,10 +378,11 @@ class vmmCreateNetwork(gobject.GObject):
# Find info about all current present media
for d in self.hal_iface.FindDeviceByCapability("volume"):
vol = self.bus.get_object("org.freedesktop.Hal", d)
if vol.GetPropertyBoolean("volume.is_disc") and \
vol.GetPropertyBoolean("volume.disc.has_data"):
devnode = vol.GetProperty("block.device")
label = vol.GetProperty("volume.label")
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
@ -416,7 +391,8 @@ class vmmCreateNetwork(gobject.GObject):
for d in self.hal_iface.FindDeviceByCapability("storage.cdrom"):
dev = self.bus.get_object("org.freedesktop.Hal", d)
devnode = dev.GetProperty("block.device")
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:

View File

@ -49,6 +49,7 @@ HW_LIST_TYPE_DISK = 2
HW_LIST_TYPE_NIC = 3
HW_LIST_TYPE_INPUT = 4
HW_LIST_TYPE_GRAPHICS = 5
HW_LIST_TYPE_BOOT = 6
class vmmDetails(gobject.GObject):
__gsignals__ = {
@ -80,6 +81,10 @@ class vmmDetails(gobject.GObject):
self.vm = vm
topwin = self.window.get_widget("vmm-details")
self.err = vmmErrorDialog(topwin,
0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
_("Unexpected Error"),
_("An unexpected error occurred"))
topwin.hide()
topwin.set_title(self.vm.get_name() + " " + topwin.get_title())
@ -134,6 +139,9 @@ class vmmDetails(gobject.GObject):
"on_config_memory_changed": self.config_memory_changed,
"on_config_maxmem_changed": self.config_maxmem_changed,
"on_config_memory_apply_clicked": self.config_memory_apply,
"on_config_boot_device_changed": self.config_boot_options_changed,
"on_config_autostart_changed": self.config_boot_options_changed,
"on_config_boot_apply_clicked": self.config_boot_options_apply,
"on_details_help_activate": self.show_help,
"on_config_cdrom_connect_clicked": self.toggle_cdrom,
@ -216,6 +224,9 @@ class vmmDetails(gobject.GObject):
self.refresh_input_page()
elif pagetype == HW_LIST_TYPE_GRAPHICS:
self.refresh_graphics_page()
elif pagetype == HW_LIST_TYPE_BOOT:
self.refresh_boot_page()
self.window.get_widget("config-boot-options-apply").set_sensitive(False)
else:
pagenum = -1
@ -275,7 +286,7 @@ class vmmDetails(gobject.GObject):
self.window.get_widget("details-menu-run").set_sensitive(False)
self.window.get_widget("config-vcpus").set_sensitive(self.vm.is_vcpu_hotplug_capable())
self.window.get_widget("config-memory").set_sensitive(self.vm.is_memory_hotplug_capable())
self.window.get_widget("config-maxmem").set_sensitive(False)
self.window.get_widget("config-maxmem").set_sensitive(True)
if status in [ libvirt.VIR_DOMAIN_SHUTDOWN, libvirt.VIR_DOMAIN_SHUTOFF, libvirt.VIR_DOMAIN_CRASHED ] or vm.is_read_only():
self.window.get_widget("control-pause").set_sensitive(False)
@ -298,6 +309,7 @@ class vmmDetails(gobject.GObject):
self.window.get_widget("details-menu-pause").set_active(False)
except:
self.ignorePause = False
raise
self.ignorePause = False
self.window.get_widget("overview-status-text").set_text(self.vm.run_status())
@ -362,7 +374,7 @@ class vmmDetails(gobject.GObject):
self.window.get_widget("state-host-cpus").set_text("%d" % self.vm.get_connection().host_active_processor_count())
status = self.vm.status()
if status in [ libvirt.VIR_DOMAIN_SHUTOFF, libvirt.VIR_DOMAIN_CRASHED ]:
cpu_max = self.vm.get_connection().get_max_vcpus()
cpu_max = self.vm.get_connection().get_max_vcpus(self.vm.get_type())
self.window.get_widget("config-vcpus").get_adjustment().upper = cpu_max
self.window.get_widget("state-vm-maxvcpus").set_text(str(cpu_max))
else:
@ -379,16 +391,25 @@ class vmmDetails(gobject.GObject):
def refresh_config_memory(self):
self.window.get_widget("state-host-memory").set_text("%d MB" % (int(round(self.vm.get_connection().host_memory_size()/1024))))
curmem = self.window.get_widget("config-memory").get_adjustment()
maxmem = self.window.get_widget("config-maxmem").get_adjustment()
if self.window.get_widget("config-memory-apply").get_property("sensitive"):
self.window.get_widget("config-memory").get_adjustment().upper = self.window.get_widget("config-maxmem").get_adjustment().value
if curmem.value > maxmem.value:
curmem.value = maxmem.value
curmem.upper = maxmem.value
else:
self.window.get_widget("config-memory").get_adjustment().value = int(round(self.vm.get_memory()/1024.0))
self.window.get_widget("config-maxmem").get_adjustment().value = int(round(self.vm.maximum_memory()/1024.0))
curmem.value = int(round(self.vm.get_memory()/1024.0))
maxmem.value = int(round(self.vm.maximum_memory()/1024.0))
# XXX hack - changing the value above will have just re-triggered
# the callback making apply button sensitive again. So we have to
# turn it off again....
self.window.get_widget("config-memory-apply").set_sensitive(False)
if not self.window.get_widget("config-memory").get_property("sensitive"):
maxmem.lower = curmem.value
self.window.get_widget("state-vm-memory").set_text("%d MB" % int(round(self.vm.get_memory()/1024.0)))
def refresh_disk_page(self):
@ -483,11 +504,13 @@ class vmmDetails(gobject.GObject):
self.window.get_widget("graphics-port").set_text(_("Automatically allocated"))
else:
self.window.get_widget("graphics-port").set_text(inputinfo[2])
self.window.get_widget("graphics-password").set_text("")
self.window.get_widget("graphics-password").set_text("-")
self.window.get_widget("graphics-keymap").set_text(inputinfo[4] or _("Same as host"))
else:
self.window.get_widget("graphics-address").set_text(_("N/A"))
self.window.get_widget("graphics-port").set_text(_("N/A"))
self.window.get_widget("graphics-password").set_text("N/A")
self.window.get_widget("graphics-keymap").set_text("N/A")
# Can't remove display from live guest
if self.vm.is_active():
@ -495,6 +518,40 @@ class vmmDetails(gobject.GObject):
else:
self.window.get_widget("config-input-remove").set_sensitive(True)
def refresh_boot_page(self):
# Refresh autostart
try:
autoval = self.vm.get_autostart()
self.window.get_widget("config-autostart").set_active(autoval)
self.window.get_widget("config-autostart").set_sensitive(True)
except libvirt.libvirtError, e:
# Autostart isn't supported
self.window.get_widget("config-autostart").set_active(False)
self.window.get_widget("config-autostart").set_sensitive(False)
# Refresh Boot Device list and correct selection
boot_combo = self.window.get_widget("config-boot-device")
if not self.vm.is_hvm():
# Boot dev selection not supported for PV guest
boot_combo.set_sensitive(False)
boot_combo.set_active(-1)
return
self.repopulate_boot_list()
bootdev = self.vm.get_boot_device()
boot_combo = self.window.get_widget("config-boot-device")
boot_model = boot_combo.get_model()
for i in range(0, len(boot_model)):
if bootdev == boot_model[i][2]:
boot_combo.set_active(i)
break
if boot_model[0][2] == None:
# If no boot devices, select the 'No Device' entry
boot_combo.set_active(0)
# TODO: if nothing selected, what to select? auto change device?
def config_vcpus_changed(self, src):
self.window.get_widget("config-vcpus-apply").set_sensitive(True)
@ -517,16 +574,67 @@ class vmmDetails(gobject.GObject):
def config_memory_apply(self, src):
status = self.vm.status()
if status in [ libvirt.VIR_DOMAIN_SHUTOFF, libvirt.VIR_DOMAIN_CRASHED ]:
memory = self.window.get_widget("config-maxmem").get_adjustment().value
logging.info("Setting max memory for " + self.vm.get_uuid() + " to " + str(memory))
self.vm.set_max_memory(memory*1024)
memory = self.window.get_widget("config-memory").get_adjustment().value
logging.info("Setting memory for " + self.vm.get_uuid() + " to " + str(memory))
self.vm.set_memory(memory*1024)
self.refresh_config_memory()
exc = None
curmem = None
maxmem = self.window.get_widget("config-maxmem").get_adjustment()
if self.window.get_widget("config-memory").get_property("sensitive"):
curmem = self.window.get_widget("config-memory").get_adjustment()
self.window.get_widget("config-memory-apply").set_sensitive(False)
logging.info("Setting max-memory for " + self.vm.get_name() + \
" to " + str(maxmem.value))
actual_cur = self.vm.get_memory()
if curmem is not None:
logging.info("Setting memory for " + self.vm.get_name() + \
" to " + str(curmem.value))
if (maxmem.value * 1024) < actual_cur:
# Set current first to avoid error
try:
self.vm.set_memory(curmem.value * 1024)
self.vm.set_max_memory(maxmem.value * 1024)
except Exception, e:
exc = e
else:
try:
self.vm.set_max_memory(maxmem.value * 1024)
self.vm.set_memory(curmem.value * 1024)
except Exception, e:
exc = e
else:
try:
self.vm.set_max_memory(maxmem.value * 1024)
except Exception, e:
exc = e
if exc:
self.err.show_err(_("Error changing memory values: %s" % str(e)),\
"".join(traceback.format_exc()))
else:
self.window.get_widget("config-memory-apply").set_sensitive(False)
def config_boot_options_changed(self, src):
self.window.get_widget("config-boot-options-apply").set_sensitive(True)
def config_boot_options_apply(self, src):
boot = self.window.get_widget("config-boot-device")
auto = self.window.get_widget("config-autostart")
if auto.get_property("sensitive"):
try:
self.vm.set_autostart(auto.get_active())
except Exception, e:
self.err.show_err(_("Error changing autostart value: %s") % \
str(e), "".join(traceback.format_exc()))
if boot.get_property("sensitive"):
try:
self.vm.set_boot_device(boot.get_model()[boot.get_active()][2])
self.window.get_widget("config-boot-options-apply").set_sensitive(False)
except Exception, e:
self.err.show_err(_("Error changing boot device: %s" % str(e)),
"".join(traceback.format_exc()))
return
def remove_disk(self, src):
vmlist = self.window.get_widget("hw-list")
@ -553,9 +661,9 @@ class vmmDetails(gobject.GObject):
else:
vnic = virtinst.VirtualNetworkInterface(type=netinfo[0], macaddr=netinfo[3])
except ValueError, e:
self.err_dialog(_("Error Removing Network: %s" % str(e)),
"".join(traceback.format_exc()))
return
self.err.show_err(_("Error Removing Network: %s" % str(e)),
"".join(traceback.format_exc()))
return False
xml = vnic.get_xml_config()
self.remove_device(xml)
@ -597,14 +705,30 @@ class vmmDetails(gobject.GObject):
hwCol.add_attribute(hw_img, 'stock-size', HW_LIST_COL_STOCK_SIZE)
hwCol.add_attribute(hw_img, 'pixbuf', HW_LIST_COL_PIXBUF)
self.window.get_widget("hw-list").append_column(hwCol)
self.prepare_boot_list()
self.populate_hw_list()
self.repopulate_boot_list()
def prepare_boot_list(self):
boot_list = self.window.get_widget("config-boot-device")
# model = [ display name, icon name, boot type (hd, fd, etc) ]
boot_list_model = gtk.ListStore(str, str, str)
boot_list.set_model(boot_list_model)
icon = gtk.CellRendererPixbuf()
boot_list.pack_start(icon, False)
boot_list.add_attribute(icon, 'stock-id', 1)
text = gtk.CellRendererText()
boot_list.pack_start(text, True)
boot_list.add_attribute(text, 'text', 0)
def populate_hw_list(self):
hw_list_model = self.window.get_widget("hw-list").get_model()
hw_list_model.clear()
hw_list_model.append(["Processor", None, 0, self.pixbuf_processor, HW_LIST_TYPE_CPU, []])
hw_list_model.append(["Memory", None, 0, self.pixbuf_memory, HW_LIST_TYPE_MEMORY, []])
hw_list_model.append(["Boot Options", None, 0, self.pixbuf_memory, HW_LIST_TYPE_BOOT, []])
self.repopulate_hw_list()
def repopulate_hw_list(self):
@ -725,6 +849,36 @@ class vmmDetails(gobject.GObject):
# Now actually remove it
hw_list_model.remove(iter)
def repopulate_boot_list(self):
hw_list_model = self.window.get_widget("hw-list").get_model()
boot_combo = self.window.get_widget("config-boot-device")
boot_model = boot_combo.get_model()
boot_model.clear()
found_dev = {}
for row in hw_list_model:
if row[4] == HW_LIST_TYPE_DISK:
disk = row[5]
if disk[2] == virtinst.VirtualDisk.DEVICE_DISK and not \
found_dev.get(virtinst.VirtualDisk.DEVICE_DISK, False):
boot_model.append(["Hard Disk", gtk.STOCK_HARDDISK, "hd"])
found_dev[virtinst.VirtualDisk.DEVICE_DISK] = True
elif disk[2] == virtinst.VirtualDisk.DEVICE_CDROM and not \
found_dev.get(virtinst.VirtualDisk.DEVICE_CDROM, False):
boot_model.append(["CDROM", gtk.STOCK_CDROM, "cdrom"])
found_dev[virtinst.VirtualDisk.DEVICE_CDROM] = True
elif disk[2] == virtinst.VirtualDisk.DEVICE_FLOPPY and not \
found_dev.get(virtinst.VirtualDisk.DEVICE_FLOPPY, False):
boot_model.append(["Floppy", gtk.STOCK_FLOPPY, "fd"])
found_dev[virtinst.VirtualDisk.DEVICE_FLOPPY] = True
elif row[4] == HW_LIST_TYPE_NIC and not \
found_dev.get(HW_LIST_TYPE_NIC, False):
boot_model.append(["Network (PXE)", gtk.STOCK_NETWORK, "network"])
found_dev[HW_LIST_TYPE_NIC] = True
if len(boot_model) <= 0:
boot_model.append([_("No Boot Device"), None, None])
boot_combo.set_model(boot_model)
def add_hardware(self, src):
if self.addhw is None:
@ -742,8 +896,8 @@ class vmmDetails(gobject.GObject):
try:
self.vm.disconnect_cdrom_device(self.window.get_widget("disk-target-device").get_text())
except Exception, e:
self._err_dialog(_("Error Removing CDROM: %s" % str(e)),
"".join(traceback.format_exc()))
self.err.show_err(_("Error Removing CDROM: %s" % str(e)),
"".join(traceback.format_exc()))
return
else:
@ -759,21 +913,14 @@ class vmmDetails(gobject.GObject):
try:
self.vm.connect_cdrom_device(type, source, target)
except Exception, e:
self._err_dialog(_("Error Connecting CDROM: %s" % str(e)),
"".join(traceback.format_exc()))
self.err.show_err(_("Error Connecting CDROM: %s" % str(e)),
"".join(traceback.format_exc()))
def remove_device(self, xml):
try:
self.vm.remove_device(xml)
except Exception, e:
self._err_dialog(_("Error Removing Device: %s" % str(e)),
"".join(traceback.format_exc()))
self.err.show_err(_("Error Removing Device: %s" % str(e)),
"".join(traceback.format_exc()))
def _err_dialog(self, summary, details):
dg = vmmErrorDialog(None, 0, gtk.MESSAGE_ERROR,
gtk.BUTTONS_CLOSE, summary, details)
dg.run()
dg.hide()
dg.destroy()
gobject.type_register(vmmDetails)

View File

@ -115,12 +115,17 @@ class vmmDomain(gobject.GObject):
return True
return False
def get_type(self):
return self.get_xml_string("/domain/@type")
def is_vcpu_hotplug_capable(self):
# Read only connections aren't allowed to change it
if self.connection.is_read_only():
return False
# Running paravirt guests can change it, or any inactive guest
if self.vm.OSType() == "linux" or self.get_id() < 0:
if self.vm.OSType() == "linux" \
or self.status() not in [libvirt.VIR_DOMAIN_RUNNING,\
libvirt.VIR_DOMAIN_PAUSED]:
return True
# Everyone else is out of luck
return False
@ -130,7 +135,9 @@ class vmmDomain(gobject.GObject):
if self.connection.is_read_only():
return False
# Running paravirt guests can change it, or any inactive guest
if self.vm.OSType() == "linux" or self.get_id() < 0:
if self.vm.OSType() == "linux" \
or self.status() not in [libvirt.VIR_DOMAIN_RUNNING,\
libvirt.VIR_DOMAIN_PAUSED]:
return True
# Everyone else is out of luck
return False
@ -390,8 +397,14 @@ class vmmDomain(gobject.GObject):
self.vm.resume()
self._update_status()
def save(self, file, ignore1=None):
self.vm.save(file)
def save(self, file, ignore1=None, background=True):
if background:
conn = libvirt.open(self.connection.uri)
vm = conn.lookupByID(self.get_id())
else:
vm = self.vm
vm.save(file)
self._update_status()
def destroy(self):
@ -545,14 +558,18 @@ class vmmDomain(gobject.GObject):
def _change_cdrom(self, newxml, origxml):
# If vm is shutoff, remove device, and redefine with media
if not self.is_active():
logging.debug("_change_cdrom: removing original xml")
self.remove_device(origxml)
try:
logging.debug("_change_cdrom: adding new xml")
self.add_device(newxml)
except Exception, e1:
logging.debug("_change_cdrom: adding new xml failed. attempting to readd original device")
try:
self.add_device(origxml) # Try to re-add original
except:
raise e1
except Exception, e2:
raise RuntimeError(_("Failed to change cdrom and re-add original device. Exceptions were: \n%s\n%s") % (str(e1), str(e2)))
raise e1
else:
self.vm.attachDevice(newxml)
vmxml = self.vm.XMLDesc(0)
@ -566,13 +583,18 @@ class vmmDomain(gobject.GObject):
doc = libxml2.parseDoc(xml)
ctx = doc.xpathNewContext()
disk_fragment = ctx.xpathEval("/disk")
driver_fragment = ctx.xpathEval("/disk/driver")
origdisk = disk_fragment[0].serialize()
disk_fragment[0].setProp("type", type)
elem = disk_fragment[0].newChild(None, "source", None)
if type == "file":
elem.setProp("file", source)
if driver_fragment:
driver_fragment.setProp("name", type)
else:
elem.setProp("dev", source)
if driver_fragment:
driver_fragment.setProp("name", "phy")
result = disk_fragment[0].serialize()
logging.debug("connect_cdrom_device produced the following XML: %s" % result)
finally:
@ -694,7 +716,8 @@ class vmmDomain(gobject.GObject):
if type == "vnc":
listen = node.prop("listen")
port = node.prop("port")
graphics.append([type, listen, port, type])
keymap = node.prop("keymap")
graphics.append([type, listen, port, type, keymap])
else:
graphics.append([type, None, None, type])
finally:
@ -823,7 +846,10 @@ class vmmDomain(gobject.GObject):
def set_memory(self, memory):
memory = int(memory)
if (memory > self.maximum_memory()):
# capture updated information due to failing to get proper maxmem setting
# if both current & max allocation are set simultaneously
maxmem = self.vm.info()
if (memory > maxmem[1]):
logging.warning("Requested memory " + str(memory) + " over maximum " + str(self.maximum_memory()))
memory = self.maximum_memory()
self.vm.setMemory(memory)
@ -832,4 +858,64 @@ class vmmDomain(gobject.GObject):
memory = int(memory)
self.vm.setMaxMemory(memory)
def get_autostart(self):
return self.vm.autostart()
def set_autostart(self, val):
if self.get_autostart() != val:
self.vm.setAutostart(val)
def get_boot_device(self):
xml = self.get_xml()
doc = None
try:
doc = libxml2.parseDoc(xml)
except:
return []
ctx = doc.xpathNewContext()
graphics = []
dev = None
try:
ret = ctx.xpathEval("/domain/os/boot[1]")
for node in ret:
dev = node.prop("dev")
finally:
if ctx != None:
ctx.xpathFreeContext()
if doc != None:
doc.freeDoc()
return dev
def set_boot_device(self, boot_type):
logging.debug("Setting boot device to type: %s" % boot_type)
xml = self.get_xml()
doc = None
try:
doc = libxml2.parseDoc(xml)
except:
return []
ctx = doc.xpathNewContext()
graphics = []
dev = None
try:
ret = ctx.xpathEval("/domain/os/boot[1]")
if len(ret) > 0:
ret[0].unlinkNode()
ret[0].freeNode()
emptyxml=doc.serialize()
index = emptyxml.find("</os>")
newxml = emptyxml[0:index] + \
"<boot dev=\"" + boot_type + "\"/>\n" + \
emptyxml[index:]
logging.debug("New boot device, redefining with: " + newxml)
self.get_connection().define_domain(newxml)
finally:
if ctx != None:
ctx.xpathFreeContext()
if doc != None:
doc.freeDoc()
# Invalidate cached xml
self.xml = None
gobject.type_register(vmmDomain)

View File

@ -55,6 +55,10 @@ class vmmEngine(gobject.GObject):
self.windowCreate = None
self.windowManager = None
self.connections = {}
self.err = vmmErrorDialog(None,
0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
_("Unexpected Error"),
_("An unexpected error occurred"))
self.timer = None
self.last_timeout = 0
@ -66,6 +70,7 @@ class vmmEngine(gobject.GObject):
self.schedule_timer()
self.load_stored_uris()
self.autostart_connections()
self.tick()
def load_stored_uris(self):
@ -75,6 +80,12 @@ class vmmEngine(gobject.GObject):
for uri in uris:
self.add_connection(uri)
def autostart_connections(self):
for uri in self.connections:
conn = self.connections[uri]["connection"]
if conn.get_autoconnect():
self.connect_to_uri(uri)
def connect_to_uri(self, uri, readOnly=None):
return self._connect_to_uri(None, uri, readOnly)
@ -185,6 +196,10 @@ class vmmEngine(gobject.GObject):
self.show_console(uri, uuid)
def _do_show_terminal(self, src, uri, uuid):
self.show_serial_console(uri, uuid)
def _do_refresh_console(self, src, uri, uuid):
self.refresh_console(uri, uuid)
def _do_refresh_terminal(self, src, uri, uuid):
self.refresh_serial_console(uri, uuid)
def _do_save_domain(self, src, uri, uuid):
self.save_domain(src, uri, uuid)
def _do_destroy_domain(self, src, uri, uuid):
@ -220,7 +235,7 @@ class vmmEngine(gobject.GObject):
if self.connections[uri]["windowHost"] == None:
manager = vmmHost(self.get_config(), con)
manager.connect("action-show-help", self._do_show_help)
manager.connect("action-show-about", self._do_show_about)
self.connections[uri]["windowHost"] = manager
self.connections[uri]["windowHost"].show()
@ -258,6 +273,30 @@ class vmmEngine(gobject.GObject):
self.connections[uri]["windowSerialConsole"][uuid] = console
self.connections[uri]["windowSerialConsole"][uuid].show()
def refresh_console(self, uri, uuid):
con = self.get_connection(uri)
if not(self.connections[uri]["windowConsole"].has_key(uuid)):
return
console = self.connections[uri]["windowConsole"][uuid]
if not(console.is_visible()):
return
console.show()
def refresh_serial_console(self, uri, uuid):
con = self.get_connection(uri)
if not(self.connections[uri]["windowSerialConsole"].has_key(uuid)):
return
console = self.connections[uri]["windowSerialConsole"][uuid]
if not(console.is_visible()):
return
console.show()
def show_details_performance(self, uri, uuid):
win = self.show_details(uri, uuid)
win.activate_performance_page()
@ -270,17 +309,21 @@ class vmmEngine(gobject.GObject):
con = self.get_connection(uri)
if not(self.connections[uri]["windowDetails"].has_key(uuid)):
details = vmmDetails(self.get_config(),
con.get_vm(uuid))
details.connect("action-show-console", self._do_show_console)
details.connect("action-show-terminal", self._do_show_terminal)
details.connect("action-save-domain", self._do_save_domain)
details.connect("action-destroy-domain", self._do_destroy_domain)
details.connect("action-show-help", self._do_show_help)
details.connect("action-suspend-domain", self._do_suspend_domain)
details.connect("action-resume-domain", self._do_resume_domain)
details.connect("action-run-domain", self._do_run_domain)
details.connect("action-shutdown-domain", self._do_shutdown_domain)
try:
details = vmmDetails(self.get_config(),
con.get_vm(uuid))
details.connect("action-show-console", self._do_show_console)
details.connect("action-show-terminal", self._do_show_terminal)
details.connect("action-save-domain", self._do_save_domain)
details.connect("action-destroy-domain", self._do_destroy_domain)
details.connect("action-show-help", self._do_show_help)
details.connect("action-suspend-domain", self._do_suspend_domain)
details.connect("action-resume-domain", self._do_resume_domain)
details.connect("action-run-domain", self._do_run_domain)
details.connect("action-shutdown-domain", self._do_shutdown_domain)
except Exception, e:
self.err.show_err(_("Error bringing up domain details: %s") % str(e),
"".join(traceback.format_exc()))
self.connections[uri]["windowDetails"][uuid] = details
self.connections[uri]["windowDetails"][uuid].show()
return self.connections[uri]["windowDetails"][uuid]
@ -302,6 +345,8 @@ class vmmEngine(gobject.GObject):
self.windowManager.connect("action-show-host", self._do_show_host)
self.windowManager.connect("action-show-connect", self._do_show_connect)
self.windowManager.connect("action-connect", self._do_connect)
self.windowManager.connect("action-refresh-console", self._do_refresh_console)
self.windowManager.connect("action-refresh-terminal", self._do_refresh_terminal)
return self.windowManager
def show_manager(self):
@ -359,13 +404,7 @@ class vmmEngine(gobject.GObject):
def save_domain(self, src, uri, uuid):
con = self.get_connection(uri, False)
if con.is_remote():
warn = gtk.MessageDialog(src.window.get_widget("vmm-details"),
gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_WARNING,
gtk.BUTTONS_OK,
_("Saving virtual machines over remote connections is not yet supported."))
result = warn.run()
warn.destroy()
self.err.val_err(_("Saving virtual machines over remote connections is not yet supported."))
return
vm = con.get_vm(uuid)
@ -395,7 +434,7 @@ class vmmEngine(gobject.GObject):
self.fcdialog.destroy()
if self._save_callback_info != []:
self._err_dialog(_("Error saving domain: %s" % self._save_callback_info[0]), self._save_callback_info[1])
self.err.show_err(_("Error saving domain: %s" % self._save_callback_info[0]), self._save_callback_info[1])
self._save_callback_info = []
def _save_callback(self, vm, file_to_save, ignore1=None):
@ -413,19 +452,12 @@ class vmmEngine(gobject.GObject):
libvirt.VIR_DOMAIN_SHUTOFF ]:
logging.warning("Destroy requested, but machine is shutdown / shutoff")
else:
message_box = gtk.MessageDialog(None, \
gtk.DIALOG_MODAL, \
gtk.MESSAGE_WARNING, \
gtk.BUTTONS_OK_CANCEL, \
_("About to destroy virtual machine %s" % vm.get_name()))
message_box.format_secondary_text(_("This will immediately destroy the VM and may corrupt its disk image. Are you sure?"))
response_id = message_box.run()
message_box.destroy()
if response_id == gtk.RESPONSE_OK:
resp = self.err.yes_no(text1=_("About to destroy virtual machine %s" % vm.get_name()), text2=_("This will immediately destroy the VM and may corrupt its disk image. Are you sure?"))
if resp:
try:
vm.destroy()
except Exception, e:
self._err_dialog(_("Error shutting down domain: %s" % str(e)), "".join(traceback.format_exc()))
self.err.show_err(_("Error shutting down domain: %s" % str(e)), "".join(traceback.format_exc()))
def suspend_domain(self, src, uri, uuid):
con = self.get_connection(uri, False)
@ -441,8 +473,8 @@ class vmmEngine(gobject.GObject):
try:
vm.suspend()
except Exception, e:
self._err_dialog(_("Error pausing domain: %s" % str(e)),
"".join(traceback.format_exc()))
self.err.show_err(_("Error pausing domain: %s" % str(e)),
"".join(traceback.format_exc()))
def resume_domain(self, src, uri, uuid):
con = self.get_connection(uri, False)
@ -456,8 +488,8 @@ class vmmEngine(gobject.GObject):
try:
vm.resume()
except Exception, e:
self._err_dialog(_("Error unpausing domain: %s" % str(e)),
"".join(traceback.format_exc()))
self.err.show_err(_("Error unpausing domain: %s" % str(e)),
"".join(traceback.format_exc()))
else:
logging.warning("Resume requested, but machine is already running")
@ -471,8 +503,8 @@ class vmmEngine(gobject.GObject):
try:
vm.startup()
except Exception, e:
self._err_dialog(_("Error starting domain: %s" % str(e)),
"".join(traceback.format_exc()))
self.err.show_err(_("Error starting domain: %s" % str(e)),
"".join(traceback.format_exc()))
def shutdown_domain(self, src, uri, uuid):
con = self.get_connection(uri, False)
@ -484,16 +516,9 @@ class vmmEngine(gobject.GObject):
try:
vm.shutdown()
except Exception, e:
self._err_dialog(_("Error shutting down domain: %s" % str(e)),
"".join(traceback.format_exc()))
self.err.show_err(_("Error shutting down domain: %s" % str(e)),
"".join(traceback.format_exc()))
else:
logging.warning("Shutdown requested, but machine is already shutting down / shutoff")
def _err_dialog(self, summary, details):
dg = vmmErrorDialog(None, 0, gtk.MESSAGE_ERROR,
gtk.BUTTONS_CLOSE, summary, details)
dg.run()
dg.hide()
dg.destroy()
gobject.type_register(vmmEngine)

View File

@ -24,21 +24,27 @@ import pango
class vmmErrorDialog (gtk.MessageDialog):
def __init__ (self, parent=None, flags=0, type=gtk.MESSAGE_INFO,
buttons=gtk.BUTTONS_NONE, message_format=None,
message_details=None):
message_details=None, default_title=_("Error")):
gtk.MessageDialog.__init__ (self,
parent, flags, type, buttons,
message_format)
self.message_format = message_format
self.message_details = message_details
self.buffer = None
self.default_title = default_title
self.set_title(self.default_title)
self.set_property("text", self.message_format)
if not message_details is None:
# Expander section with details.
expander = gtk.Expander (_("Details"))
buffer = gtk.TextBuffer ()
buffer.set_text (message_details)
self.buffer = gtk.TextBuffer ()
self.buffer.set_text (self.message_details)
sw = gtk.ScrolledWindow ()
sw.set_shadow_type (gtk.SHADOW_IN)
sw.set_size_request (400, 240)
sw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
details = gtk.TextView (buffer)
details = gtk.TextView (self.buffer)
details.set_editable (False)
details.set_overwrite (False)
details.set_cursor_visible (False)
@ -49,3 +55,41 @@ class vmmErrorDialog (gtk.MessageDialog):
sw.show ()
self.vbox.pack_start (expander)
expander.show ()
def show_err(self, summary, details, title=None):
if title is None:
title = self.default_title
self.set_title(title)
self.set_property("text", summary)
self.buffer.set_text(details)
self.run()
self.hide()
def val_err(self, text1, text2=None, title=None):
message_box = gtk.MessageDialog(self.parent, \
gtk.DIALOG_DESTROY_WITH_PARENT, \
gtk.MESSAGE_ERROR,\
gtk.BUTTONS_OK, text1)
if title is None:
title = _("Input Error")
message_box.set_title(title)
if text2 is not None:
message_box.format_secondary_text(text2)
message_box.run()
message_box.destroy()
return False
def yes_no(self, text1, text2=None):
message_box = gtk.MessageDialog(self.parent, \
gtk.DIALOG_DESTROY_WITH_PARENT, \
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

View File

@ -27,11 +27,12 @@ import logging
import os
from virtManager.createnet import vmmCreateNetwork
from virtManager.error import vmmErrorDialog
class vmmHost(gobject.GObject):
__gsignals__ = {
"action-show-help": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, [str]),
"action-show-about": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, []),
}
def __init__(self, config, conn):
self.__gobject_init__()
@ -42,12 +43,18 @@ class vmmHost(gobject.GObject):
topwin = self.window.get_widget("vmm-host")
topwin.hide()
self.err = vmmErrorDialog(topwin,
0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
_("Unexpected Error"),
_("An unexpected error occurred"))
self.window.get_widget("overview-uri").set_text(self.conn.get_uri())
self.window.get_widget("overview-hostname").set_text(self.conn.get_hostname(True))
self.window.get_widget("overview-hypervisor").set_text(self.conn.get_driver())
self.window.get_widget("overview-memory").set_text(self.conn.pretty_host_memory_size())
self.window.get_widget("overview-cpus").set_text(str(self.conn.host_active_processor_count()))
self.window.get_widget("overview-arch").set_text(self.conn.host_architecture())
self.window.get_widget("config-autoconnect").set_active(conn.get_autoconnect())
netListModel = gtk.ListStore(str, str, str)
self.window.get_widget("net-list").set_model(netListModel)
@ -81,18 +88,20 @@ class vmmHost(gobject.GObject):
self.conn.connect("net-removed", self.repopulate_networks)
# XXX not technically correct once we enable remote management
if os.getuid() != 0 and not self.conn.is_remote():
if (os.getuid() != 0 and not self.conn.is_remote()) \
or self.conn.get_state() is self.conn.STATE_DISCONNECTED:
self.window.get_widget("net-add").set_sensitive(False)
self.window.signal_autoconnect({
"on_menu_file_close_activate": self.close,
"on_vmm_host_delete_event": self.close,
"on_menu_help_about_activate": self.show_help,
"on_menu_help_about_activate": self.show_about,
"on_net_add_clicked": self.add_network,
"on_net_delete_clicked": self.delete_network,
"on_net_stop_clicked": self.stop_network,
"on_net_start_clicked": self.start_network,
"on_config_autoconnect_toggled": self.toggle_autoconnect,
})
self.conn.connect("resources-sampled", self.refresh_resources)
@ -101,6 +110,8 @@ class vmmHost(gobject.GObject):
self.refresh_resources()
def show(self):
# Update autostart value
self.window.get_widget("config-autoconnect").set_active(self.conn.get_autoconnect())
dialog = self.window.get_widget("vmm-host")
dialog.present()
@ -126,23 +137,25 @@ class vmmHost(gobject.GObject):
def add_network(self, src):
if self.conn.is_remote():
warn = gtk.MessageDialog(self.window.get_widget("vmm-manager"),
gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_WARNING,
gtk.BUTTONS_OK,
_("Creating new networks on remote connections is not yet supported"))
result = warn.run()
warn.destroy()
self.err.val_err(_("Creating new networks on remote connections is not yet supported"))
return
if self.add is None:
self.add = vmmCreateNetwork(self.config, self.conn)
self.add.show()
def toggle_autoconnect(self, ignore=None):
if self.conn.get_autoconnect() != \
self.window.get_widget("config-autoconnect").get_active():
self.conn.toggle_autoconnect()
def show_help(self, src):
# From the Details window, show the help document from the Details page
self.emit("action-show-help", "virt-manager-host-window")
def show_about(self, src):
self.emit("action-show-about")
def close(self,ignore1=None,ignore2=None):
self.window.get_widget("vmm-host").hide()
return 1

View File

@ -7,23 +7,24 @@ keytable = {
"gb":"en-gb",
"us":"en-us",
"es":"es",
"":"et",
"et":"et",
"se_FI":"fi",
"":"fo",
"":"fr",
"fi":"fi",
"fo":"fo",
"fr":"fr",
"be":"fr-be",
"":"fr-ca",
"fr-ca":"fr-ca",
"fr_CH":"fr_ch",
"hr":"hr",
"":"hu",
"":"is",
"hu":"hu",
"is":"is",
"it":"it",
"jp106":"ja",
"lt":"lt",
"lv":"lv",
"mk":"mk",
"": "nl",
"":"nl-be",
"nl": "nl",
"nl-be":"nl-be",
"no":"no",
"pl":"pl",
"pt":"pt",
@ -31,7 +32,7 @@ keytable = {
"br-abnt2":"pt-br",
"ru":"ru",
"sl":"sl",
"":"sv",
"sv":"sv",
"th":"th",
"tr":"tr",
}

View File

@ -51,6 +51,7 @@ ROW_VCPUS = 6
ROW_MEM = 7
ROW_MEM_USAGE = 8
ROW_KEY = 9
ROW_HINT = 10
# Columns in the tree view
COL_NAME = 0
@ -70,6 +71,10 @@ class vmmManager(gobject.GObject):
gobject.TYPE_NONE, (str,str)),
"action-show-terminal": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, (str,str)),
"action-refresh-console": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, (str,str)),
"action-refresh-terminal": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, (str,str)),
"action-show-details": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, (str,str)),
"action-show-about": (gobject.SIGNAL_RUN_FIRST,
@ -96,6 +101,10 @@ class vmmManager(gobject.GObject):
def __init__(self, config, engine):
self.__gobject_init__()
self.window = gtk.glade.XML(config.get_glade_dir() + "/vmm-manager.glade", "vmm-manager", domain="virt-manager")
self.err = vmmErrorDialog(self.window.get_widget("vmm-manager"),
0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
_("Unexpected Error"),
_("An unexpected error occurred"))
self.config = config
self.engine = engine
self.connections = {}
@ -283,13 +292,7 @@ class vmmManager(gobject.GObject):
def restore_saved(self, src=None):
conn = self.current_connection()
if conn.is_remote():
warn = gtk.MessageDialog(self.window.get_widget("vmm-manager"),
gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_WARNING,
gtk.BUTTONS_OK,
_("Restoring virtual machines over remote connections is not yet supported"))
result = warn.run()
warn.destroy()
self.err.val_err(_("Restoring virtual machines over remote connections is not yet supported"))
return
# get filename
@ -312,30 +315,19 @@ class vmmManager(gobject.GObject):
_("Restoring Virtual Machine"))
progWin.run()
else:
err = gtk.MessageDialog(self.window.get_widget("vmm-manager"),
gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_OK,
_("The file '%s' does not appear to be a valid saved machine image") % file_to_load)
err.run()
err.destroy()
self.err.val_err(_("The file '%s' does not appear to be a valid saved machine image") % file_to_load)
return
self.fcdialog.destroy()
if(self.domain_restore_error != ""):
self.error_msg = gtk.MessageDialog(self.window.get_widget("vmm-manager"),
gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_OK,
self.domain_restore_error)
self.error_msg.run()
self.error_msg.destroy()
self.err.val_err(self.domain_restore_error)
self.domain_restore_error = ""
def is_valid_saved_image(self, file):
try:
f = open(file, "r")
magic = f.read(16)
if magic != "LinuxGuestRecord":
if magic != "LinuxGuestRecord" and magic != "LibvirtQemudSave":
return False
return True
except:
@ -403,6 +395,9 @@ class vmmManager(gobject.GObject):
self.emit("action-show-console", uri, vmuuid)
elif not connect.is_remote():
self.emit("action-show-terminal", uri, vmuuid)
else:
self.emit("action-refresh-console", uri, vmuuid)
self.emit("action-refresh-terminal", uri, vmuuid)
def _append_vm(self, model, vm, conn):
logging.debug("About to append vm: %s" % vm.get_name())
@ -418,6 +413,8 @@ class vmmManager(gobject.GObject):
row.insert(ROW_MEM, vm.get_memory_pretty())
row.insert(ROW_MEM_USAGE, vm.current_memory_percentage())
row.insert(ROW_KEY, vm.get_uuid())
row.insert(ROW_HINT, None)
iter = model.append(parent, row)
path = model.get_path(iter)
self.rows[vm.get_uuid()] = model[path]
@ -436,6 +433,8 @@ class vmmManager(gobject.GObject):
row.insert(ROW_MEM, conn.pretty_current_memory())
row.insert(ROW_MEM_USAGE, conn.current_memory_percentage())
row.insert(ROW_KEY, conn.get_uri())
row.insert(ROW_HINT, conn.get_uri())
iter = model.append(None, row)
path = model.get_path(iter)
self.rows[conn.get_uri()] = model[path]
@ -501,6 +500,15 @@ class vmmManager(gobject.GObject):
row[ROW_MEM_USAGE] = vm.current_memory_percentage()
model.row_changed(row.path, row.iter)
if vm == self.current_vm():
if vm.is_active():
self.window.get_widget("vm-delete").set_sensitive(False)
self.window.get_widget("menu_edit_delete").set_sensitive(False)
else:
self.window.get_widget("vm-delete").set_sensitive(True)
self.window.get_widget("menu_edit_delete").set_sensitive(True)
def conn_state_changed(self, conn):
self.conn_refresh_resources(conn)
@ -711,13 +719,7 @@ class vmmManager(gobject.GObject):
def new_vm(self, ignore=None):
conn = self.current_connection()
if conn.is_remote():
warn = gtk.MessageDialog(self.window.get_widget("vmm-manager"),
gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_WARNING,
gtk.BUTTONS_OK,
_("Creating new guests on remote connections is not yet supported"))
result = warn.run()
warn.destroy()
self.err.val_err(_("Creating new guests on remote connections is not yet supported"))
else:
self.emit("action-show-create", conn.get_uri())
@ -729,14 +731,8 @@ class vmmManager(gobject.GObject):
if conn is None:
return
warn = gtk.MessageDialog(self.window.get_widget("vmm-manager"),
gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_WARNING,
gtk.BUTTONS_YES_NO,
_("This will permanently delete the connection \"%s\", are you sure?") % self.rows[conn.get_uri()][ROW_NAME])
result = warn.run()
warn.destroy()
if result == gtk.RESPONSE_NO:
result = self.err.yes_no(_("This will permanently delete the connection \"%s\", are you sure?") % self.rows[conn.get_uri()][ROW_NAME])
if not result:
return
self.engine.remove_connection(conn.get_uri())
else:
@ -746,21 +742,15 @@ class vmmManager(gobject.GObject):
return
# are you sure you want to delete this VM?
warn = gtk.MessageDialog(self.window.get_widget("vmm-manager"),
gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_WARNING,
gtk.BUTTONS_YES_NO,
_("This will permanently delete the vm \"%s,\" are you sure?") % vm.get_name())
result = warn.run()
warn.destroy()
if result == gtk.RESPONSE_NO:
result = self.err.yes_no(_("This will permanently delete the vm \"%s,\" are you sure?") % vm.get_name())
if not result:
return
conn = vm.get_connection()
try:
vm.delete()
except Exception, e:
self._err_dialog(_("Error deleting domain: %s" % str(e)),\
"".join(traceback.format_exc()))
self.err.show_err(_("Error deleting domain: %s" % str(e)),\
"".join(traceback.format_exc()))
conn.tick(noStatsUpdate=True)
def show_about(self, src):
@ -780,8 +770,14 @@ class vmmManager(gobject.GObject):
vmlist = self.window.get_widget("vm-list")
# Handle, name, ID, status, status icon, cpu, [cpu graph], vcpus, mem, mem bar, uuid
model = gtk.TreeStore(object, str, str, str, gtk.gdk.Pixbuf, str, int, str, int, str)
model = gtk.TreeStore(object, str, str, str, gtk.gdk.Pixbuf, str, int, str, int, str, str)
vmlist.set_model(model)
try:
vmlist.set_tooltip_column(ROW_HINT)
except:
# Catch & ignore errors - set_tooltip_column is in gtk >= 2.12
# and we can easily work with lower versions
pass
nameCol = gtk.TreeViewColumn(_("Name"))
idCol = gtk.TreeViewColumn(_("ID"))
@ -1020,30 +1016,17 @@ class vmmManager(gobject.GObject):
def _connect_error(self, conn, details):
if conn.get_driver() == "xen" and not conn.is_remote():
dg = vmmErrorDialog (None, 0, gtk.MESSAGE_ERROR,
gtk.BUTTONS_CLOSE,
_("Unable to open a connection to the Xen hypervisor/daemon.\n\n" +
"Verify that:\n" +
" - A Xen host kernel was booted\n" +
" - The Xen service has been started\n"),
details)
self.err.show_err(_("Unable to open a connection to the Xen hypervisor/daemon.\n\n" +
"Verify that:\n" +
" - A Xen host kernel was booted\n" +
" - The Xen service has been started\n"),
details,
title=_("Virtual Machine Manager Connection Failure"))
else:
dg = vmmErrorDialog (None, 0, gtk.MESSAGE_ERROR,
gtk.BUTTONS_CLOSE,
_("Unable to open a connection to the libvirt management daemon.\n\n" +
"Verify that:\n" +
" - The 'libvirtd' daemon has been started\n"),
details)
dg.set_title(_("Virtual Machine Manager Connection Failure"))
dg.run()
dg.hide()
dg.destroy()
def _err_dialog(self, summary, details):
dg = vmmErrorDialog(None, 0, gtk.MESSAGE_ERROR,
gtk.BUTTONS_CLOSE, summary, details)
dg.run()
dg.hide()
dg.destroy()
self.err.show_err(_("Unable to open a connection to the libvirt management daemon.\n\n" +
"Verify that:\n" +
" - The 'libvirtd' daemon has been started\n"),
details,
title=_("Virtual Machine Manager Connection Failure"))
gobject.type_register(vmmManager)

View File

@ -52,10 +52,11 @@ class vmmOpticalDriveHelper(gobject.GObject):
# Find info about all current present media
for d in self.hal_iface.FindDeviceByCapability("volume"):
vol = self.bus.get_object("org.freedesktop.Hal", d)
if vol.GetPropertyBoolean("volume.is_disc") and \
vol.GetPropertyBoolean("volume.disc.has_data"):
devnode = vol.GetProperty("block.device")
label = vol.GetProperty("volume.label")
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
@ -63,19 +64,21 @@ class vmmOpticalDriveHelper(gobject.GObject):
for d in self.hal_iface.FindDeviceByCapability("storage.cdrom"):
dev = self.bus.get_object("org.freedesktop.Hal", d)
devnode = dev.GetProperty("block.device")
devif = dbus.Interface(dev, "org.freedesktop.Hal.Device")
devnode = devif.GetProperty("block.device")
if vollabel.has_key(devnode):
self.model.append([devnode, vollabel[devnode], True, volpath[devnode]])
self.model.append([devnode, "%s (%s)" % (vollabel[devnode], devnode), True, volpath[devnode]])
else:
self.model.append([devnode, _("No media present"), False, None])
self.model.append([devnode, "%s (%s)" % (_("No media present"), devnode), False, None])
def _device_added(self, path):
vol = self.bus.get_object("org.freedesktop.Hal", path)
if vol.QueryCapability("volume"):
if vol.GetPropertyBoolean("volume.is_disc") and \
vol.GetPropertyBoolean("volume.disc.has_data"):
devnode = vol.GetProperty("block.device")
label = vol.GetProperty("volume.label")
volif = dbus.Interface(vol, "org.freedesktop.Hal.Device")
if volif.QueryCapability("volume"):
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
@ -88,8 +91,6 @@ class vmmOpticalDriveHelper(gobject.GObject):
row[3] = path
def _device_removed(self, path):
vol = self.bus.get_object("org.freedesktop.Hal", path)
active = self.widget.get_active()
idx = 0
# Search for the row containing matching HAL volume path

View File

@ -25,7 +25,7 @@
</property>
<property name="wrap_license">False</property>
<property name="website">http://virt-manager.et.redhat.com/</property>
<property name="website_label" translatable="yes">http://virt-manager.et.redhat.com/</property>
<property name="website_label">http://virt-manager.et.redhat.com/</property>
<property name="authors">Daniel P. Berrange &lt;berrange@redhat.com&gt;
Hugh O. Brock &lt;hbrock@redhat.com&gt;
Máirín Duffy &lt;duffy@redhat.com&gt;

File diff suppressed because it is too large Load Diff

View File

@ -151,6 +151,16 @@
</widget>
</child>
<child>
<widget class="GtkCheckMenuItem" id="menu-view-scale-display">
<property name="visible">True</property>
<property name="label" translatable="yes">Scale display</property>
<property name="use_underline">True</property>
<property name="active">False</property>
<signal name="activate" handler="on_menu_view_scale_display_activate" last_modification_time="Fri, 07 Mar 2008 23:45:52 GMT"/>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="menu-view-serial">
<property name="visible">True</property>
@ -749,21 +759,7 @@
<property name="shadow_type">GTK_SHADOW_NONE</property>
<child>
<widget class="GtkAlignment" id="console-vnc-align">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<placeholder/>
</child>
</widget>
<placeholder/>
</child>
</widget>
</child>

View File

@ -1084,7 +1084,7 @@
<child>
<widget class="GtkLabel" id="net-info-size">
<property name="visible">True</property>
<property name="label" translatable="yes">256</property>
<property name="label">256</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -1112,7 +1112,7 @@
<child>
<widget class="GtkLabel" id="net-info-gateway">
<property name="visible">True</property>
<property name="label" translatable="yes">192.168.1.1</property>
<property name="label">192.168.1.1</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -1140,7 +1140,7 @@
<child>
<widget class="GtkLabel" id="net-info-broadcast">
<property name="visible">True</property>
<property name="label" translatable="yes">192.168.1.255</property>
<property name="label">192.168.1.255</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -1168,7 +1168,7 @@
<child>
<widget class="GtkLabel" id="net-info-netmask">
<property name="visible">True</property>
<property name="label" translatable="yes">255.255.255.0</property>
<property name="label">255.255.255.0</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -1938,7 +1938,7 @@
<child>
<widget class="GtkLabel" id="summary-name">
<property name="visible">True</property>
<property name="label" translatable="yes">demo</property>
<property name="label">demo</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -1965,7 +1965,7 @@
<child>
<widget class="GtkLabel" id="summary-ip4-network">
<property name="visible">True</property>
<property name="label" translatable="yes">192.168.10.0/24</property>
<property name="label">192.168.10.0/24</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -1992,7 +1992,7 @@
<child>
<widget class="GtkLabel" id="summary-dhcp-end">
<property name="visible">True</property>
<property name="label" translatable="yes">5 GB</property>
<property name="label">5 GB</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -2019,7 +2019,7 @@
<child>
<widget class="GtkLabel" id="summary-ip4-gateway">
<property name="visible">True</property>
<property name="label" translatable="yes">192.168.10.254</property>
<property name="label">192.168.10.254</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -2047,7 +2047,7 @@
<child>
<widget class="GtkLabel" id="summary-ip4-netmask">
<property name="visible">True</property>
<property name="label" translatable="yes">255.255.255.0</property>
<property name="label">255.255.255.0</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -2075,7 +2075,7 @@
<child>
<widget class="GtkLabel" id="summary-dhcp-start">
<property name="visible">True</property>
<property name="label" translatable="yes">/xen/demo.img</property>
<property name="label">/xen/demo.img</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -2355,7 +2355,7 @@
<child>
<widget class="GtkLabel" id="label379">
<property name="visible">True</property>
<property name="label" translatable="yes"> </property>
<property name="label"> </property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -127,7 +127,7 @@
<widget class="GtkTable" id="table1">
<property name="border_width">3</property>
<property name="visible">True</property>
<property name="n_rows">6</property>
<property name="n_rows">7</property>
<property name="n_columns">2</property>
<property name="homogeneous">False</property>
<property name="row_spacing">3</property>
@ -276,7 +276,7 @@
<child>
<widget class="GtkLabel" id="overview-hostname">
<property name="visible">True</property>
<property name="label" translatable="yes">example.com</property>
<property name="label">example.com</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -330,7 +330,7 @@
<child>
<widget class="GtkLabel" id="overview-memory">
<property name="visible">True</property>
<property name="label" translatable="yes">2000 MB</property>
<property name="label">2000 MB</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -357,7 +357,7 @@
<child>
<widget class="GtkLabel" id="overview-cpus">
<property name="visible">True</property>
<property name="label" translatable="yes">4</property>
<property name="label">4</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -384,7 +384,7 @@
<child>
<widget class="GtkLabel" id="overview-arch">
<property name="visible">True</property>
<property name="label" translatable="yes">x86_64</property>
<property name="label">x86_64</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -439,7 +439,7 @@
<child>
<widget class="GtkLabel" id="overview-uri">
<property name="visible">True</property>
<property name="label" translatable="yes">example.com</property>
<property name="label">example.com</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -463,6 +463,57 @@
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label72">
<property name="visible">True</property>
<property name="label" translatable="yes">Autoconnect:</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="left_attach">0</property>
<property name="right_attach">1</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="config-autoconnect">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="active">False</property>
<property name="inconsistent">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_config_autoconnect_toggled" last_modification_time="Mon, 24 Mar 2008 15:38:20 GMT"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">6</property>
<property name="bottom_attach">7</property>
<property name="x_options">fill</property>
<property name="y_options"></property>
</packing>
</child>
</widget>
</child>
</widget>
@ -586,7 +637,7 @@
<child>
<widget class="GtkLabel" id="performance-cpu">
<property name="visible">True</property>
<property name="label" translatable="yes">60%</property>
<property name="label">60%</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
@ -614,7 +665,7 @@
<child>
<widget class="GtkLabel" id="performance-memory">
<property name="visible">True</property>
<property name="label" translatable="yes">1.59 GB of 2.2 GB</property>
<property name="label">1.59 GB of 2.2 GB</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>

View File

@ -21,7 +21,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires: pygtk2 >= 1.99.12-6
Requires: gnome-python2-gconf >= 1.99.11-7
# Absolutely require this version or newer
Requires: libvirt-python >= 0.4.0-1
Requires: libvirt-python >= 0.4.1
# Definitely does not work with earlier due to python API changes
Requires: dbus-python >= 0.61
# Might work with earlier, but this is what we've tested
@ -35,7 +35,7 @@ Requires: gnome-python2-gnomevfs >= 2.15.4
# Minimum we've tested with
Requires: libxml2-python >= 2.6.23
# Required to install Xen & QEMU guests
Requires: python-virtinst >= 0.300.2
Requires: python-virtinst >= 0.300.3
# Required for loading the glade UI
Requires: pygtk2-libglade
# Required for our graphics which are currently SVG format
@ -45,7 +45,7 @@ Requires: vte >= 0.12.2
# For online help
Requires: scrollkeeper
# For console widget
Requires: gtk-vnc-python
Requires: gtk-vnc-python >= 0.3.4
# For local authentication against PolicyKit
Requires: PolicyKit-gnome
@ -142,6 +142,10 @@ fi
%{_datadir}/man/man1/%{name}.1*
%changelog
* Mon Mar 10 2008 Daniel P Berrange <berrange@redhat.com> - 0.5.4-1
- Use capabilities XML when creating guests
- Allow scaling of VNC window
* Thu Jan 10 2008 Daniel P Berrange <berrange@redhat.com> - 0.5.3-1
- Reintroduce 'new' button
- Make restore work again