Break out shared storage creation UI to addstorage.py
This commit is contained in:
parent
8c5b1de33c
commit
00d5312bd6
|
@ -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"><span color='#484848'>Free Space</span></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>
|
||||
|
|
|
@ -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"><span color='#484848'>Free Space</span></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>
|
243
ui/create.ui
243
ui/create.ui
|
@ -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.</small></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"><span color='#484848'>Free Space</span></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>
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) #
|
||||
|
|
Loading…
Reference in New Issue