mirror of
https://github.com/virt-manager/virt-manager.git
synced 2024-12-24 21:34:47 +03:00
be2d9ddcb4
This base connection object will be used to simplify the API in various places, reduce libvirt API calls, and better share code between virtinst and virt-manager. For now it just centralizes connection opening. This also exposed various places where our handling for older libvirt was busted, so raise our minimum host version to 0.6.0, the first version that supports threaded client requests.
2114 lines
73 KiB
Python
2114 lines
73 KiB
Python
#
|
|
# Copyright (C) 2008 Red Hat, Inc.
|
|
# Copyright (C) 2008 Cole Robinson <crobinso@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., 51 Franklin Street, Fifth Floor, Boston,
|
|
# MA 02110-1301 USA.
|
|
#
|
|
|
|
import threading
|
|
import logging
|
|
|
|
# pylint: disable=E0611
|
|
from gi.repository import GObject
|
|
from gi.repository import Gtk
|
|
from gi.repository import Gdk
|
|
# pylint: enable=E0611
|
|
|
|
import virtinst
|
|
|
|
import virtManager.uihelpers as uihelpers
|
|
from virtManager import util
|
|
from virtManager.mediadev import MEDIA_CDROM
|
|
from virtManager.baseclass import vmmGObjectUI
|
|
from virtManager.asyncjob import vmmAsyncJob
|
|
from virtManager.storagebrowse import vmmStorageBrowser
|
|
from virtManager.details import vmmDetails
|
|
from virtManager.domain import vmmDomainVirtinst
|
|
|
|
# Number of seconds to wait for media detection
|
|
DETECT_TIMEOUT = 20
|
|
|
|
DEFAULT_MEM = 1024
|
|
|
|
PAGE_NAME = 0
|
|
PAGE_INSTALL = 1
|
|
PAGE_MEM = 2
|
|
PAGE_STORAGE = 3
|
|
PAGE_FINISH = 4
|
|
|
|
INSTALL_PAGE_ISO = 0
|
|
INSTALL_PAGE_URL = 1
|
|
INSTALL_PAGE_PXE = 2
|
|
INSTALL_PAGE_IMPORT = 3
|
|
INSTALL_PAGE_CONTAINER_APP = 4
|
|
INSTALL_PAGE_CONTAINER_OS = 5
|
|
|
|
RHEL6_OS_SUPPORT = [
|
|
"rhel3", "rhel4", "rhel5.4", "rhel6",
|
|
"win2k3", "winxp", "win2k8", "vista", "win7",
|
|
]
|
|
|
|
|
|
class vmmCreate(vmmGObjectUI):
|
|
__gsignals__ = {
|
|
"action-show-vm": (GObject.SignalFlags.RUN_FIRST, None, [str, str]),
|
|
}
|
|
|
|
def __init__(self, engine):
|
|
vmmGObjectUI.__init__(self, "vmm-create.ui", "vmm-create")
|
|
self.engine = engine
|
|
|
|
self.conn = None
|
|
self.caps = None
|
|
self.capsguest = None
|
|
self.capsdomain = None
|
|
|
|
self.guest = None
|
|
self.disk = None
|
|
self.nic = None
|
|
|
|
self.storage_browser = None
|
|
self.conn_signals = []
|
|
|
|
# Distro detection state variables
|
|
self.detectedDistro = None
|
|
self.detecting = False
|
|
self.mediaDetected = False
|
|
self.show_all_os = False
|
|
|
|
# 'Guest' class from the previous failed install
|
|
self.failed_guest = None
|
|
|
|
# Whether there was an error at dialog startup
|
|
self.have_startup_error = False
|
|
|
|
# Host space polling
|
|
self.host_storage_timer = None
|
|
|
|
# 'Configure before install' window
|
|
self.config_window = None
|
|
self.config_window_signals = []
|
|
|
|
self.builder.connect_signals({
|
|
"on_vmm_newcreate_delete_event" : self.close,
|
|
|
|
"on_create_cancel_clicked": self.close,
|
|
"on_create_back_clicked" : self.back,
|
|
"on_create_forward_clicked" : self.forward,
|
|
"on_create_finish_clicked" : self.finish,
|
|
"on_create_pages_switch_page": self.page_changed,
|
|
|
|
"on_create_vm_name_activate": self.forward,
|
|
"on_create_conn_changed": self.conn_changed,
|
|
"on_method_changed": self.method_changed,
|
|
|
|
"on_install_url_box_changed": self.url_box_changed,
|
|
"on_install_local_cdrom_toggled": self.toggle_local_cdrom,
|
|
"on_install_local_cdrom_combo_changed": self.detect_media_os,
|
|
"on_install_local_box_changed": self.detect_media_os,
|
|
"on_install_local_browse_clicked": self.browse_iso,
|
|
"on_install_import_browse_clicked": self.browse_import,
|
|
"on_install_app_browse_clicked": self.browse_app,
|
|
"on_install_oscontainer_browse_clicked": self.browse_oscontainer,
|
|
|
|
"on_install_detect_os_toggled": self.toggle_detect_os,
|
|
"on_install_os_type_changed": self.change_os_type,
|
|
"on_install_os_version_changed": self.change_os_version,
|
|
"on_install_local_iso_toggled": self.toggle_local_iso,
|
|
"on_install_detect_os_box_show": self.detect_visibility_changed,
|
|
"on_install_detect_os_box_hide": self.detect_visibility_changed,
|
|
|
|
"on_enable_storage_toggled": self.toggle_enable_storage,
|
|
"on_config_storage_browse_clicked": self.browse_storage,
|
|
"on_config_storage_select_toggled": self.toggle_storage_select,
|
|
|
|
"on_config_netdev_changed": self.netdev_changed,
|
|
"on_config_set_macaddr_toggled": self.toggle_macaddr,
|
|
|
|
"on_config_hv_changed": self.hv_changed,
|
|
"on_config_arch_changed": self.arch_changed,
|
|
})
|
|
self.bind_escape_key_close()
|
|
|
|
self.set_initial_state()
|
|
|
|
def is_visible(self):
|
|
return self.topwin.get_visible()
|
|
|
|
def show(self, parent, uri=None):
|
|
logging.debug("Showing new vm wizard")
|
|
|
|
if not self.is_visible():
|
|
self.reset_state(uri)
|
|
self.topwin.set_transient_for(parent)
|
|
|
|
self.topwin.present()
|
|
|
|
def close(self, ignore1=None, ignore2=None):
|
|
logging.debug("Closing new vm wizard")
|
|
self.topwin.hide()
|
|
self.remove_timers()
|
|
|
|
if self.config_window:
|
|
self.config_window.close()
|
|
if self.storage_browser:
|
|
self.storage_browser.close()
|
|
|
|
return 1
|
|
|
|
def _cleanup(self):
|
|
self.remove_conn()
|
|
|
|
self.conn = None
|
|
self.caps = None
|
|
self.capsguest = None
|
|
self.capsdomain = None
|
|
|
|
self.guest = None
|
|
self.disk = None
|
|
self.nic = None
|
|
|
|
if self.storage_browser:
|
|
self.storage_browser.cleanup()
|
|
self.storage_browser = None
|
|
|
|
def remove_timers(self):
|
|
try:
|
|
if self.host_storage_timer:
|
|
self.remove_gobject_timeout(self.host_storage_timer)
|
|
self.host_storage_timer = None
|
|
except:
|
|
pass
|
|
|
|
def remove_conn(self):
|
|
if not self.conn:
|
|
return
|
|
|
|
for signal in self.conn_signals:
|
|
self.conn.disconnect(signal)
|
|
self.conn_signals = []
|
|
self.conn = None
|
|
|
|
def set_conn(self, newconn, force_validate=False):
|
|
if self.conn == newconn and not force_validate:
|
|
return
|
|
|
|
self.remove_conn()
|
|
self.conn = newconn
|
|
if self.conn:
|
|
self.set_conn_state()
|
|
|
|
|
|
# State init methods
|
|
def startup_error(self, error, hideinstall=True):
|
|
self.have_startup_error = True
|
|
self.widget("startup-error-box").show()
|
|
self.widget("create-forward").set_sensitive(False)
|
|
if hideinstall:
|
|
self.widget("install-box").hide()
|
|
|
|
self.widget("startup-error").set_text("Error: %s" % error)
|
|
return False
|
|
|
|
def startup_warning(self, error):
|
|
self.widget("startup-error-box").show()
|
|
self.widget("startup-error").set_text("Warning: %s" % error)
|
|
|
|
def set_initial_state(self):
|
|
self.widget("create-pages").set_show_tabs(False)
|
|
self.widget("install-method-pages").set_show_tabs(False)
|
|
|
|
finish_img = Gtk.Image.new_from_stock(Gtk.STOCK_QUIT,
|
|
Gtk.IconSize.BUTTON)
|
|
self.widget("create-finish").set_image(finish_img)
|
|
|
|
blue = Gdk.Color.parse("#0072A8")[1]
|
|
self.widget("create-header").modify_bg(Gtk.StateType.NORMAL,
|
|
blue)
|
|
|
|
box = self.widget("create-vm-icon-box")
|
|
image = Gtk.Image.new_from_icon_name("vm_new_wizard",
|
|
Gtk.IconSize.DIALOG)
|
|
image.show()
|
|
box.pack_end(image, False, False, False)
|
|
|
|
# Connection list
|
|
self.widget("create-conn-label").set_text("")
|
|
self.widget("startup-error").set_text("")
|
|
conn_list = self.widget("create-conn")
|
|
conn_model = Gtk.ListStore(str, str)
|
|
conn_list.set_model(conn_model)
|
|
text = Gtk.CellRendererText()
|
|
conn_list.pack_start(text, True)
|
|
conn_list.add_attribute(text, 'text', 1)
|
|
|
|
# ISO media list
|
|
iso_list = self.widget("install-local-box")
|
|
iso_model = Gtk.ListStore(str)
|
|
iso_list.set_model(iso_model)
|
|
iso_list.set_entry_text_column(0)
|
|
self.widget("install-local-box").get_child().connect("activate",
|
|
self.detect_media_os)
|
|
|
|
# Lists for the install urls
|
|
media_url_list = self.widget("install-url-box")
|
|
media_url_model = Gtk.ListStore(str)
|
|
media_url_list.set_model(media_url_model)
|
|
media_url_list.set_entry_text_column(0)
|
|
self.widget("install-url-box").get_child().connect("activate",
|
|
self.detect_media_os)
|
|
|
|
ks_url_list = self.widget("install-ks-box")
|
|
ks_url_model = Gtk.ListStore(str)
|
|
ks_url_list.set_model(ks_url_model)
|
|
ks_url_list.set_entry_text_column(0)
|
|
|
|
def sep_func(model, it, combo):
|
|
ignore = combo
|
|
return model[it][2]
|
|
|
|
# Lists for distro type + variant
|
|
# [os value, os label, is seperator, is 'show all'
|
|
os_type_list = self.widget("install-os-type")
|
|
os_type_model = Gtk.ListStore(str, str, bool, bool)
|
|
os_type_list.set_model(os_type_model)
|
|
text = Gtk.CellRendererText()
|
|
os_type_list.pack_start(text, True)
|
|
os_type_list.add_attribute(text, 'text', 1)
|
|
os_type_list.set_row_separator_func(sep_func, os_type_list)
|
|
|
|
os_variant_list = self.widget("install-os-version")
|
|
os_variant_model = Gtk.ListStore(str, str, bool, bool)
|
|
os_variant_list.set_model(os_variant_model)
|
|
text = Gtk.CellRendererText()
|
|
os_variant_list.pack_start(text, True)
|
|
os_variant_list.add_attribute(text, 'text', 1)
|
|
os_variant_list.set_row_separator_func(sep_func, os_variant_list)
|
|
|
|
|
|
# Physical CD-ROM model
|
|
cd_list = self.widget("install-local-cdrom-combo")
|
|
uihelpers.init_mediadev_combo(cd_list)
|
|
|
|
# Networking
|
|
# [ interface type, device name, label, sensitive ]
|
|
net_list = self.widget("config-netdev")
|
|
bridge_box = self.widget("config-netdev-bridge-box")
|
|
uihelpers.init_network_list(net_list, bridge_box)
|
|
|
|
# Archtecture
|
|
archModel = Gtk.ListStore(str)
|
|
archList = self.widget("config-arch")
|
|
text = Gtk.CellRendererText()
|
|
archList.pack_start(text, True)
|
|
archList.add_attribute(text, 'text', 0)
|
|
archList.set_model(archModel)
|
|
|
|
hyperModel = Gtk.ListStore(str, str, str, bool)
|
|
hyperList = self.widget("config-hv")
|
|
text = Gtk.CellRendererText()
|
|
hyperList.pack_start(text, True)
|
|
hyperList.add_attribute(text, 'text', 0)
|
|
hyperList.add_attribute(text, 'sensitive', 3)
|
|
hyperList.set_model(hyperModel)
|
|
|
|
# Sparse tooltip
|
|
sparse_info = self.widget("config-storage-nosparse-info")
|
|
uihelpers.set_sparse_tooltip(sparse_info)
|
|
|
|
def reset_state(self, urihint=None):
|
|
self.failed_guest = None
|
|
self.have_startup_error = False
|
|
self.guest = None
|
|
self.disk = None
|
|
self.nic = None
|
|
self.show_all_os = False
|
|
|
|
self.widget("create-pages").set_current_page(PAGE_NAME)
|
|
self.page_changed(None, None, PAGE_NAME)
|
|
self.widget("startup-error-box").hide()
|
|
self.widget("install-box").show()
|
|
|
|
# Name page state
|
|
self.widget("create-vm-name").set_text("")
|
|
self.widget("create-vm-name").grab_focus()
|
|
self.widget("method-local").set_active(True)
|
|
self.widget("create-conn").set_active(-1)
|
|
activeconn = self.populate_conn_list(urihint)
|
|
|
|
try:
|
|
self.set_conn(activeconn, force_validate=True)
|
|
except Exception, e:
|
|
logging.exception("Error setting create wizard conn state.")
|
|
return self.startup_error(str(e))
|
|
|
|
if not activeconn:
|
|
return self.startup_error(
|
|
_("No active connection to install on."))
|
|
|
|
# Everything from this point forward should be connection independent
|
|
|
|
# Distro/Variant
|
|
self.toggle_detect_os(self.widget("install-detect-os"))
|
|
self.populate_os_type_model()
|
|
self.widget("install-os-type").set_active(0)
|
|
|
|
self.widget("install-local-box").get_child().set_text("")
|
|
iso_model = self.widget("install-local-box").get_model()
|
|
self.populate_media_model(iso_model, self.config.get_iso_paths())
|
|
|
|
# Install URL
|
|
self.widget("install-urlopts-entry").set_text("")
|
|
self.widget("install-ks-box").get_child().set_text("")
|
|
self.widget("install-url-box").get_child().set_text("")
|
|
self.widget("install-url-options").set_expanded(False)
|
|
urlmodel = self.widget("install-url-box").get_model()
|
|
ksmodel = self.widget("install-ks-box").get_model()
|
|
self.populate_media_model(urlmodel, self.config.get_media_urls())
|
|
self.populate_media_model(ksmodel, self.config.get_kickstart_urls())
|
|
|
|
# Install import
|
|
self.widget("install-import-entry").set_text("")
|
|
|
|
# Install container app
|
|
self.widget("install-app-entry").set_text("/bin/sh")
|
|
|
|
# Install container OS
|
|
self.widget("install-oscontainer-fs").set_text("")
|
|
|
|
# Mem / CPUs
|
|
self.widget("config-mem").set_value(DEFAULT_MEM)
|
|
self.widget("config-cpus").set_value(1)
|
|
|
|
# Storage
|
|
label_widget = self.widget("phys-hd-label")
|
|
label_widget.set_markup("")
|
|
if not self.host_storage_timer:
|
|
self.host_storage_timer = self.timeout_add(3 * 1000,
|
|
uihelpers.host_space_tick,
|
|
self.conn,
|
|
label_widget)
|
|
self.widget("enable-storage").set_active(True)
|
|
self.widget("config-storage-create").set_active(True)
|
|
self.widget("config-storage-size").set_value(8)
|
|
self.widget("config-storage-entry").set_text("")
|
|
self.widget("config-storage-nosparse").set_active(True)
|
|
|
|
# Final page
|
|
self.widget("summary-customize").set_active(False)
|
|
|
|
# Make sure window is a sane size
|
|
self.topwin.resize(1, 1)
|
|
|
|
def set_conn_state(self):
|
|
# Update all state that has some dependency on the current connection
|
|
self.widget("create-forward").set_sensitive(True)
|
|
|
|
if self.conn.no_install_options():
|
|
error = _("No hypervisor options were found for this "
|
|
"connection.")
|
|
|
|
if self.conn.is_qemu():
|
|
error += "\n\n"
|
|
error += _("This usually means that QEMU or KVM is not "
|
|
"installed on your machine, or the KVM kernel "
|
|
"modules are not loaded.")
|
|
return self.startup_error(error)
|
|
|
|
# A bit out of order, but populate arch + hv lists so we can
|
|
# determine a default
|
|
self.conn.invalidate_caps()
|
|
self.caps = self.conn.get_capabilities()
|
|
self.change_caps()
|
|
self.populate_hv()
|
|
|
|
if self.conn.is_xen():
|
|
if self.conn.hw_virt_supported():
|
|
if self.conn.is_bios_virt_disabled():
|
|
error = _("Host supports full virtualization, but "
|
|
"no related install options are available. "
|
|
"This may mean support is disabled in your "
|
|
"system BIOS.")
|
|
self.startup_warning(error)
|
|
|
|
else:
|
|
error = _("Host does not appear to support hardware "
|
|
"virtualization. Install options may be limited.")
|
|
self.startup_warning(error)
|
|
|
|
elif self.conn.is_qemu():
|
|
if not self.conn.is_kvm_supported():
|
|
error = _("KVM is not available. This may mean the KVM "
|
|
"package is not installed, or the KVM kernel modules "
|
|
"are not loaded. Your virtual machines may perform poorly.")
|
|
self.startup_warning(error)
|
|
|
|
# Helper state
|
|
is_local = not self.conn.is_remote()
|
|
is_storage_capable = self.conn.is_storage_capable()
|
|
can_storage = (is_local or is_storage_capable)
|
|
is_pv = (self.capsguest.os_type == "xen")
|
|
is_container = self.conn.is_container()
|
|
can_remote_url = virtinst.support.check_stream_support(
|
|
self.conn.get_backend(),
|
|
virtinst.support.SUPPORT_STREAM_UPLOAD)
|
|
|
|
# Install Options
|
|
method_tree = self.widget("method-tree")
|
|
method_pxe = self.widget("method-pxe")
|
|
method_local = self.widget("method-local")
|
|
method_import = self.widget("method-import")
|
|
method_container_app = self.widget("method-container-app")
|
|
|
|
method_tree.set_sensitive(is_local or can_remote_url)
|
|
method_local.set_sensitive(not is_pv and can_storage)
|
|
method_pxe.set_sensitive(not is_pv)
|
|
method_import.set_sensitive(can_storage)
|
|
virt_methods = [method_local, method_tree, method_pxe, method_import]
|
|
|
|
pxe_tt = None
|
|
local_tt = None
|
|
tree_tt = None
|
|
import_tt = None
|
|
|
|
if not is_local:
|
|
if not can_remote_url:
|
|
tree_tt = _("Libvirt version does not "
|
|
"support remote URL installs.")
|
|
if not is_storage_capable:
|
|
local_tt = _("Connection does not support storage management.")
|
|
import_tt = local_tt
|
|
|
|
if is_pv:
|
|
base = _("%s installs not available for paravirt guests.")
|
|
pxe_tt = base % "PXE"
|
|
local_tt = base % "CDROM/ISO"
|
|
|
|
for w in virt_methods:
|
|
if w.get_sensitive():
|
|
w.set_active(True)
|
|
break
|
|
|
|
if not (is_container or
|
|
[w for w in virt_methods if w.get_sensitive()]):
|
|
return self.startup_error(
|
|
_("No install methods available for this connection."),
|
|
hideinstall=False)
|
|
|
|
method_tree.set_tooltip_text(tree_tt or "")
|
|
method_local.set_tooltip_text(local_tt or "")
|
|
method_pxe.set_tooltip_text(pxe_tt or "")
|
|
method_import.set_tooltip_text(import_tt or "")
|
|
|
|
# Container install options
|
|
method_container_app.set_active(True)
|
|
self.widget("virt-install-box").set_property("visible",
|
|
not is_container)
|
|
self.widget("container-install-box").set_property("visible",
|
|
is_container)
|
|
|
|
# Install local
|
|
iso_option = self.widget("install-local-iso")
|
|
cdrom_option = self.widget("install-local-cdrom")
|
|
cdrom_list = self.widget("install-local-cdrom-combo")
|
|
cdrom_warn = self.widget("install-local-cdrom-warn")
|
|
|
|
sigs = uihelpers.populate_mediadev_combo(self.conn, cdrom_list,
|
|
MEDIA_CDROM)
|
|
self.conn_signals.extend(sigs)
|
|
|
|
if self.conn.mediadev_error:
|
|
cdrom_warn.show()
|
|
cdrom_option.set_sensitive(False)
|
|
cdrom_warn.set_tooltip_text(self.conn.mediadev_error)
|
|
else:
|
|
cdrom_warn.hide()
|
|
|
|
# Don't select physical CDROM if no valid media is present
|
|
use_cd = (cdrom_list.get_active() >= 0)
|
|
if use_cd:
|
|
cdrom_option.set_active(True)
|
|
else:
|
|
iso_option.set_active(True)
|
|
|
|
# Only allow ISO option for remote VM
|
|
if not is_local:
|
|
iso_option.set_active(True)
|
|
|
|
self.toggle_local_cdrom(cdrom_option)
|
|
self.toggle_local_iso(iso_option)
|
|
|
|
# Memory
|
|
memory = int(self.conn.host_memory_size())
|
|
mem_label = (_("Up to %(maxmem)s available on the host") %
|
|
{'maxmem': self.pretty_memory(memory)})
|
|
mem_label = ("<span size='small' color='#484848'>%s</span>" %
|
|
mem_label)
|
|
self.widget("config-mem").set_range(50, memory / 1024)
|
|
self.widget("phys-mem-label").set_markup(mem_label)
|
|
|
|
# CPU
|
|
phys_cpus = self.conn.host_active_processor_count()
|
|
|
|
max_v = self.conn.get_max_vcpus(self.capsdomain.hypervisor_type)
|
|
cmax = phys_cpus
|
|
if int(max_v) < int(phys_cpus):
|
|
cmax = max_v
|
|
cpu_tooltip = (_("Hypervisor only supports %d virtual CPUs.") %
|
|
max_v)
|
|
else:
|
|
cpu_tooltip = None
|
|
self.widget("config-cpus").set_tooltip_text(cpu_tooltip or "")
|
|
|
|
cmax = int(cmax)
|
|
if cmax <= 0:
|
|
cmax = 1
|
|
cpu_label = (_("Up to %(numcpus)d available") %
|
|
{'numcpus': int(phys_cpus)})
|
|
cpu_label = ("<span size='small' color='#484848'>%s</span>" %
|
|
cpu_label)
|
|
self.widget("config-cpus").set_range(1, cmax)
|
|
self.widget("phys-cpu-label").set_markup(cpu_label)
|
|
|
|
# Storage
|
|
storage_tooltip = None
|
|
|
|
use_storage = self.widget("config-storage-select")
|
|
storage_area = self.widget("config-storage-area")
|
|
|
|
storage_area.set_sensitive(can_storage)
|
|
if not can_storage:
|
|
storage_tooltip = _("Connection does not support storage"
|
|
" management.")
|
|
use_storage.set_sensitive(True)
|
|
storage_area.set_tooltip_text(storage_tooltip or "")
|
|
|
|
# Networking
|
|
net_list = self.widget("config-netdev")
|
|
net_expander = self.widget("config-advanced-expander")
|
|
net_warn_icon = self.widget("config-netdev-warn-icon")
|
|
net_warn_box = self.widget("config-netdev-warn-box")
|
|
net_expander.hide()
|
|
net_warn_icon.hide()
|
|
net_warn_box.hide()
|
|
net_expander.set_expanded(False)
|
|
|
|
do_warn = uihelpers.populate_network_list(net_list, self.conn, False)
|
|
self.set_net_warn(self.conn.netdev_error or do_warn,
|
|
self.conn.netdev_error, True)
|
|
|
|
newmac = uihelpers.generate_macaddr(self.conn)
|
|
self.widget("config-set-macaddr").set_active(bool(newmac))
|
|
self.widget("config-macaddr").set_text(newmac)
|
|
|
|
def set_net_warn(self, show_warn, msg, do_tooltip):
|
|
net_warn_icon = self.widget("config-netdev-warn-icon")
|
|
net_warn_box = self.widget("config-netdev-warn-box")
|
|
net_warn_label = self.widget("config-netdev-warn-label")
|
|
net_expander = self.widget("config-advanced-expander")
|
|
|
|
if show_warn:
|
|
net_expander.set_expanded(True)
|
|
|
|
if do_tooltip:
|
|
net_warn_icon.set_property("visible", show_warn)
|
|
if msg:
|
|
net_warn_icon.set_tooltip_text(show_warn and msg or "")
|
|
else:
|
|
net_warn_box.set_property("visible", show_warn)
|
|
markup = show_warn and ("<small>%s</small>" % msg) or ""
|
|
net_warn_label.set_markup(markup)
|
|
|
|
def populate_hv(self):
|
|
hv_list = self.widget("config-hv")
|
|
model = hv_list.get_model()
|
|
model.clear()
|
|
|
|
default = 0
|
|
tooltip = None
|
|
instmethod = self.get_config_install_page()
|
|
for guest in self.caps.guests:
|
|
gtype = guest.os_type
|
|
for dom in guest.domains:
|
|
domtype = dom.hypervisor_type
|
|
label = util.pretty_hv(gtype, domtype)
|
|
sensitive = True
|
|
|
|
# Don't add multiple rows for each arch
|
|
for m in model:
|
|
if m[0] == label:
|
|
label = None
|
|
break
|
|
if label is None:
|
|
continue
|
|
|
|
# Determine if this is the default given by guest_lookup
|
|
if (gtype == self.capsguest.os_type and
|
|
self.capsdomain.hypervisor_type == domtype):
|
|
default = len(model)
|
|
|
|
if gtype == "xen":
|
|
if (instmethod == INSTALL_PAGE_PXE or
|
|
instmethod == INSTALL_PAGE_ISO):
|
|
sensitive = False
|
|
tooltip = _("Only URL or import installs are supported "
|
|
"for paravirt.")
|
|
|
|
model.append([label, gtype, domtype, sensitive])
|
|
|
|
hv_info = self.widget("config-hv-info")
|
|
if tooltip:
|
|
hv_info.show()
|
|
hv_info.set_tooltip_text(tooltip)
|
|
else:
|
|
hv_info.hide()
|
|
|
|
hv_list.set_active(default)
|
|
|
|
def populate_arch(self):
|
|
arch_list = self.widget("config-arch")
|
|
model = arch_list.get_model()
|
|
model.clear()
|
|
|
|
default = 0
|
|
for guest in self.caps.guests:
|
|
for dom in guest.domains:
|
|
if (guest.os_type == self.capsguest.os_type and
|
|
dom.hypervisor_type == self.capsdomain.hypervisor_type):
|
|
|
|
arch = guest.arch
|
|
if arch == self.capsguest.arch:
|
|
default = len(model)
|
|
model.append([guest.arch])
|
|
|
|
arch_list.set_active(default)
|
|
|
|
def populate_conn_list(self, urihint=None):
|
|
conn_list = self.widget("create-conn")
|
|
model = conn_list.get_model()
|
|
model.clear()
|
|
|
|
default = -1
|
|
for c in self.engine.conns.values():
|
|
connobj = c["conn"]
|
|
if not connobj.is_active():
|
|
continue
|
|
|
|
if connobj.get_uri() == urihint:
|
|
default = len(model)
|
|
elif default < 0 and not connobj.is_remote():
|
|
# Favor local connections over remote connections
|
|
default = len(model)
|
|
|
|
model.append([connobj.get_uri(), connobj.get_pretty_desc_active()])
|
|
|
|
no_conns = (len(model) == 0)
|
|
|
|
if default < 0 and not no_conns:
|
|
default = 0
|
|
|
|
activeuri = ""
|
|
activedesc = ""
|
|
activeconn = None
|
|
if not no_conns:
|
|
conn_list.set_active(default)
|
|
activeuri, activedesc = model[default]
|
|
activeconn = self.engine.conns[activeuri]["conn"]
|
|
|
|
self.widget("create-conn-label").set_text(activedesc)
|
|
if len(model) <= 1:
|
|
self.widget("create-conn").hide()
|
|
self.widget("create-conn-label").show()
|
|
else:
|
|
self.widget("create-conn").show()
|
|
self.widget("create-conn-label").hide()
|
|
|
|
return activeconn
|
|
|
|
def _add_os_row(self, model, name="", label="", supported=False,
|
|
sep=False, action=False):
|
|
visible = self.show_all_os or supported
|
|
if sep or action:
|
|
visible = not self.show_all_os
|
|
|
|
if not visible:
|
|
return
|
|
|
|
model.append([name, label, sep, action])
|
|
|
|
def populate_os_type_model(self):
|
|
widget = self.widget("install-os-type")
|
|
model = widget.get_model()
|
|
model.clear()
|
|
filtervars = (not self._rhel6_defaults() and
|
|
RHEL6_OS_SUPPORT or
|
|
None)
|
|
|
|
types = virtinst.Guest.list_os_types()
|
|
types.sort()
|
|
supportl = virtinst.Guest.list_os_types(supported=True,
|
|
filtervars=filtervars)
|
|
if not filtervars:
|
|
# Kind of a hack, just show linux + windows by default since
|
|
# that's all 98% of people care about
|
|
supportl = ["linux", "windows"]
|
|
|
|
self._add_os_row(model, None, _("Generic"), True)
|
|
|
|
for t in types:
|
|
label = virtinst.Guest.get_os_type_label(t)
|
|
supported = (t in supportl)
|
|
self._add_os_row(model, t, label, supported)
|
|
|
|
# Add sep
|
|
self._add_os_row(model, sep=True)
|
|
# Add action option
|
|
self._add_os_row(model, label=_("Show all OS options"), action=True)
|
|
widget.set_active(0)
|
|
|
|
|
|
def populate_os_variant_model(self, _type):
|
|
model = self.widget("install-os-version").get_model()
|
|
model.clear()
|
|
if not _type:
|
|
self._add_os_row(model, None, _("Generic"), True)
|
|
return
|
|
|
|
filtervars = (not self._rhel6_defaults() and
|
|
RHEL6_OS_SUPPORT or
|
|
None)
|
|
preferred = self.config.preferred_distros
|
|
variants = virtinst.Guest.list_os_variants(_type, preferred)
|
|
supportl = virtinst.Guest.list_os_variants(
|
|
_type, preferred, supported=True,
|
|
filtervars=filtervars)
|
|
|
|
for v in variants:
|
|
label = virtinst.Guest.get_os_variant_label(_type, v)
|
|
supported = v in supportl
|
|
self._add_os_row(model, v, label, supported)
|
|
|
|
# Add sep
|
|
self._add_os_row(model, sep=True)
|
|
# Add action option
|
|
self._add_os_row(model, label=_("Show all OS options"), action=True)
|
|
|
|
def populate_media_model(self, model, urls):
|
|
model.clear()
|
|
if urls is not None:
|
|
for url in urls:
|
|
model.append([url])
|
|
|
|
|
|
def change_caps(self, gtype=None, dtype=None, arch=None):
|
|
if gtype is None:
|
|
# If none specified, prefer HVM. This way, the default install
|
|
# options won't be limited because we default to PV. If hvm not
|
|
# supported, differ to guest_lookup
|
|
for g in self.caps.guests:
|
|
if g.os_type == "hvm":
|
|
gtype = "hvm"
|
|
break
|
|
|
|
(newg, newdom) = virtinst.CapabilitiesParser.guest_lookup(
|
|
conn=self.conn.get_backend(),
|
|
caps=self.caps,
|
|
os_type=gtype,
|
|
typ=dtype,
|
|
accelerated=True,
|
|
arch=arch)
|
|
|
|
if (self.capsguest and self.capsdomain and
|
|
(newg.arch == self.capsguest.arch and
|
|
newg.os_type == self.capsguest.os_type and
|
|
newdom.hypervisor_type == self.capsdomain.hypervisor_type)):
|
|
# No change
|
|
return
|
|
|
|
self.capsguest = newg
|
|
self.capsdomain = newdom
|
|
logging.debug("Guest type set to os_type=%s, arch=%s, dom_type=%s",
|
|
self.capsguest.os_type,
|
|
self.capsguest.arch,
|
|
self.capsdomain.hypervisor_type)
|
|
|
|
def populate_summary(self):
|
|
distro, version, dlabel, vlabel = self.get_config_os_info()
|
|
mem = self.pretty_memory(int(self.guest.memory) * 1024)
|
|
cpu = str(int(self.guest.vcpus))
|
|
|
|
instmethod = self.get_config_install_page()
|
|
install = ""
|
|
if instmethod == INSTALL_PAGE_ISO:
|
|
install = _("Local CDROM/ISO")
|
|
elif instmethod == INSTALL_PAGE_URL:
|
|
install = _("URL Install Tree")
|
|
elif instmethod == INSTALL_PAGE_PXE:
|
|
install = _("PXE Install")
|
|
elif instmethod == INSTALL_PAGE_IMPORT:
|
|
install = _("Import existing OS image")
|
|
elif instmethod == INSTALL_PAGE_CONTAINER_APP:
|
|
install = _("Application container")
|
|
elif instmethod == INSTALL_PAGE_CONTAINER_OS:
|
|
install = _("Operating system container")
|
|
|
|
storagetmpl = "<span size='small' color='#484848'>%s</span>"
|
|
disks = self.guest.get_devices("disk")
|
|
if disks:
|
|
disk = disks[0]
|
|
storage = "%s" % self.pretty_storage(disk.size)
|
|
storage += " " + (storagetmpl % disk.path)
|
|
elif len(self.guest.get_devices("filesystem")):
|
|
fs = self.guest.get_devices("filesystem")[0]
|
|
storage = storagetmpl % fs.source
|
|
elif self.guest.installer.is_container():
|
|
storage = _("Host filesystem")
|
|
else:
|
|
storage = _("None")
|
|
|
|
osstr = ""
|
|
have_os = True
|
|
if self.guest.installer.is_container():
|
|
osstr = _("Linux")
|
|
elif not distro:
|
|
osstr = _("Generic")
|
|
have_os = False
|
|
elif not version:
|
|
osstr = _("Generic") + " " + dlabel
|
|
have_os = False
|
|
else:
|
|
osstr = vlabel
|
|
|
|
title = "Ready to begin installation of <b>%s</b>" % self.guest.name
|
|
|
|
self.widget("finish-warn-os").set_property("visible", not have_os)
|
|
self.widget("summary-title").set_markup(title)
|
|
self.widget("summary-os").set_text(osstr)
|
|
self.widget("summary-install").set_text(install)
|
|
self.widget("summary-mem").set_text(mem)
|
|
self.widget("summary-cpu").set_text(cpu)
|
|
self.widget("summary-storage").set_markup(storage)
|
|
|
|
|
|
# get_* methods
|
|
def get_config_name(self):
|
|
return self.widget("create-vm-name").get_text()
|
|
|
|
def is_install_page(self):
|
|
notebook = self.widget("create-pages")
|
|
curpage = notebook.get_current_page()
|
|
return curpage == PAGE_INSTALL
|
|
|
|
def get_config_install_page(self):
|
|
if self.widget("virt-install-box").get_property("visible"):
|
|
if self.widget("method-local").get_active():
|
|
return INSTALL_PAGE_ISO
|
|
elif self.widget("method-tree").get_active():
|
|
return INSTALL_PAGE_URL
|
|
elif self.widget("method-pxe").get_active():
|
|
return INSTALL_PAGE_PXE
|
|
elif self.widget("method-import").get_active():
|
|
return INSTALL_PAGE_IMPORT
|
|
else:
|
|
if self.widget("method-container-app").get_active():
|
|
return INSTALL_PAGE_CONTAINER_APP
|
|
if self.widget("method-container-os").get_active():
|
|
return INSTALL_PAGE_CONTAINER_OS
|
|
|
|
def get_config_os_info(self):
|
|
d_list = self.widget("install-os-type")
|
|
d_idx = d_list.get_active()
|
|
v_list = self.widget("install-os-version")
|
|
v_idx = v_list.get_active()
|
|
distro = None
|
|
dlabel = None
|
|
variant = None
|
|
vlabel = None
|
|
|
|
if d_idx >= 0:
|
|
row = d_list.get_model()[d_idx]
|
|
distro = row[0]
|
|
dlabel = row[1]
|
|
if v_idx >= 0:
|
|
row = v_list.get_model()[v_idx]
|
|
variant = row[0]
|
|
vlabel = row[1]
|
|
|
|
return (distro and str(distro),
|
|
variant and str(variant),
|
|
str(dlabel), str(vlabel))
|
|
|
|
def get_config_local_media(self, store_media=False):
|
|
if self.widget("install-local-cdrom").get_active():
|
|
cd = self.widget("install-local-cdrom-combo")
|
|
idx = cd.get_active()
|
|
model = cd.get_model()
|
|
if idx != -1:
|
|
return model[idx][uihelpers.OPTICAL_DEV_PATH]
|
|
return None
|
|
else:
|
|
ret = self.widget("install-local-box").get_child().get_text()
|
|
if ret and store_media:
|
|
self.config.add_iso_path(ret)
|
|
return ret
|
|
|
|
def get_config_detectable_media(self):
|
|
instpage = self.get_config_install_page()
|
|
media = ""
|
|
|
|
if instpage == INSTALL_PAGE_ISO:
|
|
media = self.get_config_local_media()
|
|
elif instpage == INSTALL_PAGE_URL:
|
|
media = self.widget("install-url-box").get_child().get_text()
|
|
elif instpage == INSTALL_PAGE_IMPORT:
|
|
media = self.widget("install-import-entry").get_text()
|
|
|
|
return media
|
|
|
|
def get_config_url_info(self, store_media=False):
|
|
media = self.widget("install-url-box").get_child().get_text().strip()
|
|
extra = self.widget("install-urlopts-entry").get_text().strip()
|
|
ks = self.widget("install-ks-box").get_child().get_text().strip()
|
|
|
|
if media and store_media:
|
|
self.config.add_media_url(media)
|
|
if ks and store_media:
|
|
self.config.add_kickstart_url(ks)
|
|
|
|
return (media.strip(), extra.strip(), ks.strip())
|
|
|
|
def get_config_import_path(self):
|
|
return self.widget("install-import-entry").get_text()
|
|
|
|
def get_config_container_app_path(self):
|
|
return self.widget("install-app-entry").get_text()
|
|
|
|
def get_config_container_fs_path(self):
|
|
return self.widget("install-oscontainer-fs").get_text()
|
|
|
|
def get_default_path(self, name):
|
|
# Don't generate a new path if the install failed
|
|
if self.failed_guest:
|
|
disks = self.failed_guest.get_devices("disk")
|
|
if disks:
|
|
return disks[0].path
|
|
|
|
return util.get_default_path(self.conn, name)
|
|
|
|
def is_default_storage(self):
|
|
usedef = self.widget("config-storage-create").get_active()
|
|
isimport = (self.get_config_install_page() == INSTALL_PAGE_IMPORT)
|
|
return usedef and not isimport
|
|
|
|
def get_storage_info(self):
|
|
path = None
|
|
size = uihelpers.spin_get_helper(self.widget("config-storage-size"))
|
|
sparse = not self.widget("config-storage-nosparse").get_active()
|
|
|
|
if self.get_config_install_page() == INSTALL_PAGE_IMPORT:
|
|
path = self.get_config_import_path()
|
|
size = None
|
|
sparse = False
|
|
|
|
elif self.is_default_storage():
|
|
path = self.get_default_path(self.guest.name)
|
|
logging.debug("Default storage path is: %s", path)
|
|
else:
|
|
path = self.widget("config-storage-entry").get_text()
|
|
|
|
return (path, size, sparse)
|
|
|
|
def get_config_network_info(self):
|
|
net_list = self.widget("config-netdev")
|
|
bridge_ent = self.widget("config-netdev-bridge")
|
|
macaddr = self.widget("config-macaddr").get_text()
|
|
|
|
net_type, net_src = uihelpers.get_network_selection(net_list,
|
|
bridge_ent)
|
|
|
|
return net_type, net_src, macaddr.strip()
|
|
|
|
def get_config_graphics_type(self):
|
|
return self.config.get_graphics_type()
|
|
|
|
def get_config_customize(self):
|
|
return self.widget("summary-customize").get_active()
|
|
|
|
def is_detect_active(self):
|
|
return self.widget("install-detect-os").get_active()
|
|
|
|
|
|
# Listeners
|
|
def conn_changed(self, src):
|
|
idx = src.get_active()
|
|
model = src.get_model()
|
|
|
|
if idx < 0:
|
|
conn = None
|
|
else:
|
|
uri = model[idx][0]
|
|
conn = self.engine.conns[uri]["conn"]
|
|
|
|
# If we aren't visible, let reset_state handle this for us, which
|
|
# has a better chance of reporting error
|
|
if not self.is_visible():
|
|
return
|
|
|
|
self.set_conn(conn)
|
|
|
|
def method_changed(self, src):
|
|
ignore = src
|
|
self.set_page_num_text(0)
|
|
|
|
def netdev_changed(self, ignore):
|
|
self.check_network_selection()
|
|
|
|
def check_network_selection(self):
|
|
src = self.widget("config-netdev")
|
|
idx = src.get_active()
|
|
show_pxe_warn = True
|
|
pxe_install = (self.get_config_install_page() == INSTALL_PAGE_PXE)
|
|
|
|
if not idx < 0:
|
|
row = src.get_model()[idx]
|
|
ntype = row[0]
|
|
key = row[6]
|
|
|
|
if (ntype is None or
|
|
ntype == virtinst.VirtualNetworkInterface.TYPE_USER):
|
|
show_pxe_warn = True
|
|
elif ntype != virtinst.VirtualNetworkInterface.TYPE_VIRTUAL:
|
|
show_pxe_warn = False
|
|
else:
|
|
obj = self.conn.get_net(key)
|
|
show_pxe_warn = not obj.can_pxe()
|
|
|
|
if not (show_pxe_warn and pxe_install):
|
|
return
|
|
|
|
self.set_net_warn(True,
|
|
_("Network selection does not support PXE"), False)
|
|
|
|
def hv_changed(self, src):
|
|
idx = src.get_active()
|
|
if idx < 0:
|
|
return
|
|
|
|
row = src.get_model()[idx]
|
|
|
|
self.change_caps(row[1], row[2])
|
|
self.populate_arch()
|
|
|
|
def arch_changed(self, src):
|
|
idx = src.get_active()
|
|
if idx < 0:
|
|
return
|
|
|
|
arch = src.get_model()[idx][0]
|
|
self.change_caps(self.capsguest.os_type,
|
|
self.capsdomain.hypervisor_type,
|
|
arch)
|
|
|
|
def url_box_changed(self, ignore):
|
|
# If the url_entry has focus, don't fire detect_media_os, it means
|
|
# the user is probably typing
|
|
self.mediaDetected = False
|
|
if self.widget("install-url-box").get_child().has_focus():
|
|
return
|
|
self.detect_media_os()
|
|
|
|
def should_detect_media(self):
|
|
return (self.is_detect_active() and not self.mediaDetected)
|
|
|
|
def detect_media_os(self, ignore1=None, forward=False):
|
|
if not self.should_detect_media():
|
|
return
|
|
if not self.is_install_page():
|
|
return
|
|
self.start_detection(forward=forward)
|
|
|
|
def toggle_detect_os(self, src):
|
|
dodetect = src.get_active()
|
|
|
|
if dodetect:
|
|
self.widget("install-os-type-label").show()
|
|
self.widget("install-os-version-label").show()
|
|
self.widget("install-os-type").hide()
|
|
self.widget("install-os-version").hide()
|
|
self.mediaDetected = False
|
|
self.detect_media_os() # Run detection
|
|
else:
|
|
self.widget("install-os-type-label").hide()
|
|
self.widget("install-os-version-label").hide()
|
|
self.widget("install-os-type").show()
|
|
self.widget("install-os-version").show()
|
|
|
|
def _selected_os_row(self):
|
|
box = self.widget("install-os-type")
|
|
model = box.get_model()
|
|
idx = box.get_active()
|
|
if idx == -1:
|
|
return None
|
|
|
|
return model[idx]
|
|
|
|
def change_os_type(self, box):
|
|
ignore = box
|
|
row = self._selected_os_row()
|
|
if row:
|
|
_type = row[0]
|
|
self.populate_os_variant_model(_type)
|
|
if row[3]:
|
|
self.show_all_os = True
|
|
self.populate_os_type_model()
|
|
return
|
|
|
|
variant = self.widget("install-os-version")
|
|
variant.set_active(0)
|
|
|
|
def change_os_version(self, box):
|
|
model = box.get_model()
|
|
idx = box.get_active()
|
|
if idx == -1:
|
|
return
|
|
|
|
# Get previous
|
|
os_type_list = self.widget("install-os-type")
|
|
os_type_model = os_type_list.get_model()
|
|
type_row = self._selected_os_row()
|
|
if not type_row:
|
|
return
|
|
os_type = type_row[0]
|
|
|
|
show_all = model[idx][3]
|
|
if not show_all:
|
|
return
|
|
|
|
self.show_all_os = True
|
|
self.populate_os_type_model()
|
|
|
|
for idx in range(len(os_type_model)):
|
|
if os_type_model[idx][0] == os_type:
|
|
os_type_list.set_active(idx)
|
|
break
|
|
|
|
def toggle_local_cdrom(self, src):
|
|
combo = self.widget("install-local-cdrom-combo")
|
|
is_active = src.get_active()
|
|
if is_active:
|
|
if combo.get_active() != -1:
|
|
# Local CDROM was selected with media preset, detect distro
|
|
self.detect_media_os()
|
|
|
|
self.widget("install-local-cdrom-combo").set_sensitive(is_active)
|
|
|
|
def toggle_local_iso(self, src):
|
|
uselocal = src.get_active()
|
|
self.widget("install-local-box").set_sensitive(uselocal)
|
|
self.widget("install-local-browse").set_sensitive(uselocal)
|
|
|
|
def detect_visibility_changed(self, src, ignore=None):
|
|
is_visible = src.get_property("visible")
|
|
detect_chkbox = self.widget("install-detect-os")
|
|
nodetect_label = self.widget("install-nodetect-label")
|
|
|
|
detect_chkbox.set_active(is_visible)
|
|
detect_chkbox.toggled()
|
|
|
|
if is_visible:
|
|
nodetect_label.hide()
|
|
else:
|
|
nodetect_label.show()
|
|
|
|
def browse_oscontainer(self, ignore1=None, ignore2=None):
|
|
def set_path(ignore, path):
|
|
self.widget("install-oscontainer-fs").set_text(path)
|
|
self._browse_file(set_path, is_media=False, is_dir=True)
|
|
|
|
def browse_app(self, ignore1=None, ignore2=None):
|
|
def set_path(ignore, path):
|
|
self.widget("install-app-entry").set_text(path)
|
|
self._browse_file(set_path, is_media=False)
|
|
|
|
def browse_import(self, ignore1=None, ignore2=None):
|
|
def set_path(ignore, path):
|
|
self.widget("install-import-entry").set_text(path)
|
|
self._browse_file(set_path, is_media=False)
|
|
|
|
def browse_iso(self, ignore1=None, ignore2=None):
|
|
def set_path(ignore, path):
|
|
self.widget("install-local-box").get_child().set_text(path)
|
|
self._browse_file(set_path, is_media=True)
|
|
self.widget("install-local-box").activate()
|
|
|
|
def browse_storage(self, ignore1):
|
|
def set_path(ignore, path):
|
|
self.widget("config-storage-entry").set_text(path)
|
|
self._browse_file(set_path, is_media=False)
|
|
|
|
def toggle_enable_storage(self, src):
|
|
self.widget("config-storage-box").set_sensitive(src.get_active())
|
|
|
|
|
|
def toggle_storage_select(self, src):
|
|
act = src.get_active()
|
|
self.widget("config-storage-browse-box").set_sensitive(act)
|
|
|
|
def toggle_macaddr(self, src):
|
|
self.widget("config-macaddr").set_sensitive(src.get_active())
|
|
|
|
# Navigation methods
|
|
def set_install_page(self):
|
|
instnotebook = self.widget("install-method-pages")
|
|
detectbox = self.widget("install-detect-os-box")
|
|
osbox = self.widget("install-os-distro-box")
|
|
instpage = self.get_config_install_page()
|
|
|
|
# Setting OS value for a container guest doesn't really matter
|
|
# at the moment
|
|
iscontainer = instpage in [INSTALL_PAGE_CONTAINER_APP,
|
|
INSTALL_PAGE_CONTAINER_OS]
|
|
osbox.set_property("visible", iscontainer)
|
|
|
|
# Detection only works/ is valid for URL,
|
|
# FIXME: Also works for CDROM if running as root (since we need to
|
|
# mount the iso/cdrom), but we should probably make this work for
|
|
# more distros (like windows) before we enable it
|
|
if (instpage == INSTALL_PAGE_URL):
|
|
detectbox.show()
|
|
else:
|
|
detectbox.hide()
|
|
|
|
if instpage == INSTALL_PAGE_PXE:
|
|
# Hide the install notebook for pxe, since there isn't anything
|
|
# to ask for
|
|
instnotebook.hide()
|
|
else:
|
|
instnotebook.show()
|
|
|
|
|
|
instnotebook.set_current_page(instpage)
|
|
|
|
def container_install(self):
|
|
return self.get_config_install_page() in [INSTALL_PAGE_CONTAINER_APP,
|
|
INSTALL_PAGE_CONTAINER_OS]
|
|
def skip_disk_page(self):
|
|
return self.get_config_install_page() in [INSTALL_PAGE_IMPORT,
|
|
INSTALL_PAGE_CONTAINER_APP,
|
|
INSTALL_PAGE_CONTAINER_OS]
|
|
|
|
def back(self, src_ignore):
|
|
notebook = self.widget("create-pages")
|
|
curpage = notebook.get_current_page()
|
|
next_page = curpage - 1
|
|
|
|
if curpage == PAGE_INSTALL:
|
|
self.reset_guest_type()
|
|
elif curpage == PAGE_FINISH and self.skip_disk_page():
|
|
# Skip over storage page
|
|
next_page -= 1
|
|
|
|
notebook.set_current_page(next_page)
|
|
|
|
def _get_next_pagenum(self, curpage):
|
|
next_page = curpage + 1
|
|
|
|
if next_page == PAGE_STORAGE and self.skip_disk_page():
|
|
# Skip storage page for import installs
|
|
next_page += 1
|
|
|
|
return next_page
|
|
|
|
def forward(self, src_ignore=None):
|
|
notebook = self.widget("create-pages")
|
|
curpage = notebook.get_current_page()
|
|
|
|
if self.have_startup_error:
|
|
return
|
|
|
|
if curpage == PAGE_INSTALL and self.should_detect_media():
|
|
# Make sure we have detected the OS before validating the page
|
|
self.detect_media_os(forward=True)
|
|
return
|
|
|
|
if self.validate(notebook.get_current_page()) is not True:
|
|
return
|
|
|
|
if curpage == PAGE_NAME:
|
|
self.set_install_page()
|
|
# See if we need to alter our default HV based on install method
|
|
self.guest_from_install_type()
|
|
|
|
next_page = self._get_next_pagenum(curpage)
|
|
|
|
self.widget("create-forward").grab_focus()
|
|
notebook.set_current_page(next_page)
|
|
|
|
def set_page_num_text(self, cur):
|
|
cur += 1
|
|
final = PAGE_FINISH + 1
|
|
if self.skip_disk_page():
|
|
final -= 1
|
|
cur = min(cur, final)
|
|
|
|
page_lbl = ("<span color='#59B0E2'>%s</span>" %
|
|
_("Step %(current_page)d of %(max_page)d") %
|
|
{'current_page': cur, 'max_page': final})
|
|
|
|
self.widget("config-pagenum").set_markup(page_lbl)
|
|
|
|
def page_changed(self, ignore1, ignore2, pagenum):
|
|
# Update page number
|
|
self.set_page_num_text(pagenum)
|
|
|
|
if pagenum == PAGE_NAME:
|
|
self.widget("create-back").set_sensitive(False)
|
|
else:
|
|
self.widget("create-back").set_sensitive(True)
|
|
|
|
if pagenum == PAGE_INSTALL:
|
|
self.detect_media_os()
|
|
self.widget("install-os-distro-box").set_property(
|
|
"visible",
|
|
not self.container_install())
|
|
|
|
if pagenum != PAGE_FINISH:
|
|
self.widget("create-forward").show()
|
|
self.widget("create-finish").hide()
|
|
return
|
|
|
|
# PAGE_FINISH
|
|
# This is hidden in reset_state, so that it doesn't distort
|
|
# the size of the wizard if it is expanded by default due to
|
|
# error
|
|
self.widget("config-advanced-expander").show()
|
|
|
|
self.widget("create-forward").hide()
|
|
self.widget("create-finish").show()
|
|
self.widget("create-finish").grab_focus()
|
|
self.populate_summary()
|
|
|
|
# Repopulate the HV list, so we can make install method relevant
|
|
# changes
|
|
self.populate_hv()
|
|
|
|
# Make sure the networking selection takes into account
|
|
# the install method, so we can warn if trying to PXE boot with
|
|
# insufficient network option
|
|
self.check_network_selection()
|
|
|
|
def get_graphics_device(self, guest):
|
|
if guest.installer.is_container():
|
|
return
|
|
|
|
support_spice = virtinst.support.check_conn_support(guest.conn,
|
|
virtinst.support.SUPPORT_CONN_HV_GRAPHICS_SPICE)
|
|
if not self._rhel6_defaults():
|
|
support_spice = True
|
|
|
|
gtype = self.get_config_graphics_type()
|
|
if (gtype == virtinst.VirtualGraphics.TYPE_SPICE and
|
|
not support_spice):
|
|
logging.debug("Spice requested but HV doesn't support it. "
|
|
"Using VNC graphics.")
|
|
gtype = virtinst.VirtualGraphics.TYPE_VNC
|
|
|
|
return virtinst.VirtualGraphics(conn=guest.conn, type=gtype)
|
|
|
|
def get_video_device(self, guest):
|
|
if guest.installer.is_container():
|
|
return
|
|
return virtinst.VirtualVideoDevice(conn=guest.conn)
|
|
|
|
def get_sound_device(self, guest):
|
|
if (not self.config.get_new_vm_sound() or
|
|
guest.installer.is_container()):
|
|
return
|
|
return virtinst.VirtualAudio(conn=guest.conn)
|
|
|
|
def build_guest(self, installer, name):
|
|
guest = installer.guest_from_installer()
|
|
guest.name = name
|
|
|
|
# Generate UUID (makes customize dialog happy)
|
|
try:
|
|
guest.uuid = virtinst.util.randomUUID(guest.conn)
|
|
except Exception, e:
|
|
self.err.show_err(_("Error setting UUID: %s") % str(e))
|
|
return None
|
|
|
|
# Set up default devices
|
|
try:
|
|
devs = []
|
|
devs.append(self.get_graphics_device(guest))
|
|
devs.append(self.get_video_device(guest))
|
|
devs.append(self.get_sound_device(guest))
|
|
for dev in devs:
|
|
if dev:
|
|
guest.add_device(dev)
|
|
|
|
except Exception, e:
|
|
self.err.show_err(_("Error setting up default devices:") + str(e))
|
|
return None
|
|
|
|
return guest
|
|
|
|
def validate(self, pagenum, oldguest=None):
|
|
try:
|
|
if pagenum == PAGE_NAME:
|
|
return self.validate_name_page()
|
|
elif pagenum == PAGE_INSTALL:
|
|
return self.validate_install_page(oldguest=oldguest)
|
|
elif pagenum == PAGE_MEM:
|
|
return self.validate_mem_page()
|
|
elif pagenum == PAGE_STORAGE:
|
|
return self.validate_storage_page(oldguest=oldguest)
|
|
elif pagenum == PAGE_FINISH:
|
|
return self.validate_final_page()
|
|
|
|
except Exception, e:
|
|
self.err.show_err(_("Uncaught error validating install "
|
|
"parameters: %s") % str(e))
|
|
return
|
|
|
|
def validate_name_page(self):
|
|
name = self.get_config_name()
|
|
|
|
try:
|
|
g = virtinst.Guest(self.conn.get_backend())
|
|
g.name = name
|
|
except Exception, e:
|
|
return self.err.val_err(_("Invalid System Name"), e)
|
|
|
|
return True
|
|
|
|
def validate_install_page(self, oldguest=None):
|
|
instmethod = self.get_config_install_page()
|
|
installer = None
|
|
location = None
|
|
extra = None
|
|
ks = None
|
|
cdrom = False
|
|
is_import = False
|
|
init = None
|
|
fs = None
|
|
distro, variant, ignore1, ignore2 = self.get_config_os_info()
|
|
|
|
if instmethod == INSTALL_PAGE_ISO:
|
|
instclass = virtinst.DistroInstaller
|
|
media = self.get_config_local_media()
|
|
|
|
if not media:
|
|
return self.err.val_err(
|
|
_("An install media selection is required."))
|
|
|
|
location = media
|
|
cdrom = True
|
|
|
|
elif instmethod == INSTALL_PAGE_URL:
|
|
instclass = virtinst.DistroInstaller
|
|
media, extra, ks = self.get_config_url_info()
|
|
|
|
if not media:
|
|
return self.err.val_err(_("An install tree is required."))
|
|
|
|
location = media
|
|
|
|
elif instmethod == INSTALL_PAGE_PXE:
|
|
instclass = virtinst.PXEInstaller
|
|
|
|
elif instmethod == INSTALL_PAGE_IMPORT:
|
|
instclass = virtinst.ImportInstaller
|
|
is_import = True
|
|
|
|
import_path = self.get_config_import_path()
|
|
if not import_path:
|
|
return self.err.val_err(
|
|
_("A storage path to import is required."))
|
|
|
|
elif instmethod == INSTALL_PAGE_CONTAINER_APP:
|
|
instclass = virtinst.ContainerInstaller
|
|
|
|
init = self.get_config_container_app_path()
|
|
if not init:
|
|
return self.err.val_err(_("An application path is required."))
|
|
|
|
elif instmethod == INSTALL_PAGE_CONTAINER_OS:
|
|
instclass = virtinst.ContainerInstaller
|
|
|
|
fs = self.get_config_container_fs_path()
|
|
if not fs:
|
|
return self.err.val_err(_("An OS directory path is required."))
|
|
|
|
# Build the installer and Guest instance
|
|
try:
|
|
installer = self.build_installer(instclass)
|
|
name = self.get_config_name()
|
|
self.guest = self.build_guest(installer, name)
|
|
if not self.guest:
|
|
return False
|
|
except Exception, e:
|
|
return self.err.val_err(
|
|
_("Error setting installer parameters."), e)
|
|
|
|
# Validate media location
|
|
try:
|
|
if location is not None:
|
|
self.guest.installer.location = location
|
|
if cdrom:
|
|
self.guest.installer.cdrom = True
|
|
|
|
extraargs = ""
|
|
if extra:
|
|
extraargs += extra
|
|
if ks:
|
|
extraargs += " ks=%s" % ks
|
|
|
|
if extraargs:
|
|
self.guest.installer.extraargs = extraargs
|
|
|
|
if init:
|
|
self.guest.installer.init = init
|
|
|
|
if fs:
|
|
fsdev = virtinst.VirtualFilesystem(conn=self.guest.conn)
|
|
fsdev.target = "/"
|
|
fsdev.source = fs
|
|
self.guest.add_device(fsdev)
|
|
|
|
except Exception, e:
|
|
return self.err.val_err(
|
|
_("Error setting install media location."), e)
|
|
|
|
# OS distro/variant validation
|
|
try:
|
|
if distro:
|
|
self.guest.os_type = distro
|
|
if variant:
|
|
self.guest.os_variant = variant
|
|
except ValueError, e:
|
|
return self.err.val_err(_("Error setting OS information."), e)
|
|
|
|
# Kind of wonky, run storage validation now, which will assign
|
|
# the import path. Import installer skips the storage page.
|
|
if is_import:
|
|
if not self.validate_storage_page(oldguest=oldguest):
|
|
return False
|
|
|
|
if not oldguest:
|
|
if self.guest.installer.scratchdir_required():
|
|
path = self.guest.installer.scratchdir
|
|
elif instmethod == INSTALL_PAGE_ISO:
|
|
path = self.guest.installer.location
|
|
else:
|
|
path = None
|
|
|
|
if path:
|
|
uihelpers.check_path_search_for_qemu(self.topwin,
|
|
self.conn, path)
|
|
|
|
# Validation passed, store the install path (if there is one) in
|
|
# gconf
|
|
self.get_config_local_media(store_media=True)
|
|
self.get_config_url_info(store_media=True)
|
|
return True
|
|
|
|
def validate_mem_page(self):
|
|
cpus = self.widget("config-cpus").get_value()
|
|
mem = self.widget("config-mem").get_value()
|
|
|
|
# VCPUS
|
|
try:
|
|
self.guest.vcpus = int(cpus)
|
|
except Exception, e:
|
|
return self.err.val_err(_("Error setting CPUs."), e)
|
|
|
|
# Memory
|
|
try:
|
|
self.guest.memory = int(mem)
|
|
self.guest.maxmemory = int(mem)
|
|
except Exception, e:
|
|
return self.err.val_err(_("Error setting guest memory."), e)
|
|
|
|
return True
|
|
|
|
def validate_storage_page(self, oldguest=None):
|
|
use_storage = self.widget("enable-storage").get_active()
|
|
instcd = self.get_config_install_page() == INSTALL_PAGE_ISO
|
|
conn = self.conn.get_backend()
|
|
|
|
# CD/ISO install and no disks implies LiveCD
|
|
if instcd:
|
|
self.guest.installer.livecd = not use_storage
|
|
|
|
usepath = None
|
|
if oldguest and self.disk:
|
|
usepath = self.disk.path
|
|
|
|
if self.disk and self.disk in self.guest.get_devices("disk"):
|
|
self.guest.remove_device(self.disk)
|
|
self.disk = None
|
|
|
|
# Validate storage
|
|
if not use_storage:
|
|
return True
|
|
|
|
# Make sure default pool is running
|
|
if self.is_default_storage():
|
|
ret = uihelpers.check_default_pool_active(self.topwin, self.conn)
|
|
if not ret:
|
|
return False
|
|
|
|
try:
|
|
# This can error out
|
|
diskpath, disksize, sparse = self.get_storage_info()
|
|
|
|
if usepath:
|
|
diskpath = usepath
|
|
elif self.is_default_storage() and not oldguest:
|
|
# See if the ideal disk path (/default/pool/vmname.img)
|
|
# exists, and if unused, prompt the use for using it
|
|
ideal = util.get_ideal_path(self.conn,
|
|
self.guest.name)
|
|
do_exist = False
|
|
ret = True
|
|
|
|
try:
|
|
do_exist = virtinst.VirtualDisk.path_exists(conn, ideal)
|
|
ret = virtinst.VirtualDisk.path_in_use_by(conn, ideal)
|
|
except:
|
|
logging.exception("Error checking default path usage")
|
|
|
|
if do_exist and not ret:
|
|
do_use = self.err.yes_no(
|
|
_("The following storage already exists, but is not\n"
|
|
"in use by any virtual machine:\n\n%s\n\n"
|
|
"Would you like to reuse this storage?") % ideal)
|
|
|
|
if do_use:
|
|
diskpath = ideal
|
|
|
|
if not diskpath:
|
|
return self.err.val_err(_("A storage path must be specified."))
|
|
|
|
disk = virtinst.VirtualDisk(conn=conn,
|
|
path=diskpath,
|
|
size=disksize,
|
|
sparse=sparse)
|
|
|
|
fmt = self.config.get_storage_format()
|
|
if (self.is_default_storage() and
|
|
disk.vol_install and
|
|
fmt in disk.vol_install.formats):
|
|
logging.debug("Setting disk format from prefs: %s", fmt)
|
|
disk.vol_install.format = fmt
|
|
|
|
except Exception, e:
|
|
return self.err.val_err(_("Storage parameter error."), e)
|
|
|
|
isfatal, errmsg = disk.is_size_conflict()
|
|
if not oldguest and not isfatal and errmsg:
|
|
# Fatal errors are reported when setting 'size'
|
|
res = self.err.ok_cancel(_("Not Enough Free Space"), errmsg)
|
|
if not res:
|
|
return False
|
|
|
|
# Disk collision
|
|
if not oldguest and disk.is_conflict_disk(self.guest.conn):
|
|
res = self.err.yes_no(_('Disk "%s" is already in use by another '
|
|
'guest!' % disk.path),
|
|
_("Do you really want to use the disk?"))
|
|
if not res:
|
|
return False
|
|
|
|
if not oldguest:
|
|
uihelpers.check_path_search_for_qemu(self.topwin,
|
|
self.conn, disk.path)
|
|
|
|
self.disk = disk
|
|
self.guest.add_device(self.disk)
|
|
|
|
return True
|
|
|
|
def validate_final_page(self):
|
|
# HV + Arch selection
|
|
self.guest.installer.type = self.capsdomain.hypervisor_type
|
|
self.guest.installer.os_type = self.capsguest.os_type
|
|
self.guest.installer.arch = self.capsguest.arch
|
|
|
|
nettype, devname, macaddr = self.get_config_network_info()
|
|
|
|
if nettype is None:
|
|
# No network device available
|
|
instmethod = self.get_config_install_page()
|
|
methname = None
|
|
if instmethod == INSTALL_PAGE_PXE:
|
|
methname = "PXE"
|
|
elif instmethod == INSTALL_PAGE_URL:
|
|
methname = "URL"
|
|
|
|
if methname:
|
|
return self.err.val_err(
|
|
_("Network device required for %s install.") %
|
|
methname)
|
|
|
|
nic = uihelpers.validate_network(self.topwin,
|
|
self.conn, nettype, devname, macaddr)
|
|
if nic is False:
|
|
return False
|
|
|
|
if self.nic and self.nic in self.guest.get_devices("interface"):
|
|
self.guest.remove_device(self.nic)
|
|
if nic:
|
|
self.nic = nic
|
|
self.guest.add_device(self.nic)
|
|
|
|
return True
|
|
|
|
|
|
# Interesting methods
|
|
def build_installer(self, instclass):
|
|
installer = instclass(conn=self.conn.get_backend(),
|
|
type=self.capsdomain.hypervisor_type,
|
|
os_type=self.capsguest.os_type)
|
|
installer.arch = self.capsguest.arch
|
|
|
|
return installer
|
|
|
|
def guest_from_install_type(self):
|
|
instmeth = self.get_config_install_page()
|
|
|
|
if not self.conn.is_xen() and not self.conn.is_test_conn():
|
|
return
|
|
|
|
# FIXME: some things are dependent on domain type (vcpu max)
|
|
if instmeth in [INSTALL_PAGE_URL, INSTALL_PAGE_IMPORT]:
|
|
self.change_caps(gtype="xen")
|
|
|
|
def reset_guest_type(self):
|
|
self.change_caps()
|
|
|
|
def rebuild_guest(self):
|
|
pagenum = 0
|
|
guest = self.guest
|
|
while True:
|
|
self.validate(pagenum, oldguest=guest)
|
|
if pagenum >= PAGE_FINISH:
|
|
break
|
|
pagenum = self._get_next_pagenum(pagenum)
|
|
|
|
def finish(self, src_ignore):
|
|
# Validate the final page
|
|
page = self.widget("create-pages").get_current_page()
|
|
if self.validate(page) is not True:
|
|
return False
|
|
|
|
self.rebuild_guest()
|
|
guest = self.guest
|
|
|
|
# Start the install
|
|
self.failed_guest = None
|
|
self.topwin.set_sensitive(False)
|
|
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
|
|
|
def start_install():
|
|
if not self.get_config_customize():
|
|
self.start_install(guest)
|
|
return
|
|
|
|
self.customize(guest)
|
|
|
|
self._check_start_error(start_install)
|
|
|
|
def _undo_finish(self, ignore=None):
|
|
self.topwin.set_sensitive(True)
|
|
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
|
|
|
def _check_start_error(self, cb, *args, **kwargs):
|
|
try:
|
|
cb(*args, **kwargs)
|
|
except Exception, e:
|
|
self._undo_finish()
|
|
self.err.show_err(_("Error starting installation: ") + str(e))
|
|
|
|
def customize(self, guest):
|
|
virtinst_guest = vmmDomainVirtinst(self.conn, guest, self.guest.uuid)
|
|
|
|
def cleanup_config_window():
|
|
if self.config_window:
|
|
for s in self.config_window_signals:
|
|
self.config_window.disconnect(s)
|
|
self.config_window.cleanup()
|
|
self.config_window = None
|
|
|
|
def start_install_wrapper(ignore, guest):
|
|
cleanup_config_window()
|
|
if not self.is_visible():
|
|
return
|
|
self._check_start_error(self.start_install, guest)
|
|
|
|
def details_closed(ignore):
|
|
cleanup_config_window()
|
|
self._undo_finish()
|
|
self.widget("summary-customize").set_active(False)
|
|
|
|
cleanup_config_window()
|
|
self.config_window = vmmDetails(virtinst_guest, self.topwin)
|
|
self.config_window_signals = []
|
|
self.config_window_signals.append(self.config_window.connect(
|
|
"customize-finished",
|
|
start_install_wrapper,
|
|
guest))
|
|
self.config_window_signals.append(self.config_window.connect(
|
|
"details-closed",
|
|
details_closed))
|
|
self.config_window.show()
|
|
|
|
def start_install(self, guest):
|
|
progWin = vmmAsyncJob(self.do_install, [guest],
|
|
_("Creating Virtual Machine"),
|
|
_("The virtual machine is now being "
|
|
"created. Allocation of disk storage "
|
|
"and retrieval of the installation "
|
|
"images may take a few minutes to "
|
|
"complete."),
|
|
self.topwin)
|
|
error, details = progWin.run()
|
|
|
|
self.topwin.set_sensitive(True)
|
|
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
|
|
|
if error:
|
|
error = (_("Unable to complete install: '%s'") % error)
|
|
self.err.show_err(error,
|
|
details=details)
|
|
self.failed_guest = self.guest
|
|
return
|
|
|
|
self.close()
|
|
|
|
# Launch details dialog for new VM
|
|
self.emit("action-show-vm", self.conn.get_uri(), guest.uuid)
|
|
|
|
def do_install(self, asyncjob, guest):
|
|
meter = asyncjob.get_meter()
|
|
|
|
logging.debug("Starting background install process")
|
|
guest.start_install(False, meter=meter)
|
|
logging.debug("Install completed")
|
|
|
|
# Make sure we pick up the domain object
|
|
self.conn.tick(noStatsUpdate=True)
|
|
vm = self.conn.get_vm(guest.uuid)
|
|
vm.tick()
|
|
|
|
if vm.is_shutoff():
|
|
# Domain is already shutdown, but no error was raised.
|
|
# Probably means guest had no 'install' phase, as in
|
|
# for live cds. Try to restart the domain.
|
|
vm.startup()
|
|
elif guest.installer.has_install_phase():
|
|
# Register a status listener, which will restart the
|
|
# guest after the install has finished
|
|
def cb():
|
|
vm.connect_opt_out("status-changed",
|
|
self.check_install_status, guest)
|
|
return False
|
|
self.idle_add(cb)
|
|
|
|
|
|
def check_install_status(self, vm, ignore1, ignore2, virtinst_guest=None):
|
|
if vm.is_crashed():
|
|
logging.debug("VM crashed, cancelling install plans.")
|
|
return True
|
|
|
|
if not vm.is_shutoff():
|
|
return
|
|
|
|
try:
|
|
if virtinst_guest:
|
|
continue_inst = virtinst_guest.get_continue_inst()
|
|
|
|
if continue_inst:
|
|
logging.debug("VM needs a 2 stage install, continuing.")
|
|
# Continue the install, then reconnect this opt
|
|
# out handler, removing the virtinst_guest which
|
|
# will force one final restart.
|
|
virtinst_guest.continue_install()
|
|
|
|
vm.connect_opt_out("status-changed",
|
|
self.check_install_status, None)
|
|
return True
|
|
|
|
if vm.get_install_abort():
|
|
logging.debug("User manually shutdown VM, not restarting "
|
|
"guest after install.")
|
|
return True
|
|
|
|
logging.debug("Install should be completed, starting VM.")
|
|
vm.startup()
|
|
except Exception, e:
|
|
self.err.show_err(_("Error continue install: %s") % str(e))
|
|
|
|
return True
|
|
|
|
def pretty_storage(self, size):
|
|
return "%.1f GB" % float(size)
|
|
|
|
def pretty_memory(self, mem):
|
|
return "%d MB" % (mem / 1024.0)
|
|
|
|
|
|
# Distro detection methods
|
|
|
|
def set_distro_labels(self, distro, ver):
|
|
# Helper to set auto detect result labels
|
|
if not self.is_detect_active():
|
|
return
|
|
|
|
self.widget("install-os-type-label").set_text(distro)
|
|
self.widget("install-os-version-label").set_text(ver)
|
|
|
|
def set_os_val(self, os_widget, value):
|
|
# Helper method to set the OS Type/Variant selections to the passed
|
|
# values, or -1 if not present.
|
|
model = os_widget.get_model()
|
|
|
|
def set_val():
|
|
idx = 0
|
|
for idx in range(0, len(model)):
|
|
row = model[idx]
|
|
if value and row[0] == value:
|
|
break
|
|
|
|
if idx == len(os_widget.get_model()) - 1:
|
|
idx = -1
|
|
|
|
os_widget.set_active(idx)
|
|
if idx == -1:
|
|
os_widget.set_active(0)
|
|
|
|
if idx >= 0:
|
|
return row[1]
|
|
if self.show_all_os:
|
|
return None
|
|
|
|
ret = set_val()
|
|
if ret:
|
|
return ret
|
|
|
|
# Trigger the last element in the list, which turns on show_all_os
|
|
os_widget.set_active(len(model) - 1)
|
|
ret = set_val()
|
|
if ret:
|
|
return ret
|
|
return _("Unknown")
|
|
|
|
def set_distro_selection(self, distro, ver):
|
|
# Wrapper to change OS Type/Variant values, and update the distro
|
|
# detection labels
|
|
if not self.is_detect_active():
|
|
return
|
|
|
|
dl = self.set_os_val(self.widget("install-os-type"), distro)
|
|
vl = self.set_os_val(self.widget("install-os-version"), ver)
|
|
self.set_distro_labels(dl, vl)
|
|
|
|
def check_detection(self, idx, forward):
|
|
results = None
|
|
try:
|
|
base = _("Detecting")
|
|
|
|
if not self.detectedDistro or (idx >= (DETECT_TIMEOUT * 2)):
|
|
detect_str = base + ("." * ((idx % 3) + 1))
|
|
self.set_distro_labels(detect_str, detect_str)
|
|
|
|
self.timeout_add(500, self.check_detection,
|
|
idx + 1, forward)
|
|
return
|
|
|
|
results = self.detectedDistro
|
|
except:
|
|
logging.exception("Error in distro detect timeout")
|
|
|
|
results = results or (None, None)
|
|
self.widget("create-forward").set_sensitive(True)
|
|
self.mediaDetected = True
|
|
self.detecting = False
|
|
logging.debug("Finished OS detection.")
|
|
self.set_distro_selection(*results)
|
|
if forward:
|
|
self.idle_add(self.forward, ())
|
|
|
|
def start_detection(self, forward):
|
|
if self.detecting:
|
|
return
|
|
|
|
media = self.get_config_detectable_media()
|
|
if not media:
|
|
return
|
|
|
|
self.detectedDistro = None
|
|
|
|
logging.debug("Starting OS detection thread for media=%s", media)
|
|
self.widget("create-forward").set_sensitive(False)
|
|
|
|
detectThread = threading.Thread(target=self.actually_detect,
|
|
name="Actual media detection",
|
|
args=(media,))
|
|
detectThread.setDaemon(True)
|
|
detectThread.start()
|
|
|
|
self.check_detection(0, forward)
|
|
|
|
def actually_detect(self, media):
|
|
try:
|
|
installer = self.build_installer(virtinst.DistroInstaller)
|
|
installer.location = media
|
|
|
|
self.detectedDistro = installer.detect_distro()
|
|
except:
|
|
logging.exception("Error detecting distro.")
|
|
self.detectedDistro = (None, None)
|
|
|
|
def _rhel6_defaults(self):
|
|
emu = None
|
|
if self.guest:
|
|
emu = self.guest.emulator
|
|
elif self.capsdomain:
|
|
emu = self.capsdomain.emulator
|
|
|
|
ret = self.conn.rhel6_defaults(emu)
|
|
return ret
|
|
|
|
def _browse_file(self, callback, is_media=False, is_dir=False):
|
|
if is_media:
|
|
reason = self.config.CONFIG_DIR_ISO_MEDIA
|
|
elif is_dir:
|
|
reason = self.config.CONFIG_DIR_FS
|
|
else:
|
|
reason = self.config.CONFIG_DIR_IMAGE
|
|
|
|
if self.storage_browser is None:
|
|
self.storage_browser = vmmStorageBrowser(self.conn)
|
|
|
|
self.storage_browser.rhel6_defaults = self._rhel6_defaults()
|
|
|
|
self.storage_browser.set_vm_name(self.get_config_name())
|
|
self.storage_browser.set_finish_cb(callback)
|
|
self.storage_browser.set_browse_reason(reason)
|
|
self.storage_browser.show(self.topwin, self.conn)
|