Break out shared storage creation UI to addstorage.py

This commit is contained in:
Cole Robinson 2014-01-28 16:53:11 -05:00
parent 8c5b1de33c
commit 00d5312bd6
8 changed files with 767 additions and 986 deletions

View File

@ -31,11 +31,6 @@
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="adjustment6">
<property name="upper">1000000</property>
<property name="step_increment">0.10000000000000001</property>
<property name="page_increment">10</property>
</object>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="can_focus">False</property>
@ -148,245 +143,15 @@
<property name="valign">start</property>
<property name="spacing">18</property>
<child>
<object class="GtkVBox" id="config-storage-box">
<object class="GtkAlignment" id="config-storage-align">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">4</property>
<child>
<object class="GtkHBox" id="hbox7">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="config-storage-create">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="xalign">0.5</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<child>
<object class="GtkLabel" id="label123">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">C_reate a disk image on the computer's hard drive</property>
<property name="use_markup">True</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">config-storage-create</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment15">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="bottom_padding">6</property>
<property name="left_padding">22</property>
<child>
<object class="GtkVBox" id="vbox18">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">4</property>
<child>
<object class="GtkHBox" id="hbox8">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkSpinButton" id="config-storage-size">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
<property name="width_chars">5</property>
<property name="xalign">1</property>
<property name="adjustment">adjustment6</property>
<property name="climb_rate">1</property>
<property name="digits">1</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label31">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_GB</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">config-storage-size</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="phys-hd-label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label">&lt;span color='#484848'&gt;Free Space&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox14">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="config-storage-nosparse">
<property name="label" translatable="yes">_Allocate entire disk now</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkImage" id="config-storage-nosparse-info">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-info</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox9">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkRadioButton" id="config-storage-select">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
<property name="group">config-storage-create</property>
<signal name="toggled" handler="on_config_storage_select_toggled" swapped="no"/>
<child>
<object class="GtkLabel" id="label124">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Select _managed or other existing storage</property>
<property name="use_markup">True</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">config-storage-select</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="config-storage-browse-box">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkButton" id="config-storage-browse">
<property name="label" translatable="yes">Bro_wse...</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_config_storage_browse_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="config-storage-entry">
<property name="width_request">335</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">●</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>

250
ui/addstorage.ui Normal file
View File

@ -0,0 +1,250 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<object class="GtkAdjustment" id="adjustment1">
<property name="upper">1000000</property>
<property name="step_increment">0.10000000000000001</property>
<property name="page_increment">10</property>
</object>
<object class="GtkVBox" id="config-storage-box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">4</property>
<child>
<object class="GtkHBox" id="hbox6">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="config-storage-create">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="xalign">0</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<child>
<object class="GtkLabel" id="label123">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">C_reate a disk image on the computer's hard drive</property>
<property name="use_markup">True</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">config-storage-create</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="bottom_padding">6</property>
<property name="left_padding">22</property>
<child>
<object class="GtkVBox" id="vbox18">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">4</property>
<child>
<object class="GtkHBox" id="hbox7">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkSpinButton" id="config-storage-size">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="width_chars">5</property>
<property name="text" translatable="yes">0.0</property>
<property name="xalign">1</property>
<property name="adjustment">adjustment1</property>
<property name="climb_rate">1</property>
<property name="digits">1</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label31">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_GB</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">config-storage-size</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="phys-hd-label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label">&lt;span color='#484848'&gt;Free Space&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox14">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="config-storage-nosparse">
<property name="label" translatable="yes">_Allocate entire disk now</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkImage" id="config-storage-nosparse-info">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Fully allocating storage may take longer now, but the OS install phase will be quicker.
Skipping allocation can also cause space issues on the host machine, if the maximum image size exceeds available storage space.
Tip: Storage format qcow2 and qed do not support full allocation.</property>
<property name="stock">gtk-info</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox9">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkRadioButton" id="config-storage-select">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
<property name="group">config-storage-create</property>
<signal name="toggled" handler="on_config_storage_select_toggled" swapped="no"/>
<child>
<object class="GtkLabel" id="label124">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Select _managed or other existing storage</property>
<property name="use_markup">True</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">config-storage-select</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="config-storage-browse-box">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkButton" id="config-storage-browse">
<property name="label" translatable="yes">Bro_wse...</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_config_storage_browse_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="config-storage-entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
</object>
</interface>

View File

@ -2,11 +2,6 @@
<!-- Generated with glade 3.16.1 -->
<interface>
<requires lib="gtk+" version="3.0"/>
<object class="GtkAdjustment" id="adjustment1">
<property name="upper">1000000</property>
<property name="step_increment">0.10000000000000001</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="adjustment2">
<property name="upper">128</property>
<property name="step_increment">1</property>
@ -2028,246 +2023,12 @@ is not yet supported.&lt;/small&gt;</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment3">
<object class="GtkAlignment" id="config-storage-align">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="left_padding">15</property>
<child>
<object class="GtkVBox" id="config-storage-box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">4</property>
<child>
<object class="GtkHBox" id="hbox6">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkRadioButton" id="config-storage-create">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="xalign">0.5</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<child>
<object class="GtkLabel" id="label123">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">C_reate a disk image on the computer's hard drive</property>
<property name="use_markup">True</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">config-storage-create</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="bottom_padding">6</property>
<property name="left_padding">22</property>
<child>
<object class="GtkVBox" id="vbox18">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">4</property>
<child>
<object class="GtkHBox" id="hbox7">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkSpinButton" id="config-storage-size">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="width_chars">5</property>
<property name="xalign">1</property>
<property name="adjustment">adjustment1</property>
<property name="climb_rate">1</property>
<property name="digits">1</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label31">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_GB</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">config-storage-size</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="phys-hd-label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label">&lt;span color='#484848'&gt;Free Space&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox14">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="config-storage-nosparse">
<property name="label" translatable="yes">_Allocate entire disk now</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="xalign">0.5</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkImage" id="config-storage-nosparse-info">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="stock">gtk-info</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox9">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkRadioButton" id="config-storage-select">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="xalign">0.5</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<property name="group">config-storage-create</property>
<signal name="toggled" handler="on_config_storage_select_toggled" swapped="no"/>
<child>
<object class="GtkLabel" id="label124">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Select _managed or other existing storage</property>
<property name="use_markup">True</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">config-storage-select</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="config-storage-browse-box">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkButton" id="config-storage-browse">
<property name="label" translatable="yes">Bro_wse...</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_config_storage_browse_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="config-storage-entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
</object>
<placeholder/>
</child>
</object>
<packing>

