mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-03-09 08:58:27 +03:00
Pull add hardware functionality out into separate popup window to simplify state management in details window. Implemenet adding of disks
This commit is contained in:
parent
b75b7eb57f
commit
d263695fa4
570
src/virtManager/addhardware.py
Normal file
570
src/virtManager/addhardware.py
Normal file
@ -0,0 +1,570 @@
|
||||
#
|
||||
# Copyright (C) 2006-2007 Red Hat, Inc.
|
||||
# Copyright (C) 2006 Hugh O. Brock <hbrock@redhat.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
#
|
||||
|
||||
import gobject
|
||||
import gtk
|
||||
import gtk.gdk
|
||||
import gtk.glade
|
||||
import pango
|
||||
import libvirt
|
||||
import virtinst
|
||||
import os, sys
|
||||
import re
|
||||
import subprocess
|
||||
import urlgrabber.progress as progress
|
||||
import tempfile
|
||||
import logging
|
||||
import dbus
|
||||
import traceback
|
||||
|
||||
from virtManager.asyncjob import vmmAsyncJob
|
||||
from virtManager.error import vmmErrorDialog
|
||||
|
||||
VM_STORAGE_PARTITION = 1
|
||||
VM_STORAGE_FILE = 2
|
||||
|
||||
DEFAULT_STORAGE_FILE_SIZE = 500
|
||||
|
||||
PAGE_INTRO = 0
|
||||
PAGE_DISK = 1
|
||||
PAGE_NETWORK = 2
|
||||
PAGE_SUMMARY = 3
|
||||
|
||||
class vmmCreateMeter(progress.BaseMeter):
|
||||
def __init__(self, asyncjob):
|
||||
# progress meter has to run asynchronously, so pass in the
|
||||
# async job to call back to with progress info
|
||||
progress.BaseMeter.__init__(self)
|
||||
self.asyncjob = asyncjob
|
||||
|
||||
def _do_start(self, now):
|
||||
if self.text is not None:
|
||||
text = self.text
|
||||
else:
|
||||
text = self.basename
|
||||
if self.size is None:
|
||||
out = " %5sB" % (0)
|
||||
self.asyncjob.pulse_pbar(out, text)
|
||||
else:
|
||||
out = "%3i%% %5sB" % (0, 0)
|
||||
self.asyncjob.set_pbar_fraction(0, out, text)
|
||||
|
||||
def _do_update(self, amount_read, now=None):
|
||||
if self.text is not None:
|
||||
text = self.text
|
||||
else:
|
||||
text = self.basename
|
||||
fread = progress.format_number(amount_read)
|
||||
if self.size is None:
|
||||
out = " %5sB" % (fread)
|
||||
self.asyncjob.pulse_pbar(out, text)
|
||||
else:
|
||||
frac = self.re.fraction_read()
|
||||
out = "%3i%% %5sB" % (frac*100, fread)
|
||||
self.asyncjob.set_pbar_fraction(frac, out, text)
|
||||
|
||||
def _do_end(self, amount_read, now=None):
|
||||
if self.text is not None:
|
||||
text = self.text
|
||||
else:
|
||||
text = self.basename
|
||||
fread = progress.format_number(amount_read)
|
||||
if self.size is None:
|
||||
out = " %5sB" % (fread)
|
||||
self.asyncjob.pulse_pbar(out, text)
|
||||
else:
|
||||
out = "%3i%% %5sB" % (100, fread)
|
||||
self.asyncjob.set_pbar_done(out, text)
|
||||
|
||||
class vmmAddHardware(gobject.GObject):
|
||||
__gsignals__ = {
|
||||
"action-show-help": (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, [str]),
|
||||
}
|
||||
def __init__(self, config, vm):
|
||||
self.__gobject_init__()
|
||||
self.config = config
|
||||
self.vm = vm
|
||||
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.topwin.hide()
|
||||
self.window.signal_autoconnect({
|
||||
"on_create_pages_switch_page" : self.page_changed,
|
||||
"on_create_cancel_clicked" : self.close,
|
||||
"on_vmm_create_delete_event" : self.close,
|
||||
"on_create_back_clicked" : self.back,
|
||||
"on_create_forward_clicked" : self.forward,
|
||||
"on_create_finish_clicked" : self.finish,
|
||||
"on_storage_partition_address_browse_clicked" : self.browse_storage_partition_address,
|
||||
"on_storage_file_address_browse_clicked" : self.browse_storage_file_address,
|
||||
"on_storage_file_address_changed": self.toggle_storage_size,
|
||||
"on_storage_toggled" : self.change_storage_type,
|
||||
"on_network_toggled" : self.change_network_type,
|
||||
"on_create_help_clicked": self.show_help,
|
||||
})
|
||||
|
||||
hw_list = self.window.get_widget("hardware-type")
|
||||
model = gtk.ListStore(str, gtk.gdk.Pixbuf, int)
|
||||
hw_list.set_model(model)
|
||||
icon = gtk.CellRendererPixbuf()
|
||||
hw_list.pack_start(icon, False)
|
||||
hw_list.add_attribute(icon, 'pixbuf', 1)
|
||||
text = gtk.CellRendererText()
|
||||
hw_list.pack_start(text, True)
|
||||
hw_list.add_attribute(text, 'text', 0)
|
||||
|
||||
pixbuf_disk = gtk.gdk.pixbuf_new_from_file(config.get_icon_dir() + "/icon_hdd.png")
|
||||
pixbuf_nic = gtk.gdk.pixbuf_new_from_file(config.get_icon_dir() + "/icon_ethernet.png")
|
||||
|
||||
model.append(["Storage device", pixbuf_disk, PAGE_DISK])
|
||||
model.append(["Network card", pixbuf_nic, PAGE_NETWORK])
|
||||
|
||||
self.set_initial_state()
|
||||
|
||||
def show(self):
|
||||
self.reset_state()
|
||||
self.topwin.show()
|
||||
self.topwin.present()
|
||||
|
||||
def set_initial_state(self):
|
||||
notebook = self.window.get_widget("create-pages")
|
||||
notebook.set_show_tabs(False)
|
||||
|
||||
#XXX I don't think I should have to go through and set a bunch of background colors
|
||||
# in code, but apparently I do...
|
||||
black = gtk.gdk.color_parse("#000")
|
||||
for num in range(PAGE_SUMMARY+1):
|
||||
name = "page" + str(num) + "-title"
|
||||
self.window.get_widget(name).modify_bg(gtk.STATE_NORMAL,black)
|
||||
|
||||
if os.getuid() != 0:
|
||||
self.window.get_widget("storage-partition").set_sensitive(False)
|
||||
|
||||
# set up the lists for the networks
|
||||
network_list = self.window.get_widget("net-network")
|
||||
network_model = gtk.ListStore(str, str)
|
||||
network_list.set_model(network_model)
|
||||
text = gtk.CellRendererText()
|
||||
network_list.pack_start(text, True)
|
||||
network_list.add_attribute(text, 'text', 1)
|
||||
|
||||
device_list = self.window.get_widget("net-device")
|
||||
device_model = gtk.ListStore(str)
|
||||
device_list.set_model(device_model)
|
||||
text = gtk.CellRendererText()
|
||||
device_list.pack_start(text, True)
|
||||
device_list.add_attribute(text, 'text', 0)
|
||||
|
||||
def reset_state(self):
|
||||
notebook = self.window.get_widget("create-pages")
|
||||
notebook.set_current_page(0)
|
||||
# Hide the "finish" button until the appropriate time
|
||||
self.window.get_widget("create-finish").hide()
|
||||
self.window.get_widget("create-forward").show()
|
||||
self.window.get_widget("create-back").set_sensitive(False)
|
||||
self.window.get_widget("storage-file-size").set_sensitive(False)
|
||||
|
||||
self.change_storage_type()
|
||||
self.change_network_type()
|
||||
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-partition-address").set_text("")
|
||||
self.window.get_widget("storage-file-address").set_text("")
|
||||
self.window.get_widget("storage-file-size").set_value(2000)
|
||||
self.window.get_widget("non-sparse").set_active(True)
|
||||
|
||||
model = self.window.get_widget("net-network").get_model()
|
||||
self.populate_network_model(model)
|
||||
device = self.window.get_widget("net-device").get_model()
|
||||
self.populate_device_model(device)
|
||||
|
||||
|
||||
def forward(self, ignore=None):
|
||||
notebook = self.window.get_widget("create-pages")
|
||||
if(self.validate(notebook.get_current_page()) != True):
|
||||
return
|
||||
|
||||
if notebook.get_current_page() == PAGE_INTRO:
|
||||
notebook.set_current_page(self.get_config_hardware_type())
|
||||
else:
|
||||
notebook.set_current_page(PAGE_SUMMARY)
|
||||
self.window.get_widget("create-finish").show()
|
||||
self.window.get_widget("create-forward").hide()
|
||||
self.window.get_widget("create-back").set_sensitive(True)
|
||||
|
||||
def back(self, ignore=None):
|
||||
notebook = self.window.get_widget("create-pages")
|
||||
|
||||
if notebook.get_current_page() == PAGE_SUMMARY:
|
||||
notebook.set_current_page(self.get_config_hardware_type())
|
||||
self.window.get_widget("create-finish").hide()
|
||||
else:
|
||||
notebook.set_current_page(PAGE_INTRO)
|
||||
self.window.get_widget("create-back").set_sensitive(False)
|
||||
self.window.get_widget("create-forward").show()
|
||||
|
||||
def get_config_hardware_type(self):
|
||||
type = self.window.get_widget("hardware-type")
|
||||
if type.get_active_iter() == None:
|
||||
return None
|
||||
return type.get_model().get_value(type.get_active_iter(), 2)
|
||||
|
||||
def get_config_disk_image(self):
|
||||
if self.window.get_widget("storage-partition").get_active():
|
||||
return self.window.get_widget("storage-partition-address").get_text()
|
||||
else:
|
||||
return self.window.get_widget("storage-file-address").get_text()
|
||||
|
||||
def get_config_disk_size(self):
|
||||
if self.window.get_widget("storage-partition").get_active():
|
||||
return None
|
||||
else:
|
||||
return self.window.get_widget("storage-file-size").get_value()
|
||||
|
||||
def get_config_network(self):
|
||||
if os.getuid() != 0:
|
||||
return ["user"]
|
||||
|
||||
if self.window.get_widget("net-type-network").get_active():
|
||||
net = self.window.get_widget("net-network")
|
||||
model = net.get_model()
|
||||
return ["network", model.get_value(net.get_active_iter(), 0)]
|
||||
else:
|
||||
dev = self.window.get_widget("net-device")
|
||||
model = dev.get_model()
|
||||
return ["bridge", model.get_value(dev.get_active_iter(), 0)]
|
||||
|
||||
def page_changed(self, notebook, page, page_number):
|
||||
if page_number == PAGE_DISK:
|
||||
pass
|
||||
elif page_number == PAGE_NETWORK:
|
||||
pass
|
||||
elif page_number == PAGE_SUMMARY:
|
||||
hwpage = self.get_config_hardware_type()
|
||||
|
||||
if hwpage == PAGE_DISK:
|
||||
self.window.get_widget("summary-disk").show()
|
||||
self.window.get_widget("summary-network").hide()
|
||||
self.window.get_widget("summary-disk-image").set_text(self.get_config_disk_image())
|
||||
disksize = self.get_config_disk_size()
|
||||
if disksize != None:
|
||||
self.window.get_widget("summary-disk-size").set_text(str(int(disksize)) + " MB")
|
||||
else:
|
||||
self.window.get_widget("summary-disk-size").set_text("-")
|
||||
elif hwpage == PAGE_NETWORK:
|
||||
self.window.get_widget("summary-disk").hide()
|
||||
self.window.get_widget("summary-network").show()
|
||||
net = self.get_config_network()
|
||||
if net[0] == "bridge":
|
||||
self.window.get_widget("summary-net-type").set_text(_("Shared physical device"))
|
||||
self.window.get_widget("summary-net-target").set_text(net[1])
|
||||
elif net[0] == "network":
|
||||
self.window.get_widget("summary-net-type").set_text(_("Virtual network"))
|
||||
self.window.get_widget("summary-net-target").set_text(net[1])
|
||||
elif net[0] == "user":
|
||||
self.window.get_widget("summary-net-type").set_text(_("Usermode networking"))
|
||||
self.window.get_widget("summary-net-target").set_text("-")
|
||||
else:
|
||||
raise ValueError, "Unknown networking type " + net[0]
|
||||
|
||||
def close(self, ignore1=None,ignore2=None):
|
||||
self.topwin.hide()
|
||||
return 1
|
||||
|
||||
def is_visible(self):
|
||||
if self.topwin.flags() & gtk.VISIBLE:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def finish(self, ignore=None):
|
||||
hw = self.get_config_hardware_type()
|
||||
|
||||
self.install_error = None
|
||||
self.topwin.set_sensitive(False)
|
||||
self.topwin.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
|
||||
|
||||
if hw == PAGE_NETWORK:
|
||||
self.add_network()
|
||||
elif hw == PAGE_DISK:
|
||||
self.add_storage()
|
||||
|
||||
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.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
|
||||
# their mistakes
|
||||
#self.close()
|
||||
return
|
||||
|
||||
self.topwin.set_sensitive(True)
|
||||
self.topwin.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.TOP_LEFT_ARROW))
|
||||
self.close()
|
||||
|
||||
def add_network(self):
|
||||
net = self.get_config_network()
|
||||
vnic = None
|
||||
if net[0] == "bridge":
|
||||
vnic = virtinst.VirtualNetworkInterface(type=net[0], bridge=net[1])
|
||||
elif net[0] == "network":
|
||||
vnic = virtinst.VirtualNetworkInterface(type=net[0], network=net[1])
|
||||
else:
|
||||
raise ValueError, "Unsupported networking type " + net[0]
|
||||
|
||||
vnic.setup(self.vm.get_connection().vmm)
|
||||
xml = vnic.get_xml_config()
|
||||
logging.debug("Adding network " + xml)
|
||||
self.vm.add_device(xml)
|
||||
|
||||
def add_storage(self):
|
||||
filesize = None
|
||||
disk = None
|
||||
if self.get_config_disk_size() != None:
|
||||
filesize = self.get_config_disk_size() / 1024.0
|
||||
try:
|
||||
disk = virtinst.VirtualDisk(self.get_config_disk_image(), filesize, sparse = self.is_sparse_file())
|
||||
if disk.type == virtinst.VirtualDisk.TYPE_FILE and \
|
||||
not self.vm.is_hvm() \
|
||||
and virtinst.util.is_blktap_capable():
|
||||
disk.driver_name = virtinst.VirtualDisk.DRIVER_TAP
|
||||
except ValueError, e:
|
||||
self._validation_error_box(_("Invalid storage address"), e.args[0])
|
||||
return
|
||||
|
||||
used = {}
|
||||
for d in self.vm.get_disk_devices():
|
||||
dev = d[3]
|
||||
used[dev] = 1
|
||||
|
||||
nodes = []
|
||||
if self.vm.is_hvm():
|
||||
for n in range(4):
|
||||
nodes.append("hd%c" % (ord('a')+n))
|
||||
else:
|
||||
for n in range(26):
|
||||
nodes.append("xvd%c" % (ord('a')+n))
|
||||
|
||||
node = None
|
||||
for n in nodes:
|
||||
if not used.has_key(n):
|
||||
node = n
|
||||
break
|
||||
|
||||
if node is None:
|
||||
self._validation_error_box(_("Too many virtual disks"),
|
||||
_("There are no more available virtual disk device nodes"))
|
||||
return
|
||||
|
||||
progWin = vmmAsyncJob(self.config, self.do_file_allocate, [disk],
|
||||
title=_("Creating Storage File"),
|
||||
text=_("Allocation of disk storage may take a few minutes " + \
|
||||
"to complete."))
|
||||
progWin.run()
|
||||
|
||||
xml = disk.get_xml_config(node)
|
||||
logging.debug("Adding disk " + xml)
|
||||
self.vm.add_device(xml)
|
||||
|
||||
def do_file_allocate(self, disk, asyncjob):
|
||||
meter = vmmCreateMeter(asyncjob)
|
||||
try:
|
||||
logging.debug("Starting background file allocate process")
|
||||
disk.setup(meter)
|
||||
logging.debug("Allocation completed")
|
||||
except:
|
||||
(type, value, stacktrace) = sys.exc_info ()
|
||||
|
||||
# Detailed error message, in English so it can be Googled.
|
||||
details = \
|
||||
"Unable to complete install '%s'" % \
|
||||
(str(type) + " " + str(value) + "\n" + \
|
||||
traceback.format_exc (stacktrace))
|
||||
|
||||
self.install_error = _("Unable to complete install: '%s'") % str(value)
|
||||
self.install_details = details
|
||||
logging.error(details)
|
||||
|
||||
def browse_storage_partition_address(self, src, ignore=None):
|
||||
part = self._browse_file(_("Locate Storage Partition"), "/dev")
|
||||
if part != None:
|
||||
self.window.get_widget("storage-partition-address").set_text(part)
|
||||
|
||||
def browse_storage_file_address(self, src, ignore=None):
|
||||
self.window.get_widget("storage-file-size").set_sensitive(True)
|
||||
fcdialog = gtk.FileChooserDialog(_("Locate or Create New Storage File"),
|
||||
self.window.get_widget("vmm-create"),
|
||||
gtk.FILE_CHOOSER_ACTION_SAVE,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT),
|
||||
None)
|
||||
|
||||
fcdialog.set_current_folder(self.config.get_default_image_dir(self.vm.get_connection()))
|
||||
fcdialog.set_do_overwrite_confirmation(True)
|
||||
fcdialog.connect("confirm-overwrite", self.confirm_overwrite_callback)
|
||||
response = fcdialog.run()
|
||||
fcdialog.hide()
|
||||
file = None
|
||||
if(response == gtk.RESPONSE_ACCEPT):
|
||||
file = fcdialog.get_filename()
|
||||
if file != None:
|
||||
self.window.get_widget("storage-file-address").set_text(file)
|
||||
|
||||
def toggle_storage_size(self, ignore1=None, ignore2=None):
|
||||
file = self.get_config_disk_image()
|
||||
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)
|
||||
else:
|
||||
self.window.get_widget("storage-file-size").set_sensitive(False)
|
||||
self.window.get_widget("non-sparse").set_sensitive(False)
|
||||
if os.path.isfile(file):
|
||||
size = os.path.getsize(file)/(1024*1024)
|
||||
self.window.get_widget("storage-file-size").set_value(size)
|
||||
else:
|
||||
self.window.get_widget("storage-file-size").set_value(0)
|
||||
|
||||
def confirm_overwrite_callback(self, chooser):
|
||||
# Only called when the user has chosen an existing file
|
||||
self.window.get_widget("storage-file-size").set_sensitive(False)
|
||||
return gtk.FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME
|
||||
|
||||
def change_storage_type(self, ignore=None):
|
||||
if self.window.get_widget("storage-partition").get_active():
|
||||
self.window.get_widget("storage-partition-box").set_sensitive(True)
|
||||
self.window.get_widget("storage-file-box").set_sensitive(False)
|
||||
self.window.get_widget("storage-file-size").set_sensitive(False)
|
||||
self.window.get_widget("non-sparse").set_sensitive(False)
|
||||
else:
|
||||
self.window.get_widget("storage-partition-box").set_sensitive(False)
|
||||
self.window.get_widget("storage-file-box").set_sensitive(True)
|
||||
self.toggle_storage_size()
|
||||
|
||||
def change_network_type(self, ignore=None):
|
||||
if self.window.get_widget("net-type-network").get_active():
|
||||
self.window.get_widget("net-network").set_sensitive(True)
|
||||
self.window.get_widget("net-device").set_sensitive(False)
|
||||
else:
|
||||
self.window.get_widget("net-network").set_sensitive(False)
|
||||
self.window.get_widget("net-device").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
|
||||
|
||||
elif page_num == PAGE_DISK:
|
||||
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
|
||||
|
||||
if not self.window.get_widget("storage-partition").get_active():
|
||||
if os.path.isdir(disk):
|
||||
self._validation_error_box(_("Storage Address Is Directory"), \
|
||||
_("You chose 'Simple File' storage for your storage method, but chose a directory instead of a file. Please enter a new filename or choose an existing file."))
|
||||
return False
|
||||
|
||||
d = virtinst.VirtualDisk(self.get_config_disk_image(), self.get_config_disk_size(), sparse = self.is_sparse_file())
|
||||
if d.is_conflict_disk(self.vm.get_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 ?"))
|
||||
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
|
||||
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 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_network_model(self, model):
|
||||
model.clear()
|
||||
for uuid in self.vm.get_connection().list_net_uuids():
|
||||
net = self.vm.get_connection().get_net(uuid)
|
||||
model.append([net.get_label(), net.get_name()])
|
||||
|
||||
def populate_device_model(self, model):
|
||||
model.clear()
|
||||
for name in self.vm.get_connection().list_net_device_paths():
|
||||
net = self.vm.get_connection().get_net_device(name)
|
||||
if net.is_shared():
|
||||
model.append([net.get_bridge()])
|
||||
|
||||
def is_sparse_file(self):
|
||||
if self.window.get_widget("non-sparse").get_active():
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def show_help(self, src):
|
||||
# help to show depends on the notebook page, yahoo
|
||||
page = self.window.get_widget("create-pages").get_current_page()
|
||||
if page == PAGE_INTRO:
|
||||
self.emit("action-show-help", "virt-manager-create-wizard")
|
||||
elif page == PAGE_DISK:
|
||||
self.emit("action-show-help", "virt-manager-storage-space")
|
||||
elif page == PAGE_NETWORK:
|
||||
self.emit("action-show-help", "virt-manager-network")
|
||||
|
||||
gobject.type_register(vmmAddHardware)
|
@ -26,6 +26,7 @@ import logging
|
||||
import traceback
|
||||
|
||||
from virtManager.error import vmmErrorDialog
|
||||
from virtManager.addhardware import vmmAddHardware
|
||||
|
||||
import virtinst
|
||||
import urlgrabber.progress as progress
|
||||
@ -35,7 +36,6 @@ VMM_HW_CPU = 0
|
||||
VMM_HW_MEMORY = 1
|
||||
VMM_HW_DISK = 2
|
||||
VMM_HW_NIC = 3
|
||||
VMM_HW_DEVICES = [_("Virtual Disk"), _("Virtual NIC")]
|
||||
|
||||
class vmmDetails(gobject.GObject):
|
||||
__gsignals__ = {
|
||||
@ -68,6 +68,7 @@ class vmmDetails(gobject.GObject):
|
||||
|
||||
self.window.get_widget("hw-panel").set_show_tabs(False)
|
||||
|
||||
self.addhw = None
|
||||
|
||||
self.cpu_usage_graph = sparkline.Sparkline()
|
||||
self.window.get_widget("graph-table").attach(self.cpu_usage_graph, 1, 2, 0, 1)
|
||||
@ -75,7 +76,6 @@ class vmmDetails(gobject.GObject):
|
||||
self.memory_usage_graph = sparkline.Sparkline()
|
||||
self.window.get_widget("graph-table").attach(self.memory_usage_graph, 1, 2, 1, 2)
|
||||
|
||||
|
||||
self.network_traffic_graph = sparkline.Sparkline()
|
||||
self.window.get_widget("graph-table").attach(self.network_traffic_graph, 1, 2, 3, 4)
|
||||
|
||||
@ -111,45 +111,21 @@ class vmmDetails(gobject.GObject):
|
||||
"on_storage_file_address_changed": self.toggle_storage_size,
|
||||
"on_storage_toggled" : self.change_storage_type,
|
||||
"on_add_hardware_button_clicked": self.add_hardware,
|
||||
"on_vnic_apply_clicked": self.add_vnic,
|
||||
"on_vnic_cancel_clicked": self.clean_up_add_hardware,
|
||||
"on_vbd_add_apply_clicked": self.add_vbd,
|
||||
"on_vbd_add_cancel_clicked": self.clean_up_add_hardware,
|
||||
|
||||
})
|
||||
|
||||
self.vm.connect("status-changed", self.update_widget_states)
|
||||
self.vm.connect("resources-sampled", self.refresh_resources)
|
||||
self.window.get_widget("hw-list").get_selection().connect("changed", self.hw_selected)
|
||||
|
||||
# set up the list for new hardware devices
|
||||
hw_type_list = self.window.get_widget("add-hardware-device")
|
||||
hw_type_model = hw_type_list.get_model()
|
||||
for device_name in VMM_HW_DEVICES:
|
||||
hw_type_model.append([device_name])
|
||||
hw_type_list.set_active(0)
|
||||
|
||||
# list for network pulldown
|
||||
network_list = self.window.get_widget("network-name-pulldown")
|
||||
network_model = gtk.ListStore(str, str, str)
|
||||
network_list.set_model(network_model)
|
||||
text = gtk.CellRendererText()
|
||||
network_list.pack_start(text, True)
|
||||
network_list.add_attribute(text, 'text', 0)
|
||||
|
||||
#using this as the flag for whether the network page is in edit mode. Ugh.
|
||||
self.adding_hardware = False
|
||||
|
||||
self.update_widget_states(vm, vm.status())
|
||||
self.refresh_resources(vm)
|
||||
|
||||
self.pixbuf_processor = gtk.gdk.pixbuf_new_from_file(config.get_icon_dir() + "/icon_cpu.png")
|
||||
self.pixbuf_memory = gtk.gdk.pixbuf_new_from_file(config.get_icon_dir() + "/icon_cpu.png")
|
||||
self.pixbuf_disk = gtk.gdk.pixbuf_new_from_file(config.get_icon_dir() + "/icon_hdd.png")
|
||||
self.pixbuf_network = gtk.gdk.pixbuf_new_from_file(config.get_icon_dir() + "/icon_ethernet.png")
|
||||
self.pixbuf_nic = gtk.gdk.pixbuf_new_from_file(config.get_icon_dir() + "/icon_ethernet.png")
|
||||
self.prepare_hw_list()
|
||||
self.hw_selected()
|
||||
|
||||
|
||||
def toggle_toolbar(self, src):
|
||||
if src.get_active():
|
||||
@ -170,8 +146,7 @@ class vmmDetails(gobject.GObject):
|
||||
|
||||
def show_help(self, src):
|
||||
# From the Details window, show the help document from the Details page
|
||||
self.emit("action-show-help", "virt-manager-details-window")
|
||||
|
||||
self.emit("action-show-help", "virt-manager-details-window")
|
||||
|
||||
def activate_performance_page(self):
|
||||
self.window.get_widget("details-pages").set_current_page(0)
|
||||
@ -189,14 +164,14 @@ class vmmDetails(gobject.GObject):
|
||||
return 0
|
||||
|
||||
def hw_selected(self, src=None):
|
||||
self.adding_hardware = False
|
||||
vmlist = self.window.get_widget("hw-list")
|
||||
selection = vmlist.get_selection()
|
||||
active = selection.get_selected()
|
||||
if active[1] != None:
|
||||
pagetype = active[0].get_value(active[1], 3)
|
||||
pagetype = active[0].get_value(active[1], 2)
|
||||
self.window.get_widget("hw-panel").set_sensitive(True)
|
||||
|
||||
pagenum = -1
|
||||
if pagetype == VMM_HW_CPU:
|
||||
self.window.get_widget("config-vcpus-apply").set_sensitive(False)
|
||||
self.refresh_config_cpu()
|
||||
@ -209,18 +184,13 @@ class vmmDetails(gobject.GObject):
|
||||
self.refresh_disk_page()
|
||||
pagenum = 2
|
||||
elif pagetype == VMM_HW_NIC:
|
||||
self.window.get_widget("network-name-pulldown").hide()
|
||||
self.window.get_widget("network-buttons").hide()
|
||||
self.window.get_widget("network-name").set_editable(False)
|
||||
self.window.get_widget("network-mac-address").set_editable(False)
|
||||
self.window.get_widget("net-devlabel-label").show()
|
||||
self.window.get_widget("network-device-name").show()
|
||||
self.refresh_network_page()
|
||||
pagenum = 3
|
||||
|
||||
self.window.get_widget("hw-panel").set_current_page(pagenum)
|
||||
else:
|
||||
logging.debug("In hw_selected with null tree iter")
|
||||
self.window.get_widget("hw-panel").set_sensitive(True)
|
||||
self.window.get_widget("hw-panel").set_sensitive(False)
|
||||
selection.select_path(0)
|
||||
self.window.get_widget("hw-panel").set_current_page(0)
|
||||
|
||||
@ -342,66 +312,34 @@ class vmmDetails(gobject.GObject):
|
||||
self.window.get_widget("details-menu-serial").set_sensitive(False)
|
||||
|
||||
def refresh_resources(self, ignore):
|
||||
self.refresh_summary()
|
||||
if self.window.get_widget("details-pages").get_current_page() == 1:
|
||||
details = self.window.get_widget("details-pages")
|
||||
if details.get_current_page() == 0:
|
||||
self.refresh_summary()
|
||||
else:
|
||||
#XXX for this week this only works for active domains, and it's temporary.
|
||||
if self.vm.is_active():
|
||||
self.window.get_widget("add-hardware-button").set_sensitive(True)
|
||||
else:
|
||||
self.window.get_widget("add-hardware-button").set_sensitive(False)
|
||||
|
||||
if self.adding_hardware:
|
||||
return
|
||||
|
||||
# reload the hw model, go to the correct page, and refresh that page
|
||||
hw_list = self.window.get_widget("hw-list")
|
||||
hw_panel = self.window.get_widget("hw-panel")
|
||||
selection = hw_list.get_selection()
|
||||
active = selection.get_selected()
|
||||
if active[1] != None:
|
||||
pagetype = active[0].get_value(active[1], 3)
|
||||
device_info = active[0].get_value(active[1], 4)
|
||||
self.populate_hw_list()
|
||||
pagetype = active[0].get_value(active[1], 2)
|
||||
device_info = active[0].get_value(active[1], 3)
|
||||
self.repopulate_hw_list()
|
||||
hw_model = hw_list.get_model()
|
||||
if pagetype == VMM_HW_CPU:
|
||||
self.refresh_config_cpu()
|
||||
pagenum = 0
|
||||
selection.select_path(0)
|
||||
elif pagetype == VMM_HW_MEMORY:
|
||||
self.refresh_config_memory()
|
||||
pagenum = 1
|
||||
selection.select_path(1)
|
||||
elif pagetype == VMM_HW_DISK:
|
||||
self.refresh_disk_page()
|
||||
# try to match the old source dev to one of the new source devs
|
||||
selection.select_path(0)
|
||||
pagenum = 0
|
||||
i=0
|
||||
for hw in hw_model:
|
||||
if hw[3] == VMM_HW_DISK:
|
||||
if device_info[1] == hw[4][1]:
|
||||
selection.select_path(i)
|
||||
pagenum = 2
|
||||
break
|
||||
i = i + 1
|
||||
elif pagetype == VMM_HW_NIC:
|
||||
self.refresh_network_page()
|
||||
selection.select_path(0)
|
||||
pagenum = 0
|
||||
i=0
|
||||
for hw in hw_model:
|
||||
if hw[3] == VMM_HW_NIC:
|
||||
if device_info[3] == hw[4][3]:
|
||||
selection.select_path(i)
|
||||
pagenum = 3
|
||||
break
|
||||
i = i + 1
|
||||
hw_panel.set_current_page(pagenum)
|
||||
|
||||
else:
|
||||
logging.debug("In hw_selected with null tree iter")
|
||||
hw_panel.set_sensitive(True)
|
||||
selection.select_path(0)
|
||||
hw_panel.set_current_page(0)
|
||||
|
||||
def refresh_summary(self):
|
||||
self.window.get_widget("overview-cpu-usage-text").set_text("%d %%" % self.vm.cpu_time_percentage())
|
||||
@ -460,32 +398,24 @@ class vmmDetails(gobject.GObject):
|
||||
selection = vmlist.get_selection()
|
||||
active = selection.get_selected()
|
||||
if active[1] != None:
|
||||
diskinfo = active[0].get_value(active[1], 4)
|
||||
# fill the fields on the screen
|
||||
self.window.get_widget("disk-type").set_text(diskinfo[0])
|
||||
self.window.get_widget("storage-source").set_text(diskinfo[1])
|
||||
self.window.get_widget("storage-device").set_text(diskinfo[2])
|
||||
self.window.get_widget("device-label").set_text(diskinfo[3])
|
||||
diskinfo = active[0].get_value(active[1], 3)
|
||||
self.window.get_widget("disk-source-type").set_text(diskinfo[0])
|
||||
self.window.get_widget("disk-source-path").set_text(diskinfo[1])
|
||||
self.window.get_widget("disk-target-type").set_text(diskinfo[2])
|
||||
self.window.get_widget("disk-target-device").set_text(diskinfo[3])
|
||||
|
||||
def refresh_network_page(self):
|
||||
# get the line what was clicked
|
||||
if not self.adding_hardware:
|
||||
# viewing net page, not adding a device. If adding, don't try to refresh
|
||||
vmlist = self.window.get_widget("hw-list")
|
||||
selection = vmlist.get_selection()
|
||||
active = selection.get_selected()
|
||||
if active[1] != None:
|
||||
netinfo = active[0].get_value(active[1], 4)
|
||||
if netinfo[1] == "-":
|
||||
netname = "No network name"
|
||||
else:
|
||||
netname = netinfo[1]
|
||||
name_widget = self.window.get_widget("network-name")
|
||||
name_widget.set_text(netname)
|
||||
name_widget.show()
|
||||
self.window.get_widget("network-mac-address").set_text(netinfo[3])
|
||||
self.window.get_widget("network-device-name").set_text(netinfo[2])
|
||||
|
||||
# viewing net page, not adding a device. If adding, don't try to refresh
|
||||
vmlist = self.window.get_widget("hw-list")
|
||||
selection = vmlist.get_selection()
|
||||
active = selection.get_selected()
|
||||
if active[1] != None:
|
||||
netinfo = active[0].get_value(active[1], 3)
|
||||
self.window.get_widget("network-source-type").set_text(netinfo[0])
|
||||
self.window.get_widget("network-source-device").set_text(netinfo[1])
|
||||
self.window.get_widget("network-target-device").set_text(netinfo[2])
|
||||
self.window.get_widget("network-mac-address").set_text(netinfo[3])
|
||||
|
||||
def config_vcpus_changed(self, src):
|
||||
self.window.get_widget("config-vcpus-apply").set_sensitive(True)
|
||||
|
||||
@ -576,171 +506,71 @@ class vmmDetails(gobject.GObject):
|
||||
self.toggle_storage_size()
|
||||
|
||||
def prepare_hw_list(self):
|
||||
hw_list_model = gtk.ListStore(int, str, gtk.gdk.Pixbuf, int, gobject.TYPE_PYOBJECT)
|
||||
hw_list_model = gtk.ListStore(str, gtk.gdk.Pixbuf, int, gobject.TYPE_PYOBJECT)
|
||||
self.window.get_widget("hw-list").set_model(hw_list_model)
|
||||
self.populate_hw_list()
|
||||
|
||||
hwCol = gtk.TreeViewColumn("Hardware")
|
||||
hw_txt = gtk.CellRendererText()
|
||||
hw_img = gtk.CellRendererPixbuf()
|
||||
hwCol.pack_start(hw_txt, True)
|
||||
hwCol.pack_start(hw_img, False)
|
||||
hwCol.add_attribute(hw_txt, 'text', 1)
|
||||
hwCol.add_attribute(hw_img, 'pixbuf', 2)
|
||||
hwCol.add_attribute(hw_txt, 'text', 0)
|
||||
hwCol.add_attribute(hw_img, 'pixbuf', 1)
|
||||
self.window.get_widget("hw-list").append_column(hwCol)
|
||||
|
||||
self.populate_hw_list()
|
||||
|
||||
def populate_hw_list(self):
|
||||
hw_list_model = self.window.get_widget("hw-list").get_model()
|
||||
hw_list_model.clear()
|
||||
hw_list_model.append([0, "Processor", self.pixbuf_processor, VMM_HW_CPU, []])
|
||||
hw_list_model.append([1, "Memory", self.pixbuf_memory, VMM_HW_MEMORY, []])
|
||||
hw_list_model.append(["Processor", self.pixbuf_processor, VMM_HW_CPU, []])
|
||||
hw_list_model.append(["Memory", self.pixbuf_memory, VMM_HW_MEMORY, []])
|
||||
self.repopulate_hw_list()
|
||||
|
||||
def repopulate_hw_list(self):
|
||||
hw_list_model = self.window.get_widget("hw-list").get_model()
|
||||
|
||||
# Populate list of disks
|
||||
for disk in self.vm.get_disk_devices():
|
||||
missing = True
|
||||
insertAt = 0
|
||||
for row in hw_list_model:
|
||||
if row[2] == VMM_HW_DISK and row[3][3] == disk[3]:
|
||||
# Update metadata
|
||||
row[3] = disk
|
||||
missing = False
|
||||
# The insert position must be *before* any NICs
|
||||
if row[2] != VMM_HW_NIC:
|
||||
insertAt = insertAt + 1
|
||||
|
||||
# Add in row
|
||||
if missing:
|
||||
hw_list_model.insert(insertAt, ["Disk %s" % disk[3], self.pixbuf_disk, VMM_HW_DISK, disk])
|
||||
|
||||
# Populate list of NICs
|
||||
for nic in self.vm.get_network_devices():
|
||||
missing = True
|
||||
insertAt = 0
|
||||
for row in hw_list_model:
|
||||
if row[2] == VMM_HW_NIC and row[3][3] == nic[3]:
|
||||
# Update metadata
|
||||
row[3] = nic
|
||||
missing = False
|
||||
|
||||
# Insert poisition is at end....
|
||||
# XXX until we add support for Mice, etc
|
||||
insertAt = insertAt + 1
|
||||
|
||||
# Add in row
|
||||
if missing:
|
||||
hw_list_model.insert(insertAt, ["NIC %s" % nic[2], self.pixbuf_nic, VMM_HW_NIC, nic])
|
||||
|
||||
#all disks
|
||||
disk_list = self.vm.get_disk_devices()
|
||||
for i in range(len(disk_list)):
|
||||
hw_list_model.append([i + 2, "Disk %d" % (i + 1), self.pixbuf_disk, VMM_HW_DISK, disk_list[i]])
|
||||
|
||||
#all nics
|
||||
nic_list = self.vm.get_network_devices()
|
||||
offset = len(disk_list) + 2
|
||||
for i in range(len(nic_list)):
|
||||
hw_list_model.append([offset + i, "Network %d" % (i + 1), self.pixbuf_network, VMM_HW_NIC, nic_list[i]])
|
||||
|
||||
def add_hardware(self, src):
|
||||
self.adding_hardware = True
|
||||
widget = self.window.get_widget("add-hardware-device")
|
||||
iter = widget.get_active_iter()
|
||||
device = widget.get_model().get_value(iter, 0)
|
||||
if VMM_HW_DEVICES.index(device) == 0:
|
||||
# a new virtual disk
|
||||
self.window.get_widget("hw-panel").set_current_page(4)
|
||||
elif VMM_HW_DEVICES.index(device) == 1:
|
||||
# a new vnic
|
||||
network_menu = self.window.get_widget("network-name-pulldown")
|
||||
self.populate_network_model(network_menu.get_model())
|
||||
network_menu.set_active(0)
|
||||
network_menu.show()
|
||||
self.window.get_widget("network-buttons").show()
|
||||
self.window.get_widget("network-name").hide()
|
||||
mac_addr = self.window.get_widget("network-mac-address")
|
||||
mac_addr.set_editable(True)
|
||||
mac_addr.set_text("")
|
||||
self.window.get_widget("net-devlabel-label").hide()
|
||||
self.window.get_widget("network-device-name").hide()
|
||||
self.window.get_widget("hw-panel").set_current_page(3)
|
||||
else:
|
||||
pass
|
||||
|
||||
def populate_network_model(self, model):
|
||||
model.clear()
|
||||
for uuid in self.vm.get_connection().list_net_uuids():
|
||||
net = self.vm.get_connection().get_net(uuid)
|
||||
model.append([net.get_label(), net.get_name(), "network"])
|
||||
br = virtinst.util.default_bridge()
|
||||
model.append([_("default bridge"), br, "bridge"])
|
||||
if self.addhw is None:
|
||||
self.addhw = vmmAddHardware(self.config, self.vm)
|
||||
|
||||
def add_vnic(self, src):
|
||||
network = None
|
||||
bridge = None
|
||||
net_name_widget = self.window.get_widget("network-name-pulldown")
|
||||
net_name = net_name_widget.get_model().get_value(net_name_widget.get_active_iter(), 1)
|
||||
net_type = net_name_widget.get_model().get_value(net_name_widget.get_active_iter(), 2)
|
||||
mac_addr = self.window.get_widget("network-mac-address").get_text()
|
||||
if mac_addr == "":
|
||||
mac_addr = None
|
||||
if net_type == "network":
|
||||
network = net_name
|
||||
else:
|
||||
bridge = net_name
|
||||
self.vm.add_network_device(mac_addr, net_type, bridge, network)
|
||||
self.clean_up_add_hardware()
|
||||
self.addhw.show()
|
||||
|
||||
def add_vbd(self, src):
|
||||
# disks
|
||||
# filesize = None
|
||||
# if self.vm.is_hvm():
|
||||
# disknode = "hd"
|
||||
# else:
|
||||
# disknode = "xvd"
|
||||
# if self.get_config_disk_size() != None:
|
||||
# filesize = self.get_config_disk_size() / 1024.0
|
||||
# try:
|
||||
# d = virtinst.VirtualDisk(self.get_config_disk_image(), filesize, sparse = self.is_sparse_file())
|
||||
# if d.type == virtinst.VirtualDisk.TYPE_FILE and \
|
||||
# self.vm.is_hvm() == False \
|
||||
# and virtinst.util.is_blktap_capable():
|
||||
# d.driver_name = virtinst.VirtualDisk.DRIVER_TAP
|
||||
# if d.type == virtinst.VirtualDisk.TYPE_FILE and not \
|
||||
# self.is_sparse_file():
|
||||
# self.non_sparse = True
|
||||
# else:
|
||||
# self.non_sparse = False
|
||||
# except ValueError, e:
|
||||
# self._validation_error_box(_("Invalid storage address"), e.args[0])
|
||||
# return
|
||||
|
||||
# #XXX add the progress bar in here...
|
||||
# d.setup(progress.BaseMeter)
|
||||
# xml = d.get_xml_config(disknode)
|
||||
# logging.debug("Disk XML: %s" % d)
|
||||
# self.vm.add_disk_device(xml)
|
||||
self.clean_up_add_hardware()
|
||||
|
||||
def get_config_disk_image(self):
|
||||
if self.window.get_widget("storage-partition").get_active():
|
||||
return self.window.get_widget("storage-partition-address").get_text()
|
||||
else:
|
||||
return self.window.get_widget("storage-file-address").get_text()
|
||||
|
||||
def get_config_disk_size(self):
|
||||
if self.window.get_widget("storage-partition").get_active():
|
||||
return None
|
||||
else:
|
||||
return self.window.get_widget("storage-file-size").get_value()
|
||||
|
||||
def is_sparse_file(self):
|
||||
if self.window.get_widget("non-sparse").get_active():
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def clean_up_add_hardware(self, src=None):
|
||||
self.adding_hardware = False
|
||||
self.hw_selected()
|
||||
|
||||
def _validation_error_box(self, text1, text2=None):
|
||||
message_box = gtk.MessageDialog(self.window.get_widget("vmm-details"), \
|
||||
0, \
|
||||
gtk.MESSAGE_ERROR, \
|
||||
gtk.BUTTONS_OK, \
|
||||
text1)
|
||||
if text2 != None:
|
||||
message_box.format_secondary_text(text2)
|
||||
message_box.run()
|
||||
message_box.destroy()
|
||||
|
||||
def _browse_file(self, dialog_name, folder=None, type=None):
|
||||
# user wants to browse for an ISO
|
||||
fcdialog = gtk.FileChooserDialog(dialog_name,
|
||||
self.window.get_widget("vmm-details"),
|
||||
gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT),
|
||||
None)
|
||||
if type != None:
|
||||
f = gtk.FileFilter()
|
||||
f.add_pattern("*." + type)
|
||||
fcdialog.set_filter(f)
|
||||
if folder != None:
|
||||
fcdialog.set_current_folder(folder)
|
||||
response = fcdialog.run()
|
||||
fcdialog.hide()
|
||||
if(response == gtk.RESPONSE_ACCEPT):
|
||||
filename = fcdialog.get_filename()
|
||||
fcdialog.destroy()
|
||||
return filename
|
||||
else:
|
||||
fcdialog.destroy()
|
||||
return None
|
||||
|
||||
gobject.type_register(vmmDetails)
|
||||
|
@ -23,7 +23,7 @@ import libxml2
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import virtinst
|
||||
|
||||
|
||||
class vmmDomain(gobject.GObject):
|
||||
__gsignals__ = {
|
||||
@ -44,6 +44,12 @@ class vmmDomain(gobject.GObject):
|
||||
self.lastStatus = None
|
||||
self.record = []
|
||||
self._update_status()
|
||||
self.xml = None
|
||||
|
||||
def get_xml(self):
|
||||
if self.xml is None:
|
||||
self.xml = self.vm.XMLDesc(0)
|
||||
return self.xml
|
||||
|
||||
def set_handle(self, vm):
|
||||
self.vm = vm
|
||||
@ -65,7 +71,7 @@ class vmmDomain(gobject.GObject):
|
||||
if id < 0:
|
||||
return "-"
|
||||
return str(id)
|
||||
|
||||
|
||||
def get_name(self):
|
||||
return self.vm.name()
|
||||
|
||||
@ -129,6 +135,8 @@ class vmmDomain(gobject.GObject):
|
||||
self.emit("status-changed", status)
|
||||
|
||||
def tick(self, now):
|
||||
# Clear cached XML
|
||||
self.xml = None
|
||||
hostInfo = self.connection.get_host_info()
|
||||
info = self.vm.info()
|
||||
expected = self.config.get_stats_history_length()
|
||||
@ -390,7 +398,7 @@ class vmmDomain(gobject.GObject):
|
||||
return self.config.get_vm_status_icon(self.status())
|
||||
|
||||
def get_xml_string(self, path):
|
||||
xml = self.vm.XMLDesc(0)
|
||||
xml = self.get_xml()
|
||||
doc = None
|
||||
try:
|
||||
doc = libxml2.parseDoc(xml)
|
||||
@ -432,7 +440,7 @@ class vmmDomain(gobject.GObject):
|
||||
return [type, None, None]
|
||||
|
||||
def get_disk_devices(self):
|
||||
xml = self.vm.XMLDesc(0)
|
||||
xml = self.get_xml()
|
||||
doc = None
|
||||
try:
|
||||
doc = libxml2.parseDoc(xml)
|
||||
@ -476,7 +484,7 @@ class vmmDomain(gobject.GObject):
|
||||
self.vm.attachDevice(xml)
|
||||
|
||||
def get_network_devices(self):
|
||||
xml = self.vm.XMLDesc(0)
|
||||
xml = self.get_xml()
|
||||
doc = None
|
||||
try:
|
||||
doc = libxml2.parseDoc(xml)
|
||||
@ -487,31 +495,29 @@ class vmmDomain(gobject.GObject):
|
||||
try:
|
||||
ret = ctx.xpathEval("/domain/devices/interface")
|
||||
|
||||
n = 0
|
||||
for node in ret:
|
||||
type = node.prop("type")
|
||||
devmac = None
|
||||
source = None
|
||||
target = "Unspecified"
|
||||
target = "eth%d" % n
|
||||
n = n + 1
|
||||
for child in node.children:
|
||||
if child.name == "source":
|
||||
if type == "bridge":
|
||||
if child.prop("bridge") != None:
|
||||
source = child.prop("bridge")
|
||||
else:
|
||||
source = child.prop("dev")
|
||||
source = child.prop("bridge")
|
||||
elif type == "ethernet":
|
||||
source = child.prop("dev")
|
||||
elif type == "network":
|
||||
source = child.prop("network")
|
||||
elif type == "user":
|
||||
source = "SLIRP network"
|
||||
source = None
|
||||
else:
|
||||
source = None
|
||||
elif child.name == "mac":
|
||||
devmac = child.prop("address")
|
||||
elif child.name == "target":
|
||||
target = child.prop("dev")
|
||||
|
||||
if source == None:
|
||||
source = "-"
|
||||
nics.append([type, source, target, devmac])
|
||||
|
||||
finally:
|
||||
if ctx != None:
|
||||
ctx.xpathFreeContext()
|
||||
@ -519,12 +525,12 @@ class vmmDomain(gobject.GObject):
|
||||
doc.freeDoc()
|
||||
return nics
|
||||
|
||||
def add_network_device(self, macaddr, type="bridge", bridge=None, network=None):
|
||||
vnic = virtinst.VirtualNetworkInterface(macaddr, type, bridge, network)
|
||||
vnic.setup(self.connection.vmm)
|
||||
xml = vnic.get_xml_config()
|
||||
def add_device(self, xml):
|
||||
self.vm.attachDevice(xml)
|
||||
|
||||
def remove_device(self, xml):
|
||||
self.vm.detachDevice(xml)
|
||||
|
||||
def set_vcpu_count(self, vcpus):
|
||||
vcpus = int(vcpus)
|
||||
self.vm.setVcpus(vcpus)
|
||||
|
2002
src/vmm-add-hardware.glade
Normal file
2002
src/vmm-add-hardware.glade
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,397 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||
|
||||
<glade-interface>
|
||||
|
||||
<widget class="GtkDialog" id="vmm-add-virtual-nic">
|
||||
<property name="visible">True</property>
|
||||
<property name="title" translatable="yes">Add Virtual NIC</property>
|
||||
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||
<property name="modal">False</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="destroy_with_parent">False</property>
|
||||
<property name="decorated">True</property>
|
||||
<property name="skip_taskbar_hint">False</property>
|
||||
<property name="skip_pager_hint">False</property>
|
||||
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
|
||||
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
|
||||
<property name="focus_on_map">True</property>
|
||||
<property name="urgency_hint">False</property>
|
||||
<property name="has_separator">True</property>
|
||||
|
||||
<child internal-child="vbox">
|
||||
<widget class="GtkVBox" id="dialog-vbox5">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child internal-child="action_area">
|
||||
<widget class="GtkHButtonBox" id="dialog-action_area5">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">GTK_BUTTONBOX_END</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkButton" id="cancelbutton1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
<property name="response_id">-6</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkButton" id="okbutton1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="label">gtk-ok</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
<property name="response_id">-5</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">GTK_PACK_END</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox19">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label115">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Add virtual NIC with:</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.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">5</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="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkRadioButton" id="mac-addr-random">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="label" translatable="yes">Randomly generated MAC address</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
<property name="active">True</property>
|
||||
<property name="inconsistent">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<accessibility>
|
||||
<atkproperty name="AtkObject::accessible_name" translatable="yes">mac-addr-random</atkproperty>
|
||||
</accessibility>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkRadioButton" id="mac-addr-selected">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="label" translatable="yes">Specified MAC address:</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>
|
||||
<property name="group">mac-addr-random</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment41">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xscale">1</property>
|
||||
<property name="yscale">1</property>
|
||||
<property name="top_padding">0</property>
|
||||
<property name="bottom_padding">0</property>
|
||||
<property name="left_padding">11</property>
|
||||
<property name="right_padding">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox22">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label116">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">00:16:3e:</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.5</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="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">True</property>
|
||||
<property name="visibility">True</property>
|
||||
<property name="max_length">2</property>
|
||||
<property name="text" translatable="yes"></property>
|
||||
<property name="has_frame">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="activates_default">False</property>
|
||||
<property name="width_chars">2</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label117">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">:</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.5</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="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkEntry" id="entry2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">True</property>
|
||||
<property name="visibility">True</property>
|
||||
<property name="max_length">2</property>
|
||||
<property name="text" translatable="yes"></property>
|
||||
<property name="has_frame">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="activates_default">True</property>
|
||||
<property name="width_chars">2</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label118">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">:</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.5</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="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkEntry" id="entry3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">True</property>
|
||||
<property name="visibility">True</property>
|
||||
<property name="max_length">2</property>
|
||||
<property name="text" translatable="yes"></property>
|
||||
<property name="has_frame">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="activates_default">False</property>
|
||||
<property name="width_chars">2</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox23">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment42">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xscale">1</property>
|
||||
<property name="yscale">1</property>
|
||||
<property name="top_padding">0</property>
|
||||
<property name="bottom_padding">0</property>
|
||||
<property name="left_padding">13</property>
|
||||
<property name="right_padding">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImage" id="image76">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-info</property>
|
||||
<property name="icon_size">4</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment43">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xscale">1</property>
|
||||
<property name="yscale">1</property>
|
||||
<property name="top_padding">0</property>
|
||||
<property name="bottom_padding">0</property>
|
||||
<property name="left_padding">3</property>
|
||||
<property name="right_padding">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label119">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><b>Tip:</b> Acceptable values for hex digits are the numbers 0-9 and the letters A-F</property>
|
||||
<property name="use_underline">False</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0.5</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>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
|
||||
</glade-interface>
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user