From 121cf5528d95cc261199024c970b56e1735db448 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sun, 16 Jun 2019 22:00:57 -0400 Subject: virtManager: rename create.py -> createvm.py To be more consistent with other create wizard naming --- ui/create.ui | 2622 ----------------------------------------------- ui/createvm.ui | 2622 +++++++++++++++++++++++++++++++++++++++++++++++ virtManager/create.py | 2213 --------------------------------------- virtManager/createvm.py | 2213 +++++++++++++++++++++++++++++++++++++++ virtManager/engine.py | 4 +- virtManager/manager.py | 4 +- 6 files changed, 4839 insertions(+), 4839 deletions(-) delete mode 100644 ui/create.ui create mode 100644 ui/createvm.ui delete mode 100644 virtManager/create.py create mode 100644 virtManager/createvm.py diff --git a/ui/create.ui b/ui/create.ui deleted file mode 100644 index 4df6f6fe..00000000 --- a/ui/create.ui +++ /dev/null @@ -1,2622 +0,0 @@ - - - - - - 128 - 1 - 10 - - - 8096 - 1 - 10 - - - True - False - gtk-new - - - 500 - 500 - False - New VM - False - dialog - - - - - - - True - False - vertical - - - True - False - queue - - - True - False - 6 - 10 - - - True - False - vm_new_wizard - 6 - - - False - False - 0 - - - - - True - False - - - True - False - vertical - - - True - False - start - <span size='large' color='white'>Create a new virtual machine</span> - True - - - False - True - 0 - - - - - True - False - start - <span color='#59B0E2'>Step foo of bar</span> - True - - - pagenum-label - - - - - True - True - 1 - - - - - True - True - 0 - - - - - True - True - 1 - - - - - - - False - False - 0 - - - - - True - False - 12 - vertical - 15 - - - True - False - 3 - 3 - - - True - True - False - - - - True - False - 24 - - - True - False - start - False - 12 - - - True - False - vertical - 8 - - - True - False - start - Choose virtualization type - - - False - True - 0 - - - - - True - False - 15 - - - True - False - vertical - 3 - - - _Virtual machine - True - True - False - start - True - True - True - - - - False - False - 0 - - - - - _Container - True - True - False - start - True - True - vz-virt-type-hvm - - - - False - False - 1 - - - - - - - False - True - 1 - - - - - 0 - 0 - - - - - True - False - False - vertical - 8 - - - True - False - start - Choose how you would like to install the operating system - - - False - True - 0 - - - - - True - False - 15 - - - True - False - vertical - 3 - - - _Local install media (ISO image or CDROM) - True - True - False - start - True - True - True - - - - False - True - 0 - - - - - Network _Install (HTTP, HTTPS, or FTP) - True - True - False - start - True - True - method-local - - - - False - True - 1 - - - - - Network _Boot (PXE) - True - True - False - start - True - True - method-local - - - - False - True - 2 - - - - - Import _existing disk image - True - True - False - start - True - True - method-local - - - - False - True - 3 - - - - - - - True - True - 1 - - - - - 0 - 1 - - - - - True - False - vertical - 8 - - - True - False - start - Choose the container type - - - False - True - 0 - - - - - True - False - 15 - - - True - False - vertical - 3 - - - _Application container - True - True - False - start - True - True - True - - - - False - False - 0 - - - - - O_perating system container - True - True - False - start - True - True - method-container-app - - - - False - False - 1 - - - - - - - False - True - 1 - - - - - 0 - 2 - - - - - 0 - 2 - - - - - True - False - False - 4 - 6 - - - True - False - end - C_onnection: - True - create-conn - - - 0 - 0 - - - - - True - False - center - - - 400 - True - False - - - - create-conn - - - - - False - True - 0 - - - - - True - False - start - conn label - end - 25 - - - True - True - 1 - - - - - 1 - 0 - 2 - - - - - 0 - 0 - - - - - True - False - vertical - - - True - False - start - start - 6 - - - True - False - start - start - False - gtk-dialog-warning - - - 0 - 0 - - - - - True - False - start - start - False - Error message -bar - True - 45 - - - 1 - 0 - - - - - False - True - 0 - - - - - 0 - 1 - - - - - True - False - vertical - 6 - - - True - False - start - start - 6 - - - True - False - start - start - False - gtk-dialog-warning - - - 0 - 0 - - - - - True - False - start - start - False - Error message -bar - True - 45 - - - 1 - 0 - - - - - 0 - 0 - - - - - True - True - start - True - - - True - False - 8 - 15 - - - True - False - 6 - 6 - - - True - False - - - - 1 - 2 - - - - - True - False - - - - 1 - 0 - - - - - True - False - end - _Xen Type: - True - xen-type - - - 0 - 0 - - - - - True - False - end - _Architecture: - True - arch - - - 0 - 2 - - - - - True - False - end - _Machine Type: - True - machine - - - 0 - 3 - - - - - True - False - - - - 1 - 3 - - - - - True - False - end - _Virt Type: - True - virt-type - - - 0 - 1 - - - - - True - False - - - - 1 - 1 - - - - - - - - - True - False - Architecture options - - - - - 0 - 1 - - - - - 0 - 3 - - - - - - - True - False - Name - - - False - - - - - True - False - vertical - 24 - - - True - True - False - - - True - False - vertical - 6 - - - True - False - start - Choose _ISO or CDROM install media: - True - - - False - True - 0 - - - - - True - False - 5 - - - True - False - - - - - - True - True - 0 - - - - - Bro_wse... - True - True - True - True - - - - install-iso-browse - - - - - False - True - 1 - - - - - False - True - 1 - - - - - - - True - False - ISO - - - False - - - - - True - False - vertical - 6 - - - True - False - start - Provide the operating system install U_RL: - True - install-url-entry - - - False - True - 0 - - - - - True - False - 4 - 6 - - - True - False - center - True - True - - - True - - - - - install-url-entry - - - - - - - 0 - 0 - - - - - True - True - - - True - False - - - True - False - 4 - 6 - - - True - False - end - Kerne_l options: - True - install-urlopts-entry - - - 0 - 0 - - - - - True - True - center - True - - - - 1 - 0 - - - - - - - - - True - False - URL _Options - True - - - - - 0 - 1 - - - - - False - True - 1 - - - - - 1 - - - - - True - False - URL - - - 1 - False - - - - - True - False - - - False - No input needed for pxe. -User shouldn't see this. - - - True - False - 0 - - - - - 2 - - - - - True - False - PXE - - - 2 - False - - - - - True - False - vertical - 24 - - - True - False - vertical - 6 - - - True - False - start - Provide the existing stora_ge path: - True - install-import-entry - - - False - True - 0 - - - - - True - False - 6 - - - True - True - - - - True - True - 0 - - - - - B_rowse... - True - True - True - True - - - - install-import-browse - - - - - False - True - 1 - - - - - False - True - 1 - - - - - False - True - 0 - - - - - True - False - 6 - 6 - - - True - False - start - _Kernel path: - True - kernel - - - 0 - 1 - - - - - True - False - start - _Initrd path: - True - initrd - - - 0 - 2 - - - - - True - False - start - _DTB path: - True - dtb - - - 0 - 3 - - - - - True - True - True - - - 1 - 1 - - - - - True - True - True - - - 1 - 2 - - - - - True - True - True - - - 1 - 3 - - - - - Br_owse... - True - True - True - True - - - - kernel-browse - - - - - 2 - 1 - - - - - Bro_wse... - True - True - True - True - - - - initrd-browse - - - - - 2 - 2 - - - - - Brow_se... - True - True - True - True - - - - dtb-browse - - - - - 2 - 3 - - - - - True - False - 6 - - - 0 - 0 - 3 - - - - - True - False - start - Kerne_l args: - True - kernel-args - - - 0 - 4 - - - - - True - True - - - 1 - 4 - 2 - - - - - False - True - 1 - - - - - 3 - - - - - True - False - Import - - - 3 - False - - - - - True - False - start - True - vertical - 6 - - - True - False - start - Provide the _application path: - True - install-app-entry - - - False - True - 0 - - - - - True - False - 6 - - - True - True - - - - True - True - 0 - - - - - B_rowse... - True - True - True - True - - - - install-app-browse - - - - - False - True - 1 - - - - - False - True - 1 - - - - - 4 - - - - - True - False - App Container - - - 4 - False - - - - - True - False - vertical - 6 - - - True - False - start - Provide the existing OS root _directory: - True - install-oscontainer-fs - - - False - True - 0 - - - - - True - False - vertical - 12 - - - True - False - 6 - - - True - True - - - - True - True - 0 - - - - - B_rowse... - True - True - True - True - - - - install-oscontainer-browse - - - - - False - True - 1 - - - - - False - True - 0 - - - - - True - False - 6 - - - True - False - gtk-dialog-warning - - - False - True - 0 - - - - - True - False - start - <small>The OS directory tree must already exist. To enable OS directory tree creation, -please install <a href="https://github.com/virt-manager/virt-bootstrap">virt-bootstrap</a></small> - True - - - True - True - 1 - - - - - False - True - 1 - - - - - True - False - 6 - - - True - False - gtk-dialog-warning - - - False - True - 0 - - - - - True - False - start - <small>The OS directory tree must already exist. Creating an OS directory tree for remote -connections is not yet supported.</small> - True - - - True - True - 1 - - - - - False - True - 2 - - - - - Create OS directory tree from container image - True - True - False - True - - - - False - True - 3 - - - - - True - False - False - 10 - vertical - 10 - - - True - False - 3 - - - True - False - Source URI: - - - 0 - 0 - - - - - True - False - True - True - - - True - Possible URL formats: - * file:///path/to/rootfs.tar - * docker://registry:port/image:tag - * virt-builder://template - - - - - - 1 - 0 - - - - - False - True - 0 - - - - - Do not verify TLS certificates of registry - True - True - False - True - - - False - True - 1 - - - - - True - True - - - True - False - - - True - False - 10 - Username: - - - 0 - 0 - - - - - True - False - 10 - Password: - - - 0 - 1 - - - - - True - True - - - 1 - 0 - - - - - True - True - False - - password - - - 1 - 1 - - - - - - - True - False - Credentials for accessing the source registry - - - - - False - True - 2 - - - - - False - True - 4 - - - - - True - False - 3 - - - True - False - Root password: - - - False - True - 0 - - - - - True - True - False - - - - True - True - 1 - - - - - False - True - 5 - - - - - False - True - 1 - - - - - 5 - - - - - True - False - OS Container - - - 5 - False - - - - - True - False - vertical - 6 - - - True - False - start - Select _container template: - True - install-container-template - - - False - True - 0 - - - - - True - False - 6 - - - True - True - True - - - False - True - 0 - - - - - False - True - 1 - - - - - 6 - - - - - True - False - VZ templates - - - 6 - False - - - - - True - True - 0 - - - - - True - False - vertical - 6 - - - True - False - start - C_hoose the operating system you are installing: - True - - - False - True - 0 - - - - - True - False - - - - - - False - True - 1 - - - - - True - False - - - A_utomatically detect from the installation media / source - True - True - False - True - True - True - - - - False - True - 0 - - - - - True - False - - - False - True - 1 - - - - - False - True - 2 - - - - - False - True - 1 - - - - - 1 - - - - - True - False - Install - - - 1 - False - - - - - True - False - vertical - 40 - - - True - False - vertical - 12 - - - True - False - start - Choose Memory and CPU settings: - - - False - False - 0 - - - - - True - False - 4 - 6 - - - True - False - end - start - 6 - _Memory: - True - - - 0 - 0 - - - - - True - False - end - start - 6 - C_PUs: - True - - - 0 - 1 - - - - - True - False - vertical - 4 - - - True - True - start - 7 - adjustment3 - 1 - - - False - True - 0 - - - - - True - False - start - True - (Insert host mem) - - - True - True - 1 - - - - - 1 - 0 - - - - - True - False - vertical - 4 - - - True - True - start - 7 - adjustment2 - 1 - - - cpus - - - - - False - True - 0 - - - - - True - False - start - True - Insert Phys cpu count - - - False - True - 1 - - - - - 1 - 1 - - - - - False - True - 1 - - - - - False - True - 0 - - - - - 2 - - - - - True - False - start - Memory - - - 2 - False - - - - - True - False - vertical - 12 - - - _Enable storage for this virtual machine - True - True - False - True - True - True - - - - False - False - 0 - - - - - True - False - - - - - - False - False - 1 - - - - - 3 - - - - - True - False - Storage - - - 3 - False - - - - - True - False - vertical - 15 - - - True - False - vertical - 12 - - - True - False - start - Ready to begin the installation - True - - - False - True - 0 - - - - - True - False - False - 6 - 6 - - - True - False - start - label - - - 1 - 2 - - - - - True - False - start - label - - - 1 - 3 - - - - - True - False - start - label - - - 1 - 4 - - - - - C_ustomize configuration before install - True - True - False - start - False - True - True - - - 1 - 6 - - - - - True - True - start - 40 - - - - 1 - 0 - - - - - True - False - end - False - <span color='#484848'>_Name:</span> - True - True - create-vm-name - - - 0 - 0 - - - - - True - False - start - label - - - 1 - 1 - - - - - True - False - end - False - <span color='#484848'>Install:</span> - True - - - 0 - 2 - - - - - True - False - end - False - <span color='#484848'>Memory:</span> - True - - - 0 - 3 - - - - - True - False - end - False - <span color='#484848'>CPUs:</span> - True - - - 0 - 4 - - - - - True - False - end - False - <span color='#484848'>Storage:</span> - True - - - 0 - 5 - - - - - True - False - end - False - <span color='#484848'>OS:</span> - True - - - 0 - 1 - - - - - True - False - start - 3 - - - True - False - start - label - True - - - False - True - 0 - - - - - True - False - start - label - start - 27 - - - False - True - 1 - - - - - 1 - 5 - - - - - True - False - False - - - - - - 0 - 6 - - - - - False - True - 1 - - - - - False - True - 0 - - - - - True - True - 6 - - - True - False - 20 - - - True - False - vertical - 6 - - - True - False - 6 - - - True - False - - - - - - 0 - 1 - - - - - True - False - 6 - - - True - False - gtk-dialog-warning - - - False - True - 0 - - - - - True - False - <small>pxe warning</small> - True - - - False - True - 1 - - - - - 0 - 0 - - - - - False - True - 0 - - - - - - - - - True - False - N_etwork selection - True - - - - - False - False - 1 - - - - - 4 - - - - - True - False - Finish - - - 4 - False - - - - - - - True - True - 0 - - - - - True - False - 12 - end - - - gtk-cancel - True - True - True - True - - - - False - False - 1 - - - - - gtk-go-back - True - False - True - True - True - - - - False - False - 2 - - - - - gtk-go-forward - True - True - True - True - - - - False - False - 3 - - - - - _Finish - True - True - image20 - True - - - - False - False - 4 - - - - - False - True - 1 - - - - - True - True - 1 - - - - - - diff --git a/ui/createvm.ui b/ui/createvm.ui new file mode 100644 index 00000000..4df6f6fe --- /dev/null +++ b/ui/createvm.ui @@ -0,0 +1,2622 @@ + + + + + + 128 + 1 + 10 + + + 8096 + 1 + 10 + + + True + False + gtk-new + + + 500 + 500 + False + New VM + False + dialog + + + + + + + True + False + vertical + + + True + False + queue + + + True + False + 6 + 10 + + + True + False + vm_new_wizard + 6 + + + False + False + 0 + + + + + True + False + + + True + False + vertical + + + True + False + start + <span size='large' color='white'>Create a new virtual machine</span> + True + + + False + True + 0 + + + + + True + False + start + <span color='#59B0E2'>Step foo of bar</span> + True + + + pagenum-label + + + + + True + True + 1 + + + + + True + True + 0 + + + + + True + True + 1 + + + + + + + False + False + 0 + + + + + True + False + 12 + vertical + 15 + + + True + False + 3 + 3 + + + True + True + False + + + + True + False + 24 + + + True + False + start + False + 12 + + + True + False + vertical + 8 + + + True + False + start + Choose virtualization type + + + False + True + 0 + + + + + True + False + 15 + + + True + False + vertical + 3 + + + _Virtual machine + True + True + False + start + True + True + True + + + + False + False + 0 + + + + + _Container + True + True + False + start + True + True + vz-virt-type-hvm + + + + False + False + 1 + + + + + + + False + True + 1 + + + + + 0 + 0 + + + + + True + False + False + vertical + 8 + + + True + False + start + Choose how you would like to install the operating system + + + False + True + 0 + + + + + True + False + 15 + + + True + False + vertical + 3 + + + _Local install media (ISO image or CDROM) + True + True + False + start + True + True + True + + + + False + True + 0 + + + + + Network _Install (HTTP, HTTPS, or FTP) + True + True + False + start + True + True + method-local + + + + False + True + 1 + + + + + Network _Boot (PXE) + True + True + False + start + True + True + method-local + + + + False + True + 2 + + + + + Import _existing disk image + True + True + False + start + True + True + method-local + + + + False + True + 3 + + + + + + + True + True + 1 + + + + + 0 + 1 + + + + + True + False + vertical + 8 + + + True + False + start + Choose the container type + + + False + True + 0 + + + + + True + False + 15 + + + True + False + vertical + 3 + + + _Application container + True + True + False + start + True + True + True + + + + False + False + 0 + + + + + O_perating system container + True + True + False + start + True + True + method-container-app + + + + False + False + 1 + + + + + + + False + True + 1 + + + + + 0 + 2 + + + + + 0 + 2 + + + + + True + False + False + 4 + 6 + + + True + False + end + C_onnection: + True + create-conn + + + 0 + 0 + + + + + True + False + center + + + 400 + True + False + + + + create-conn + + + + + False + True + 0 + + + + + True + False + start + conn label + end + 25 + + + True + True + 1 + + + + + 1 + 0 + 2 + + + + + 0 + 0 + + + + + True + False + vertical + + + True + False + start + start + 6 + + + True + False + start + start + False + gtk-dialog-warning + + + 0 + 0 + + + + + True + False + start + start + False + Error message +bar + True + 45 + + + 1 + 0 + + + + + False + True + 0 + + + + + 0 + 1 + + + + + True + False + vertical + 6 + + + True + False + start + start + 6 + + + True + False + start + start + False + gtk-dialog-warning + + + 0 + 0 + + + + + True + False + start + start + False + Error message +bar + True + 45 + + + 1 + 0 + + + + + 0 + 0 + + + + + True + True + start + True + + + True + False + 8 + 15 + + + True + False + 6 + 6 + + + True + False + + + + 1 + 2 + + + + + True + False + + + + 1 + 0 + + + + + True + False + end + _Xen Type: + True + xen-type + + + 0 + 0 + + + + + True + False + end + _Architecture: + True + arch + + + 0 + 2 + + + + + True + False + end + _Machine Type: + True + machine + + + 0 + 3 + + + + + True + False + + + + 1 + 3 + + + + + True + False + end + _Virt Type: + True + virt-type + + + 0 + 1 + + + + + True + False + + + + 1 + 1 + + + + + + + + + True + False + Architecture options + + + + + 0 + 1 + + + + + 0 + 3 + + + + + + + True + False + Name + + + False + + + + + True + False + vertical + 24 + + + True + True + False + + + True + False + vertical + 6 + + + True + False + start + Choose _ISO or CDROM install media: + True + + + False + True + 0 + + + + + True + False + 5 + + + True + False + + + + + + True + True + 0 + + + + + Bro_wse... + True + True + True + True + + + + install-iso-browse + + + + + False + True + 1 + + + + + False + True + 1 + + + + + + + True + False + ISO + + + False + + + + + True + False + vertical + 6 + + + True + False + start + Provide the operating system install U_RL: + True + install-url-entry + + + False + True + 0 + + + + + True + False + 4 + 6 + + + True + False + center + True + True + + + True + + + + + install-url-entry + + + + + + + 0 + 0 + + + + + True + True + + + True + False + + + True + False + 4 + 6 + + + True + False + end + Kerne_l options: + True + install-urlopts-entry + + + 0 + 0 + + + + + True + True + center + True + + + + 1 + 0 + + + + + + + + + True + False + URL _Options + True + + + + + 0 + 1 + + + + + False + True + 1 + + + + + 1 + + + + + True + False + URL + + + 1 + False + + + + + True + False + + + False + No input needed for pxe. +User shouldn't see this. + + + True + False + 0 + + + + + 2 + + + + + True + False + PXE + + + 2 + False + + + + + True + False + vertical + 24 + + + True + False + vertical + 6 + + + True + False + start + Provide the existing stora_ge path: + True + install-import-entry + + + False + True + 0 + + + + + True + False + 6 + + + True + True + + + + True + True + 0 + + + + + B_rowse... + True + True + True + True + + + + install-import-browse + + + + + False + True + 1 + + + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + 6 + 6 + + + True + False + start + _Kernel path: + True + kernel + + + 0 + 1 + + + + + True + False + start + _Initrd path: + True + initrd + + + 0 + 2 + + + + + True + False + start + _DTB path: + True + dtb + + + 0 + 3 + + + + + True + True + True + + + 1 + 1 + + + + + True + True + True + + + 1 + 2 + + + + + True + True + True + + + 1 + 3 + + + + + Br_owse... + True + True + True + True + + + + kernel-browse + + + + + 2 + 1 + + + + + Bro_wse... + True + True + True + True + + + + initrd-browse + + + + + 2 + 2 + + + + + Brow_se... + True + True + True + True + + + + dtb-browse + + + + + 2 + 3 + + + + + True + False + 6 + + + 0 + 0 + 3 + + + + + True + False + start + Kerne_l args: + True + kernel-args + + + 0 + 4 + + + + + True + True + + + 1 + 4 + 2 + + + + + False + True + 1 + + + + + 3 + + + + + True + False + Import + + + 3 + False + + + + + True + False + start + True + vertical + 6 + + + True + False + start + Provide the _application path: + True + install-app-entry + + + False + True + 0 + + + + + True + False + 6 + + + True + True + + + + True + True + 0 + + + + + B_rowse... + True + True + True + True + + + + install-app-browse + + + + + False + True + 1 + + + + + False + True + 1 + + + + + 4 + + + + + True + False + App Container + + + 4 + False + + + + + True + False + vertical + 6 + + + True + False + start + Provide the existing OS root _directory: + True + install-oscontainer-fs + + + False + True + 0 + + + + + True + False + vertical + 12 + + + True + False + 6 + + + True + True + + + + True + True + 0 + + + + + B_rowse... + True + True + True + True + + + + install-oscontainer-browse + + + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + 6 + + + True + False + gtk-dialog-warning + + + False + True + 0 + + + + + True + False + start + <small>The OS directory tree must already exist. To enable OS directory tree creation, +please install <a href="https://github.com/virt-manager/virt-bootstrap">virt-bootstrap</a></small> + True + + + True + True + 1 + + + + + False + True + 1 + + + + + True + False + 6 + + + True + False + gtk-dialog-warning + + + False + True + 0 + + + + + True + False + start + <small>The OS directory tree must already exist. Creating an OS directory tree for remote +connections is not yet supported.</small> + True + + + True + True + 1 + + + + + False + True + 2 + + + + + Create OS directory tree from container image + True + True + False + True + + + + False + True + 3 + + + + + True + False + False + 10 + vertical + 10 + + + True + False + 3 + + + True + False + Source URI: + + + 0 + 0 + + + + + True + False + True + True + + + True + Possible URL formats: + * file:///path/to/rootfs.tar + * docker://registry:port/image:tag + * virt-builder://template + + + + + + 1 + 0 + + + + + False + True + 0 + + + + + Do not verify TLS certificates of registry + True + True + False + True + + + False + True + 1 + + + + + True + True + + + True + False + + + True + False + 10 + Username: + + + 0 + 0 + + + + + True + False + 10 + Password: + + + 0 + 1 + + + + + True + True + + + 1 + 0 + + + + + True + True + False + + password + + + 1 + 1 + + + + + + + True + False + Credentials for accessing the source registry + + + + + False + True + 2 + + + + + False + True + 4 + + + + + True + False + 3 + + + True + False + Root password: + + + False + True + 0 + + + + + True + True + False + + + + True + True + 1 + + + + + False + True + 5 + + + + + False + True + 1 + + + + + 5 + + + + + True + False + OS Container + + + 5 + False + + + + + True + False + vertical + 6 + + + True + False + start + Select _container template: + True + install-container-template + + + False + True + 0 + + + + + True + False + 6 + + + True + True + True + + + False + True + 0 + + + + + False + True + 1 + + + + + 6 + + + + + True + False + VZ templates + + + 6 + False + + + + + True + True + 0 + + + + + True + False + vertical + 6 + + + True + False + start + C_hoose the operating system you are installing: + True + + + False + True + 0 + + + + + True + False + + + + + + False + True + 1 + + + + + True + False + + + A_utomatically detect from the installation media / source + True + True + False + True + True + True + + + + False + True + 0 + + + + + True + False + + + False + True + 1 + + + + + False + True + 2 + + + + + False + True + 1 + + + + + 1 + + + + + True + False + Install + + + 1 + False + + + + + True + False + vertical + 40 + + + True + False + vertical + 12 + + + True + False + start + Choose Memory and CPU settings: + + + False + False + 0 + + + + + True + False + 4 + 6 + + + True + False + end + start + 6 + _Memory: + True + + + 0 + 0 + + + + + True + False + end + start + 6 + C_PUs: + True + + + 0 + 1 + + + + + True + False + vertical + 4 + + + True + True + start + 7 + adjustment3 + 1 + + + False + True + 0 + + + + + True + False + start + True + (Insert host mem) + + + True + True + 1 + + + + + 1 + 0 + + + + + True + False + vertical + 4 + + + True + True + start + 7 + adjustment2 + 1 + + + cpus + + + + + False + True + 0 + + + + + True + False + start + True + Insert Phys cpu count + + + False + True + 1 + + + + + 1 + 1 + + + + + False + True + 1 + + + + + False + True + 0 + + + + + 2 + + + + + True + False + start + Memory + + + 2 + False + + + + + True + False + vertical + 12 + + + _Enable storage for this virtual machine + True + True + False + True + True + True + + + + False + False + 0 + + + + + True + False + + + + + + False + False + 1 + + + + + 3 + + + + + True + False + Storage + + + 3 + False + + + + + True + False + vertical + 15 + + + True + False + vertical + 12 + + + True + False + start + Ready to begin the installation + True + + + False + True + 0 + + + + + True + False + False + 6 + 6 + + + True + False + start + label + + + 1 + 2 + + + + + True + False + start + label + + + 1 + 3 + + + + + True + False + start + label + + + 1 + 4 + + + + + C_ustomize configuration before install + True + True + False + start + False + True + True + + + 1 + 6 + + + + + True + True + start + 40 + + + + 1 + 0 + + + + + True + False + end + False + <span color='#484848'>_Name:</span> + True + True + create-vm-name + + + 0 + 0 + + + + + True + False + start + label + + + 1 + 1 + + + + + True + False + end + False + <span color='#484848'>Install:</span> + True + + + 0 + 2 + + + + + True + False + end + False + <span color='#484848'>Memory:</span> + True + + + 0 + 3 + + + + + True + False + end + False + <span color='#484848'>CPUs:</span> + True + + + 0 + 4 + + + + + True + False + end + False + <span color='#484848'>Storage:</span> + True + + + 0 + 5 + + + + + True + False + end + False + <span color='#484848'>OS:</span> + True + + + 0 + 1 + + + + + True + False + start + 3 + + + True + False + start + label + True + + + False + True + 0 + + + + + True + False + start + label + start + 27 + + + False + True + 1 + + + + + 1 + 5 + + + + + True + False + False + + + + + + 0 + 6 + + + + + False + True + 1 + + + + + False + True + 0 + + + + + True + True + 6 + + + True + False + 20 + + + True + False + vertical + 6 + + + True + False + 6 + + + True + False + + + + + + 0 + 1 + + + + + True + False + 6 + + + True + False + gtk-dialog-warning + + + False + True + 0 + + + + + True + False + <small>pxe warning</small> + True + + + False + True + 1 + + + + + 0 + 0 + + + + + False + True + 0 + + + + + + + + + True + False + N_etwork selection + True + + + + + False + False + 1 + + + + + 4 + + + + + True + False + Finish + + + 4 + False + + + + + + + True + True + 0 + + + + + True + False + 12 + end + + + gtk-cancel + True + True + True + True + + + + False + False + 1 + + + + + gtk-go-back + True + False + True + True + True + + + + False + False + 2 + + + + + gtk-go-forward + True + True + True + True + + + + False + False + 3 + + + + + _Finish + True + True + image20 + True + + + + False + False + 4 + + + + + False + True + 1 + + + + + True + True + 1 + + + + + + diff --git a/virtManager/create.py b/virtManager/create.py deleted file mode 100644 index fbb66d66..00000000 --- a/virtManager/create.py +++ /dev/null @@ -1,2213 +0,0 @@ -# Copyright (C) 2008, 2013, 2014, 2015 Red Hat, Inc. -# Copyright (C) 2008 Cole Robinson -# -# This work is licensed under the GNU GPLv2 or later. -# See the COPYING file in the top-level directory. - -import io -import pkgutil -import os -import threading -import time - -from gi.repository import Gdk -from gi.repository import Gtk -from gi.repository import Pango - -import virtinst -import virtinst.generatename -from virtinst import log - -from . import uiutil -from .asyncjob import vmmAsyncJob -from .baseclass import vmmGObjectUI -from .connmanager import vmmConnectionManager -from .device.addstorage import vmmAddStorage -from .device.mediacombo import vmmMediaCombo -from .device.netlist import vmmNetworkList -from .engine import vmmEngine -from .object.domain import vmmDomainVirtinst -from .oslist import vmmOSList -from .storagebrowse import vmmStorageBrowser -from .vmwindow import vmmVMWindow - -# Number of seconds to wait for media detection -DETECT_TIMEOUT = 20 - -DEFAULT_MEM = 1024 - -(PAGE_NAME, - PAGE_INSTALL, - PAGE_MEM, - PAGE_STORAGE, - PAGE_FINISH) = range(5) - -(INSTALL_PAGE_ISO, - INSTALL_PAGE_URL, - INSTALL_PAGE_PXE, - INSTALL_PAGE_IMPORT, - INSTALL_PAGE_CONTAINER_APP, - INSTALL_PAGE_CONTAINER_OS, - INSTALL_PAGE_VZ_TEMPLATE) = range(7) - -# Column numbers for os type/version list models -(OS_COL_ID, - OS_COL_LABEL, - OS_COL_IS_SEP, - OS_COL_IS_SHOW_ALL) = range(4) - - -##################### -# Pretty UI helpers # -##################### - -def _pretty_arch(_a): - if _a == "armv7l": - return "arm" - return _a - - -def _pretty_storage(size): - return _("%.1f GiB") % float(size) - - -def _pretty_memory(mem): - return _("%d MiB") % (mem / 1024.0) - - -########################################################### -# Helpers for tracking devices we create from this wizard # -########################################################### - -def _mark_vmm_device(dev): - setattr(dev, "vmm_create_wizard_device", True) - - -def _get_vmm_device(guest, devkey): - for dev in getattr(guest.devices, devkey): - if hasattr(dev, "vmm_create_wizard_device"): - return dev - - -def _remove_vmm_device(guest, devkey): - dev = _get_vmm_device(guest, devkey) - if dev: - guest.remove_device(dev) - - -def is_virt_bootstrap_installed(): - return pkgutil.find_loader('virtBootstrap') is not None - - -############## -# Main class # -############## - -class vmmCreate(vmmGObjectUI): - @classmethod - def show_instance(cls, parentobj, uri=None): - try: - if not cls._instance: - cls._instance = vmmCreate() - cls._instance.show(parentobj and parentobj.topwin or None, uri=uri) - except Exception as e: - if not parentobj: - raise - parentobj.err.show_err( - _("Error launching create dialog: %s") % str(e)) - - def __init__(self): - vmmGObjectUI.__init__(self, "create.ui", "vmm-create") - self._cleanup_on_app_close() - - self.conn = None - self._capsinfo = None - - self._guest = None - self._failed_guest = None - - # Distro detection state variables - self._detect_os_in_progress = False - self._os_already_detected_for_media = False - - self._customize_window = None - - self._storage_browser = None - self._netlist = None - - self._addstorage = vmmAddStorage(self.conn, self.builder, self.topwin) - self.widget("storage-align").add(self._addstorage.top_box) - def _browse_file_cb(ignore, widget): - self._browse_file(widget) - self._addstorage.connect("browse-clicked", _browse_file_cb) - - self._mediacombo = vmmMediaCombo(self.conn, self.builder, self.topwin) - self._mediacombo.connect("changed", self._iso_changed_cb) - self._mediacombo.connect("activate", self._iso_activated_cb) - self._mediacombo.set_mnemonic_label( - self.widget("install-iso-label")) - self.widget("install-iso-align").add(self._mediacombo.top_box) - - self.builder.connect_signals({ - "on_vmm_newcreate_delete_event": self._close_requested, - - "on_create_cancel_clicked": self._close_requested, - "on_create_back_clicked": self._back_clicked, - "on_create_forward_clicked": self._forward_clicked, - "on_create_finish_clicked": self._finish_clicked, - "on_create_pages_switch_page": self._page_changed, - - "on_create_conn_changed": self._conn_changed, - "on_method_changed": self._method_changed, - "on_xen_type_changed": self._xen_type_changed, - "on_arch_changed": self._arch_changed, - "on_virt_type_changed": self._virt_type_changed, - "on_machine_changed": self._machine_changed, - "on_vz_virt_type_changed": self._vz_virt_type_changed, - - "on_install_iso_browse_clicked": self._browse_iso, - "on_install_url_entry_changed": self._url_changed, - "on_install_url_entry_activate": self._url_activated, - "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_container_source_toggle": self._container_source_toggle, - - "on_install_detect_os_toggled": self._detect_os_toggled_cb, - - "on_kernel_browse_clicked": self._browse_kernel, - "on_initrd_browse_clicked": self._browse_initrd, - "on_dtb_browse_clicked": self._browse_dtb, - - "on_enable_storage_toggled": self._toggle_enable_storage, - - "on_create_vm_name_changed": self._name_changed, - }) - self.bind_escape_key_close() - - self._init_state() - - - - ########################### - # Standard window methods # - ########################### - - def show(self, parent, uri): - log.debug("Showing new vm wizard") - - if not self.is_visible(): - self._reset_state(uri) - self.topwin.set_transient_for(parent) - vmmEngine.get_instance().increment_window_counter() - - self.topwin.present() - - def _close(self, ignore1=None, ignore2=None): - if self.is_visible(): - log.debug("Closing new vm wizard") - vmmEngine.get_instance().decrement_window_counter() - - self.topwin.hide() - - self._cleanup_customize_window() - if self._storage_browser: - self._storage_browser.close() - self._set_conn(None) - self._failed_guest = None - self._guest = None - - def _cleanup(self): - if self._storage_browser: - self._storage_browser.cleanup() - self._storage_browser = None - if self._netlist: - self._netlist.cleanup() - self._netlist = None - if self._mediacombo: - self._mediacombo.cleanup() - self._mediacombo = None - if self._addstorage: - self._addstorage.cleanup() - self._addstorage = None - - self.conn = None - self._capsinfo = None - self._guest = None - - - ########################## - # Initial state handling # - ########################## - - def _show_startup_error(self, error, hideinstall=True): - self.widget("startup-error-box").show() - self.widget("create-forward").set_sensitive(False) - if hideinstall: - self.widget("install-box").hide() - self.widget("arch-expander").hide() - - self.widget("startup-error").set_text("%s: %s" % (_("Error"), error)) - return False - - def _show_startup_warning(self, error): - self.widget("startup-error-box").show() - self.widget("startup-error").set_markup( - "%s: %s" % (_("Warning"), error)) - - def _show_arch_warning(self, error): - self.widget("arch-warning-box").show() - self.widget("arch-warning").set_markup( - "%s: %s" % (_("Warning"), error)) - - - def _init_state(self): - self.widget("create-pages").set_show_tabs(False) - self.widget("install-method-pages").set_show_tabs(False) - - blue = Gdk.Color.parse("#0072A8")[1] - self.widget("header").modify_bg(Gtk.StateType.NORMAL, blue) - - # 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 = uiutil.init_combo_text_column(conn_list, 1) - text.set_property("ellipsize", Pango.EllipsizeMode.MIDDLE) - - def set_model_list(widget_id): - lst = self.widget(widget_id) - model = Gtk.ListStore(str) - lst.set_model(model) - lst.set_entry_text_column(0) - - # Lists for the install urls - set_model_list("install-url-combo") - - # Lists for OS container bootstrap - set_model_list("install-oscontainer-source-url-combo") - - # Architecture - archList = self.widget("arch") - # [label, guest.os.arch value] - archModel = Gtk.ListStore(str, str) - archList.set_model(archModel) - uiutil.init_combo_text_column(archList, 0) - archList.set_row_separator_func( - lambda m, i, ignore: m[i][0] is None, None) - - # guest.os.type value for xen (hvm vs. xen) - hyperList = self.widget("xen-type") - # [label, guest.os_type value] - hyperModel = Gtk.ListStore(str, str) - hyperList.set_model(hyperModel) - uiutil.init_combo_text_column(hyperList, 0) - - # guest.os.machine value - lst = self.widget("machine") - # [machine ID] - model = Gtk.ListStore(str) - lst.set_model(model) - uiutil.init_combo_text_column(lst, 0) - lst.set_row_separator_func(lambda m, i, ignore: m[i][0] is None, None) - - # guest.type value for xen (qemu vs kvm) - lst = self.widget("virt-type") - # [label, guest.type value] - model = Gtk.ListStore(str, str) - lst.set_model(model) - uiutil.init_combo_text_column(lst, 0) - - # OS distro list - self._os_list = vmmOSList() - self.widget("install-os-align").add(self._os_list.search_entry) - self.widget("os-label").set_mnemonic_widget(self._os_list.search_entry) - - def _reset_state(self, urihint=None): - """ - Reset all UI state to default values. Conn specific state is - populated in _populate_conn_state - """ - self.reset_finish_cursor() - - self.widget("create-pages").set_current_page(PAGE_NAME) - self._page_changed(None, None, PAGE_NAME) - - # Name page state - self.widget("create-vm-name").set_text("") - self.widget("method-local").set_active(True) - self.widget("create-conn").set_active(-1) - activeconn = self._populate_conn_list(urihint) - self.widget("arch-expander").set_expanded(False) - self.widget("vz-virt-type-hvm").set_active(True) - - if self._set_conn(activeconn) is False: - return False - - - # Everything from this point forward should be connection independent - - # Distro/Variant - self._os_list.reset_state() - self._os_already_detected_for_media = False - - def _populate_media_model(media_model, urls): - media_model.clear() - if urls is None: - return - for url in urls: - media_model.append([url]) - - # Install local - self._mediacombo.reset_state() - - # Install URL - self.widget("install-urlopts-entry").set_text("") - self.widget("install-url-entry").set_text("") - self.widget("install-url-options").set_expanded(False) - urlmodel = self.widget("install-url-combo").get_model() - _populate_media_model(urlmodel, self.config.get_media_urls()) - - # Install import - self.widget("install-import-entry").set_text("") - self.widget("kernel").set_text("") - self.widget("initrd").set_text("") - self.widget("dtb").set_text("") - - # Install container app - self.widget("install-app-entry").set_text("/bin/sh") - - # Install container OS - self.widget("install-oscontainer-fs").set_text("") - self.widget("install-oscontainer-source-url-entry").set_text("") - self.widget("install-oscontainer-source-user").set_text("") - self.widget("install-oscontainer-source-passwd").set_text("") - self.widget("install-oscontainer-source-insecure").set_active(False) - self.widget("install-oscontainer-bootstrap").set_active(False) - self.widget("install-oscontainer-auth-options").set_expanded(False) - self.widget("install-oscontainer-rootpw").set_text("") - src_model = (self.widget("install-oscontainer-source-url-combo") - .get_model()) - _populate_media_model(src_model, self.config.get_container_urls()) - - # Install VZ container from template - self.widget("install-container-template").set_text("centos-7-x86_64") - - # Storage - self.widget("enable-storage").set_active(True) - self._addstorage.reset_state() - self._addstorage.widget("storage-create").set_active(True) - self._addstorage.widget("storage-entry").set_text("") - - # Final page - self.widget("summary-customize").set_active(False) - - - def _set_caps_state(self): - """ - Set state that is dependent on when capsinfo changes - """ - self.widget("arch-warning-box").hide() - guest = self._build_guest(None) - - # 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 = guest.os.is_xenpv() - is_container = self.conn.is_container() - is_vz = self.conn.is_vz() - is_vz_container = is_vz and guest.os.is_container() - can_remote_url = self.conn.get_backend().support_remote_url_install() - - installable_arch = bool(guest.os.is_x86() or - guest.os.is_ppc64() or - guest.os.is_s390x()) - - if guest.prefers_uefi(): - try: - guest.set_uefi_path(guest.get_uefi_path()) - installable_arch = True - log.debug("UEFI found, setting it as default.") - except Exception as e: - installable_arch = False - log.debug("Error checking for UEFI default", exc_info=True) - msg = _("Failed to setup UEFI: %s\n" - "Install options are limited.") % e - self._show_arch_warning(msg) - - # 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) and - installable_arch) - method_local.set_sensitive(not is_pv and can_storage and - installable_arch) - method_pxe.set_sensitive(not is_pv and installable_arch) - 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" - - if not installable_arch: - msg = (_("Architecture '%s' is not installable") % - guest.os.arch) - tree_tt = msg - local_tt = msg - pxe_tt = msg - - if not any([w.get_active() and w.get_sensitive() - for w in virt_methods]): - 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._show_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("container-install-box").set_visible(is_container) - self.widget("vz-install-box").set_visible(is_vz) - self.widget("virt-install-box").set_visible( - not is_container and not is_vz_container) - - show_dtb = ("arm" in guest.os.arch or - "microblaze" in guest.os.arch or - "ppc" in guest.os.arch) - self.widget("kernel-box").set_visible(not installable_arch) - uiutil.set_grid_row_visible(self.widget("dtb"), show_dtb) - - def _populate_conn_state(self): - """ - Update all state that has some dependency on the current connection - """ - self.conn.schedule_priority_tick(pollnet=True, - pollpool=True, polliface=True, - pollnodedev=True) - - self.widget("install-box").show() - self.widget("create-forward").set_sensitive(True) - - self._capsinfo = None - self.conn.invalidate_caps() - self._change_caps() - is_local = not self.conn.is_remote() - - if not self._capsinfo.guest.has_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._show_startup_error(error) - - # A bit out of order, but populate the xen/virt/arch/machine lists - # so we can work with a default. - self._populate_xen_type() - self._populate_arch() - self._populate_virt_type() - - show_arch = (self.widget("xen-type").get_visible() or - self.widget("virt-type").get_visible() or - self.widget("arch").get_visible() or - self.widget("machine").get_visible()) - uiutil.set_grid_row_visible(self.widget("arch-expander"), show_arch) - - if self.conn.is_xen(): - has_hvm_guests = False - for g in self.conn.caps.guests: - if g.os_type == "hvm": - has_hvm_guests = True - - if not has_hvm_guests: - error = _("Host is not advertising support for full " - "virtualization. Install options may be limited.") - self._show_startup_warning(error) - - elif self.conn.is_qemu(): - if not self._capsinfo.guest.is_kvm_available(): - 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._show_startup_warning(error) - - elif self.conn.is_vz(): - has_hvm_guests = False - has_exe_guests = False - for g in self.conn.caps.guests: - if g.os_type == "hvm": - has_hvm_guests = True - if g.os_type == "exe": - has_exe_guests = True - - self.widget("vz-virt-type-hvm").set_sensitive(has_hvm_guests) - self.widget("vz-virt-type-exe").set_sensitive(has_exe_guests) - self.widget("vz-virt-type-hvm").set_active(has_hvm_guests) - self.widget("vz-virt-type-exe").set_active( - not has_hvm_guests and has_exe_guests) - - # ISO media - # Dependent on connection so we need to do this here - self._mediacombo.set_conn(self.conn) - self._mediacombo.reset_state() - - # Allow container bootstrap only for local connection and - # only if virt-bootstrap is installed. Otherwise, show message. - vb_installed = is_virt_bootstrap_installed() - vb_enabled = is_local and vb_installed - - oscontainer_widget_conf = { - "install-oscontainer-notsupport-conn": not is_local, - "install-oscontainer-notsupport": not vb_installed, - "install-oscontainer-bootstrap": vb_enabled, - "install-oscontainer-source": vb_enabled, - "install-oscontainer-rootpw-box": vb_enabled - } - for w in oscontainer_widget_conf: - self.widget(w).set_visible(oscontainer_widget_conf[w]) - - # Memory - memory = int(self.conn.host_memory_size()) - mem_label = (_("Up to %(maxmem)s available on the host") % - {'maxmem': _pretty_memory(memory)}) - mem_label = ("%s" % - mem_label) - self.widget("mem").set_range(50, memory // 1024) - self.widget("phys-mem-label").set_markup(mem_label) - - # CPU - phys_cpus = int(self.conn.host_active_processor_count()) - cmax = phys_cpus - if cmax <= 0: - cmax = 1 - cpu_label = (_("Up to %(numcpus)d available") % - {'numcpus': int(phys_cpus)}) - cpu_label = ("%s" % - cpu_label) - self.widget("cpus").set_range(1, cmax) - self.widget("phys-cpu-label").set_markup(cpu_label) - - # Storage - self._addstorage.conn = self.conn - self._addstorage.reset_state() - - # Networking - self.widget("advanced-expander").set_expanded(False) - - self._netlist = vmmNetworkList(self.conn, self.builder, self.topwin) - self.widget("netdev-ui-align").add(self._netlist.top_box) - self._netlist.connect("changed", self._netdev_changed) - self._netlist.reset_state() - - def _conn_state_changed(self, conn): - if conn.is_disconnected(): - self._close() - - def _set_conn(self, newconn): - self.widget("startup-error-box").hide() - self.widget("arch-warning-box").hide() - - oldconn = self.conn - self.conn = newconn - if oldconn: - oldconn.disconnect_by_obj(self) - if self._netlist: - self.widget("netdev-ui-align").remove(self._netlist.top_box) - self._netlist.cleanup() - self._netlist = None - - if not self.conn: - return self._show_startup_error( - _("No active connection to install on.")) - self.conn.connect("state-changed", self._conn_state_changed) - - try: - self._populate_conn_state() - except Exception as e: - log.exception("Error setting create wizard conn state.") - return self._show_startup_error(str(e)) - - - def _change_caps(self, gtype=None, arch=None, domtype=None): - """ - Change the cached capsinfo for the passed values, and trigger - all needed UI refreshing - """ - if gtype is None: - # If none specified, prefer HVM so install options aren't limited - # with a default PV choice. - for g in self.conn.caps.guests: - if g.os_type == "hvm": - gtype = "hvm" - break - - capsinfo = self.conn.caps.guest_lookup(os_type=gtype, - arch=arch, - typ=domtype) - - if self._capsinfo: - if (self._capsinfo.guest == capsinfo.guest and - self._capsinfo.domain == capsinfo.domain): - return - - self._capsinfo = capsinfo - log.debug("Guest type set to os_type=%s, arch=%s, dom_type=%s", - self._capsinfo.os_type, - self._capsinfo.arch, - self._capsinfo.hypervisor_type) - self._populate_machine() - self._set_caps_state() - - - ################################################## - # Helpers for populating hv/arch/machine/conn UI # - ################################################## - - def _populate_xen_type(self): - model = self.widget("xen-type").get_model() - model.clear() - - default = 0 - guests = [] - if self.conn.is_xen() or self.conn.is_test(): - guests = self.conn.caps.guests[:] - - for guest in guests: - if not guest.domains: - continue - - gtype = guest.os_type - dom = guest.domains[0] - domtype = dom.hypervisor_type - label = self.conn.pretty_hv(gtype, domtype) - - # 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._capsinfo.os_type and - domtype == self._capsinfo.hypervisor_type): - default = len(model) - - model.append([label, gtype]) - - show = bool(len(model)) - uiutil.set_grid_row_visible(self.widget("xen-type"), show) - if show: - self.widget("xen-type").set_active(default) - - def _populate_arch(self): - model = self.widget("arch").get_model() - model.clear() - - default = 0 - archs = [] - for guest in self.conn.caps.guests: - if guest.os_type == self._capsinfo.os_type: - archs.append(guest.arch) - - # Combine x86/i686 to avoid confusion - if (self.conn.caps.host.cpu.arch == "x86_64" and - "x86_64" in archs and "i686" in archs): - archs.remove("i686") - archs.sort() - - prios = ["x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", - "s390x"] - if self.conn.caps.host.cpu.arch not in prios: - prios = [] - else: - for p in prios[:]: - if p not in archs: - prios.remove(p) - else: - archs.remove(p) - if prios: - if archs: - prios += [None] - archs = prios + archs - - default = 0 - if self._capsinfo.arch in archs: - default = archs.index(self._capsinfo.arch) - - for arch in archs: - model.append([_pretty_arch(arch), arch]) - - show = not (len(archs) < 2) - uiutil.set_grid_row_visible(self.widget("arch"), show) - self.widget("arch").set_active(default) - - def _populate_virt_type(self): - model = self.widget("virt-type").get_model() - model.clear() - - # Allow choosing between qemu and kvm for archs that traditionally - # have a decent amount of TCG usage, like armv7l. Also include - # aarch64 which can be used for arm32 VMs as well - domains = [d.hypervisor_type for d in self._capsinfo.guest.domains[:]] - if not self.conn.is_qemu(): - domains = [] - elif self._capsinfo.arch in ["i686", "x86_64", "ppc64", "ppc64le"]: - domains = [] - - default = 0 - if self._capsinfo.hypervisor_type in domains: - default = domains.index(self._capsinfo.hypervisor_type) - - prios = ["kvm"] - for domain in prios: - if domain not in domains: - continue - domains.remove(domain) - domains.insert(0, domain) - - for domain in domains: - label = self.conn.pretty_hv(self._capsinfo.os_type, domain) - model.append([label, domain]) - - show = bool(len(model) > 1) - uiutil.set_grid_row_visible(self.widget("virt-type"), show) - self.widget("virt-type").set_active(default) - - def _populate_machine(self): - model = self.widget("machine").get_model() - - machines = self._capsinfo.machines[:] - if self._capsinfo.arch in ["i686", "x86_64"]: - machines = [] - machines.sort() - - defmachine = None - prios = [] - recommended_machine = virtinst.Guest.get_recommended_machine( - self._capsinfo) - if recommended_machine: - defmachine = recommended_machine - prios = [defmachine] - - for p in prios[:]: - if p not in machines: - prios.remove(p) - else: - machines.remove(p) - if prios: - machines = prios + [None] + machines - - default = 0 - if defmachine and defmachine in machines: - default = machines.index(defmachine) - - self.widget("machine").disconnect_by_func(self._machine_changed) - try: - model.clear() - for m in machines: - model.append([m]) - - show = (len(machines) > 1) - uiutil.set_grid_row_visible(self.widget("machine"), show) - if show: - self.widget("machine").set_active(default) - finally: - self.widget("machine").connect("changed", self._machine_changed) - - def _populate_conn_list(self, urihint=None): - conn_list = self.widget("create-conn") - model = conn_list.get_model() - model.clear() - - default = -1 - connmanager = vmmConnectionManager.get_instance() - for connobj in connmanager.conns.values(): - 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()]) - - 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 = connmanager.conns[activeuri] - - 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 - - - ############################### - # Misc UI populating routines # - ############################### - - def _populate_summary_storage(self, path=None): - storagetmpl = "%s" - storagesize = "" - storagepath = "" - - disk = _get_vmm_device(self._guest, "disk") - if disk: - if disk.wants_storage_creation(): - storagesize = "%s" % _pretty_storage(disk.get_size()) - if not path: - path = disk.path - storagepath = (storagetmpl % path) - elif len(self._guest.devices.filesystem): - fs = self._guest.devices.filesystem[0] - storagepath = storagetmpl % fs.source - elif self._guest.os.is_container(): - storagepath = _("Host filesystem") - else: - storagepath = _("None") - - self.widget("summary-storage").set_markup(storagesize) - self.widget("summary-storage").set_visible(bool(storagesize)) - self.widget("summary-storage-path").set_markup(storagepath) - - def _populate_summary(self): - mem = _pretty_memory(int(self._guest.memory)) - 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") - elif instmethod == INSTALL_PAGE_VZ_TEMPLATE: - install = _("Virtuozzo container") - - self.widget("summary-os").set_text(self._guest.osinfo.label) - self.widget("summary-install").set_text(install) - self.widget("summary-mem").set_text(mem) - self.widget("summary-cpu").set_text(cpu) - self._populate_summary_storage() - - self._netdev_changed(None) - - - ################################ - # UI state getters and helpers # - ################################ - - def _get_config_name(self): - return self.widget("create-vm-name").get_text() - - def _get_config_machine(self): - return uiutil.get_list_selection(self.widget("machine"), - check_visible=True) - - def _get_config_install_page(self): - if self.widget("vz-install-box").get_visible(): - if self.widget("vz-virt-type-exe").get_active(): - return INSTALL_PAGE_VZ_TEMPLATE - if self.widget("virt-install-box").get_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 _is_container_install(self): - return self._get_config_install_page() in [INSTALL_PAGE_CONTAINER_APP, - INSTALL_PAGE_CONTAINER_OS, - INSTALL_PAGE_VZ_TEMPLATE] - - - def _get_config_oscontainer_bootstrap(self): - return self.widget("install-oscontainer-bootstrap").get_active() - - - def _get_config_oscontainer_source_url(self, store_media=False): - src_url = (self.widget("install-oscontainer-source-url-entry") - .get_text().strip()) - - if src_url and store_media: - self.config.add_container_url(src_url) - - return src_url - - - def _get_config_oscontainer_source_username(self): - return (self.widget("install-oscontainer-source-user") - .get_text().strip()) - - - def _get_config_oscontainer_source_password(self): - return self.widget("install-oscontainer-source-passwd").get_text() - - - def _get_config_oscontainer_isecure(self): - return self.widget("install-oscontainer-source-insecure").get_active() - - - def _get_config_oscontainer_root_password(self): - return self.widget("install-oscontainer-rootpw").get_text() - - - def _should_skip_disk_page(self): - return self._get_config_install_page() in [INSTALL_PAGE_IMPORT, - INSTALL_PAGE_CONTAINER_APP, - INSTALL_PAGE_CONTAINER_OS, - INSTALL_PAGE_VZ_TEMPLATE] - - def _get_config_local_media(self, store_media=False): - return self._mediacombo.get_path(store_media=store_media) - - def _get_config_detectable_media(self): - instpage = self._get_config_install_page() - cdrom = None - location = None - - if instpage == INSTALL_PAGE_ISO: - cdrom = self._get_config_local_media() - elif instpage == INSTALL_PAGE_URL: - location = self.widget("install-url-entry").get_text() - - return cdrom, location - - def _get_config_url_info(self, store_media=False): - media = self.widget("install-url-entry").get_text().strip() - extra = self.widget("install-urlopts-entry").get_text().strip() - - if media and store_media: - self.config.add_media_url(media) - - return (media, extra) - - def _get_config_import_path(self): - return self.widget("install-import-entry").get_text() - - def _is_default_storage(self): - return (self._addstorage.is_default_storage() and - not self._should_skip_disk_page()) - - def _is_os_detect_active(self): - return self.widget("install-detect-os").get_active() - - - ################ - # UI Listeners # - ################ - - def _close_requested(self, *ignore1, **ignore2): - """ - When user tries to close the dialog, check for any disks that - we should auto cleanup - """ - if (self._failed_guest and - self._failed_guest.installer_instance.get_created_disks( - self._failed_guest)): - - def _cleanup_disks(asyncjob): - meter = asyncjob.get_meter() - self._failed_guest.installer_instance.cleanup_created_disks( - self._failed_guest, meter) - - def _cleanup_disks_finished(error, details): - if error: - log.debug("Error cleaning up disk images:" - "\nerror=%s\ndetails=%s", error, details) - self.idle_add(self._close) - - progWin = vmmAsyncJob( - _cleanup_disks, [], - _cleanup_disks_finished, [], - _("Removing disk images"), - _("Removing disk images we created for this virtual machine."), - self.topwin) - progWin.run() - - else: - self._close() - - return 1 - - - # Intro page listeners - def _conn_changed(self, src): - uri = uiutil.get_list_selection(src) - newconn = None - connmanager = vmmConnectionManager.get_instance() - if uri: - newconn = connmanager.conns[uri] - - # 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 - - if self.conn is not newconn: - self._set_conn(newconn) - - def _method_changed(self, src): - ignore = src - # Reset the page number, since the total page numbers depend - # on the chosen install method - self._set_page_num_text(0) - - def _machine_changed(self, ignore): - self._set_caps_state() - - def _xen_type_changed(self, ignore): - os_type = uiutil.get_list_selection(self.widget("xen-type"), column=1) - if not os_type: - return - - self._change_caps(os_type) - self._populate_arch() - - def _arch_changed(self, ignore): - arch = uiutil.get_list_selection(self.widget("arch"), column=1) - if not arch: - return - - self._change_caps(self._capsinfo.os_type, arch) - self._populate_virt_type() - - def _virt_type_changed(self, ignore): - domtype = uiutil.get_list_selection(self.widget("virt-type"), column=1) - if not domtype: - return - - self._change_caps(self._capsinfo.os_type, self._capsinfo.arch, domtype) - - def _vz_virt_type_changed(self, ignore): - is_hvm = self.widget("vz-virt-type-hvm").get_active() - if is_hvm: - self._change_caps("hvm") - else: - self._change_caps("exe") - - # Install page listeners - def _detectable_media_widget_changed(self, widget, checkfocus=True): - self._os_already_detected_for_media = False - - # If the text entry widget has focus, don't fire detect_media_os, - # it means the user is probably typing. It will be detected - # when the user activates the widget, or we try to switch pages - if (checkfocus and - hasattr(widget, "get_text") and widget.has_focus()): - return - - self._start_detect_os_if_needed() - - def _url_changed(self, src): - self._detectable_media_widget_changed(src) - def _url_activated(self, src): - self._detectable_media_widget_changed(src, checkfocus=False) - def _iso_changed_cb(self, mediacombo, entry): - self._detectable_media_widget_changed(entry) - def _iso_activated_cb(self, mediacombo, entry): - self._detectable_media_widget_changed(entry, checkfocus=False) - - def _detect_os_toggled_cb(self, src): - if not src.is_visible(): - return - - # We are only here if the user explicitly changed detection UI - dodetect = src.get_active() - self._change_os_detect(not dodetect) - if dodetect: - self._os_already_detected_for_media = False - self._start_detect_os_if_needed() - - def _browse_oscontainer(self, ignore): - self._browse_file("install-oscontainer-fs", is_dir=True) - def _browse_app(self, ignore): - self._browse_file("install-app-entry") - def _browse_import(self, ignore): - self._browse_file("install-import-entry") - def _browse_iso(self, ignore): - def set_path(ignore, path): - self._mediacombo.set_path(path) - self._browse_file(None, cb=set_path, is_media=True) - def _browse_kernel(self, ignore): - self._browse_file("kernel") - def _browse_initrd(self, ignore): - self._browse_file("initrd") - def _browse_dtb(self, ignore): - self._browse_file("dtb") - - - # Storage page listeners - def _toggle_enable_storage(self, src): - self.widget("storage-align").set_sensitive(src.get_active()) - - - # Summary page listeners - def _name_changed(self, src): - newname = src.get_text() - if not src.is_visible(): - return - if not newname: - return - - try: - path, ignore = self._get_storage_path(newname, do_log=False) - self._populate_summary_storage(path=path) - except Exception: - log.debug("Error generating storage path on name change " - "for name=%s", newname, exc_info=True) - - - def _netdev_changed(self, ignore): - row = self._netlist.get_network_row() - pxe_install = (self._get_config_install_page() == INSTALL_PAGE_PXE) - - ntype = row[0] - connkey = row[6] - expand = (ntype != "network" and ntype != "bridge") - no_network = ntype is None - - if (no_network or ntype == virtinst.DeviceInterface.TYPE_USER): - can_pxe = False - elif ntype != virtinst.DeviceInterface.TYPE_VIRTUAL: - can_pxe = True - else: - can_pxe = self.conn.get_net(connkey).can_pxe() - - if expand: - self.widget("advanced-expander").set_expanded(True) - - self.widget("netdev-warn-box").set_visible(False) - def _show_netdev_warn(msg): - self.widget("advanced-expander").set_expanded(True) - self.widget("netdev-warn-box").set_visible(True) - self.widget("netdev-warn-label").set_markup( - "%s" % msg) - - if no_network: - _show_netdev_warn(_("No network selected")) - elif not can_pxe and pxe_install: - _show_netdev_warn(_("Network selection does not support PXE")) - - - # Enable/Disable container source URL entry on checkbox click - def _container_source_toggle(self, ignore): - enable_src = self.widget("install-oscontainer-bootstrap").get_active() - self.widget("install-oscontainer-source").set_sensitive(enable_src) - self.widget("install-oscontainer-rootpw-box").set_sensitive(enable_src) - - # Auto-generate a path if not specified - if enable_src and not self.widget("install-oscontainer-fs").get_text(): - if os.geteuid() == 0: - fs_dir = ['/var/lib/libvirt/filesystems/'] - else: - fs_dir = [os.environ['HOME'], - '.local/share/libvirt/filesystems/'] - - default_name = virtinst.Guest.generate_name(self._guest) - fs = fs_dir + [default_name] - self.widget("install-oscontainer-fs").set_text(os.path.join(*fs)) - - - ######################## - # Misc helper routines # - ######################## - - def _browse_file(self, cbwidget, cb=None, 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 cb: - callback = cb - else: - def callback(ignore, text): - widget = cbwidget - if isinstance(cbwidget, str): - widget = self.widget(cbwidget) - widget.set_text(text) - - if self._storage_browser and self._storage_browser.conn != self.conn: - self._storage_browser.cleanup() - self._storage_browser = None - if self._storage_browser is None: - self._storage_browser = vmmStorageBrowser(self.conn) - - 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) - - - ###################### - # Navigation methods # - ###################### - - def _set_page_num_text(self, cur): - """ - Set the 'page 1 of 4' style text in the wizard header - """ - cur += 1 - final = PAGE_FINISH + 1 - if self._should_skip_disk_page(): - final -= 1 - cur = min(cur, final) - - page_lbl = ("%s" % - _("Step %(current_page)d of %(max_page)d") % - {'current_page': cur, 'max_page': final}) - - self.widget("header-pagenum").set_markup(page_lbl) - - def _change_os_detect(self, sensitive): - self._os_list.set_sensitive(sensitive) - if not sensitive and not self._os_list.get_selected_os(): - self._os_list.search_entry.set_text( - _("Waiting for install media / source")) - - def _set_install_page(self): - instpage = self._get_config_install_page() - - # Setting OS value for container doesn't matter presently - self.widget("install-os-distro-box").set_visible( - not self._is_container_install()) - - enabledetect = False - if instpage == INSTALL_PAGE_URL: - enabledetect = True - elif instpage == INSTALL_PAGE_ISO and not self.conn.is_remote(): - enabledetect = True - - self.widget("install-detect-os-box").set_visible(enabledetect) - dodetect = (enabledetect and - self.widget("install-detect-os").get_active()) - self._change_os_detect(not dodetect) - - # PXE installs have nothing to ask for - self.widget("install-method-pages").set_visible( - instpage != INSTALL_PAGE_PXE) - self.widget("install-method-pages").set_current_page(instpage) - - def _back_clicked(self, src_ignore): - notebook = self.widget("create-pages") - curpage = notebook.get_current_page() - next_page = curpage - 1 - - if curpage == PAGE_FINISH and self._should_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._should_skip_disk_page(): - # Skip storage page for import installs - next_page += 1 - - return next_page - - def _forward_clicked(self, src_ignore=None): - notebook = self.widget("create-pages") - curpage = notebook.get_current_page() - - if curpage == PAGE_INSTALL: - # Make sure we have detected the OS before validating the page - did_start = self._start_detect_os_if_needed( - forward_after_finish=True) - if did_start: - return - - if self._validate(curpage) is not True: - return - - if curpage == PAGE_NAME: - self._set_install_page() - - next_page = self._get_next_pagenum(curpage) - - self.widget("create-forward").grab_focus() - notebook.set_current_page(next_page) - - - def _page_changed(self, ignore1, ignore2, pagenum): - if pagenum == PAGE_FINISH: - try: - self._populate_summary() - except Exception as e: - self.err.show_err(_("Error populating summary page: %s") % - str(e)) - return - - self.widget("create-finish").grab_focus() - - self._set_page_num_text(pagenum) - self.widget("create-back").set_sensitive(pagenum != PAGE_NAME) - self.widget("create-forward").set_visible(pagenum != PAGE_FINISH) - self.widget("create-finish").set_visible(pagenum == PAGE_FINISH) - - # Hide all other pages, so the dialog isn't all stretched out - # because of one large page. - for nr in range(self.widget("create-pages").get_n_pages()): - page = self.widget("create-pages").get_nth_page(nr) - page.set_visible(nr == pagenum) - - - ############################ - # Page validation routines # - ############################ - - def _build_guest(self, variant): - guest = virtinst.Guest(self.conn.get_backend()) - guest.set_capabilities_defaults(self._capsinfo) - - # If no machine was selected don't clear recommended machine - machine = self._get_config_machine() - if machine: - guest.os.machine = machine - - # Validation catches user manually typing an invalid value - try: - if variant: - guest.set_os_name(variant) - except ValueError as e: - self.err.val_err(_("Error setting OS information."), str(e)) - return None - - guest.default_graphics_type = self.config.get_graphics_type() - guest.skip_default_sound = not self.config.get_new_vm_sound() - guest.skip_default_usbredir = ( - self.config.get_add_spice_usbredir() == "no") - guest.x86_cpu_default = self.config.get_default_cpu_setting() - - return guest - - def _validate(self, pagenum): - try: - if pagenum == PAGE_NAME: - return self._validate_intro_page() - elif pagenum == PAGE_INSTALL: - return self._validate_install_page() - elif pagenum == PAGE_MEM: - return self._validate_mem_page() - elif pagenum == PAGE_STORAGE: - return self._validate_storage_page() - elif pagenum == PAGE_FINISH: - return self._validate_final_page() - except Exception as e: - self.err.show_err(_("Uncaught error validating install " - "parameters: %s") % str(e)) - return - - def _validate_intro_page(self): - # We just set this here because it's needed soon after for distro - # detection. But the 'real' self._guest is created in validate_install, - # and it just uses _build_guest, so don't ever add any other guest - # altering here. - self._guest = self._build_guest(None) - if not self._guest: - return False - return True - - def _validate_install_page(self): - instmethod = self._get_config_install_page() - installer = None - location = None - extra = None - cdrom = None - install_bootdev = None - is_import = False - init = None - fs = None - template = None - osobj = self._os_list.get_selected_os() - - if not self._is_container_install() and not osobj: - return self.err.val_err(_("You must select an OS.") + - "\n\n" + self._os_list.eol_text) - - if instmethod == INSTALL_PAGE_ISO: - media = self._get_config_local_media() - if not media: - return self.err.val_err( - _("An install media selection is required.")) - cdrom = media - - elif instmethod == INSTALL_PAGE_URL: - media, extra = 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: - install_bootdev = "network" - - elif instmethod == INSTALL_PAGE_IMPORT: - 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.")) - - if not virtinst.DeviceDisk.path_definitely_exists( - self.conn.get_backend(), - import_path): - return self.err.val_err(_("The import path must point to " - "an existing storage.")) - - elif instmethod == INSTALL_PAGE_CONTAINER_APP: - init = self.widget("install-app-entry").get_text() - if not init: - return self.err.val_err(_("An application path is required.")) - - elif instmethod == INSTALL_PAGE_CONTAINER_OS: - fs = self.widget("install-oscontainer-fs").get_text() - if not fs: - return self.err.val_err(_("An OS directory path is required.")) - - if self._get_config_oscontainer_bootstrap(): - src_url = self._get_config_oscontainer_source_url() - user = self._get_config_oscontainer_source_username() - passwd = self._get_config_oscontainer_source_password() - - # Check if the source path was provided - if not src_url: - return self.err.val_err(_("Source URL is required")) - - # Require username and password when authenticate - # to source registry. - if user and not passwd: - return self.err.val_err(_("Please specify password " - "for accessing source registry")) - - # Validate destination path - if os.path.exists(fs): - if not os.path.isdir(fs): - return self.err.val_err(_("Destination path " - "is not directory: %s") % fs) - if not os.access(fs, os.W_OK): - return self.err.val_err(_("No write permissions for " - "directory path: %s") % fs) - if os.listdir(fs) != []: - # Show Yes/No dialog if the destination is not empty - res = self.err.yes_no( - _("OS root directory is not empty"), - _("Creating root file system in a non-empty " - "directory might fail due to file conflicts.\n" - "Would you like to continue?")) - if not res: - return False - - - elif instmethod == INSTALL_PAGE_VZ_TEMPLATE: - template = self.widget("install-container-template").get_text() - if not template: - return self.err.val_err(_("A template name is required.")) - - # Build the installer and Guest instance - try: - # Overwrite the guest - installer = virtinst.Installer( - self.conn.get_backend(), - location=location, cdrom=cdrom, - install_bootdev=install_bootdev) - variant = osobj and osobj.name or None - self._guest = self._build_guest(variant) - if not self._guest: - return False - except Exception as e: - return self.err.val_err( - _("Error setting installer parameters."), e) - - # Validate media location - try: - if extra: - installer.set_extra_args([extra]) - if init: - self._guest.os.init = init - - if fs: - fsdev = virtinst.DeviceFilesystem(self._guest.conn) - fsdev.target = "/" - fsdev.source = fs - self._guest.add_device(fsdev) - - if template: - fsdev = virtinst.DeviceFilesystem(self._guest.conn) - fsdev.target = "/" - fsdev.type = "template" - fsdev.source = template - self._guest.add_device(fsdev) - - except Exception as e: - return self.err.val_err( - _("Error setting install media location."), e) - - # Setting kernel - if instmethod == INSTALL_PAGE_IMPORT: - kernel = self.widget("kernel").get_text() or None - kargs = self.widget("kernel-args").get_text() or None - initrd = self.widget("initrd").get_text() or None - dtb = self.widget("dtb").get_text() or None - - if not self.widget("dtb").get_visible(): - dtb = None - if not self.widget("kernel").get_visible(): - kernel = None - initrd = None - kargs = None - - self._guest.os.kernel = kernel - self._guest.os.initrd = initrd - self._guest.os.dtb = dtb - self._guest.os.kernel_args = kargs - - try: - name = virtinst.Guest.generate_name(self._guest) - self.widget("create-vm-name").set_text(name) - self._guest.validate_name(self._guest.conn, name) - self._guest.name = name - except Exception as e: - return self.err.val_err(_("Error setting default name."), 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(): - return False - - for path in installer.get_search_paths(self._guest): - self._addstorage.check_path_search( - self, self.conn, path) - - res = self._guest.osinfo.get_recommended_resources() - ram = res.get_recommended_ram(self._guest.os.arch) - n_cpus = res.get_recommended_ncpus(self._guest.os.arch) - storage = res.get_recommended_storage(self._guest.os.arch) - log.debug("Recommended resources for os=%s: " - "ram=%s ncpus=%s storage=%s", - self._guest.osinfo.name, ram, n_cpus, storage) - - # Change the default values suggested to the user. - ram_size = DEFAULT_MEM - if ram: - ram_size = ram // (1024 ** 2) - self.widget("mem").set_value(ram_size) - - self.widget("cpus").set_value(n_cpus or 1) - - if storage: - storage_size = storage // (1024 ** 3) - self._addstorage.widget("storage-size").set_value(storage_size) - - # Stash the installer in the _guest instance so we don't need - # to cache both objects individually - self._guest.installer_instance = installer - - # Validation passed, store the install path (if there is one) in - # gsettings - self._get_config_oscontainer_source_url(store_media=True) - 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("cpus").get_value() - mem = self.widget("mem").get_value() - - # VCPUS - try: - self._guest.vcpus = int(cpus) - except Exception as e: - return self.err.val_err(_("Error setting CPUs."), e) - - # Memory - try: - self._guest.currentMemory = int(mem) * 1024 - self._guest.memory = int(mem) * 1024 - except Exception as e: - return self.err.val_err(_("Error setting guest memory."), e) - - return True - - def _get_storage_path(self, vmname, do_log): - failed_disk = None - if self._failed_guest: - failed_disk = _get_vmm_device(self._failed_guest, "disk") - - path = None - path_already_created = False - - if self._get_config_install_page() == INSTALL_PAGE_IMPORT: - path = self._get_config_import_path() - - elif self._is_default_storage(): - if failed_disk: - # Don't generate a new path if the install failed - path = failed_disk.path - path_already_created = failed_disk.storage_was_created - if do_log: - log.debug("Reusing failed disk path=%s " - "already_created=%s", path, path_already_created) - else: - path = self._addstorage.get_default_path(vmname) - if do_log: - log.debug("Default storage path is: %s", path) - - return path, path_already_created - - def _validate_storage_page(self): - path, path_already_created = self._get_storage_path( - self._guest.name, do_log=True) - - disk = None - storage_enabled = self.widget("enable-storage").get_active() - try: - if storage_enabled: - disk = self._addstorage.build_device( - self._guest.name, path=path) - - if disk and self._addstorage.validate_device(disk) is False: - return False - except Exception as e: - return self.err.val_err(_("Storage parameter error."), e) - - if self._get_config_install_page() == INSTALL_PAGE_ISO: - # CD/ISO install and no disks implies LiveCD - self._guest.installer_instance.livecd = not storage_enabled - - _remove_vmm_device(self._guest, "disk") - - if not storage_enabled: - return True - - disk.storage_was_created = path_already_created - _mark_vmm_device(disk) - self._guest.add_device(disk) - - return True - - - def _validate_final_page(self): - # HV + Arch selection - name = self._get_config_name() - if name != self._guest.name: - try: - self._guest.validate_name(self._guest.conn, name) - self._guest.name = name - except Exception as e: - return self.err.val_err(_("Invalid guest name"), str(e)) - if self._is_default_storage(): - log.debug("User changed VM name and using default " - "storage, re-validating with new default storage path.") - # User changed the name and we are using default storage - # which depends on the VM name. Revalidate things - if not self._validate_storage_page(): - return False - - nettype = self._netlist.get_network_selection()[0] - 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) - - macaddr = virtinst.DeviceInterface.generate_mac( - self.conn.get_backend()) - - net = self._netlist.build_device(macaddr) - self._netlist.validate_device(net) - - _remove_vmm_device(self._guest, "interface") - if net: - _mark_vmm_device(net) - self._guest.add_device(net) - - return True - - - ############################# - # Distro detection handling # - ############################# - - def _start_detect_os_if_needed(self, forward_after_finish=False): - """ - Will kick off the OS detection thread if all conditions are met, - like we actually have media to detect, detection isn't already - in progress, etc. - - Returns True if we actually start the detection process - """ - is_install_page = (self.widget("create-pages").get_current_page() == - PAGE_INSTALL) - cdrom, location = self._get_config_detectable_media() - - if self._detect_os_in_progress: - return - if not is_install_page: - return - if not cdrom and not location: - return - if not self._is_os_detect_active(): - return - if self._os_already_detected_for_media: - return - - self._do_start_detect_os(cdrom, location, forward_after_finish) - return True - - def _do_start_detect_os(self, cdrom, location, forward_after_finish): - self._detect_os_in_progress = False - - log.debug("Starting OS detection thread for cdrom=%s location=%s", - cdrom, location) - self.widget("create-forward").set_sensitive(False) - - class ThreadResults(object): - """ - Helper object to track results from the detection thread - """ - _DETECT_FAILED = 1 - _DETECT_INPROGRESS = 2 - def __init__(self): - self._results = self._DETECT_INPROGRESS - - def in_progress(self): - return self._results == self._DETECT_INPROGRESS - - def set_failed(self): - self._results = self._DETECT_FAILED - - def set_distro(self, distro): - self._results = distro - def get_distro(self): - if self._results == self._DETECT_FAILED: - return None - return self._results - - thread_results = ThreadResults() - detectThread = threading.Thread(target=self._detect_thread_cb, - name="Actual media detection", - args=(cdrom, location, thread_results)) - detectThread.setDaemon(True) - detectThread.start() - - self._os_list.search_entry.set_text(_("Detecting...")) - spin = self.widget("install-detect-os-spinner") - spin.start() - - self._report_detect_os_progress(0, thread_results, - forward_after_finish) - - def _detect_thread_cb(self, cdrom, location, thread_results): - """ - Thread callback that does the actual detection - """ - try: - installer = virtinst.Installer(self.conn.get_backend(), - cdrom=cdrom, - location=location) - distro = installer.detect_distro(self._guest) - thread_results.set_distro(distro) - except Exception: - log.exception("Error detecting distro.") - thread_results.set_failed() - - def _report_detect_os_progress(self, idx, thread_results, - forward_after_finish): - """ - Checks detection progress via the _detect_os_results variable - and updates the UI labels, counts the number of iterations, - etc. - - We set a hard time limit on the distro detection to avoid the - chance of the detection hanging (like slow URL lookup) - """ - try: - if (thread_results.in_progress() and - (idx < (DETECT_TIMEOUT * 2))): - # Thread is still going and we haven't hit the timeout yet, - # so update the UI labels and reschedule this function - self.timeout_add(500, self._report_detect_os_progress, - idx + 1, thread_results, forward_after_finish) - return - - distro = thread_results.get_distro() - except Exception: - distro = None - log.exception("Error in distro detect timeout") - - spin = self.widget("install-detect-os-spinner") - spin.stop() - log.debug("Finished UI OS detection.") - - self.widget("create-forward").set_sensitive(True) - self._os_already_detected_for_media = True - self._detect_os_in_progress = False - - if not self._is_os_detect_active(): - # If the user changed the OS detect checkbox in the meantime, - # don't update the UI - return - - if distro: - self._os_list.select_os(virtinst.OSDB.lookup_os(distro)) - else: - self._os_list.reset_state() - self._os_list.search_entry.set_text(_("None detected")) - - if forward_after_finish: - self.idle_add(self._forward_clicked, ()) - - - ########################## - # Guest install routines # - ########################## - - def _finish_clicked(self, src_ignore): - # Validate the final page - page = self.widget("create-pages").get_current_page() - if self._validate(page) is not True: - return False - - log.debug("Starting create finish() sequence") - self._failed_guest = None - guest = self._guest - - try: - self.set_finish_cursor() - - # This encodes all the virtinst defaults up front, so the customize - # dialog actually shows disk buses, cache values, default devices, - # etc. Not required for straight start_install but doesn't hurt. - guest.installer_instance.set_install_defaults(guest) - - if not self.widget("summary-customize").get_active(): - self._start_install(guest) - return - - log.debug("User requested 'customize', launching dialog") - self._show_customize_dialog(self._guest) - except Exception as e: - self.reset_finish_cursor() - self.err.show_err(_("Error starting installation: ") + str(e)) - return - - def _cleanup_customize_window(self): - if not self._customize_window: - return - - # We can re-enter this: cleanup() -> close() -> "details-closed" - window = self._customize_window - self._customize_window = None - window.cleanup() - - def _show_customize_dialog(self, origguest): - orig_vdomain = vmmDomainVirtinst(self.conn, origguest, origguest.uuid) - - def customize_finished_cb(src, vdomain): - if not self.is_visible(): - return - log.debug("User finished customize dialog, starting install") - self._failed_guest = None - self._start_install(vdomain.get_backend()) - - def config_canceled_cb(src): - log.debug("User closed customize window, closing wizard") - self._close_requested() - - # We specifically don't use vmmVMWindow.get_instance here since - # it's not a top level VM window - self._cleanup_customize_window() - self._customize_window = vmmVMWindow(orig_vdomain, self.topwin) - self._customize_window.connect( - "customize-finished", customize_finished_cb) - self._customize_window.connect("closed", config_canceled_cb) - self._customize_window.show() - - def _install_finished_cb(self, error, details, guest, parentobj): - self.reset_finish_cursor(parentobj.topwin) - - if error: - error = (_("Unable to complete install: '%s'") % error) - parentobj.err.show_err(error, details=details) - self._failed_guest = guest - return - - foundvm = None - for vm in self.conn.list_vms(): - if vm.get_uuid() == guest.uuid: - foundvm = vm - break - - self._close() - - # Launch details dialog for new VM - vmmVMWindow.get_instance(self, foundvm).show() - - - def _start_install(self, guest): - """ - Launch the async job to start the install - """ - bootstrap_args = {} - # If creating new container and "container bootstrap" is enabled - if (guest.os.is_container() and - self._get_config_oscontainer_bootstrap()): - bootstrap_arg_keys = { - 'src': self._get_config_oscontainer_source_url, - 'dest': self.widget("install-oscontainer-fs").get_text, - 'user': self._get_config_oscontainer_source_username, - 'passwd': self._get_config_oscontainer_source_password, - 'insecure': self._get_config_oscontainer_isecure, - 'root_password': self._get_config_oscontainer_root_password, - } - for key, getter in bootstrap_arg_keys.items(): - bootstrap_args[key] = getter() - - parentobj = self._customize_window or self - progWin = vmmAsyncJob(self._do_async_install, [guest, bootstrap_args], - self._install_finished_cb, [guest, parentobj], - _("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."), - parentobj.topwin) - progWin.run() - - def _do_async_install(self, asyncjob, guest, bootstrap_args): - """ - Kick off the actual install - """ - meter = asyncjob.get_meter() - - if bootstrap_args: - # Start container bootstrap - self._create_directory_tree(asyncjob, meter, bootstrap_args) - if asyncjob.has_error(): - # Do not continue if virt-bootstrap failed - return - - # Build a list of pools we should refresh, if we are creating storage - refresh_pools = [] - for disk in guest.devices.disk: - if not disk.wants_storage_creation(): - continue - - pool = disk.get_parent_pool() - if not pool: - continue - - poolname = pool.name() - if poolname not in refresh_pools: - refresh_pools.append(poolname) - - log.debug("Starting background install process") - guest.installer_instance.start_install(guest, meter=meter) - log.debug("Install completed") - - # Wait for VM to show up - self.conn.schedule_priority_tick(pollvm=True) - count = 0 - foundvm = None - while count < 200: - for vm in self.conn.list_vms(): - if vm.get_uuid() == guest.uuid: - foundvm = vm - if foundvm: - break - count += 1 - time.sleep(.1) - - if not foundvm: - raise RuntimeError( - _("VM '%s' didn't show up after expected time.") % guest.name) - vm = foundvm - - 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_instance.has_install_phase(): - # Register a status listener, which will restart the - # guest after the install has finished - def cb(): - vm.connect_opt_out("state-changed", - self._check_install_status) - return False - self.idle_add(cb) - - # Kick off pool updates - for poolname in refresh_pools: - try: - pool = self.conn.get_pool(poolname) - self.idle_add(pool.refresh) - except Exception: - log.debug("Error looking up pool=%s for refresh after " - "VM creation.", poolname, exc_info=True) - - - def _check_install_status(self, vm): - """ - Watch the domain that we are installing, waiting for the state - to change, so we can restart it as needed - """ - if vm.is_crashed(): - log.debug("VM crashed, cancelling install plans.") - return True - - if not vm.is_shutoff(): - return - - if vm.get_install_abort(): - log.debug("User manually shutdown VM, not restarting " - "guest after install.") - return True - - try: - log.debug("Install should be completed, starting VM.") - vm.startup() - except Exception as e: - self.err.show_err(_("Error continue install: %s") % str(e)) - - return True - - - def _create_directory_tree(self, asyncjob, meter, bootstrap_args): - """ - Call bootstrap method from virtBootstrap and show logger messages - as state/details. - """ - import logging - import virtBootstrap - - meter.start(text=_("Bootstraping container"), size=100) - def progress_update_cb(prog): - meter.text = _(prog['status']) - meter.update(prog['value']) - - asyncjob.details_enable() - # Use logging filter to show messages of the progreess on the GUI - class SetStateFilter(logging.Filter): - def filter(self, record): - asyncjob.details_update("%s\n" % record.getMessage()) - return True - - # Use string buffer to store log messages - log_stream = io.StringIO() - - # Get virt-bootstrap logger - vbLogger = logging.getLogger('virtBootstrap') - vbLogger.setLevel(logging.DEBUG) - # Create handler to store log messages in the string buffer - hdlr = logging.StreamHandler(log_stream) - hdlr.setFormatter(logging.Formatter('%(message)s')) - # Use logging filter to show messages on GUI - hdlr.addFilter(SetStateFilter()) - vbLogger.addHandler(hdlr) - - # Key word arguments to be passed - kwargs = {'uri': bootstrap_args['src'], - 'dest': bootstrap_args['dest'], - 'not_secure': bootstrap_args['insecure'], - 'progress_cb': progress_update_cb} - if bootstrap_args['user'] and bootstrap_args['passwd']: - kwargs['username'] = bootstrap_args['user'] - kwargs['password'] = bootstrap_args['passwd'] - if bootstrap_args['root_password']: - kwargs['root_password'] = bootstrap_args['root_password'] - log.debug('Start container bootstrap') - try: - virtBootstrap.bootstrap(**kwargs) - # Success - uncheck the 'install-oscontainer-bootstrap' checkbox - - def cb(): - self.widget("install-oscontainer-bootstrap").set_active(False) - self.idle_add(cb) - except Exception as err: - asyncjob.set_error("virt-bootstrap did not complete successfully", - '%s\n%s' % (err, log_stream.getvalue())) diff --git a/virtManager/createvm.py b/virtManager/createvm.py new file mode 100644 index 00000000..a9497dd0 --- /dev/null +++ b/virtManager/createvm.py @@ -0,0 +1,2213 @@ +# Copyright (C) 2008, 2013, 2014, 2015 Red Hat, Inc. +# Copyright (C) 2008 Cole Robinson +# +# This work is licensed under the GNU GPLv2 or later. +# See the COPYING file in the top-level directory. + +import io +import pkgutil +import os +import threading +import time + +from gi.repository import Gdk +from gi.repository import Gtk +from gi.repository import Pango + +import virtinst +import virtinst.generatename +from virtinst import log + +from . import uiutil +from .asyncjob import vmmAsyncJob +from .baseclass import vmmGObjectUI +from .connmanager import vmmConnectionManager +from .device.addstorage import vmmAddStorage +from .device.mediacombo import vmmMediaCombo +from .device.netlist import vmmNetworkList +from .engine import vmmEngine +from .object.domain import vmmDomainVirtinst +from .oslist import vmmOSList +from .storagebrowse import vmmStorageBrowser +from .vmwindow import vmmVMWindow + +# Number of seconds to wait for media detection +DETECT_TIMEOUT = 20 + +DEFAULT_MEM = 1024 + +(PAGE_NAME, + PAGE_INSTALL, + PAGE_MEM, + PAGE_STORAGE, + PAGE_FINISH) = range(5) + +(INSTALL_PAGE_ISO, + INSTALL_PAGE_URL, + INSTALL_PAGE_PXE, + INSTALL_PAGE_IMPORT, + INSTALL_PAGE_CONTAINER_APP, + INSTALL_PAGE_CONTAINER_OS, + INSTALL_PAGE_VZ_TEMPLATE) = range(7) + +# Column numbers for os type/version list models +(OS_COL_ID, + OS_COL_LABEL, + OS_COL_IS_SEP, + OS_COL_IS_SHOW_ALL) = range(4) + + +##################### +# Pretty UI helpers # +##################### + +def _pretty_arch(_a): + if _a == "armv7l": + return "arm" + return _a + + +def _pretty_storage(size): + return _("%.1f GiB") % float(size) + + +def _pretty_memory(mem): + return _("%d MiB") % (mem / 1024.0) + + +########################################################### +# Helpers for tracking devices we create from this wizard # +########################################################### + +def _mark_vmm_device(dev): + setattr(dev, "vmm_create_wizard_device", True) + + +def _get_vmm_device(guest, devkey): + for dev in getattr(guest.devices, devkey): + if hasattr(dev, "vmm_create_wizard_device"): + return dev + + +def _remove_vmm_device(guest, devkey): + dev = _get_vmm_device(guest, devkey) + if dev: + guest.remove_device(dev) + + +def is_virt_bootstrap_installed(): + return pkgutil.find_loader('virtBootstrap') is not None + + +############## +# Main class # +############## + +class vmmCreateVM(vmmGObjectUI): + @classmethod + def show_instance(cls, parentobj, uri=None): + try: + if not cls._instance: + cls._instance = vmmCreateVM() + cls._instance.show(parentobj and parentobj.topwin or None, uri=uri) + except Exception as e: + if not parentobj: + raise + parentobj.err.show_err( + _("Error launching create dialog: %s") % str(e)) + + def __init__(self): + vmmGObjectUI.__init__(self, "createvm.ui", "vmm-create") + self._cleanup_on_app_close() + + self.conn = None + self._capsinfo = None + + self._guest = None + self._failed_guest = None + + # Distro detection state variables + self._detect_os_in_progress = False + self._os_already_detected_for_media = False + + self._customize_window = None + + self._storage_browser = None + self._netlist = None + + self._addstorage = vmmAddStorage(self.conn, self.builder, self.topwin) + self.widget("storage-align").add(self._addstorage.top_box) + def _browse_file_cb(ignore, widget): + self._browse_file(widget) + self._addstorage.connect("browse-clicked", _browse_file_cb) + + self._mediacombo = vmmMediaCombo(self.conn, self.builder, self.topwin) + self._mediacombo.connect("changed", self._iso_changed_cb) + self._mediacombo.connect("activate", self._iso_activated_cb) + self._mediacombo.set_mnemonic_label( + self.widget("install-iso-label")) + self.widget("install-iso-align").add(self._mediacombo.top_box) + + self.builder.connect_signals({ + "on_vmm_newcreate_delete_event": self._close_requested, + + "on_create_cancel_clicked": self._close_requested, + "on_create_back_clicked": self._back_clicked, + "on_create_forward_clicked": self._forward_clicked, + "on_create_finish_clicked": self._finish_clicked, + "on_create_pages_switch_page": self._page_changed, + + "on_create_conn_changed": self._conn_changed, + "on_method_changed": self._method_changed, + "on_xen_type_changed": self._xen_type_changed, + "on_arch_changed": self._arch_changed, + "on_virt_type_changed": self._virt_type_changed, + "on_machine_changed": self._machine_changed, + "on_vz_virt_type_changed": self._vz_virt_type_changed, + + "on_install_iso_browse_clicked": self._browse_iso, + "on_install_url_entry_changed": self._url_changed, + "on_install_url_entry_activate": self._url_activated, + "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_container_source_toggle": self._container_source_toggle, + + "on_install_detect_os_toggled": self._detect_os_toggled_cb, + + "on_kernel_browse_clicked": self._browse_kernel, + "on_initrd_browse_clicked": self._browse_initrd, + "on_dtb_browse_clicked": self._browse_dtb, + + "on_enable_storage_toggled": self._toggle_enable_storage, + + "on_create_vm_name_changed": self._name_changed, + }) + self.bind_escape_key_close() + + self._init_state() + + + + ########################### + # Standard window methods # + ########################### + + def show(self, parent, uri): + log.debug("Showing new vm wizard") + + if not self.is_visible(): + self._reset_state(uri) + self.topwin.set_transient_for(parent) + vmmEngine.get_instance().increment_window_counter() + + self.topwin.present() + + def _close(self, ignore1=None, ignore2=None): + if self.is_visible(): + log.debug("Closing new vm wizard") + vmmEngine.get_instance().decrement_window_counter() + + self.topwin.hide() + + self._cleanup_customize_window() + if self._storage_browser: + self._storage_browser.close() + self._set_conn(None) + self._failed_guest = None + self._guest = None + + def _cleanup(self): + if self._storage_browser: + self._storage_browser.cleanup() + self._storage_browser = None + if self._netlist: + self._netlist.cleanup() + self._netlist = None + if self._mediacombo: + self._mediacombo.cleanup() + self._mediacombo = None + if self._addstorage: + self._addstorage.cleanup() + self._addstorage = None + + self.conn = None + self._capsinfo = None + self._guest = None + + + ########################## + # Initial state handling # + ########################## + + def _show_startup_error(self, error, hideinstall=True): + self.widget("startup-error-box").show() + self.widget("create-forward").set_sensitive(False) + if hideinstall: + self.widget("install-box").hide() + self.widget("arch-expander").hide() + + self.widget("startup-error").set_text("%s: %s" % (_("Error"), error)) + return False + + def _show_startup_warning(self, error): + self.widget("startup-error-box").show() + self.widget("startup-error").set_markup( + "%s: %s" % (_("Warning"), error)) + + def _show_arch_warning(self, error): + self.widget("arch-warning-box").show() + self.widget("arch-warning").set_markup( + "%s: %s" % (_("Warning"), error)) + + + def _init_state(self): + self.widget("create-pages").set_show_tabs(False) + self.widget("install-method-pages").set_show_tabs(False) + + blue = Gdk.Color.parse("#0072A8")[1] + self.widget("header").modify_bg(Gtk.StateType.NORMAL, blue) + + # 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 = uiutil.init_combo_text_column(conn_list, 1) + text.set_property("ellipsize", Pango.EllipsizeMode.MIDDLE) + + def set_model_list(widget_id): + lst = self.widget(widget_id) + model = Gtk.ListStore(str) + lst.set_model(model) + lst.set_entry_text_column(0) + + # Lists for the install urls + set_model_list("install-url-combo") + + # Lists for OS container bootstrap + set_model_list("install-oscontainer-source-url-combo") + + # Architecture + archList = self.widget("arch") + # [label, guest.os.arch value] + archModel = Gtk.ListStore(str, str) + archList.set_model(archModel) + uiutil.init_combo_text_column(archList, 0) + archList.set_row_separator_func( + lambda m, i, ignore: m[i][0] is None, None) + + # guest.os.type value for xen (hvm vs. xen) + hyperList = self.widget("xen-type") + # [label, guest.os_type value] + hyperModel = Gtk.ListStore(str, str) + hyperList.set_model(hyperModel) + uiutil.init_combo_text_column(hyperList, 0) + + # guest.os.machine value + lst = self.widget("machine") + # [machine ID] + model = Gtk.ListStore(str) + lst.set_model(model) + uiutil.init_combo_text_column(lst, 0) + lst.set_row_separator_func(lambda m, i, ignore: m[i][0] is None, None) + + # guest.type value for xen (qemu vs kvm) + lst = self.widget("virt-type") + # [label, guest.type value] + model = Gtk.ListStore(str, str) + lst.set_model(model) + uiutil.init_combo_text_column(lst, 0) + + # OS distro list + self._os_list = vmmOSList() + self.widget("install-os-align").add(self._os_list.search_entry) + self.widget("os-label").set_mnemonic_widget(self._os_list.search_entry) + + def _reset_state(self, urihint=None): + """ + Reset all UI state to default values. Conn specific state is + populated in _populate_conn_state + """ + self.reset_finish_cursor() + + self.widget("create-pages").set_current_page(PAGE_NAME) + self._page_changed(None, None, PAGE_NAME) + + # Name page state + self.widget("create-vm-name").set_text("") + self.widget("method-local").set_active(True) + self.widget("create-conn").set_active(-1) + activeconn = self._populate_conn_list(urihint) + self.widget("arch-expander").set_expanded(False) + self.widget("vz-virt-type-hvm").set_active(True) + + if self._set_conn(activeconn) is False: + return False + + + # Everything from this point forward should be connection independent + + # Distro/Variant + self._os_list.reset_state() + self._os_already_detected_for_media = False + + def _populate_media_model(media_model, urls): + media_model.clear() + if urls is None: + return + for url in urls: + media_model.append([url]) + + # Install local + self._mediacombo.reset_state() + + # Install URL + self.widget("install-urlopts-entry").set_text("") + self.widget("install-url-entry").set_text("") + self.widget("install-url-options").set_expanded(False) + urlmodel = self.widget("install-url-combo").get_model() + _populate_media_model(urlmodel, self.config.get_media_urls()) + + # Install import + self.widget("install-import-entry").set_text("") + self.widget("kernel").set_text("") + self.widget("initrd").set_text("") + self.widget("dtb").set_text("") + + # Install container app + self.widget("install-app-entry").set_text("/bin/sh") + + # Install container OS + self.widget("install-oscontainer-fs").set_text("") + self.widget("install-oscontainer-source-url-entry").set_text("") + self.widget("install-oscontainer-source-user").set_text("") + self.widget("install-oscontainer-source-passwd").set_text("") + self.widget("install-oscontainer-source-insecure").set_active(False) + self.widget("install-oscontainer-bootstrap").set_active(False) + self.widget("install-oscontainer-auth-options").set_expanded(False) + self.widget("install-oscontainer-rootpw").set_text("") + src_model = (self.widget("install-oscontainer-source-url-combo") + .get_model()) + _populate_media_model(src_model, self.config.get_container_urls()) + + # Install VZ container from template + self.widget("install-container-template").set_text("centos-7-x86_64") + + # Storage + self.widget("enable-storage").set_active(True) + self._addstorage.reset_state() + self._addstorage.widget("storage-create").set_active(True) + self._addstorage.widget("storage-entry").set_text("") + + # Final page + self.widget("summary-customize").set_active(False) + + + def _set_caps_state(self): + """ + Set state that is dependent on when capsinfo changes + """ + self.widget("arch-warning-box").hide() + guest = self._build_guest(None) + + # 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 = guest.os.is_xenpv() + is_container = self.conn.is_container() + is_vz = self.conn.is_vz() + is_vz_container = is_vz and guest.os.is_container() + can_remote_url = self.conn.get_backend().support_remote_url_install() + + installable_arch = bool(guest.os.is_x86() or + guest.os.is_ppc64() or + guest.os.is_s390x()) + + if guest.prefers_uefi(): + try: + guest.set_uefi_path(guest.get_uefi_path()) + installable_arch = True + log.debug("UEFI found, setting it as default.") + except Exception as e: + installable_arch = False + log.debug("Error checking for UEFI default", exc_info=True) + msg = _("Failed to setup UEFI: %s\n" + "Install options are limited.") % e + self._show_arch_warning(msg) + + # 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) and + installable_arch) + method_local.set_sensitive(not is_pv and can_storage and + installable_arch) + method_pxe.set_sensitive(not is_pv and installable_arch) + 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" + + if not installable_arch: + msg = (_("Architecture '%s' is not installable") % + guest.os.arch) + tree_tt = msg + local_tt = msg + pxe_tt = msg + + if not any([w.get_active() and w.get_sensitive() + for w in virt_methods]): + 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._show_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("container-install-box").set_visible(is_container) + self.widget("vz-install-box").set_visible(is_vz) + self.widget("virt-install-box").set_visible( + not is_container and not is_vz_container) + + show_dtb = ("arm" in guest.os.arch or + "microblaze" in guest.os.arch or + "ppc" in guest.os.arch) + self.widget("kernel-box").set_visible(not installable_arch) + uiutil.set_grid_row_visible(self.widget("dtb"), show_dtb) + + def _populate_conn_state(self): + """ + Update all state that has some dependency on the current connection + """ + self.conn.schedule_priority_tick(pollnet=True, + pollpool=True, polliface=True, + pollnodedev=True) + + self.widget("install-box").show() + self.widget("create-forward").set_sensitive(True) + + self._capsinfo = None + self.conn.invalidate_caps() + self._change_caps() + is_local = not self.conn.is_remote() + + if not self._capsinfo.guest.has_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._show_startup_error(error) + + # A bit out of order, but populate the xen/virt/arch/machine lists + # so we can work with a default. + self._populate_xen_type() + self._populate_arch() + self._populate_virt_type() + + show_arch = (self.widget("xen-type").get_visible() or + self.widget("virt-type").get_visible() or + self.widget("arch").get_visible() or + self.widget("machine").get_visible()) + uiutil.set_grid_row_visible(self.widget("arch-expander"), show_arch) + + if self.conn.is_xen(): + has_hvm_guests = False + for g in self.conn.caps.guests: + if g.os_type == "hvm": + has_hvm_guests = True + + if not has_hvm_guests: + error = _("Host is not advertising support for full " + "virtualization. Install options may be limited.") + self._show_startup_warning(error) + + elif self.conn.is_qemu(): + if not self._capsinfo.guest.is_kvm_available(): + 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._show_startup_warning(error) + + elif self.conn.is_vz(): + has_hvm_guests = False + has_exe_guests = False + for g in self.conn.caps.guests: + if g.os_type == "hvm": + has_hvm_guests = True + if g.os_type == "exe": + has_exe_guests = True + + self.widget("vz-virt-type-hvm").set_sensitive(has_hvm_guests) + self.widget("vz-virt-type-exe").set_sensitive(has_exe_guests) + self.widget("vz-virt-type-hvm").set_active(has_hvm_guests) + self.widget("vz-virt-type-exe").set_active( + not has_hvm_guests and has_exe_guests) + + # ISO media + # Dependent on connection so we need to do this here + self._mediacombo.set_conn(self.conn) + self._mediacombo.reset_state() + + # Allow container bootstrap only for local connection and + # only if virt-bootstrap is installed. Otherwise, show message. + vb_installed = is_virt_bootstrap_installed() + vb_enabled = is_local and vb_installed + + oscontainer_widget_conf = { + "install-oscontainer-notsupport-conn": not is_local, + "install-oscontainer-notsupport": not vb_installed, + "install-oscontainer-bootstrap": vb_enabled, + "install-oscontainer-source": vb_enabled, + "install-oscontainer-rootpw-box": vb_enabled + } + for w in oscontainer_widget_conf: + self.widget(w).set_visible(oscontainer_widget_conf[w]) + + # Memory + memory = int(self.conn.host_memory_size()) + mem_label = (_("Up to %(maxmem)s available on the host") % + {'maxmem': _pretty_memory(memory)}) + mem_label = ("%s" % + mem_label) + self.widget("mem").set_range(50, memory // 1024) + self.widget("phys-mem-label").set_markup(mem_label) + + # CPU + phys_cpus = int(self.conn.host_active_processor_count()) + cmax = phys_cpus + if cmax <= 0: + cmax = 1 + cpu_label = (_("Up to %(numcpus)d available") % + {'numcpus': int(phys_cpus)}) + cpu_label = ("%s" % + cpu_label) + self.widget("cpus").set_range(1, cmax) + self.widget("phys-cpu-label").set_markup(cpu_label) + + # Storage + self._addstorage.conn = self.conn + self._addstorage.reset_state() + + # Networking + self.widget("advanced-expander").set_expanded(False) + + self._netlist = vmmNetworkList(self.conn, self.builder, self.topwin) + self.widget("netdev-ui-align").add(self._netlist.top_box) + self._netlist.connect("changed", self._netdev_changed) + self._netlist.reset_state() + + def _conn_state_changed(self, conn): + if conn.is_disconnected(): + self._close() + + def _set_conn(self, newconn): + self.widget("startup-error-box").hide() + self.widget("arch-warning-box").hide() + + oldconn = self.conn + self.conn = newconn + if oldconn: + oldconn.disconnect_by_obj(self) + if self._netlist: + self.widget("netdev-ui-align").remove(self._netlist.top_box) + self._netlist.cleanup() + self._netlist = None + + if not self.conn: + return self._show_startup_error( + _("No active connection to install on.")) + self.conn.connect("state-changed", self._conn_state_changed) + + try: + self._populate_conn_state() + except Exception as e: + log.exception("Error setting create wizard conn state.") + return self._show_startup_error(str(e)) + + + def _change_caps(self, gtype=None, arch=None, domtype=None): + """ + Change the cached capsinfo for the passed values, and trigger + all needed UI refreshing + """ + if gtype is None: + # If none specified, prefer HVM so install options aren't limited + # with a default PV choice. + for g in self.conn.caps.guests: + if g.os_type == "hvm": + gtype = "hvm" + break + + capsinfo = self.conn.caps.guest_lookup(os_type=gtype, + arch=arch, + typ=domtype) + + if self._capsinfo: + if (self._capsinfo.guest == capsinfo.guest and + self._capsinfo.domain == capsinfo.domain): + return + + self._capsinfo = capsinfo + log.debug("Guest type set to os_type=%s, arch=%s, dom_type=%s", + self._capsinfo.os_type, + self._capsinfo.arch, + self._capsinfo.hypervisor_type) + self._populate_machine() + self._set_caps_state() + + + ################################################## + # Helpers for populating hv/arch/machine/conn UI # + ################################################## + + def _populate_xen_type(self): + model = self.widget("xen-type").get_model() + model.clear() + + default = 0 + guests = [] + if self.conn.is_xen() or self.conn.is_test(): + guests = self.conn.caps.guests[:] + + for guest in guests: + if not guest.domains: + continue + + gtype = guest.os_type + dom = guest.domains[0] + domtype = dom.hypervisor_type + label = self.conn.pretty_hv(gtype, domtype) + + # 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._capsinfo.os_type and + domtype == self._capsinfo.hypervisor_type): + default = len(model) + + model.append([label, gtype]) + + show = bool(len(model)) + uiutil.set_grid_row_visible(self.widget("xen-type"), show) + if show: + self.widget("xen-type").set_active(default) + + def _populate_arch(self): + model = self.widget("arch").get_model() + model.clear() + + default = 0 + archs = [] + for guest in self.conn.caps.guests: + if guest.os_type == self._capsinfo.os_type: + archs.append(guest.arch) + + # Combine x86/i686 to avoid confusion + if (self.conn.caps.host.cpu.arch == "x86_64" and + "x86_64" in archs and "i686" in archs): + archs.remove("i686") + archs.sort() + + prios = ["x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", + "s390x"] + if self.conn.caps.host.cpu.arch not in prios: + prios = [] + else: + for p in prios[:]: + if p not in archs: + prios.remove(p) + else: + archs.remove(p) + if prios: + if archs: + prios += [None] + archs = prios + archs + + default = 0 + if self._capsinfo.arch in archs: + default = archs.index(self._capsinfo.arch) + + for arch in archs: + model.append([_pretty_arch(arch), arch]) + + show = not (len(archs) < 2) + uiutil.set_grid_row_visible(self.widget("arch"), show) + self.widget("arch").set_active(default) + + def _populate_virt_type(self): + model = self.widget("virt-type").get_model() + model.clear() + + # Allow choosing between qemu and kvm for archs that traditionally + # have a decent amount of TCG usage, like armv7l. Also include + # aarch64 which can be used for arm32 VMs as well + domains = [d.hypervisor_type for d in self._capsinfo.guest.domains[:]] + if not self.conn.is_qemu(): + domains = [] + elif self._capsinfo.arch in ["i686", "x86_64", "ppc64", "ppc64le"]: + domains = [] + + default = 0 + if self._capsinfo.hypervisor_type in domains: + default = domains.index(self._capsinfo.hypervisor_type) + + prios = ["kvm"] + for domain in prios: + if domain not in domains: + continue + domains.remove(domain) + domains.insert(0, domain) + + for domain in domains: + label = self.conn.pretty_hv(self._capsinfo.os_type, domain) + model.append([label, domain]) + + show = bool(len(model) > 1) + uiutil.set_grid_row_visible(self.widget("virt-type"), show) + self.widget("virt-type").set_active(default) + + def _populate_machine(self): + model = self.widget("machine").get_model() + + machines = self._capsinfo.machines[:] + if self._capsinfo.arch in ["i686", "x86_64"]: + machines = [] + machines.sort() + + defmachine = None + prios = [] + recommended_machine = virtinst.Guest.get_recommended_machine( + self._capsinfo) + if recommended_machine: + defmachine = recommended_machine + prios = [defmachine] + + for p in prios[:]: + if p not in machines: + prios.remove(p) + else: + machines.remove(p) + if prios: + machines = prios + [None] + machines + + default = 0 + if defmachine and defmachine in machines: + default = machines.index(defmachine) + + self.widget("machine").disconnect_by_func(self._machine_changed) + try: + model.clear() + for m in machines: + model.append([m]) + + show = (len(machines) > 1) + uiutil.set_grid_row_visible(self.widget("machine"), show) + if show: + self.widget("machine").set_active(default) + finally: + self.widget("machine").connect("changed", self._machine_changed) + + def _populate_conn_list(self, urihint=None): + conn_list = self.widget("create-conn") + model = conn_list.get_model() + model.clear() + + default = -1 + connmanager = vmmConnectionManager.get_instance() + for connobj in connmanager.conns.values(): + 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()]) + + 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 = connmanager.conns[activeuri] + + 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 + + + ############################### + # Misc UI populating routines # + ############################### + + def _populate_summary_storage(self, path=None): + storagetmpl = "%s" + storagesize = "" + storagepath = "" + + disk = _get_vmm_device(self._guest, "disk") + if disk: + if disk.wants_storage_creation(): + storagesize = "%s" % _pretty_storage(disk.get_size()) + if not path: + path = disk.path + storagepath = (storagetmpl % path) + elif len(self._guest.devices.filesystem): + fs = self._guest.devices.filesystem[0] + storagepath = storagetmpl % fs.source + elif self._guest.os.is_container(): + storagepath = _("Host filesystem") + else: + storagepath = _("None") + + self.widget("summary-storage").set_markup(storagesize) + self.widget("summary-storage").set_visible(bool(storagesize)) + self.widget("summary-storage-path").set_markup(storagepath) + + def _populate_summary(self): + mem = _pretty_memory(int(self._guest.memory)) + 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") + elif instmethod == INSTALL_PAGE_VZ_TEMPLATE: + install = _("Virtuozzo container") + + self.widget("summary-os").set_text(self._guest.osinfo.label) + self.widget("summary-install").set_text(install) + self.widget("summary-mem").set_text(mem) + self.widget("summary-cpu").set_text(cpu) + self._populate_summary_storage() + + self._netdev_changed(None) + + + ################################ + # UI state getters and helpers # + ################################ + + def _get_config_name(self): + return self.widget("create-vm-name").get_text() + + def _get_config_machine(self): + return uiutil.get_list_selection(self.widget("machine"), + check_visible=True) + + def _get_config_install_page(self): + if self.widget("vz-install-box").get_visible(): + if self.widget("vz-virt-type-exe").get_active(): + return INSTALL_PAGE_VZ_TEMPLATE + if self.widget("virt-install-box").get_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 _is_container_install(self): + return self._get_config_install_page() in [INSTALL_PAGE_CONTAINER_APP, + INSTALL_PAGE_CONTAINER_OS, + INSTALL_PAGE_VZ_TEMPLATE] + + + def _get_config_oscontainer_bootstrap(self): + return self.widget("install-oscontainer-bootstrap").get_active() + + + def _get_config_oscontainer_source_url(self, store_media=False): + src_url = (self.widget("install-oscontainer-source-url-entry") + .get_text().strip()) + + if src_url and store_media: + self.config.add_container_url(src_url) + + return src_url + + + def _get_config_oscontainer_source_username(self): + return (self.widget("install-oscontainer-source-user") + .get_text().strip()) + + + def _get_config_oscontainer_source_password(self): + return self.widget("install-oscontainer-source-passwd").get_text() + + + def _get_config_oscontainer_isecure(self): + return self.widget("install-oscontainer-source-insecure").get_active() + + + def _get_config_oscontainer_root_password(self): + return self.widget("install-oscontainer-rootpw").get_text() + + + def _should_skip_disk_page(self): + return self._get_config_install_page() in [INSTALL_PAGE_IMPORT, + INSTALL_PAGE_CONTAINER_APP, + INSTALL_PAGE_CONTAINER_OS, + INSTALL_PAGE_VZ_TEMPLATE] + + def _get_config_local_media(self, store_media=False): + return self._mediacombo.get_path(store_media=store_media) + + def _get_config_detectable_media(self): + instpage = self._get_config_install_page() + cdrom = None + location = None + + if instpage == INSTALL_PAGE_ISO: + cdrom = self._get_config_local_media() + elif instpage == INSTALL_PAGE_URL: + location = self.widget("install-url-entry").get_text() + + return cdrom, location + + def _get_config_url_info(self, store_media=False): + media = self.widget("install-url-entry").get_text().strip() + extra = self.widget("install-urlopts-entry").get_text().strip() + + if media and store_media: + self.config.add_media_url(media) + + return (media, extra) + + def _get_config_import_path(self): + return self.widget("install-import-entry").get_text() + + def _is_default_storage(self): + return (self._addstorage.is_default_storage() and + not self._should_skip_disk_page()) + + def _is_os_detect_active(self): + return self.widget("install-detect-os").get_active() + + + ################ + # UI Listeners # + ################ + + def _close_requested(self, *ignore1, **ignore2): + """ + When user tries to close the dialog, check for any disks that + we should auto cleanup + """ + if (self._failed_guest and + self._failed_guest.installer_instance.get_created_disks( + self._failed_guest)): + + def _cleanup_disks(asyncjob): + meter = asyncjob.get_meter() + self._failed_guest.installer_instance.cleanup_created_disks( + self._failed_guest, meter) + + def _cleanup_disks_finished(error, details): + if error: + log.debug("Error cleaning up disk images:" + "\nerror=%s\ndetails=%s", error, details) + self.idle_add(self._close) + + progWin = vmmAsyncJob( + _cleanup_disks, [], + _cleanup_disks_finished, [], + _("Removing disk images"), + _("Removing disk images we created for this virtual machine."), + self.topwin) + progWin.run() + + else: + self._close() + + return 1 + + + # Intro page listeners + def _conn_changed(self, src): + uri = uiutil.get_list_selection(src) + newconn = None + connmanager = vmmConnectionManager.get_instance() + if uri: + newconn = connmanager.conns[uri] + + # 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 + + if self.conn is not newconn: + self._set_conn(newconn) + + def _method_changed(self, src): + ignore = src + # Reset the page number, since the total page numbers depend + # on the chosen install method + self._set_page_num_text(0) + + def _machine_changed(self, ignore): + self._set_caps_state() + + def _xen_type_changed(self, ignore): + os_type = uiutil.get_list_selection(self.widget("xen-type"), column=1) + if not os_type: + return + + self._change_caps(os_type) + self._populate_arch() + + def _arch_changed(self, ignore): + arch = uiutil.get_list_selection(self.widget("arch"), column=1) + if not arch: + return + + self._change_caps(self._capsinfo.os_type, arch) + self._populate_virt_type() + + def _virt_type_changed(self, ignore): + domtype = uiutil.get_list_selection(self.widget("virt-type"), column=1) + if not domtype: + return + + self._change_caps(self._capsinfo.os_type, self._capsinfo.arch, domtype) + + def _vz_virt_type_changed(self, ignore): + is_hvm = self.widget("vz-virt-type-hvm").get_active() + if is_hvm: + self._change_caps("hvm") + else: + self._change_caps("exe") + + # Install page listeners + def _detectable_media_widget_changed(self, widget, checkfocus=True): + self._os_already_detected_for_media = False + + # If the text entry widget has focus, don't fire detect_media_os, + # it means the user is probably typing. It will be detected + # when the user activates the widget, or we try to switch pages + if (checkfocus and + hasattr(widget, "get_text") and widget.has_focus()): + return + + self._start_detect_os_if_needed() + + def _url_changed(self, src): + self._detectable_media_widget_changed(src) + def _url_activated(self, src): + self._detectable_media_widget_changed(src, checkfocus=False) + def _iso_changed_cb(self, mediacombo, entry): + self._detectable_media_widget_changed(entry) + def _iso_activated_cb(self, mediacombo, entry): + self._detectable_media_widget_changed(entry, checkfocus=False) + + def _detect_os_toggled_cb(self, src): + if not src.is_visible(): + return + + # We are only here if the user explicitly changed detection UI + dodetect = src.get_active() + self._change_os_detect(not dodetect) + if dodetect: + self._os_already_detected_for_media = False + self._start_detect_os_if_needed() + + def _browse_oscontainer(self, ignore): + self._browse_file("install-oscontainer-fs", is_dir=True) + def _browse_app(self, ignore): + self._browse_file("install-app-entry") + def _browse_import(self, ignore): + self._browse_file("install-import-entry") + def _browse_iso(self, ignore): + def set_path(ignore, path): + self._mediacombo.set_path(path) + self._browse_file(None, cb=set_path, is_media=True) + def _browse_kernel(self, ignore): + self._browse_file("kernel") + def _browse_initrd(self, ignore): + self._browse_file("initrd") + def _browse_dtb(self, ignore): + self._browse_file("dtb") + + + # Storage page listeners + def _toggle_enable_storage(self, src): + self.widget("storage-align").set_sensitive(src.get_active()) + + + # Summary page listeners + def _name_changed(self, src): + newname = src.get_text() + if not src.is_visible(): + return + if not newname: + return + + try: + path, ignore = self._get_storage_path(newname, do_log=False) + self._populate_summary_storage(path=path) + except Exception: + log.debug("Error generating storage path on name change " + "for name=%s", newname, exc_info=True) + + + def _netdev_changed(self, ignore): + row = self._netlist.get_network_row() + pxe_install = (self._get_config_install_page() == INSTALL_PAGE_PXE) + + ntype = row[0] + connkey = row[6] + expand = (ntype != "network" and ntype != "bridge") + no_network = ntype is None + + if (no_network or ntype == virtinst.DeviceInterface.TYPE_USER): + can_pxe = False + elif ntype != virtinst.DeviceInterface.TYPE_VIRTUAL: + can_pxe = True + else: + can_pxe = self.conn.get_net(connkey).can_pxe() + + if expand: + self.widget("advanced-expander").set_expanded(True) + + self.widget("netdev-warn-box").set_visible(False) + def _show_netdev_warn(msg): + self.widget("advanced-expander").set_expanded(True) + self.widget("netdev-warn-box").set_visible(True) + self.widget("netdev-warn-label").set_markup( + "%s" % msg) + + if no_network: + _show_netdev_warn(_("No network selected")) + elif not can_pxe and pxe_install: + _show_netdev_warn(_("Network selection does not support PXE")) + + + # Enable/Disable container source URL entry on checkbox click + def _container_source_toggle(self, ignore): + enable_src = self.widget("install-oscontainer-bootstrap").get_active() + self.widget("install-oscontainer-source").set_sensitive(enable_src) + self.widget("install-oscontainer-rootpw-box").set_sensitive(enable_src) + + # Auto-generate a path if not specified + if enable_src and not self.widget("install-oscontainer-fs").get_text(): + if os.geteuid() == 0: + fs_dir = ['/var/lib/libvirt/filesystems/'] + else: + fs_dir = [os.environ['HOME'], + '.local/share/libvirt/filesystems/'] + + default_name = virtinst.Guest.generate_name(self._guest) + fs = fs_dir + [default_name] + self.widget("install-oscontainer-fs").set_text(os.path.join(*fs)) + + + ######################## + # Misc helper routines # + ######################## + + def _browse_file(self, cbwidget, cb=None, 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 cb: + callback = cb + else: + def callback(ignore, text): + widget = cbwidget + if isinstance(cbwidget, str): + widget = self.widget(cbwidget) + widget.set_text(text) + + if self._storage_browser and self._storage_browser.conn != self.conn: + self._storage_browser.cleanup() + self._storage_browser = None + if self._storage_browser is None: + self._storage_browser = vmmStorageBrowser(self.conn) + + 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) + + + ###################### + # Navigation methods # + ###################### + + def _set_page_num_text(self, cur): + """ + Set the 'page 1 of 4' style text in the wizard header + """ + cur += 1 + final = PAGE_FINISH + 1 + if self._should_skip_disk_page(): + final -= 1 + cur = min(cur, final) + + page_lbl = ("%s" % + _("Step %(current_page)d of %(max_page)d") % + {'current_page': cur, 'max_page': final}) + + self.widget("header-pagenum").set_markup(page_lbl) + + def _change_os_detect(self, sensitive): + self._os_list.set_sensitive(sensitive) + if not sensitive and not self._os_list.get_selected_os(): + self._os_list.search_entry.set_text( + _("Waiting for install media / source")) + + def _set_install_page(self): + instpage = self._get_config_install_page() + + # Setting OS value for container doesn't matter presently + self.widget("install-os-distro-box").set_visible( + not self._is_container_install()) + + enabledetect = False + if instpage == INSTALL_PAGE_URL: + enabledetect = True + elif instpage == INSTALL_PAGE_ISO and not self.conn.is_remote(): + enabledetect = True + + self.widget("install-detect-os-box").set_visible(enabledetect) + dodetect = (enabledetect and + self.widget("install-detect-os").get_active()) + self._change_os_detect(not dodetect) + + # PXE installs have nothing to ask for + self.widget("install-method-pages").set_visible( + instpage != INSTALL_PAGE_PXE) + self.widget("install-method-pages").set_current_page(instpage) + + def _back_clicked(self, src_ignore): + notebook = self.widget("create-pages") + curpage = notebook.get_current_page() + next_page = curpage - 1 + + if curpage == PAGE_FINISH and self._should_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._should_skip_disk_page(): + # Skip storage page for import installs + next_page += 1 + + return next_page + + def _forward_clicked(self, src_ignore=None): + notebook = self.widget("create-pages") + curpage = notebook.get_current_page() + + if curpage == PAGE_INSTALL: + # Make sure we have detected the OS before validating the page + did_start = self._start_detect_os_if_needed( + forward_after_finish=True) + if did_start: + return + + if self._validate(curpage) is not True: + return + + if curpage == PAGE_NAME: + self._set_install_page() + + next_page = self._get_next_pagenum(curpage) + + self.widget("create-forward").grab_focus() + notebook.set_current_page(next_page) + + + def _page_changed(self, ignore1, ignore2, pagenum): + if pagenum == PAGE_FINISH: + try: + self._populate_summary() + except Exception as e: + self.err.show_err(_("Error populating summary page: %s") % + str(e)) + return + + self.widget("create-finish").grab_focus() + + self._set_page_num_text(pagenum) + self.widget("create-back").set_sensitive(pagenum != PAGE_NAME) + self.widget("create-forward").set_visible(pagenum != PAGE_FINISH) + self.widget("create-finish").set_visible(pagenum == PAGE_FINISH) + + # Hide all other pages, so the dialog isn't all stretched out + # because of one large page. + for nr in range(self.widget("create-pages").get_n_pages()): + page = self.widget("create-pages").get_nth_page(nr) + page.set_visible(nr == pagenum) + + + ############################ + # Page validation routines # + ############################ + + def _build_guest(self, variant): + guest = virtinst.Guest(self.conn.get_backend()) + guest.set_capabilities_defaults(self._capsinfo) + + # If no machine was selected don't clear recommended machine + machine = self._get_config_machine() + if machine: + guest.os.machine = machine + + # Validation catches user manually typing an invalid value + try: + if variant: + guest.set_os_name(variant) + except ValueError as e: + self.err.val_err(_("Error setting OS information."), str(e)) + return None + + guest.default_graphics_type = self.config.get_graphics_type() + guest.skip_default_sound = not self.config.get_new_vm_sound() + guest.skip_default_usbredir = ( + self.config.get_add_spice_usbredir() == "no") + guest.x86_cpu_default = self.config.get_default_cpu_setting() + + return guest + + def _validate(self, pagenum): + try: + if pagenum == PAGE_NAME: + return self._validate_intro_page() + elif pagenum == PAGE_INSTALL: + return self._validate_install_page() + elif pagenum == PAGE_MEM: + return self._validate_mem_page() + elif pagenum == PAGE_STORAGE: + return self._validate_storage_page() + elif pagenum == PAGE_FINISH: + return self._validate_final_page() + except Exception as e: + self.err.show_err(_("Uncaught error validating install " + "parameters: %s") % str(e)) + return + + def _validate_intro_page(self): + # We just set this here because it's needed soon after for distro + # detection. But the 'real' self._guest is created in validate_install, + # and it just uses _build_guest, so don't ever add any other guest + # altering here. + self._guest = self._build_guest(None) + if not self._guest: + return False + return True + + def _validate_install_page(self): + instmethod = self._get_config_install_page() + installer = None + location = None + extra = None + cdrom = None + install_bootdev = None + is_import = False + init = None + fs = None + template = None + osobj = self._os_list.get_selected_os() + + if not self._is_container_install() and not osobj: + return self.err.val_err(_("You must select an OS.") + + "\n\n" + self._os_list.eol_text) + + if instmethod == INSTALL_PAGE_ISO: + media = self._get_config_local_media() + if not media: + return self.err.val_err( + _("An install media selection is required.")) + cdrom = media + + elif instmethod == INSTALL_PAGE_URL: + media, extra = 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: + install_bootdev = "network" + + elif instmethod == INSTALL_PAGE_IMPORT: + 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.")) + + if not virtinst.DeviceDisk.path_definitely_exists( + self.conn.get_backend(), + import_path): + return self.err.val_err(_("The import path must point to " + "an existing storage.")) + + elif instmethod == INSTALL_PAGE_CONTAINER_APP: + init = self.widget("install-app-entry").get_text() + if not init: + return self.err.val_err(_("An application path is required.")) + + elif instmethod == INSTALL_PAGE_CONTAINER_OS: + fs = self.widget("install-oscontainer-fs").get_text() + if not fs: + return self.err.val_err(_("An OS directory path is required.")) + + if self._get_config_oscontainer_bootstrap(): + src_url = self._get_config_oscontainer_source_url() + user = self._get_config_oscontainer_source_username() + passwd = self._get_config_oscontainer_source_password() + + # Check if the source path was provided + if not src_url: + return self.err.val_err(_("Source URL is required")) + + # Require username and password when authenticate + # to source registry. + if user and not passwd: + return self.err.val_err(_("Please specify password " + "for accessing source registry")) + + # Validate destination path + if os.path.exists(fs): + if not os.path.isdir(fs): + return self.err.val_err(_("Destination path " + "is not directory: %s") % fs) + if not os.access(fs, os.W_OK): + return self.err.val_err(_("No write permissions for " + "directory path: %s") % fs) + if os.listdir(fs) != []: + # Show Yes/No dialog if the destination is not empty + res = self.err.yes_no( + _("OS root directory is not empty"), + _("Creating root file system in a non-empty " + "directory might fail due to file conflicts.\n" + "Would you like to continue?")) + if not res: + return False + + + elif instmethod == INSTALL_PAGE_VZ_TEMPLATE: + template = self.widget("install-container-template").get_text() + if not template: + return self.err.val_err(_("A template name is required.")) + + # Build the installer and Guest instance + try: + # Overwrite the guest + installer = virtinst.Installer( + self.conn.get_backend(), + location=location, cdrom=cdrom, + install_bootdev=install_bootdev) + variant = osobj and osobj.name or None + self._guest = self._build_guest(variant) + if not self._guest: + return False + except Exception as e: + return self.err.val_err( + _("Error setting installer parameters."), e) + + # Validate media location + try: + if extra: + installer.set_extra_args([extra]) + if init: + self._guest.os.init = init + + if fs: + fsdev = virtinst.DeviceFilesystem(self._guest.conn) + fsdev.target = "/" + fsdev.source = fs + self._guest.add_device(fsdev) + + if template: + fsdev = virtinst.DeviceFilesystem(self._guest.conn) + fsdev.target = "/" + fsdev.type = "template" + fsdev.source = template + self._guest.add_device(fsdev) + + except Exception as e: + return self.err.val_err( + _("Error setting install media location."), e) + + # Setting kernel + if instmethod == INSTALL_PAGE_IMPORT: + kernel = self.widget("kernel").get_text() or None + kargs = self.widget("kernel-args").get_text() or None + initrd = self.widget("initrd").get_text() or None + dtb = self.widget("dtb").get_text() or None + + if not self.widget("dtb").get_visible(): + dtb = None + if not self.widget("kernel").get_visible(): + kernel = None + initrd = None + kargs = None + + self._guest.os.kernel = kernel + self._guest.os.initrd = initrd + self._guest.os.dtb = dtb + self._guest.os.kernel_args = kargs + + try: + name = virtinst.Guest.generate_name(self._guest) + self.widget("create-vm-name").set_text(name) + self._guest.validate_name(self._guest.conn, name) + self._guest.name = name + except Exception as e: + return self.err.val_err(_("Error setting default name."), 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(): + return False + + for path in installer.get_search_paths(self._guest): + self._addstorage.check_path_search( + self, self.conn, path) + + res = self._guest.osinfo.get_recommended_resources() + ram = res.get_recommended_ram(self._guest.os.arch) + n_cpus = res.get_recommended_ncpus(self._guest.os.arch) + storage = res.get_recommended_storage(self._guest.os.arch) + log.debug("Recommended resources for os=%s: " + "ram=%s ncpus=%s storage=%s", + self._guest.osinfo.name, ram, n_cpus, storage) + + # Change the default values suggested to the user. + ram_size = DEFAULT_MEM + if ram: + ram_size = ram // (1024 ** 2) + self.widget("mem").set_value(ram_size) + + self.widget("cpus").set_value(n_cpus or 1) + + if storage: + storage_size = storage // (1024 ** 3) + self._addstorage.widget("storage-size").set_value(storage_size) + + # Stash the installer in the _guest instance so we don't need + # to cache both objects individually + self._guest.installer_instance = installer + + # Validation passed, store the install path (if there is one) in + # gsettings + self._get_config_oscontainer_source_url(store_media=True) + 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("cpus").get_value() + mem = self.widget("mem").get_value() + + # VCPUS + try: + self._guest.vcpus = int(cpus) + except Exception as e: + return self.err.val_err(_("Error setting CPUs."), e) + + # Memory + try: + self._guest.currentMemory = int(mem) * 1024 + self._guest.memory = int(mem) * 1024 + except Exception as e: + return self.err.val_err(_("Error setting guest memory."), e) + + return True + + def _get_storage_path(self, vmname, do_log): + failed_disk = None + if self._failed_guest: + failed_disk = _get_vmm_device(self._failed_guest, "disk") + + path = None + path_already_created = False + + if self._get_config_install_page() == INSTALL_PAGE_IMPORT: + path = self._get_config_import_path() + + elif self._is_default_storage(): + if failed_disk: + # Don't generate a new path if the install failed + path = failed_disk.path + path_already_created = failed_disk.storage_was_created + if do_log: + log.debug("Reusing failed disk path=%s " + "already_created=%s", path, path_already_created) + else: + path = self._addstorage.get_default_path(vmname) + if do_log: + log.debug("Default storage path is: %s", path) + + return path, path_already_created + + def _validate_storage_page(self): + path, path_already_created = self._get_storage_path( + self._guest.name, do_log=True) + + disk = None + storage_enabled = self.widget("enable-storage").get_active() + try: + if storage_enabled: + disk = self._addstorage.build_device( + self._guest.name, path=path) + + if disk and self._addstorage.validate_device(disk) is False: + return False + except Exception as e: + return self.err.val_err(_("Storage parameter error."), e) + + if self._get_config_install_page() == INSTALL_PAGE_ISO: + # CD/ISO install and no disks implies LiveCD + self._guest.installer_instance.livecd = not storage_enabled + + _remove_vmm_device(self._guest, "disk") + + if not storage_enabled: + return True + + disk.storage_was_created = path_already_created + _mark_vmm_device(disk) + self._guest.add_device(disk) + + return True + + + def _validate_final_page(self): + # HV + Arch selection + name = self._get_config_name() + if name != self._guest.name: + try: + self._guest.validate_name(self._guest.conn, name) + self._guest.name = name + except Exception as e: + return self.err.val_err(_("Invalid guest name"), str(e)) + if self._is_default_storage(): + log.debug("User changed VM name and using default " + "storage, re-validating with new default storage path.") + # User changed the name and we are using default storage + # which depends on the VM name. Revalidate things + if not self._validate_storage_page(): + return False + + nettype = self._netlist.get_network_selection()[0] + 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) + + macaddr = virtinst.DeviceInterface.generate_mac( + self.conn.get_backend()) + + net = self._netlist.build_device(macaddr) + self._netlist.validate_device(net) + + _remove_vmm_device(self._guest, "interface") + if net: + _mark_vmm_device(net) + self._guest.add_device(net) + + return True + + + ############################# + # Distro detection handling # + ############################# + + def _start_detect_os_if_needed(self, forward_after_finish=False): + """ + Will kick off the OS detection thread if all conditions are met, + like we actually have media to detect, detection isn't already + in progress, etc. + + Returns True if we actually start the detection process + """ + is_install_page = (self.widget("create-pages").get_current_page() == + PAGE_INSTALL) + cdrom, location = self._get_config_detectable_media() + + if self._detect_os_in_progress: + return + if not is_install_page: + return + if not cdrom and not location: + return + if not self._is_os_detect_active(): + return + if self._os_already_detected_for_media: + return + + self._do_start_detect_os(cdrom, location, forward_after_finish) + return True + + def _do_start_detect_os(self, cdrom, location, forward_after_finish): + self._detect_os_in_progress = False + + log.debug("Starting OS detection thread for cdrom=%s location=%s", + cdrom, location) + self.widget("create-forward").set_sensitive(False) + + class ThreadResults(object): + """ + Helper object to track results from the detection thread + """ + _DETECT_FAILED = 1 + _DETECT_INPROGRESS = 2 + def __init__(self): + self._results = self._DETECT_INPROGRESS + + def in_progress(self): + return self._results == self._DETECT_INPROGRESS + + def set_failed(self): + self._results = self._DETECT_FAILED + + def set_distro(self, distro): + self._results = distro + def get_distro(self): + if self._results == self._DETECT_FAILED: + return None + return self._results + + thread_results = ThreadResults() + detectThread = threading.Thread(target=self._detect_thread_cb, + name="Actual media detection", + args=(cdrom, location, thread_results)) + detectThread.setDaemon(True) + detectThread.start() + + self._os_list.search_entry.set_text(_("Detecting...")) + spin = self.widget("install-detect-os-spinner") + spin.start() + + self._report_detect_os_progress(0, thread_results, + forward_after_finish) + + def _detect_thread_cb(self, cdrom, location, thread_results): + """ + Thread callback that does the actual detection + """ + try: + installer = virtinst.Installer(self.conn.get_backend(), + cdrom=cdrom, + location=location) + distro = installer.detect_distro(self._guest) + thread_results.set_distro(distro) + except Exception: + log.exception("Error detecting distro.") + thread_results.set_failed() + + def _report_detect_os_progress(self, idx, thread_results, + forward_after_finish): + """ + Checks detection progress via the _detect_os_results variable + and updates the UI labels, counts the number of iterations, + etc. + + We set a hard time limit on the distro detection to avoid the + chance of the detection hanging (like slow URL lookup) + """ + try: + if (thread_results.in_progress() and + (idx < (DETECT_TIMEOUT * 2))): + # Thread is still going and we haven't hit the timeout yet, + # so update the UI labels and reschedule this function + self.timeout_add(500, self._report_detect_os_progress, + idx + 1, thread_results, forward_after_finish) + return + + distro = thread_results.get_distro() + except Exception: + distro = None + log.exception("Error in distro detect timeout") + + spin = self.widget("install-detect-os-spinner") + spin.stop() + log.debug("Finished UI OS detection.") + + self.widget("create-forward").set_sensitive(True) + self._os_already_detected_for_media = True + self._detect_os_in_progress = False + + if not self._is_os_detect_active(): + # If the user changed the OS detect checkbox in the meantime, + # don't update the UI + return + + if distro: + self._os_list.select_os(virtinst.OSDB.lookup_os(distro)) + else: + self._os_list.reset_state() + self._os_list.search_entry.set_text(_("None detected")) + + if forward_after_finish: + self.idle_add(self._forward_clicked, ()) + + + ########################## + # Guest install routines # + ########################## + + def _finish_clicked(self, src_ignore): + # Validate the final page + page = self.widget("create-pages").get_current_page() + if self._validate(page) is not True: + return False + + log.debug("Starting create finish() sequence") + self._failed_guest = None + guest = self._guest + + try: + self.set_finish_cursor() + + # This encodes all the virtinst defaults up front, so the customize + # dialog actually shows disk buses, cache values, default devices, + # etc. Not required for straight start_install but doesn't hurt. + guest.installer_instance.set_install_defaults(guest) + + if not self.widget("summary-customize").get_active(): + self._start_install(guest) + return + + log.debug("User requested 'customize', launching dialog") + self._show_customize_dialog(self._guest) + except Exception as e: + self.reset_finish_cursor() + self.err.show_err(_("Error starting installation: ") + str(e)) + return + + def _cleanup_customize_window(self): + if not self._customize_window: + return + + # We can re-enter this: cleanup() -> close() -> "details-closed" + window = self._customize_window + self._customize_window = None + window.cleanup() + + def _show_customize_dialog(self, origguest): + orig_vdomain = vmmDomainVirtinst(self.conn, origguest, origguest.uuid) + + def customize_finished_cb(src, vdomain): + if not self.is_visible(): + return + log.debug("User finished customize dialog, starting install") + self._failed_guest = None + self._start_install(vdomain.get_backend()) + + def config_canceled_cb(src): + log.debug("User closed customize window, closing wizard") + self._close_requested() + + # We specifically don't use vmmVMWindow.get_instance here since + # it's not a top level VM window + self._cleanup_customize_window() + self._customize_window = vmmVMWindow(orig_vdomain, self.topwin) + self._customize_window.connect( + "customize-finished", customize_finished_cb) + self._customize_window.connect("closed", config_canceled_cb) + self._customize_window.show() + + def _install_finished_cb(self, error, details, guest, parentobj): + self.reset_finish_cursor(parentobj.topwin) + + if error: + error = (_("Unable to complete install: '%s'") % error) + parentobj.err.show_err(error, details=details) + self._failed_guest = guest + return + + foundvm = None + for vm in self.conn.list_vms(): + if vm.get_uuid() == guest.uuid: + foundvm = vm + break + + self._close() + + # Launch details dialog for new VM + vmmVMWindow.get_instance(self, foundvm).show() + + + def _start_install(self, guest): + """ + Launch the async job to start the install + """ + bootstrap_args = {} + # If creating new container and "container bootstrap" is enabled + if (guest.os.is_container() and + self._get_config_oscontainer_bootstrap()): + bootstrap_arg_keys = { + 'src': self._get_config_oscontainer_source_url, + 'dest': self.widget("install-oscontainer-fs").get_text, + 'user': self._get_config_oscontainer_source_username, + 'passwd': self._get_config_oscontainer_source_password, + 'insecure': self._get_config_oscontainer_isecure, + 'root_password': self._get_config_oscontainer_root_password, + } + for key, getter in bootstrap_arg_keys.items(): + bootstrap_args[key] = getter() + + parentobj = self._customize_window or self + progWin = vmmAsyncJob(self._do_async_install, [guest, bootstrap_args], + self._install_finished_cb, [guest, parentobj], + _("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."), + parentobj.topwin) + progWin.run() + + def _do_async_install(self, asyncjob, guest, bootstrap_args): + """ + Kick off the actual install + """ + meter = asyncjob.get_meter() + + if bootstrap_args: + # Start container bootstrap + self._create_directory_tree(asyncjob, meter, bootstrap_args) + if asyncjob.has_error(): + # Do not continue if virt-bootstrap failed + return + + # Build a list of pools we should refresh, if we are creating storage + refresh_pools = [] + for disk in guest.devices.disk: + if not disk.wants_storage_creation(): + continue + + pool = disk.get_parent_pool() + if not pool: + continue + + poolname = pool.name() + if poolname not in refresh_pools: + refresh_pools.append(poolname) + + log.debug("Starting background install process") + guest.installer_instance.start_install(guest, meter=meter) + log.debug("Install completed") + + # Wait for VM to show up + self.conn.schedule_priority_tick(pollvm=True) + count = 0 + foundvm = None + while count < 200: + for vm in self.conn.list_vms(): + if vm.get_uuid() == guest.uuid: + foundvm = vm + if foundvm: + break + count += 1 + time.sleep(.1) + + if not foundvm: + raise RuntimeError( + _("VM '%s' didn't show up after expected time.") % guest.name) + vm = foundvm + + 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_instance.has_install_phase(): + # Register a status listener, which will restart the + # guest after the install has finished + def cb(): + vm.connect_opt_out("state-changed", + self._check_install_status) + return False + self.idle_add(cb) + + # Kick off pool updates + for poolname in refresh_pools: + try: + pool = self.conn.get_pool(poolname) + self.idle_add(pool.refresh) + except Exception: + log.debug("Error looking up pool=%s for refresh after " + "VM creation.", poolname, exc_info=True) + + + def _check_install_status(self, vm): + """ + Watch the domain that we are installing, waiting for the state + to change, so we can restart it as needed + """ + if vm.is_crashed(): + log.debug("VM crashed, cancelling install plans.") + return True + + if not vm.is_shutoff(): + return + + if vm.get_install_abort(): + log.debug("User manually shutdown VM, not restarting " + "guest after install.") + return True + + try: + log.debug("Install should be completed, starting VM.") + vm.startup() + except Exception as e: + self.err.show_err(_("Error continue install: %s") % str(e)) + + return True + + + def _create_directory_tree(self, asyncjob, meter, bootstrap_args): + """ + Call bootstrap method from virtBootstrap and show logger messages + as state/details. + """ + import logging + import virtBootstrap + + meter.start(text=_("Bootstraping container"), size=100) + def progress_update_cb(prog): + meter.text = _(prog['status']) + meter.update(prog['value']) + + asyncjob.details_enable() + # Use logging filter to show messages of the progreess on the GUI + class SetStateFilter(logging.Filter): + def filter(self, record): + asyncjob.details_update("%s\n" % record.getMessage()) + return True + + # Use string buffer to store log messages + log_stream = io.StringIO() + + # Get virt-bootstrap logger + vbLogger = logging.getLogger('virtBootstrap') + vbLogger.setLevel(logging.DEBUG) + # Create handler to store log messages in the string buffer + hdlr = logging.StreamHandler(log_stream) + hdlr.setFormatter(logging.Formatter('%(message)s')) + # Use logging filter to show messages on GUI + hdlr.addFilter(SetStateFilter()) + vbLogger.addHandler(hdlr) + + # Key word arguments to be passed + kwargs = {'uri': bootstrap_args['src'], + 'dest': bootstrap_args['dest'], + 'not_secure': bootstrap_args['insecure'], + 'progress_cb': progress_update_cb} + if bootstrap_args['user'] and bootstrap_args['passwd']: + kwargs['username'] = bootstrap_args['user'] + kwargs['password'] = bootstrap_args['passwd'] + if bootstrap_args['root_password']: + kwargs['root_password'] = bootstrap_args['root_password'] + log.debug('Start container bootstrap') + try: + virtBootstrap.bootstrap(**kwargs) + # Success - uncheck the 'install-oscontainer-bootstrap' checkbox + + def cb(): + self.widget("install-oscontainer-bootstrap").set_active(False) + self.idle_add(cb) + except Exception as err: + asyncjob.set_error("virt-bootstrap did not complete successfully", + '%s\n%s' % (err, log_stream.getvalue())) diff --git a/virtManager/engine.py b/virtManager/engine.py index f87d9755..88fbd208 100644 --- a/virtManager/engine.py +++ b/virtManager/engine.py @@ -506,8 +506,8 @@ class vmmEngine(vmmGObject): manager.set_initial_selection(uri) manager.show() elif show_window == self.CLI_SHOW_DOMAIN_CREATOR: - from .create import vmmCreate - vmmCreate.show_instance(None, uri) + from .createvm import vmmCreateVM + vmmCreateVM.show_instance(None, uri) elif show_window == self.CLI_SHOW_HOST_SUMMARY: from .host import vmmHost vmmHost.show_instance(None, self._connobjs[uri]) diff --git a/virtManager/manager.py b/virtManager/manager.py index db806189..ab77a9e0 100644 --- a/virtManager/manager.py +++ b/virtManager/manager.py @@ -453,9 +453,9 @@ class vmmManager(vmmGObjectUI): vmmConnect.get_instance(self).show(self.topwin) def new_vm(self, _src): - from .create import vmmCreate + from .createvm import vmmCreateVM conn = self.current_conn() - vmmCreate.show_instance(self, conn and conn.get_uri() or None) + vmmCreateVM.show_instance(self, conn and conn.get_uri() or None) def show_about(self, _src): from .about import vmmAbout -- cgit v1.2.1