View File

@ -34,13 +34,13 @@ from virtinst import (VirtualChannelDevice, VirtualParallelDevice,
VirtualTPMDevice, VirtualPanicDevice)
from virtinst import VirtualController
from virtManager import sharedui
from virtManager import uiutil
from virtManager.fsdetails import vmmFSDetails
from virtManager.netlist import vmmNetworkList
from virtManager.asyncjob import vmmAsyncJob
from virtManager.storagebrowse import vmmStorageBrowser
from virtManager.baseclass import vmmGObjectUI
from virtManager.addstorage import vmmAddStorage
PAGE_ERROR = 0
PAGE_DISK = 1
@ -74,19 +74,23 @@ class vmmAddHardware(vmmGObjectUI):
self.fsDetails = vmmFSDetails(vm, self.builder, self.topwin)
self.widget("fs-box").add(self.fsDetails.top_box)
self.netlist = vmmNetworkList(self.conn, self.builder, self.topwin)
self.widget("network-source-label-align").add(self.netlist.top_label)
self.widget("network-source-ui-align").add(self.netlist.top_box)
self.widget("network-vport-align").add(self.netlist.top_vport)
self.addstorage = vmmAddStorage(self.conn, self.builder, self.topwin)
self.widget("config-storage-align").add(self.addstorage.top_box)
self.addstorage.connect("browse-clicked", self._browse_storage_cb)
self.addstorage.connect("storage-toggled", self.toggle_storage_select)
self.builder.connect_signals({
"on_create_cancel_clicked" : self.close,
"on_vmm_create_delete_event" : self.close,
"on_create_finish_clicked" : self.finish,
"on_hw_list_changed": self.hw_selected,
"on_config_storage_browse_clicked": self.browse_storage,
"on_config_storage_select_toggled": self.toggle_storage_select,
"on_config_storage_bustype_changed": self.populate_disk_device,
"on_mac_address_clicked" : self.change_macaddr_use,
@ -141,6 +145,8 @@ class vmmAddHardware(vmmGObjectUI):
self.fsDetails = None
self.netlist.cleanup()
self.netlist = None
self.addstorage.cleanup()
self.addstorage = None
def is_visible(self):
return self.topwin.get_visible()
@ -211,10 +217,6 @@ class vmmAddHardware(vmmGObjectUI):
# Disk format mode
self.populate_disk_format_combo_wrapper(True)
# Sparse tooltip
sparse_info = self.widget("config-storage-nosparse-info")
sharedui.set_sparse_tooltip(sparse_info)
# Input device type
input_list = self.widget("input-type")
input_model = Gtk.ListStore(str, str, str)
@ -392,23 +394,9 @@ class vmmAddHardware(vmmGObjectUI):
def reset_state(self):
# Storage init
label_widget = self.widget("phys-hd-label")
label_widget.set_markup("")
sharedui.update_host_space(self.conn, label_widget)
self.widget("config-storage-create").set_active(True)
self.widget("config-storage-size").set_value(8)
self.widget("config-storage-entry").set_text("")
fmt = self.conn.get_default_storage_format()
can_alloc = fmt in ["raw"]
self.widget("config-storage-nosparse").set_active(can_alloc)
self.widget("config-storage-nosparse").set_sensitive(can_alloc)
self.widget("config-storage-nosparse").set_tooltip_text(
not can_alloc and
(_("Disk format '%s' does not support full allocation.") % fmt) or
"")
self.populate_disk_format_combo_wrapper(True)
self.populate_disk_bus()
self.addstorage.reset_state()
# Network init
newmac = virtinst.VirtualNetworkInterface.generate_mac(
@ -765,7 +753,12 @@ class vmmAddHardware(vmmGObjectUI):
model.append([m])
if create:
fmt = vm.conn.get_default_storage_format()
combo.set_active(0)
for row in model:
if row[0] == fmt:
combo.set_active_iter(row.iter)
break
#########################
@ -868,6 +861,7 @@ class vmmAddHardware(vmmGObjectUI):
if not create:
format_list.get_child().set_text("")
########################
# get_config_* methods #
########################
@ -941,52 +935,6 @@ class vmmAddHardware(vmmGObjectUI):
return row[2]
# Disk getters
def is_default_storage(self):
return self.widget("config-storage-create").get_active()
def get_storage_info(self, collidelist):
path = None
size = self.widget("config-storage-size").get_value()
sparse = not self.widget("config-storage-nosparse").get_active()
if self.is_default_storage():
path = sharedui.get_default_path(self.conn,
self.vm.get_name(),
collidelist=collidelist)
logging.debug("Default storage path is: %s", path)
else:
path = self.widget("config-storage-entry").get_text()
return (path or None, size, sparse)
def check_ideal_path(self, diskpath, collidelist):
# See if the ideal disk path (/default/pool/vmname.img)
# exists, and if unused, prompt the use for using it
conn = self.conn.get_backend()
ideal = sharedui.get_ideal_path(self.conn, self.vm.get_name())
if ideal in collidelist:
return diskpath
do_exist = False
ret = True
try:
do_exist = virtinst.VirtualDisk.path_exists(conn, ideal)
ret = virtinst.VirtualDisk.path_in_use_by(conn, ideal)
except:
logging.exception("Error checking default path usage")
if not do_exist or ret:
return diskpath
do_use = self.err.yes_no(
_("The following storage already exists, but is not\n"
"in use by any virtual machine:\n\n%s\n\n"
"Would you like to reuse this storage?") % ideal)
if do_use:
return ideal
return diskpath
def get_config_disk_bus(self):
return uiutil.get_list_selection(
self.widget("config-storage-bustype"), 0)
@ -1195,17 +1143,10 @@ class vmmAddHardware(vmmGObjectUI):
# Storage listeners
def browse_storage(self, ignore1):
self._browse_file(self.widget("config-storage-entry"))
def toggle_storage_select(self, src):
def toggle_storage_select(self, ignore, src):
act = src.get_active()
self.widget("config-storage-browse-box").set_sensitive(act)
self.populate_disk_format_combo_wrapper(not act)
def set_disk_storage_path(self, ignore, path):
self.widget("config-storage-entry").set_text(path)
# Network listeners
def change_macaddr_use(self, ignore=None):
if self.widget("mac-address").get_active():
@ -1417,12 +1358,12 @@ class vmmAddHardware(vmmGObjectUI):
controller = getattr(self._dev, "vmm_controller", None)
if controller is not None:
logging.debug("Adding controller:\n%s",
self._dev.vmm_controller.get_xml_config())
controller.get_xml_config())
# Hotplug device
attach_err = False
try:
if controller is not None:
self.vm.attach_device(self._dev.vmm_controller)
self.vm.attach_device(controller)
self.vm.attach_device(self._dev)
except Exception, e:
logging.debug("Device could not be hotplugged: %s", str(e))
@ -1446,7 +1387,7 @@ class vmmAddHardware(vmmGObjectUI):
# Alter persistent config
try:
if controller is not None:
self.vm.add_device(self._dev.vmm_controller)
self.vm.add_device(controller)
self.vm.add_device(self._dev)
except Exception, e:
self.err.show_err(_("Error adding device: %s" % str(e)))
@ -1544,108 +1485,73 @@ class vmmAddHardware(vmmGObjectUI):
self._dev.validate()
return ret
def _set_disk_controller(self, disk, controller_model):
# Add a SCSI controller with model virtio-scsi if needed
disk.vmm_controller = None
if controller_model != "virtio-scsi":
return
controllers = self.vm.get_controller_devices()
ctrls_scsi = [x for x in controllers if
(x.type == VirtualController.TYPE_SCSI)]
if len(ctrls_scsi) > 0:
index_new = max([x.index for x in ctrls_scsi]) + 1
else:
index_new = 0
controller = VirtualController(self.conn.get_backend())
controller.type = "scsi"
controller.model = controller_model
disk.vmm_controller = controller
for d in controllers:
if controller.type == d.type:
controller.index = index_new
if controller_model == d.model:
disk.vmm_controller = None
controller = d
break
disk.address.type = disk.address.ADDRESS_TYPE_DRIVE
disk.address.controller = controller.index
def validate_page_storage(self):
bus = self.get_config_disk_bus()
device = self.get_config_disk_device()
cache = self.get_config_disk_cache()
fmt = self.get_config_disk_format()
controller_model = None
conn = self.conn.get_backend()
controller_model = None
if bus == "virtio-scsi":
bus = "scsi"
controller_model = "virtio-scsi"
# Make sure default pool is running
if self.is_default_storage():
ret = sharedui.check_default_pool_active(self.err, self.conn)
if not ret:
return False
readonly = False
if device == virtinst.VirtualDisk.DEVICE_CDROM:
readonly = True
collidelist = [d.path for d in self.vm.get_disk_devices()]
disk = self.addstorage.validate_storage(self.vm.get_name(),
collidelist=collidelist, device=device, fmt=fmt)
if disk in [True, False]:
return disk
try:
# This can error out
collidelist = [d.path for d in self.vm.get_disk_devices()]
diskpath, disksize, sparse = self.get_storage_info(collidelist)
if self.is_default_storage():
diskpath = self.check_ideal_path(diskpath, collidelist)
disk = virtinst.VirtualDisk(conn)
disk.path = diskpath
disk.read_only = readonly
disk.device = device
disk.bus = bus
disk.set_create_storage(size=disksize, sparse=sparse,
fmt=fmt or None)
if cache:
disk.driver_cache = cache
if not fmt:
fmt = self.conn.get_default_storage_format()
if (self.is_default_storage() and
disk.get_vol_install() and
fmt in disk.get_vol_install().list_formats()):
logging.debug("Setting disk format from prefs: %s", fmt)
disk.get_vol_install().format = fmt
# Generate target
if not self.is_customize_dialog:
used = []
disks = (self.vm.get_disk_devices() +
self.vm.get_disk_devices(inactive=True))
for d in disks:
used.append(d.target)
disk.generate_target(used)
self._set_disk_controller(disk, controller_model)
except Exception, e:
return self.err.val_err(_("Storage parameter error."), e)
# Generate target
if not self.is_customize_dialog:
used = []
disks = (self.vm.get_disk_devices() +
self.vm.get_disk_devices(inactive=True))
for d in disks:
used.append(d.target)
disk.generate_target(used)
isfatal, errmsg = disk.is_size_conflict()
if not isfatal and errmsg:
# Fatal errors are reported when setting 'size'
res = self.err.ok_cancel(_("Not Enough Free Space"), errmsg)
if not res:
return False
# Disk collision
names = disk.is_conflict_disk()
if names:
res = self.err.yes_no(
_('Disk "%s" is already in use by other guests %s') %
(disk.path, names),
_("Do you really want to use the disk?"))
if not res:
return False
sharedui.check_path_search_for_qemu(self.err, self.conn, disk.path)
# Add a SCSI controller with model virtio-scsi if needed
disk.vmm_controller = None
if (controller_model == "virtio-scsi") and (bus == "scsi"):
controllers = self.vm.get_controller_devices()
ctrls_scsi = [x for x in controllers if
(x.type == VirtualController.TYPE_SCSI)]
if len(ctrls_scsi) > 0:
index_new = max([x.index for x in ctrls_scsi]) + 1
else:
index_new = 0
controller = VirtualController(conn)
controller.type = "scsi"
controller.model = controller_model
disk.vmm_controller = controller
for d in controllers:
if controller.type == d.type:
controller.index = index_new
if controller_model == d.model:
disk.vmm_controller = None
controller = d
break
disk.address.type = disk.address.ADDRESS_TYPE_DRIVE
disk.address.controller = controller.index
if self.addstorage.validate_disk_object(disk) is False:
return False
self._dev = disk
return True
@ -1938,6 +1844,9 @@ class vmmAddHardware(vmmGObjectUI):
# Unsorted helpers #
####################
def _browse_storage_cb(self, ignore, widget):
self._browse_file(widget)
def _browse_file(self, textent, isdir=False):
def set_storage_cb(src, path):
if path:

395
virtManager/addstorage.py Normal file
View File

@ -0,0 +1,395 @@
#
# Copyright (C) 2014 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.
#
import logging
import os
import pwd
import statvfs
# pylint: disable=E0611
from gi.repository import GObject
from gi.repository import Gtk
# pylint: enable=E0611
import virtinst
from virtManager import uiutil
from virtManager.baseclass import vmmGObjectUI
class vmmAddStorage(vmmGObjectUI):
__gsignals__ = {
"browse-clicked": (GObject.SignalFlags.RUN_FIRST, None, [object]),
"storage-toggled": (GObject.SignalFlags.RUN_FIRST, None, [object])
}
def __init__(self, conn, builder, topwin):
vmmGObjectUI.__init__(self, "addstorage.ui", None,
builder=builder, topwin=topwin)
self.conn = conn
self.storage_browser = None
self.builder.connect_signals({
"on_config_storage_browse_clicked": self._browse_storage,
"on_config_storage_select_toggled": self._toggle_storage_select,
})
self.top_box = self.widget("config-storage-box")
def _cleanup(self):
self.conn = None
##########################
# Initialization methods #
##########################
def _get_default_dir(self):
pool = self.conn.get_default_pool()
if pool:
return pool.get_target_path()
return self.config.get_default_image_dir(self.conn)
def _get_ideal_path_info(self, name):
path = self._get_default_dir()
suffix = ".img"
return (path, name, suffix)
def _get_ideal_path(self, name):
target, name, suffix = self._get_ideal_path_info(name)
return os.path.join(target, name) + suffix
def _host_disk_space(self):
pool = self.conn.get_default_pool()
path = self._get_default_dir()
avail = 0
if pool and pool.is_active():
# FIXME: make sure not inactive?
# FIXME: use a conn specific function after we send pool-added
pool.refresh()
avail = int(pool.get_available())
elif not self.conn.is_remote() and os.path.exists(path):
vfs = os.statvfs(os.path.dirname(path))
avail = vfs[statvfs.F_FRSIZE] * vfs[statvfs.F_BAVAIL]
return float(avail / 1024.0 / 1024.0 / 1024.0)
def _update_host_space(self):
widget = self.widget("phys-hd-label")
try:
max_storage = self._host_disk_space()
except:
logging.exception("Error determining host disk space")
widget.set_markup("")
return
def pretty_storage(size):
return "%.1f GB" % float(size)
hd_label = ("%s available in the default location" %
pretty_storage(max_storage))
hd_label = ("<span color='#484848'>%s</span>" % hd_label)
widget.set_markup(hd_label)
def _is_default_storage(self):
return bool(self.widget("config-storage-create").get_active())
def _check_default_pool_active(self):
default_pool = self.conn.get_default_pool()
if default_pool and not default_pool.is_active():
res = self.err.yes_no(_("Default pool is not active."),
_("Storage pool '%s' is not active. "
"Would you like to start the pool "
"now?") % default_pool.get_name())
if not res:
return False
# Try to start the pool
try:
default_pool.start()
logging.info("Started pool '%s'", default_pool.get_name())
except Exception, e:
return self.err.show_err(_("Could not start storage_pool "
"'%s': %s") %
(default_pool.get_name(), str(e)))
return True
##############
# Public API #
##############
@staticmethod
def check_path_search_for_qemu(src, conn, path):
if conn.is_remote() or not conn.is_qemu_system():
return
user = src.config.default_qemu_user
for i in conn.caps.host.secmodels:
if i.model == "dac":
label = i.baselabels.get("kvm") or i.baselabels.get("qemu")
if not label:
continue
pwuid = pwd.getpwuid(int(label.split(":")[0].replace("+", "")))
if pwuid:
user = pwuid[0]
skip_paths = src.config.get_perms_fix_ignore()
broken_paths = virtinst.VirtualDisk.check_path_search_for_user(
conn.get_backend(), path, user)
for p in broken_paths:
if p in skip_paths:
broken_paths.remove(p)
if not broken_paths:
return
logging.debug("No search access for dirs: %s", broken_paths)
resp, chkres = src.err.warn_chkbox(
_("The emulator may not have search permissions "
"for the path '%s'.") % path,
_("Do you want to correct this now?"),
_("Don't ask about these directories again."),
buttons=Gtk.ButtonsType.YES_NO)
if chkres:
src.config.add_perms_fix_ignore(broken_paths)
if not resp:
return
logging.debug("Attempting to correct permission issues.")
errors = virtinst.VirtualDisk.fix_path_search_for_user(conn.get_backend(),
path, user)
if not errors:
return
errmsg = _("Errors were encountered changing permissions for the "
"following directories:")
details = ""
for path, error in errors.items():
if path not in broken_paths:
continue
details += "%s : %s\n" % (path, error)
logging.debug("Permission errors:\n%s", details)
ignore, chkres = src.err.err_chkbox(errmsg, details,
_("Don't ask about these directories again."))
if chkres:
src.config.add_perms_fix_ignore(errors.keys())
def reset_state(self):
self._update_host_space()
self.widget("config-storage-create").set_active(True)
self.widget("config-storage-size").set_value(8)
self.widget("config-storage-entry").set_text("")
self.widget("config-storage-nosparse").set_active(True)
fmt = self.conn.get_default_storage_format()
can_alloc = fmt in ["raw"]
self.widget("config-storage-nosparse").set_active(can_alloc)
self.widget("config-storage-nosparse").set_sensitive(can_alloc)
self.widget("config-storage-nosparse").set_tooltip_text(
not can_alloc and
(_("Disk format '%s' does not support full allocation.") % fmt) or
"")
storage_tooltip = None
can_storage = (not self.conn.is_remote() or
self.conn.is_storage_capable())
use_storage = self.widget("config-storage-select")
storage_area = self.widget("config-storage-box")
storage_area.set_sensitive(can_storage)
if not can_storage:
storage_tooltip = _("Connection does not support storage"
" management.")
use_storage.set_sensitive(True)
storage_area.set_tooltip_text(storage_tooltip or "")
def get_default_path(self, name, collidelist=None):
collidelist = collidelist or []
pool = self.conn.get_default_pool()
default_dir = self._get_default_dir()
def path_exists(p):
return os.path.exists(p) or p in collidelist
if not pool:
# Use old generating method
origf = os.path.join(default_dir, name + ".img")
f = origf
n = 1
while path_exists(f) and n < 100:
f = os.path.join(default_dir, name +
"-" + str(n) + ".img")
n += 1
if path_exists(f):
f = origf
path = f
else:
target, ignore, suffix = self._get_ideal_path_info(name)
# Sanitize collidelist to work with the collision checker
newcollidelist = []
for c in collidelist:
if c and os.path.dirname(c) == pool.get_target_path():
newcollidelist.append(os.path.basename(c))
path = virtinst.StorageVolume.find_free_name(
pool.get_backend(), name,
suffix=suffix, collidelist=newcollidelist)
path = os.path.join(target, path)
return path
def is_default_storage(self):
return self.widget("config-storage-create").get_active()
def _check_ideal_path(self, path, vmname, collidelist=None):
# See if the ideal disk path (/default/pool/vmname.img)
# exists, and if unused, prompt the use for using it
conn = self.conn.get_backend()
collidelist = collidelist or []
ideal = self._get_ideal_path(vmname)
if ideal in collidelist:
return path
do_exist = False
ret = True
try:
do_exist = virtinst.VirtualDisk.path_exists(conn, ideal)
ret = virtinst.VirtualDisk.path_in_use_by(conn, ideal)
except:
logging.exception("Error checking default path usage")
if not do_exist or ret:
return path
do_use = self.err.yes_no(
_("The following storage already exists, but is not\n"
"in use by any virtual machine:\n\n%s\n\n"
"Would you like to reuse this storage?") % ideal)
if do_use:
return ideal
return path
def validate_storage(self, vmname,
path=None, size=None, sparse=None,
device="disk", fmt=None, collidelist=None):
use_storage = self.widget("config-storage-box").is_sensitive()
is_default = self.is_default_storage()
conn = self.conn.get_backend()
# Validate storage
if not use_storage:
return True
# Make sure default pool is running
if is_default:
ret = self._check_default_pool_active()
if not ret:
return False
readonly = False
if device == virtinst.VirtualDisk.DEVICE_CDROM:
readonly = True
try:
if size is None and sparse is None:
size = uiutil.spin_get_helper(
self.widget("config-storage-size"))
sparse = (
not self.widget("config-storage-nosparse").get_active())
if path is None:
if is_default:
path = self.get_default_path(vmname)
else:
path = self.widget("config-storage-entry").get_text()
if is_default:
path = self._check_ideal_path(path, vmname, collidelist)
if not path and device != "disk":
return self.err.val_err(_("A storage path must be specified."))
disk = virtinst.VirtualDisk(conn)
disk.path = path
disk.read_only = readonly
disk.device = device
disk.set_create_storage(size=size, sparse=sparse,
fmt=fmt or None)
if not fmt:
fmt = self.conn.get_default_storage_format()
if (self.is_default_storage() and
disk.get_vol_install() and
fmt in disk.get_vol_install().list_formats()):
logging.debug("Setting disk format from prefs: %s", fmt)
disk.get_vol_install().format = fmt
disk.validate()
except Exception, e:
return self.err.val_err(_("Storage parameter error."), e)
return disk
def validate_disk_object(self, disk):
isfatal, errmsg = disk.is_size_conflict()
if not isfatal and errmsg:
# Fatal errors are reported when setting 'size'
res = self.err.ok_cancel(_("Not Enough Free Space"), errmsg)
if not res:
return False
# Disk collision
names = disk.is_conflict_disk()
if names:
res = self.err.yes_no(
_('Disk "%s" is already in use by other guests %s') %
(disk.path, names),
_("Do you really want to use the disk?"))
if not res:
return False
self.check_path_search_for_qemu(self, self.conn, disk.path)
#############
# Listeners #
#############
def _browse_storage(self, ignore):
self.emit("browse-clicked", self.widget("config-storage-entry"))
def _toggle_storage_select(self, src):
act = src.get_active()
self.widget("config-storage-browse-box").set_sensitive(act)
self.emit("storage-toggled", src)

View File

@ -24,11 +24,11 @@ import logging
from gi.repository import GObject
# pylint: enable=E0611
from virtManager import sharedui
from virtManager.baseclass import vmmGObjectUI
from virtManager.mediadev import MEDIA_FLOPPY
from virtManager.mediacombo import vmmMediaCombo
from virtManager.storagebrowse import vmmStorageBrowser
from virtManager.addstorage import vmmAddStorage
class vmmChooseCD(vmmGObjectUI):
@ -125,7 +125,7 @@ class vmmChooseCD(vmmGObjectUI):
if not res:
return False
sharedui.check_path_search_for_qemu(self.err, self.conn, path)
vmmAddStorage.check_path_search_for_qemu(self, self.conn, path)
self.emit("cdrom-chosen", self.disk, path)
self.close()

View File

@ -31,7 +31,6 @@ from gi.repository import Gdk
import virtinst
from virtinst import util
from virtManager import sharedui
from virtManager import uiutil
from virtManager.mediadev import MEDIA_CDROM
from virtManager.baseclass import vmmGObjectUI
@ -41,6 +40,7 @@ from virtManager.details import vmmDetails
from virtManager.domain import vmmDomainVirtinst
from virtManager.netlist import vmmNetworkList
from virtManager.mediacombo import vmmMediaCombo
from virtManager.addstorage import vmmAddStorage
# Number of seconds to wait for media detection
DETECT_TIMEOUT = 20
@ -112,6 +112,10 @@ class vmmCreate(vmmGObjectUI):
self.netlist = None
self.mediacombo = None
self.addstorage = vmmAddStorage(self.conn, self.builder, self.topwin)
self.widget("config-storage-align").add(self.addstorage.top_box)
self.addstorage.connect("browse-clicked", self._browse_file_cb)
self.builder.connect_signals({
"on_vmm_newcreate_delete_event" : self.close,
@ -147,8 +151,6 @@ class vmmCreate(vmmGObjectUI):
"on_config_dtb_browse_clicked": self.browse_dtb,
"on_enable_storage_toggled": self.toggle_enable_storage,
"on_config_storage_browse_clicked": self.browse_storage,
"on_config_storage_select_toggled": self.toggle_storage_select,
"on_config_set_macaddr_toggled": self.toggle_macaddr,
@ -207,6 +209,9 @@ class vmmCreate(vmmGObjectUI):
if self.mediacombo:
self.mediacombo.cleanup()
self.mediacombo = None
if self.addstorage:
self.addstorage.cleanup()
self.addstorage = None
def remove_conn(self):
if not self.conn:
@ -317,10 +322,6 @@ class vmmCreate(vmmGObjectUI):
uiutil.set_combo_text_column(lst, 0)
lst.set_row_separator_func(lambda m, i, ignore: m[i][0] is None, None)
# Sparse tooltip
sparse_info = self.widget("config-storage-nosparse-info")
sharedui.set_sparse_tooltip(sparse_info)
def reset_state(self, urihint=None):
self.failed_guest = None
self.have_startup_error = False
@ -388,23 +389,8 @@ class vmmCreate(vmmGObjectUI):
self.widget("config-cpus").set_value(1)
# Storage
label_widget = self.widget("phys-hd-label")
label_widget.set_markup("")
sharedui.update_host_space(self.conn, label_widget)
self.widget("enable-storage").set_active(True)
self.widget("config-storage-create").set_active(True)
self.widget("config-storage-size").set_value(8)
self.widget("config-storage-entry").set_text("")
self.widget("config-storage-nosparse").set_active(True)
fmt = self.conn.get_default_storage_format()
can_alloc = fmt in ["raw"]
self.widget("config-storage-nosparse").set_active(can_alloc)
self.widget("config-storage-nosparse").set_sensitive(can_alloc)
self.widget("config-storage-nosparse").set_tooltip_text(
not can_alloc and
(_("Disk format '%s' does not support full allocation.") % fmt) or
"")
self.addstorage.reset_state()
# Final page
self.widget("summary-customize").set_active(False)
@ -573,8 +559,6 @@ class vmmCreate(vmmGObjectUI):
# Only allow ISO option for remote VM
is_local = not self.conn.is_remote()
is_storage_capable = self.conn.is_storage_capable()
can_storage = (is_local or is_storage_capable)
if not is_local:
iso_option.set_active(True)
@ -603,17 +587,8 @@ class vmmCreate(vmmGObjectUI):
self.widget("phys-cpu-label").set_markup(cpu_label)
# Storage
storage_tooltip = None
use_storage = self.widget("config-storage-select")
storage_area = self.widget("config-storage-area")
storage_area.set_sensitive(can_storage)
if not can_storage:
storage_tooltip = _("Connection does not support storage"
" management.")
use_storage.set_sensitive(True)
storage_area.set_tooltip_text(storage_tooltip or "")
self.addstorage.conn = self.conn
self.addstorage.reset_state()
# Networking
newmac = virtinst.VirtualNetworkInterface.generate_mac(
@ -1060,31 +1035,13 @@ class vmmCreate(vmmGObjectUI):
if disks:
return disks[0].path
return sharedui.get_default_path(self.conn, name)
return self.addstorage.get_default_path(name)
def is_default_storage(self):
usedef = self.widget("config-storage-create").get_active()
usedef = self.addstorage.is_default_storage()
isimport = (self.get_config_install_page() == INSTALL_PAGE_IMPORT)
return usedef and not isimport
def get_storage_info(self):
path = None
size = uiutil.spin_get_helper(self.widget("config-storage-size"))
sparse = not self.widget("config-storage-nosparse").get_active()
if self.get_config_install_page() == INSTALL_PAGE_IMPORT:
path = self.get_config_import_path()
size = None
sparse = False
elif self.is_default_storage():
path = self.get_default_path(self.guest.name)
logging.debug("Default storage path is: %s", path)
else:
path = self.widget("config-storage-entry").get_text()
return (path, size, sparse)
def get_config_customize(self):
return self.widget("summary-customize").get_active()
def is_detect_active(self):
@ -1263,8 +1220,6 @@ class vmmCreate(vmmGObjectUI):
self.widget("install-local-box").get_child().set_text(path)
self._browse_file(None, cb=set_path, is_media=True)
self.widget("install-local-box").activate()
def browse_storage(self, ignore):
self._browse_file("config-storage-entry")
def browse_kernel(self, ignore):
self._browse_file("config-kernel")
def browse_initrd(self, ignore):
@ -1273,12 +1228,7 @@ class vmmCreate(vmmGObjectUI):
self._browse_file("config-dtb")
def toggle_enable_storage(self, src):
self.widget("config-storage-box").set_sensitive(src.get_active())
def toggle_storage_select(self, src):
act = src.get_active()
self.widget("config-storage-browse-box").set_sensitive(act)
self.widget("config-storage-align").set_sensitive(src.get_active())
def toggle_macaddr(self, src):
self.widget("config-macaddr").set_sensitive(src.get_active())
@ -1661,7 +1611,8 @@ class vmmCreate(vmmGObjectUI):
path = None
if path:
sharedui.check_path_search_for_qemu(self.err, self.conn, path)
self.addstorage.check_path_search_for_qemu(
self, self.conn, path)
# Validation passed, store the install path (if there is one) in
# gconf
@ -1689,93 +1640,37 @@ class vmmCreate(vmmGObjectUI):
return True
def validate_storage_page(self):
use_storage = self.widget("enable-storage").get_active()
instcd = self.get_config_install_page() == INSTALL_PAGE_ISO
conn = self.conn.get_backend()
# CD/ISO install and no disks implies LiveCD
if instcd:
self.guest.installer.livecd = not use_storage
if self.disk and self.disk in self.guest.get_devices("disk"):
self.guest.remove_device(self.disk)
self.disk = None
# Validate storage
if not use_storage:
return True
path = None
size = None
sparse = None
# Make sure default pool is running
if self.is_default_storage():
ret = sharedui.check_default_pool_active(self.err, self.conn)
if not ret:
return False
if self.get_config_install_page() == INSTALL_PAGE_IMPORT:
path = self.get_config_import_path()
size = None
sparse = False
elif self.is_default_storage():
path = self.get_default_path(self.guest.name)
logging.debug("Default storage path is: %s", path)
try:
# This can error out
diskpath, disksize, sparse = self.get_storage_info()
ret = self.addstorage.validate_storage(
self.guest.name, path=path, size=size, sparse=sparse)
no_storage = (ret is True)
if self.is_default_storage():
# See if the ideal disk path (/default/pool/vmname.img)
# exists, and if unused, prompt the use for using it
ideal = sharedui.get_ideal_path(self.conn,
self.guest.name)
do_exist = False
ret = True
if self.get_config_install_page() == INSTALL_PAGE_ISO:
# CD/ISO install and no disks implies LiveCD
self.guest.installer.livecd = no_storage
try:
do_exist = virtinst.VirtualDisk.path_exists(conn, ideal)
ret = virtinst.VirtualDisk.path_in_use_by(conn, ideal)
except:
logging.exception("Error checking default path usage")
if ret in [True, False]:
return ret
if do_exist and not ret:
do_use = self.err.yes_no(
_("The following storage already exists, but is not\n"
"in use by any virtual machine:\n\n%s\n\n"
"Would you like to reuse this storage?") % ideal)
if self.addstorage.validate_disk_object(ret) is False:
return False
if do_use:
diskpath = ideal
if not diskpath:
return self.err.val_err(_("A storage path must be specified."))
disk = virtinst.VirtualDisk(conn)
disk.path = diskpath
disk.set_create_storage(size=disksize, sparse=sparse)
fmt = self.conn.get_default_storage_format()
if (self.is_default_storage() and
disk.get_vol_install() and
fmt in disk.get_vol_install().list_formats()):
logging.debug("Setting disk format from prefs: %s", fmt)
disk.get_vol_install().format = fmt
disk.validate()
except Exception, e:
return self.err.val_err(_("Storage parameter error."), e)
isfatal, errmsg = disk.is_size_conflict()
if not isfatal and errmsg:
# Fatal errors are reported when setting 'size'
res = self.err.ok_cancel(_("Not Enough Free Space"), errmsg)
if not res:
return False
# Disk collision
names = disk.is_conflict_disk()
if names:
res = self.err.yes_no(
_('Disk "%s" is already in use by other guests %s') %
(disk.path, names),
_("Do you really want to use the disk?"))
if not res:
return False
sharedui.check_path_search_for_qemu(self.err, self.conn, disk.path)
self.disk = disk
self.disk = ret
self.guest.add_device(self.disk)
return True
@ -2105,6 +2000,9 @@ class vmmCreate(vmmGObjectUI):
logging.exception("Error detecting distro.")
self.detectedDistro = -1
def _browse_file_cb(self, ignore, widget):
self._browse_file(widget)
def _stable_defaults(self):
emu = None
if self.guest:
@ -2127,7 +2025,10 @@ class vmmCreate(vmmGObjectUI):
callback = cb
else:
def callback(ignore, text):
self.widget(cbwidget).set_text(text)
widget = cbwidget
if type(cbwidget) is str:
widget = self.widget(cbwidget)
widget.set_text(text)
if self.storage_browser is None:
self.storage_browser = vmmStorageBrowser(self.conn)

View File

@ -18,210 +18,10 @@
# MA 02110-1301 USA.
#
import logging
import os
import statvfs
import pwd
# pylint: disable=E0611
from gi.repository import Gtk
# pylint: enable=E0611
import virtinst
from virtManager import config
############################################################
# Helpers for shared storage UI between create/addhardware #
############################################################
def set_sparse_tooltip(widget):
sparse_str = _("Fully allocating storage may take longer now, "
"but the OS install phase will be quicker. \n\n"
"Skipping allocation can also cause space issues on "
"the host machine, if the maximum image size exceeds "
"available storage space. \n\n"
"Tip: Storage format qcow2 and qed "
"do not support full allocation.")
widget.set_tooltip_text(sparse_str)
def _get_default_dir(conn):
pool = conn.get_default_pool()
if pool:
return pool.get_target_path()
return config.running_config.get_default_image_dir(conn)
def host_disk_space(conn):
pool = conn.get_default_pool()
path = _get_default_dir(conn)
avail = 0
if pool and pool.is_active():
# FIXME: make sure not inactive?
# FIXME: use a conn specific function after we send pool-added
pool.refresh()
avail = int(pool.get_available())
elif not conn.is_remote() and os.path.exists(path):
vfs = os.statvfs(os.path.dirname(path))
avail = vfs[statvfs.F_FRSIZE] * vfs[statvfs.F_BAVAIL]
return float(avail / 1024.0 / 1024.0 / 1024.0)
def update_host_space(conn, widget):
try:
max_storage = host_disk_space(conn)
except:
logging.exception("Error determining host disk space")
return
def pretty_storage(size):
return "%.1f GB" % float(size)
hd_label = ("%s available in the default location" %
pretty_storage(max_storage))
hd_label = ("<span color='#484848'>%s</span>" % hd_label)
widget.set_markup(hd_label)
def check_default_pool_active(err, conn):
default_pool = conn.get_default_pool()
if default_pool and not default_pool.is_active():
res = err.yes_no(_("Default pool is not active."),
_("Storage pool '%s' is not active. "
"Would you like to start the pool "
"now?") % default_pool.get_name())
if not res:
return False
# Try to start the pool
try:
default_pool.start()
logging.info("Started pool '%s'", default_pool.get_name())
except Exception, e:
return err.show_err(_("Could not start storage_pool "
"'%s': %s") %
(default_pool.get_name(), str(e)))
return True
def _get_ideal_path_info(conn, name):
path = _get_default_dir(conn)
suffix = ".img"
return (path, name, suffix)
def get_ideal_path(conn, name):
target, name, suffix = _get_ideal_path_info(conn, name)
return os.path.join(target, name) + suffix
def get_default_path(conn, name, collidelist=None):
collidelist = collidelist or []
pool = conn.get_default_pool()
default_dir = _get_default_dir(conn)
def path_exists(p):
return os.path.exists(p) or p in collidelist
if not pool:
# Use old generating method
origf = os.path.join(default_dir, name + ".img")
f = origf
n = 1
while path_exists(f) and n < 100:
f = os.path.join(default_dir, name +
"-" + str(n) + ".img")
n += 1
if path_exists(f):
f = origf
path = f
else:
target, ignore, suffix = _get_ideal_path_info(conn, name)
# Sanitize collidelist to work with the collision checker
newcollidelist = []
for c in collidelist:
if c and os.path.dirname(c) == pool.get_target_path():
newcollidelist.append(os.path.basename(c))
path = virtinst.StorageVolume.find_free_name(
pool.get_backend(), name,
suffix=suffix, collidelist=newcollidelist)
path = os.path.join(target, path)
return path
def check_path_search_for_qemu(err, conn, path):
if conn.is_remote() or not conn.is_qemu_system():
return
user = config.running_config.default_qemu_user
for i in conn.caps.host.secmodels:
if i.model == "dac":
label = i.baselabels.get("kvm") or i.baselabels.get("qemu")
if not label:
continue
pwuid = pwd.getpwuid(int(label.split(":")[0].replace("+", "")))
if pwuid:
user = pwuid[0]
skip_paths = config.running_config.get_perms_fix_ignore()
broken_paths = virtinst.VirtualDisk.check_path_search_for_user(
conn.get_backend(),
path, user)
for p in broken_paths:
if p in skip_paths:
broken_paths.remove(p)
if not broken_paths:
return
logging.debug("No search access for dirs: %s", broken_paths)
resp, chkres = err.warn_chkbox(
_("The emulator may not have search permissions "
"for the path '%s'.") % path,
_("Do you want to correct this now?"),
_("Don't ask about these directories again."),
buttons=Gtk.ButtonsType.YES_NO)
if chkres:
config.running_config.add_perms_fix_ignore(broken_paths)
if not resp:
return
logging.debug("Attempting to correct permission issues.")
errors = virtinst.VirtualDisk.fix_path_search_for_user(conn.get_backend(),
path, user)
if not errors:
return
errmsg = _("Errors were encountered changing permissions for the "
"following directories:")
details = ""
for path, error in errors.items():
if path not in broken_paths:
continue
details += "%s : %s\n" % (path, error)
logging.debug("Permission errors:\n%s", details)
ignore, chkres = err.err_chkbox(errmsg, details,
_("Don't ask about these directories again."))
if chkres:
config.running_config.add_perms_fix_ignore(errors.keys())
####################################################################
# Build toolbar shutdown button menu (manager and details toolbar) #