124
122
"on_install_local_cdrom_combo_changed": self.detect_media_os,
125
123
"on_install_local_box_changed": self.detect_media_os,
126
124
"on_install_local_browse_clicked": self.browse_iso,
125
"on_install_import_browse_clicked": self.browse_import,
128
127
"on_install_detect_os_toggled": self.toggle_detect_os,
129
128
"on_install_os_type_changed": self.change_os_type,
188
187
# State init methods
189
188
def startup_error(self, error):
190
self.window.get_widget("startup-error").show()
189
self.window.get_widget("startup-error-box").show()
191
190
self.window.get_widget("install-box").hide()
192
191
self.window.get_widget("create-forward").set_sensitive(False)
194
193
self.window.get_widget("startup-error").set_text("Error: %s" % error)
196
def startup_warning(self, error):
197
self.window.get_widget("startup-error-box").show()
198
self.window.get_widget("startup-error").set_text("Warning: %s" %
197
201
def set_initial_state(self):
199
203
self.window.get_widget("create-pages").set_show_tabs(False)
267
271
# [ interface type, device name, label, sensitive ]
268
272
net_list = self.window.get_widget("config-netdev")
269
uihelpers.init_network_list(net_list)
273
bridge_box = self.window.get_widget("config-netdev-bridge-box")
274
uihelpers.init_network_list(net_list, bridge_box)
272
277
archModel = gtk.ListStore(str)
284
289
hyperList.add_attribute(text, 'sensitive', 3)
285
290
hyperList.set_model(hyperModel)
287
293
sparse_info = self.window.get_widget("config-storage-nosparse-info")
288
sparse_str = _("Fully allocating storage will take longer now, "
289
"but the OS install phase will be quicker. \n\n"
290
"Skipping allocation can also cause space issues on "
291
"the host machine, if the maximum image size exceeds "
292
"available storage space.")
293
util.tooltip_wrapper(sparse_info, sparse_str)
294
uihelpers.set_sparse_tooltip(sparse_info)
295
296
def reset_state(self, urihint=None):
297
298
self.failed_guest = None
298
299
self.window.get_widget("create-pages").set_current_page(PAGE_NAME)
299
300
self.page_changed(None, None, PAGE_NAME)
300
self.window.get_widget("startup-error").hide()
301
self.window.get_widget("startup-error-box").hide()
301
302
self.window.get_widget("install-box").show()
303
304
# Name page state
306
307
self.window.get_widget("method-local").set_active(True)
307
308
self.window.get_widget("create-conn").set_active(-1)
308
309
activeconn = self.populate_conn_list(urihint)
309
self.set_conn(activeconn)
312
self.set_conn(activeconn, force_validate=True)
314
logging.exception("Error setting create wizard conn state.")
315
return self.startup_error(str(e))
310
317
if not activeconn:
311
return self.startup_error(_("No active connection to install on."))
318
return self.startup_error(
319
_("No active connection to install on."))
313
321
# Everything from this point forward should be connection independent
331
339
self.populate_media_model(urlmodel, self.config.get_media_urls())
332
340
self.populate_media_model(ksmodel, self.config.get_kickstart_urls())
343
self.window.get_widget("install-import-entry").set_text("")
335
346
self.window.get_widget("config-mem").set_value(512)
336
347
self.window.get_widget("config-cpus").set_value(1)
350
label_widget = self.window.get_widget("phys-hd-label")
339
351
if not self.host_storage_timer:
340
self.host_storage_timer = gobject.timeout_add(3 * 1000,
341
self.host_space_tick)
352
self.host_storage_timer = util.safe_timeout_add(3 * 1000,
353
uihelpers.host_space_tick,
354
self.conn, self.config,
342
356
self.window.get_widget("enable-storage").set_active(True)
343
357
self.window.get_widget("config-storage-create").set_active(True)
344
358
self.window.get_widget("config-storage-size").set_value(8)
357
371
if self.conn.is_read_only():
358
372
return self.startup_error(_("Connection is read only."))
374
if self.conn.no_install_options():
375
error = _("No hypervisor options were found for this\n"
378
if self.conn.is_qemu():
380
error += _("This usually means that qemu or kvm is not\n"
381
"installed on your machine. Please ensure they\n"
382
"are installed as intended.")
383
return self.startup_error(error)
360
385
# A bit out of order, but populate arch + hv lists so we can
361
386
# determine a default
362
387
self.caps = self.conn.get_capabilities()
367
logging.exception("Error determining default hypervisor")
368
return self.startup_error(_("No guests are supported for this"
370
389
self.populate_hv()
391
if self.conn.is_xen():
392
if self.conn.hw_virt_supported():
393
if self.conn.is_bios_virt_disabled():
394
error = _("Host supports full virtualization, but\n"
395
"no related install options are available.\n"
396
"This may mean support is disabled in your\n"
398
self.startup_warning(error)
401
error = _("Host does not appear to support hardware\n"
402
"virtualization. Install options may be limited.")
403
self.startup_warning(error)
405
elif self.conn.is_qemu():
406
if not self.conn.is_kvm_supported():
407
error = _("KVM is not available. This may mean the KVM\n"
408
"package is not installed, or the KVM kernel modules \n"
409
"are not loaded. Your virtual machines may perform poorly.")
410
self.startup_warning(error)
372
412
is_local = not self.conn.is_remote()
373
413
is_storage_capable = self.conn.is_storage_capable()
374
414
is_pv = (self.capsguest.os_type == "xen")
412
452
util.tooltip_wrapper(method_local, local_tt)
413
453
util.tooltip_wrapper(method_pxe, pxe_tt)
415
# Attempt to create the default pool
418
if is_storage_capable:
419
# FIXME: Emit 'pool-added' or something?
420
util.build_default_pool(self.conn.vmm)
423
logging.debug("Building default pool failed: %s" % str(e))
427
456
iso_option = self.window.get_widget("install-local-iso")
428
457
cdrom_option = self.window.get_widget("install-local-cdrom")
502
531
util.tooltip_wrapper(storage_area, storage_tooltip)
534
net_expander = self.window.get_widget("config-advanced-expander")
505
536
net_list = self.window.get_widget("config-netdev")
506
537
net_warn = self.window.get_widget("config-netdev-warn")
507
uihelpers.populate_network_list(net_list, self.conn)
538
do_warn = uihelpers.populate_network_list(net_list, self.conn)
509
if self.conn.netdev_error:
540
if self.conn.netdev_error or do_warn:
511
util.tooltip_wrapper(net_warn, self.conn.netdev_error)
542
net_expander.set_expanded(True)
544
if self.conn.netdev_error:
545
util.tooltip_wrapper(net_warn, self.conn.netdev_error)
548
net_expander.set_expanded(False)
515
550
newmac = uihelpers.generate_macaddr(self.conn)
516
551
self.window.get_widget("config-set-macaddr").set_active(bool(newmac))
780
822
return (media.strip(), extra.strip(), ks.strip())
824
def get_config_import_path(self):
825
return self.window.get_widget("install-import-entry").get_text()
827
def get_default_path(self, name):
828
# Don't generate a new path if the install failed
829
if self.failed_guest:
830
if len(self.failed_guest.disks) > 0:
831
return self.failed_guest.disks[0].path
833
return util.get_default_path(self.conn, self.config, name)
835
def is_default_storage(self):
836
return self.window.get_widget("config-storage-create").get_active()
782
838
def get_storage_info(self):
784
840
size = self.window.get_widget("config-storage-size").get_value()
785
841
sparse = not self.window.get_widget("config-storage-nosparse").get_active()
786
if self.window.get_widget("config-storage-create").get_active():
843
if self.get_config_install_page() == INSTALL_PAGE_IMPORT:
844
path = self.get_config_import_path()
848
elif self.is_default_storage():
787
849
path = self.get_default_path(self.guest.name)
788
850
logging.debug("Default storage path is: %s" % path)
792
854
return (path, size, sparse)
794
def get_default_path(self, name):
797
# Don't generate a new path if the install failed
798
if self.failed_guest:
799
if len(self.failed_guest.disks) > 0:
800
return self.failed_guest.disks[0].path
804
# Use old generating method
805
d = self.config.get_default_image_dir(self.conn)
806
origf = os.path.join(d, name + ".img")
810
while os.path.exists(f) and n < 100:
811
f = os.path.join(d, self.get_config_name() +
812
"-" + str(n) + ".img")
814
if os.path.exists(f):
819
pool = self.conn.vmm.storagePoolLookupByName(util.DEFAULT_POOL_NAME)
820
path = virtinst.Storage.StorageVolume.find_free_name(name,
821
pool_object=pool, suffix=".img")
823
path = os.path.join(util.DEFAULT_POOL_PATH, path)
827
def host_disk_space(self, path=None):
829
path = util.DEFAULT_POOL_PATH
833
# FIXME: make sure not inactive?
834
# FIXME: use a conn specific function after we send pool-added
835
pool = virtinst.util.lookup_pool_by_path(self.conn.vmm, path)
838
avail = int(virtinst.util.get_xml_path(pool.XMLDesc(0),
841
if not avail and not self.conn.is_remote():
842
vfs = os.statvfs(os.path.dirname(path))
843
avail = vfs[statvfs.F_FRSIZE] * vfs[statvfs.F_BAVAIL]
845
return float(avail / 1024.0 / 1024.0 / 1024.0)
847
def host_space_tick(self):
848
max_storage = self.host_disk_space()
849
if self.host_storage == max_storage:
851
self.host_storage = max_storage
853
hd_label = ("%s available in the default location" %
854
self.pretty_storage(max_storage))
855
hd_label = ("<span color='#484848'>%s</span>" % hd_label)
856
self.window.get_widget("phys-hd-label").set_markup(hd_label)
857
self.window.get_widget("config-storage-size").set_range(1, self.host_storage)
861
856
def get_config_network_info(self):
862
netidx = self.window.get_widget("config-netdev").get_active()
863
netinfo = self.window.get_widget("config-netdev").get_model()[netidx]
857
net_list = self.window.get_widget("config-netdev")
858
bridge_ent = self.window.get_widget("config-netdev-bridge")
864
859
macaddr = self.window.get_widget("config-macaddr").get_text()
866
return netinfo[0], netinfo[1], macaddr.strip()
861
net_type, net_src = uihelpers.get_network_selection(net_list,
864
return net_type, net_src, macaddr.strip()
868
866
def get_config_sound(self):
869
867
if self.conn.is_remote():
883
881
model = src.get_model()
887
conn = self.engine.connections[uri]["connection"]
889
# If we aren't visible, let reset_state handle this for us, which
890
# has a better chance of reporting error
891
if not self.is_visible():
890
conn = self.engine.connections[uri]["connection"]
891
894
self.set_conn(conn)
893
896
def hv_changed(self, src):
975
978
nodetect_label.show()
980
def browse_import(self, ignore1=None, ignore2=None):
981
def set_import_path(ignore, path):
982
self.window.get_widget("install-import-entry").set_text(path)
984
self._browse_file(set_import_path, is_media=False)
977
986
def browse_iso(self, ignore1=None, ignore2=None):
978
self._browse_file(self.set_iso_storage_path,
987
def set_iso_storage_path(ignore, path):
988
self.window.get_widget("install-local-box").child.set_text(path)
990
self._browse_file(set_iso_storage_path, is_media=True)
980
991
self.window.get_widget("install-local-box").activate()
982
993
def toggle_enable_storage(self, src):
983
994
self.window.get_widget("config-storage-box").set_sensitive(src.get_active())
985
996
def browse_storage(self, ignore1):
986
self._browse_file(self.set_disk_storage_path,
997
def set_disk_storage_path(ignore, path):
998
self.window.get_widget("config-storage-entry").set_text(path)
1000
self._browse_file(set_disk_storage_path,
989
1003
def toggle_storage_select(self, src):
993
1007
def toggle_macaddr(self, src):
994
1008
self.window.get_widget("config-macaddr").set_sensitive(src.get_active())
996
def set_iso_storage_path(self, ignore, path):
997
self.window.get_widget("install-local-box").child.set_text(path)
999
def set_disk_storage_path(self, ignore, path):
1000
self.window.get_widget("config-storage-entry").set_text(path)
1002
1010
# Navigation methods
1003
1011
def set_install_page(self):
1004
1012
instnotebook = self.window.get_widget("install-method-pages")
1042
1051
if curpage == PAGE_NAME:
1043
1052
self.set_install_page()
1044
1053
# See if we need to alter our default HV based on install method
1045
# FIXME: URL installs also come into play with whether we want
1047
1054
self.guest_from_install_type()
1056
next_page = curpage + 1
1057
if next_page == PAGE_STORAGE and is_import:
1058
# Skip storage page for import installs
1049
1061
self.window.get_widget("create-forward").grab_focus()
1050
notebook.set_current_page(curpage + 1)
1062
notebook.set_current_page(next_page)
1052
1064
def page_changed(self, ignore1, ignore2, pagenum):
1067
1079
self.detect_media_os()
1069
1081
if pagenum == PAGE_FINISH:
1082
# This is hidden in reset_state, so that it doesn't distort
1083
# the size of the wizard if it is expanded by default due to
1085
self.window.get_widget("config-advanced-expander").show()
1070
1087
self.window.get_widget("create-forward").hide()
1071
1088
self.window.get_widget("create-finish").show()
1072
1089
self.window.get_widget("create-finish").grab_focus()
1238
1264
return self.err.val_err(_("Error setting OS information."),
1267
# Kind of wonky, run storage validation now, which will assign
1268
# the import path. Import installer skips the storage page.
1270
if not self.validate_storage_page(revalidate):
1241
1273
if not revalidate:
1242
1274
if self.guest.installer.scratchdir_required():
1243
1275
path = self.guest.installer.scratchdir
1290
1322
# This can error out
1291
1323
diskpath, disksize, sparse = self.get_storage_info()
1325
if self.is_default_storage() and not revalidate:
1326
# See if the ideal disk path (/default/pool/vmname.img)
1327
# exists, and if unused, prompt the use for using it
1328
ideal = util.get_ideal_path(self.conn, self.config,
1334
do_exist = virtinst.VirtualDisk.path_exists(
1335
self.conn.vmm, ideal)
1337
ret = virtinst.VirtualDisk.path_in_use_by(self.conn.vmm,
1340
logging.exception("Error checking default path usage")
1342
if do_exist and not ret:
1343
do_use = self.err.yes_no(
1344
_("The following path already exists, but is not\n"
1345
"in use by any virtual machine:\n\n%s\n\n"
1346
"Would you like to use this path?") % ideal)
1293
1351
if not diskpath:
1294
1352
return self.verr(_("A storage path must be specified."))
1467
1525
self.failed_guest = self.guest
1470
# Ensure new VM is loaded
1471
# FIXME: Hmm, shouldn't we emit a signal here rather than do this?
1472
self.conn.tick(noStatsUpdate=True)
1528
vm = self.conn.get_vm(guest.uuid)
1474
1530
if self.config.get_console_popup() == 1:
1475
1531
# user has requested console on new created vms only
1476
vm = self.conn.get_vm(guest.uuid)
1477
(gtype, ignore, ignore, ignore, ignore) = vm.get_graphics_console()
1532
gtype = vm.get_graphics_console()[0]
1478
1533
if gtype == "vnc":
1479
1534
self.emit("action-show-console", self.conn.get_uri(),
1492
1547
logging.debug("Starting background install process")
1494
1549
guest.conn = util.dup_conn(self.config, self.conn)
1495
# FIXME: This is why we need a more unified virtinst device API: so
1496
# we can do things like swap out the connection on all
1497
# devices for a forked one. At least we should get a helper
1498
# method in the Guest API.
1499
for disk in guest.disks:
1500
disk.conn = guest.conn
1550
for dev in guest.get_all_devices():
1551
dev.conn = guest.conn
1502
1553
dom = guest.start_install(False, meter = meter)
1503
1554
if dom == None:
1506
1557
logging.error("Guest install did not return a domain")
1508
1559
logging.debug("Install completed")
1561
# Make sure we pick up the domain object
1562
self.conn.tick(noStatsUpdate=True)
1563
vm = self.conn.get_vm(guest.uuid)
1566
# Domain is already shutdown, but no error was raised.
1567
# Probably means guest had no 'install' phase, as in
1568
# for live cds. Try to restart the domain.
1571
# Register a status listener, which will restart the
1572
# guest after the install has finished
1573
util.connect_opt_out(vm, "status-changed",
1574
self.check_install_status, guest)
1510
1577
(_type, value, stacktrace) = sys.exc_info ()
1519
1586
asyncjob.set_error(error, details)
1588
def check_install_status(self, vm, ignore1, ignore2, virtinst_guest=None):
1590
logging.debug("VM crashed, cancelling install plans.")
1593
if not vm.is_shutoff():
1598
continue_inst = virtinst_guest.get_continue_inst()
1601
logging.debug("VM needs a 2 stage install, continuing.")
1602
# Continue the install, then reconnect this opt
1603
# out handler, removing the virtinst_guest which
1604
# will force one final restart.
1605
virtinst_guest.continue_install()
1606
util.connect_opt_out(vm, "status-changed",
1607
self.check_install_status, None)
1610
logging.debug("Install should be completed, starting VM.")
1612
except Exception, e:
1613
self.err.show_err(_("Error continue install: %s") % str(e),
1614
"".join(traceback.format_exc()))
1521
1618
def pretty_storage(self, size):
1522
1619
return "%.1f Gb" % float(size)