mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-02-25 18:55:27 -06:00
Break out shared storage UI to storagelist.py (bz 1060433)
We were already sharing a chunk of this in a haphazard way. Now officially break it all out, similar to netlist.py. This mostly unifies the views of host->storage and storagebrowser.py
This commit is contained in:
@@ -1408,6 +1408,19 @@ ba</description>
|
||||
</permissions>
|
||||
</target>
|
||||
</volume>
|
||||
<volume type='file'>
|
||||
<name>dir-vol</name>
|
||||
<capacity>1000000</capacity>
|
||||
<allocation>50000</allocation>
|
||||
<target>
|
||||
<format type='dir'/>
|
||||
<permissions>
|
||||
<mode>0700</mode>
|
||||
<owner>10736</owner>
|
||||
<group>10736</group>
|
||||
</permissions>
|
||||
</target>
|
||||
</volume>
|
||||
<volume type='file'>
|
||||
<name>iso-vol</name>
|
||||
<capacity>1000000</capacity>
|
||||
|
||||
672
ui/host.ui
672
ui/host.ui
File diff suppressed because it is too large
Load Diff
@@ -2,19 +2,9 @@
|
||||
<!-- Generated with glade 3.18.3 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.8"/>
|
||||
<object class="GtkImage" id="image1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-new</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="image2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-open</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="vmm-storage-browse">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">12</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="title" translatable="yes">Choose Storage Volume</property>
|
||||
<property name="default_width">750</property>
|
||||
<property name="default_height">500</property>
|
||||
@@ -22,265 +12,11 @@
|
||||
<property name="type_hint">dialog</property>
|
||||
<signal name="delete-event" handler="on_vmm_storage_browse_delete_event" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkBox" id="box2">
|
||||
<object class="GtkAlignment" id="storage-align">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">10</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow1">
|
||||
<property name="width_request">150</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="pool-list">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection"/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes"><b>Volumes</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="pool-refresh">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_pool_refresh_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-refresh</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="vol-delete">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_vol_delete_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-delete</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="vol-list">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<signal name="row-activated" handler="on_vol_list_row_activated" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection1">
|
||||
<signal name="changed" handler="on_vol_list_changed" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</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">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="GtkBox" id="box3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="vexpand">False</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="buttonbox3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">start</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="browse-local">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<signal name="clicked" handler="on_browse_local_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="top_padding">2</property>
|
||||
<property name="bottom_padding">2</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Browse Local</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">browse-local</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</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="GtkButtonBox" id="buttonbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="layout_style">start</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="new-volume">
|
||||
<property name="label" translatable="yes">_New Volume</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="image">image1</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="clicked" handler="on_new_volume_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
<property name="non_homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="browse-cancel">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_browse_cancel_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="non_homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="choose-volume">
|
||||
<property name="label" translatable="yes">Choose _Volume</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="image">image2</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="clicked" handler="on_choose_volume_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
<property name="non_homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</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">1</property>
|
||||
</packing>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
634
ui/storagelist.ui
Normal file
634
ui/storagelist.ui
Normal file
@@ -0,0 +1,634 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.18.3 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.8"/>
|
||||
<object class="GtkImage" id="image3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-apply</property>
|
||||
</object>
|
||||
<object class="GtkGrid" id="storage-grid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">3</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox9">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">end</property>
|
||||
<property name="border_width">3</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox10">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="pool-add">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Add Pool</property>
|
||||
<signal name="clicked" handler="on_pool_add_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-add</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="pool-start">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Start Pool</property>
|
||||
<signal name="clicked" handler="on_pool_start_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="someicon">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-media-play</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="pool-stop">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Stop Pool</property>
|
||||
<signal name="clicked" handler="on_pool_stop_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-cancel</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="pool-delete">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Delete Pool</property>
|
||||
<signal name="clicked" handler="on_pool_delete_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-delete</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">3</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="GtkHBox" id="hbox11">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="browse-local">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Browse local filesystem</property>
|
||||
<signal name="clicked" handler="on_browse_local_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="top_padding">2</property>
|
||||
<property name="bottom_padding">2</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Browse Local</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="browse-cancel">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Cancel and close dialog</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_browse_cancel_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="choose-volume">
|
||||
<property name="label" translatable="yes">Choose Volume</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Choose the selected volume</property>
|
||||
<property name="image">image3</property>
|
||||
<signal name="clicked" handler="on_choose_volume_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="pool-apply">
|
||||
<property name="label">gtk-apply</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Apply pool changes</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_pool_apply_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned" id="storage-pane">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="border_width">3</property>
|
||||
<property name="position">200</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="pool-list">
|
||||
<property name="width_request">134</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_visible">False</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection"/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkNotebook" id="storage-pages">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="show_border">False</property>
|
||||
<child>
|
||||
<object class="GtkAlignment" id="alignment13">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">6</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="pool-details">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="table5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">5</property>
|
||||
<property name="column_spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label73">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Pool Type:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="pool-type">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">Pool Type</property>
|
||||
<property name="selectable">True</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="pool-name">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label"><b>Poolname:</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="pool-sizes">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label"><span size="large">1234 GiB Free</span> / <i>6000 GiB In Use</i></property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="pool-state-box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">3</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="pool-state-icon">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="stock">gtk-missing-image</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="pool-state">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Active</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="pool-autostart">
|
||||
<property name="label">Some label</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>
|
||||
<signal name="toggled" handler="on_pool_autostart_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="pool-location">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">label</property>
|
||||
<property name="selectable">True</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="pool-name-entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<signal name="changed" handler="on_pool_name_changed" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label11">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Name:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label60">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Location:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label173">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">State:</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label76">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">A_utostart:</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="mnemonic_widget">pool-autostart</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">3</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox12">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label77">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes"><b>Volumes</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="vol-add">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="clicked" handler="on_vol_add_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-add</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="pool-refresh">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="has_tooltip">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Refresh volume list</property>
|
||||
<signal name="clicked" handler="on_pool_refresh_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image8">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-refresh</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="vol-delete">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Delete volume</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="clicked" handler="on_vol_delete_clicked" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="image2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-delete</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</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">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="vol-list">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<signal name="button-press-event" handler="on_vol_list_button_press_event" swapped="no"/>
|
||||
<signal name="row-activated" handler="on_vol_list_row_activated" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection1">
|
||||
<signal name="changed" handler="on_vol_list_changed" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</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">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label123">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">info</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="storage-error-label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">some error here</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="label24">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">error</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -2025,9 +2025,8 @@ class vmmAddHardware(vmmGObjectUI):
|
||||
if self.storage_browser is None:
|
||||
self.storage_browser = vmmStorageBrowser(conn)
|
||||
|
||||
self.storage_browser.stable_defaults = self.vm.stable_defaults()
|
||||
|
||||
self.storage_browser.set_stable_defaults(self.vm.stable_defaults())
|
||||
self.storage_browser.set_finish_cb(set_storage_cb)
|
||||
self.storage_browser.set_browse_reason(reason)
|
||||
|
||||
self.storage_browser.show(self.topwin, conn)
|
||||
self.storage_browser.show(self.topwin)
|
||||
|
||||
@@ -150,7 +150,7 @@ class vmmChooseCD(vmmGObjectUI):
|
||||
self.storage_browser = vmmStorageBrowser(self.conn)
|
||||
self.storage_browser.set_finish_cb(self.set_storage_path)
|
||||
|
||||
self.storage_browser.stable_defaults = self.vm.stable_defaults()
|
||||
self.storage_browser.set_stable_defaults(self.vm.stable_defaults())
|
||||
|
||||
if self.media_type == vmmMediaCombo.MEDIA_FLOPPY:
|
||||
self.storage_browser.set_browse_reason(
|
||||
@@ -158,4 +158,4 @@ class vmmChooseCD(vmmGObjectUI):
|
||||
else:
|
||||
self.storage_browser.set_browse_reason(
|
||||
self.config.CONFIG_DIR_ISO_MEDIA)
|
||||
self.storage_browser.show(self.topwin, self.conn)
|
||||
self.storage_browser.show(self.topwin)
|
||||
|
||||
@@ -832,8 +832,11 @@ class vmmCloneVM(vmmGObjectUI):
|
||||
def callback(src_ignore, txt):
|
||||
self.widget("change-storage-new").set_text(txt)
|
||||
|
||||
if self.storage_browser and self.storage_browser.conn != self.conn:
|
||||
self.storage_browser.cleanup()
|
||||
self.storage_browser = None
|
||||
if self.storage_browser is None:
|
||||
self.storage_browser = vmmStorageBrowser(self.conn)
|
||||
self.storage_browser.set_finish_cb(callback)
|
||||
|
||||
self.storage_browser.show(self.topwin, self.conn)
|
||||
self.storage_browser.show(self.topwin)
|
||||
|
||||
@@ -2087,12 +2087,14 @@ class vmmCreate(vmmGObjectUI):
|
||||
widget = self.widget(cbwidget)
|
||||
widget.set_text(text)
|
||||
|
||||
if self.storage_browser and self.storage_browser.conn != self.conn:
|
||||
self.storage_browser.cleanup()
|
||||
self.storage_browser = None
|
||||
if self.storage_browser is None:
|
||||
self.storage_browser = vmmStorageBrowser(self.conn)
|
||||
|
||||
self.storage_browser.stable_defaults = self._stable_defaults()
|
||||
|
||||
self.storage_browser.set_stable_defaults(self._stable_defaults())
|
||||
self.storage_browser.set_vm_name(self.get_config_name())
|
||||
self.storage_browser.set_finish_cb(callback)
|
||||
self.storage_browser.set_browse_reason(reason)
|
||||
self.storage_browser.show(self.topwin, self.conn)
|
||||
self.storage_browser.show(self.topwin)
|
||||
|
||||
@@ -337,6 +337,10 @@ class vmmCreateVolume(vmmGObjectUI):
|
||||
return self.err.val_err(info, details, modal=self.topwin.get_modal())
|
||||
|
||||
def _browse_file(self):
|
||||
if self.storage_browser and self.storage_browser.conn != self.conn:
|
||||
self.storage_browser.cleanup()
|
||||
self.storage_browser = None
|
||||
|
||||
if self.storage_browser is None:
|
||||
def cb(src, text):
|
||||
ignore = src
|
||||
@@ -346,8 +350,7 @@ class vmmCreateVolume(vmmGObjectUI):
|
||||
self.storage_browser = vmmStorageBrowser(self.conn)
|
||||
self.storage_browser.set_finish_cb(cb)
|
||||
self.storage_browser.topwin.set_modal(self.topwin.get_modal())
|
||||
self.storage_browser.can_new_volume = False
|
||||
self.storage_browser.set_browse_reason(
|
||||
self.config.CONFIG_DIR_IMAGE)
|
||||
|
||||
self.storage_browser.show(self.topwin, self.conn)
|
||||
self.storage_browser.show(self.topwin)
|
||||
|
||||
@@ -1589,7 +1589,7 @@ class vmmDetails(vmmGObjectUI):
|
||||
|
||||
self.storage_browser.set_finish_cb(callback)
|
||||
self.storage_browser.set_browse_reason(reason)
|
||||
self.storage_browser.show(self.topwin, self.conn)
|
||||
self.storage_browser.show(self.topwin)
|
||||
|
||||
def boot_kernel_toggled(self, src):
|
||||
self.widget("boot-kernel-box").set_sensitive(src.get_active())
|
||||
|
||||
@@ -54,19 +54,27 @@ class vmmErrorDialog(vmmGObject):
|
||||
self._parent = parent
|
||||
self._simple = None
|
||||
|
||||
# Allows the error owner to easily override default modality
|
||||
self._modal_default = False
|
||||
|
||||
def _cleanup(self):
|
||||
pass
|
||||
|
||||
def set_modal_default(self, val):
|
||||
self._modal_default = val
|
||||
def set_parent(self, parent):
|
||||
self._parent = parent
|
||||
def get_parent(self):
|
||||
return self._parent
|
||||
|
||||
def show_err(self, summary, details=None, title="",
|
||||
modal=False, debug=True,
|
||||
modal=None, debug=True,
|
||||
dialog_type=Gtk.MessageType.ERROR,
|
||||
buttons=Gtk.ButtonsType.CLOSE,
|
||||
text2=None):
|
||||
if modal is None:
|
||||
modal = self._modal_default
|
||||
|
||||
if details is None:
|
||||
details = summary
|
||||
tb = "".join(traceback.format_exc()).strip()
|
||||
@@ -93,6 +101,7 @@ class vmmErrorDialog(vmmGObject):
|
||||
details=details, title=title,
|
||||
modal=modal)
|
||||
|
||||
|
||||
###################################
|
||||
# Simple one shot message dialogs #
|
||||
###################################
|
||||
|
||||
@@ -342,16 +342,18 @@ class vmmFSDetails(vmmGObjectUI):
|
||||
if path:
|
||||
textent.set_text(path)
|
||||
|
||||
conn = self.conn
|
||||
reason = (isdir and
|
||||
self.config.CONFIG_DIR_FS or
|
||||
self.config.CONFIG_DIR_IMAGE)
|
||||
|
||||
if self.storage_browser and self.storage_browser.conn != self.conn:
|
||||
self.storage_browser.cleanup()
|
||||
self.storage_browser = None
|
||||
if self.storage_browser is None:
|
||||
self.storage_browser = vmmStorageBrowser(conn)
|
||||
|
||||
self.storage_browser.stable_defaults = self.vm.stable_defaults()
|
||||
self.storage_browser = vmmStorageBrowser(self.conn)
|
||||
|
||||
self.storage_browser.set_stable_defaults(self.vm.stable_defaults())
|
||||
self.storage_browser.set_finish_cb(set_storage_cb)
|
||||
self.storage_browser.set_browse_reason(reason)
|
||||
|
||||
self.storage_browser.show(self.topwin.get_ancestor(Gtk.Window), conn)
|
||||
self.storage_browser.show(self.topwin.get_ancestor(Gtk.Window))
|
||||
|
||||
@@ -22,20 +22,16 @@ import logging
|
||||
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
|
||||
from virtinst import VirtualDisk
|
||||
from virtinst import StoragePool
|
||||
from virtinst import Interface
|
||||
|
||||
from . import uiutil
|
||||
from .asyncjob import vmmAsyncJob
|
||||
from .createnet import vmmCreateNetwork
|
||||
from .createpool import vmmCreatePool
|
||||
from .createvol import vmmCreateVolume
|
||||
from .createinterface import vmmCreateInterface
|
||||
from .baseclass import vmmGObjectUI
|
||||
from .createnet import vmmCreateNetwork
|
||||
from .createinterface import vmmCreateInterface
|
||||
from .graphwidgets import Sparkline
|
||||
from .storagelist import vmmStorageList
|
||||
|
||||
INTERFACE_PAGE_INFO = 0
|
||||
INTERFACE_PAGE_ERROR = 1
|
||||
@@ -46,11 +42,6 @@ EDIT_NET_AUTOSTART,
|
||||
EDIT_NET_QOS,
|
||||
) = range(3)
|
||||
|
||||
EDIT_POOL_IDS = (
|
||||
EDIT_POOL_NAME,
|
||||
EDIT_POOL_AUTOSTART,
|
||||
) = range(100, 102)
|
||||
|
||||
EDIT_INTERFACE_IDS = (
|
||||
EDIT_INTERFACE_STARTMODE,
|
||||
) = range(200, 201)
|
||||
@@ -76,11 +67,7 @@ class vmmHost(vmmGObjectUI):
|
||||
self.ICON_SHUTOFF = "state_shutoff"
|
||||
|
||||
self.addnet = None
|
||||
self.addpool = None
|
||||
self.addvol = None
|
||||
self.addinterface = None
|
||||
self.volmenu = None
|
||||
self._in_refresh = False
|
||||
|
||||
self.active_edits = []
|
||||
|
||||
@@ -88,8 +75,9 @@ class vmmHost(vmmGObjectUI):
|
||||
self.memory_usage_graph = None
|
||||
self.init_conn_state()
|
||||
|
||||
self.init_net_state()
|
||||
self.storagelist = None
|
||||
self.init_storage_state()
|
||||
self.init_net_state()
|
||||
self.init_interface_state()
|
||||
|
||||
self.builder.connect_signals({
|
||||
@@ -111,20 +99,6 @@ class vmmHost(vmmGObjectUI):
|
||||
"on_net_name_changed": (lambda *x:
|
||||
self.enable_net_apply(x, EDIT_NET_NAME)),
|
||||
|
||||
"on_pool_add_clicked" : self.add_pool,
|
||||
"on_vol_add_clicked" : self.add_vol,
|
||||
"on_pool_stop_clicked": self.stop_pool,
|
||||
"on_pool_start_clicked": self.start_pool,
|
||||
"on_pool_delete_clicked": self.delete_pool,
|
||||
"on_pool_refresh_clicked": self.pool_refresh,
|
||||
"on_pool_autostart_toggled": self.pool_autostart_changed,
|
||||
"on_vol_delete_clicked": self.delete_vol,
|
||||
"on_vol_list_button_press_event": self.popup_vol_menu,
|
||||
"on_pool_apply_clicked": (lambda *x: self.pool_apply()),
|
||||
"on_vol_list_changed": self.vol_selected,
|
||||
"on_pool_name_changed": (lambda *x:
|
||||
self.enable_pool_apply(x, EDIT_POOL_NAME)),
|
||||
|
||||
"on_interface_add_clicked" : self.add_interface,
|
||||
"on_interface_start_clicked" : self.start_interface,
|
||||
"on_interface_stop_clicked" : self.stop_interface,
|
||||
@@ -153,7 +127,6 @@ class vmmHost(vmmGObjectUI):
|
||||
})
|
||||
|
||||
self.repopulate_networks()
|
||||
self.repopulate_storage_pools()
|
||||
self.repopulate_interfaces()
|
||||
|
||||
self.conn.connect("net-added", self.repopulate_networks)
|
||||
@@ -161,11 +134,6 @@ class vmmHost(vmmGObjectUI):
|
||||
self.conn.connect("net-started", self.refresh_network)
|
||||
self.conn.connect("net-stopped", self.refresh_network)
|
||||
|
||||
self.conn.connect("pool-added", self.repopulate_storage_pools)
|
||||
self.conn.connect("pool-removed", self.repopulate_storage_pools)
|
||||
self.conn.connect("pool-started", self.refresh_storage_pool)
|
||||
self.conn.connect("pool-stopped", self.refresh_storage_pool)
|
||||
|
||||
self.conn.connect("interface-added", self.repopulate_interfaces)
|
||||
self.conn.connect("interface-removed", self.repopulate_interfaces)
|
||||
self.conn.connect("interface-started", self.refresh_interface)
|
||||
@@ -173,7 +141,11 @@ class vmmHost(vmmGObjectUI):
|
||||
|
||||
self.conn.connect("state-changed", self.conn_state_changed)
|
||||
self.conn.connect("resources-sampled", self.refresh_resources)
|
||||
self.reset_state()
|
||||
|
||||
self.refresh_resources()
|
||||
self.conn_state_changed()
|
||||
self.widget("config-autoconnect").set_active(
|
||||
self.conn.get_autoconnect())
|
||||
|
||||
|
||||
def init_net_state(self):
|
||||
@@ -200,51 +172,9 @@ class vmmHost(vmmGObjectUI):
|
||||
netListModel.set_sort_column_id(1, Gtk.SortType.ASCENDING)
|
||||
|
||||
def init_storage_state(self):
|
||||
self.widget("storage-pages").set_show_tabs(False)
|
||||
self.storagelist = vmmStorageList(self.conn, self.builder, self.topwin)
|
||||
self.widget("storage-align").add(self.storagelist.top_box)
|
||||
|
||||
self.volmenu = Gtk.Menu()
|
||||
volCopyPath = Gtk.ImageMenuItem.new_with_label(_("Copy Volume Path"))
|
||||
volCopyImage = Gtk.Image()
|
||||
volCopyImage.set_from_stock(Gtk.STOCK_COPY, Gtk.IconSize.MENU)
|
||||
volCopyPath.set_image(volCopyImage)
|
||||
volCopyPath.show()
|
||||
volCopyPath.connect("activate", self.copy_vol_path)
|
||||
self.volmenu.add(volCopyPath)
|
||||
|
||||
volListModel = Gtk.ListStore(str, str, str, str, str)
|
||||
self.widget("vol-list").set_model(volListModel)
|
||||
|
||||
volCol = Gtk.TreeViewColumn("Volumes")
|
||||
vol_txt1 = Gtk.CellRendererText()
|
||||
volCol.pack_start(vol_txt1, True)
|
||||
volCol.add_attribute(vol_txt1, 'text', 1)
|
||||
volCol.set_sort_column_id(1)
|
||||
self.widget("vol-list").append_column(volCol)
|
||||
|
||||
volSizeCol = Gtk.TreeViewColumn("Size")
|
||||
vol_txt2 = Gtk.CellRendererText()
|
||||
volSizeCol.pack_start(vol_txt2, False)
|
||||
volSizeCol.add_attribute(vol_txt2, 'text', 2)
|
||||
volSizeCol.set_sort_column_id(2)
|
||||
self.widget("vol-list").append_column(volSizeCol)
|
||||
|
||||
volFormatCol = Gtk.TreeViewColumn("Format")
|
||||
vol_txt3 = Gtk.CellRendererText()
|
||||
volFormatCol.pack_start(vol_txt3, False)
|
||||
volFormatCol.add_attribute(vol_txt3, 'text', 3)
|
||||
volFormatCol.set_sort_column_id(3)
|
||||
self.widget("vol-list").append_column(volFormatCol)
|
||||
|
||||
volUseCol = Gtk.TreeViewColumn("Used By")
|
||||
vol_txt4 = Gtk.CellRendererText()
|
||||
volUseCol.pack_start(vol_txt4, False)
|
||||
volUseCol.add_attribute(vol_txt4, 'text', 4)
|
||||
volUseCol.set_sort_column_id(4)
|
||||
self.widget("vol-list").append_column(volUseCol)
|
||||
|
||||
volListModel.set_sort_column_id(1, Gtk.SortType.ASCENDING)
|
||||
|
||||
self.init_pool_list()
|
||||
|
||||
def init_interface_state(self):
|
||||
self.widget("interface-pages").set_show_tabs(False)
|
||||
@@ -346,25 +276,17 @@ class vmmHost(vmmGObjectUI):
|
||||
def _cleanup(self):
|
||||
self.conn = None
|
||||
|
||||
self.storagelist.cleanup()
|
||||
self.storagelist = None
|
||||
|
||||
if self.addnet:
|
||||
self.addnet.cleanup()
|
||||
self.addnet = None
|
||||
|
||||
if self.addpool:
|
||||
self.addpool.cleanup()
|
||||
self.addpool = None
|
||||
|
||||
if self.addvol:
|
||||
self.addvol.cleanup()
|
||||
self.addvol = None
|
||||
|
||||
if self.addinterface:
|
||||
self.addinterface.cleanup()
|
||||
self.addinterface = None
|
||||
|
||||
self.volmenu.destroy()
|
||||
self.volmenu = None
|
||||
|
||||
self.cpu_usage_graph.destroy()
|
||||
self.cpu_usage_graph = None
|
||||
|
||||
@@ -380,13 +302,6 @@ class vmmHost(vmmGObjectUI):
|
||||
def exit_app(self, src_ignore):
|
||||
self.emit("action-exit-app")
|
||||
|
||||
def reset_state(self):
|
||||
self.refresh_resources()
|
||||
self.conn_state_changed()
|
||||
|
||||
# Update autostart value
|
||||
auto = self.conn.get_autoconnect()
|
||||
self.widget("config-autoconnect").set_active(auto)
|
||||
|
||||
def page_changed(self, src, child, pagenum):
|
||||
ignore = src
|
||||
@@ -396,8 +311,7 @@ class vmmHost(vmmGObjectUI):
|
||||
self.repopulate_networks()
|
||||
self.conn.schedule_priority_tick(pollnet=True)
|
||||
elif pagenum == 2:
|
||||
self.repopulate_storage_volumes()
|
||||
self.conn.schedule_priority_tick(pollpool=True)
|
||||
self.storagelist.refresh_page()
|
||||
elif pagenum == 3:
|
||||
self.repopulate_interfaces()
|
||||
self.conn.schedule_priority_tick(polliface=True)
|
||||
@@ -425,18 +339,14 @@ class vmmHost(vmmGObjectUI):
|
||||
self.widget("menu_file_restore_saved").set_sensitive(conn_active)
|
||||
self.widget("net-add").set_sensitive(conn_active and
|
||||
self.conn.is_network_capable())
|
||||
self.widget("pool-add").set_sensitive(conn_active and
|
||||
self.conn.is_storage_capable())
|
||||
self.widget("interface-add").set_sensitive(conn_active and
|
||||
self.conn.is_interface_capable())
|
||||
|
||||
if not conn_active:
|
||||
self.set_net_error_page(_("Connection not active."))
|
||||
self.set_storage_error_page(_("Connection not active."))
|
||||
self.set_interface_error_page(_("Connection not active."))
|
||||
|
||||
self.repopulate_networks()
|
||||
self.repopulate_storage_pools()
|
||||
self.repopulate_interfaces()
|
||||
return
|
||||
|
||||
@@ -445,10 +355,6 @@ class vmmHost(vmmGObjectUI):
|
||||
_("Libvirt connection does not support virtual network "
|
||||
"management."))
|
||||
|
||||
if not self.conn.is_storage_capable():
|
||||
self.set_storage_error_page(
|
||||
_("Libvirt connection does not support storage management."))
|
||||
|
||||
if not self.conn.is_interface_capable():
|
||||
self.set_interface_error_page(
|
||||
_("Libvirt connection does not support interface management."))
|
||||
@@ -800,313 +706,6 @@ class vmmHost(vmmGObjectUI):
|
||||
curnet and curnet.get_connkey() or None)
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# Storage Manager methods
|
||||
# ------------------------------
|
||||
|
||||
|
||||
def stop_pool(self, src_ignore):
|
||||
pool = self.current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
logging.debug("Stopping pool '%s'", pool.get_name())
|
||||
vmmAsyncJob.simple_async_noshow(pool.stop, [], self,
|
||||
_("Error stopping pool '%s'") % pool.get_name())
|
||||
|
||||
def start_pool(self, src_ignore):
|
||||
pool = self.current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
logging.debug("Starting pool '%s'", pool.get_name())
|
||||
vmmAsyncJob.simple_async_noshow(pool.start, [], self,
|
||||
_("Error starting pool '%s'") % pool.get_name())
|
||||
|
||||
def delete_pool(self, src_ignore):
|
||||
pool = self.current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
result = self.err.yes_no(_("Are you sure you want to permanently "
|
||||
"delete the pool %s?") % pool.get_name())
|
||||
if not result:
|
||||
return
|
||||
|
||||
logging.debug("Deleting pool '%s'", pool.get_name())
|
||||
vmmAsyncJob.simple_async_noshow(pool.delete, [], self,
|
||||
_("Error deleting pool '%s'") % pool.get_name())
|
||||
|
||||
def pool_refresh(self, src_ignore):
|
||||
if self._in_refresh:
|
||||
logging.debug("Already refreshing the pool, skipping")
|
||||
return
|
||||
|
||||
if not self.confirm_changes():
|
||||
return
|
||||
|
||||
pool = self.current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
self._in_refresh = True
|
||||
|
||||
def cb():
|
||||
try:
|
||||
pool.refresh()
|
||||
self.idle_add(self.refresh_current_pool)
|
||||
finally:
|
||||
self._in_refresh = False
|
||||
|
||||
logging.debug("Refresh pool '%s'", pool.get_name())
|
||||
vmmAsyncJob.simple_async_noshow(cb, [], self,
|
||||
_("Error refreshing pool '%s'") % pool.get_name())
|
||||
|
||||
def delete_vol(self, src_ignore):
|
||||
vol = self.current_vol()
|
||||
if vol is None:
|
||||
return
|
||||
|
||||
result = self.err.yes_no(_("Are you sure you want to permanently "
|
||||
"delete the volume %s?") % vol.get_name())
|
||||
if not result:
|
||||
return
|
||||
|
||||
def cb():
|
||||
vol.delete()
|
||||
def idlecb():
|
||||
self.refresh_current_pool()
|
||||
self.repopulate_storage_volumes()
|
||||
self.idle_add(idlecb)
|
||||
|
||||
logging.debug("Deleting volume '%s'", vol.get_name())
|
||||
vmmAsyncJob.simple_async_noshow(cb, [], self,
|
||||
_("Error refreshing volume '%s'") % vol.get_name())
|
||||
|
||||
def add_pool(self, src_ignore):
|
||||
logging.debug("Launching 'Add Pool' wizard")
|
||||
try:
|
||||
if self.addpool is None:
|
||||
self.addpool = vmmCreatePool(self.conn)
|
||||
self.addpool.show(self.topwin)
|
||||
except Exception, e:
|
||||
self.err.show_err(_("Error launching pool wizard: %s") % str(e))
|
||||
|
||||
def add_vol(self, src_ignore):
|
||||
pool = self.current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
logging.debug("Launching 'Add Volume' wizard for pool '%s'",
|
||||
pool.get_name())
|
||||
try:
|
||||
if self.addvol is None:
|
||||
self.addvol = vmmCreateVolume(self.conn, pool)
|
||||
self.addvol.connect("vol-created", self.refresh_current_pool)
|
||||
else:
|
||||
self.addvol.set_parent_pool(self.conn, pool)
|
||||
self.addvol.show(self.topwin)
|
||||
except Exception, e:
|
||||
self.err.show_err(_("Error launching volume wizard: %s") % str(e))
|
||||
|
||||
def refresh_current_pool(self, ignore1=None):
|
||||
cp = self.current_pool()
|
||||
if cp is None:
|
||||
return
|
||||
cp.refresh()
|
||||
self.refresh_storage_pool(None, cp.get_connkey())
|
||||
|
||||
def current_pool(self):
|
||||
connkey = uiutil.get_list_selection(self.widget("pool-list"), 0)
|
||||
try:
|
||||
return connkey and self.conn.get_pool(connkey)
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def current_vol(self):
|
||||
pool = self.current_pool()
|
||||
if not pool:
|
||||
return None
|
||||
|
||||
connkey = uiutil.get_list_selection(self.widget("vol-list"), 0)
|
||||
try:
|
||||
return connkey and pool.get_volume(connkey)
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def pool_apply(self):
|
||||
pool = self.current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
logging.debug("Applying changes for pool '%s'", pool.get_name())
|
||||
try:
|
||||
if EDIT_POOL_AUTOSTART in self.active_edits:
|
||||
auto = self.widget("pool-autostart").get_active()
|
||||
pool.set_autostart(auto)
|
||||
if EDIT_POOL_NAME in self.active_edits:
|
||||
pool.define_name(self.widget("pool-name-entry").get_text())
|
||||
self.idle_add(self.repopulate_storage_pools)
|
||||
except Exception, e:
|
||||
self.err.show_err(_("Error changing pool settings: %s") % str(e))
|
||||
return
|
||||
|
||||
self.disable_pool_apply()
|
||||
|
||||
def disable_pool_apply(self):
|
||||
for i in EDIT_POOL_IDS:
|
||||
if i in self.active_edits:
|
||||
self.active_edits.remove(i)
|
||||
|
||||
self.widget("pool-apply").set_sensitive(False)
|
||||
|
||||
def enable_pool_apply(self, *arglist):
|
||||
edittype = arglist[-1]
|
||||
self.widget("pool-apply").set_sensitive(True)
|
||||
if edittype not in self.active_edits:
|
||||
self.active_edits.append(edittype)
|
||||
|
||||
def pool_autostart_changed(self, src_ignore):
|
||||
auto = self.widget("pool-autostart").get_active()
|
||||
self.widget("pool-autostart").set_label(auto and
|
||||
_("On Boot") or
|
||||
_("Never"))
|
||||
self.enable_pool_apply(EDIT_POOL_AUTOSTART)
|
||||
|
||||
def set_storage_error_page(self, msg):
|
||||
self.reset_pool_state()
|
||||
self.widget("storage-pages").set_current_page(1)
|
||||
self.widget("storage-error-label").set_text(msg)
|
||||
|
||||
def pool_selected(self, src):
|
||||
model, treeiter = src.get_selected()
|
||||
if treeiter is None:
|
||||
self.set_storage_error_page(_("No storage pool selected."))
|
||||
return
|
||||
|
||||
self.widget("storage-pages").set_current_page(0)
|
||||
connkey = model[treeiter][0]
|
||||
|
||||
try:
|
||||
self.populate_pool_state(connkey)
|
||||
except Exception, e:
|
||||
logging.exception(e)
|
||||
self.set_storage_error_page(_("Error selecting pool: %s") % e)
|
||||
self.disable_pool_apply()
|
||||
|
||||
def populate_pool_state(self, connkey):
|
||||
pool = self.conn.get_pool(connkey)
|
||||
pool.tick()
|
||||
auto = pool.get_autostart()
|
||||
active = pool.is_active()
|
||||
|
||||
# Set pool details state
|
||||
self.widget("pool-details").set_sensitive(True)
|
||||
self.widget("pool-name").set_markup("<b>%s:</b>" %
|
||||
pool.get_name())
|
||||
self.widget("pool-name-entry").set_text(pool.get_name())
|
||||
self.widget("pool-name-entry").set_editable(not active)
|
||||
self.widget("pool-sizes").set_markup(
|
||||
"""<span size="large">%s Free</span> / <i>%s In Use</i>""" %
|
||||
(pool.get_pretty_available(), pool.get_pretty_allocation()))
|
||||
self.widget("pool-type").set_text(
|
||||
StoragePool.get_pool_type_desc(pool.get_type()))
|
||||
self.widget("pool-location").set_text(
|
||||
pool.get_target_path())
|
||||
self.widget("pool-state-icon").set_from_icon_name(
|
||||
((active and self.ICON_RUNNING) or self.ICON_SHUTOFF),
|
||||
Gtk.IconSize.BUTTON)
|
||||
self.widget("pool-state").set_text(
|
||||
(active and _("Active")) or _("Inactive"))
|
||||
self.widget("pool-autostart").set_label(
|
||||
(auto and _("On Boot")) or _("Never"))
|
||||
self.widget("pool-autostart").set_active(auto)
|
||||
|
||||
self.widget("vol-list").set_sensitive(active)
|
||||
self.repopulate_storage_volumes()
|
||||
|
||||
self.widget("pool-delete").set_sensitive(not active)
|
||||
self.widget("pool-stop").set_sensitive(active)
|
||||
self.widget("pool-start").set_sensitive(not active)
|
||||
self.widget("vol-add").set_sensitive(active)
|
||||
self.widget("vol-add").set_tooltip_text(_("Create new volume"))
|
||||
self.widget("vol-delete").set_sensitive(False)
|
||||
|
||||
if active and not pool.supports_volume_creation():
|
||||
self.widget("vol-add").set_sensitive(False)
|
||||
self.widget("vol-add").set_tooltip_text(
|
||||
_("Pool does not support volume creation"))
|
||||
|
||||
def refresh_storage_pool(self, src, connkey):
|
||||
ignore = src
|
||||
refresh_pool_in_list(self.widget("pool-list"), self.conn, connkey)
|
||||
curpool = self.current_pool()
|
||||
if curpool.get_connkey() != connkey:
|
||||
return
|
||||
|
||||
# Currently selected pool changed state: force a 'pool_selected' to
|
||||
# update vol list
|
||||
self.pool_selected(self.widget("pool-list").get_selection())
|
||||
|
||||
def reset_pool_state(self):
|
||||
self.widget("pool-details").set_sensitive(False)
|
||||
self.widget("pool-name").set_text("")
|
||||
self.widget("pool-name-entry").set_text("")
|
||||
self.widget("pool-sizes").set_markup("""<span size="large"> </span>""")
|
||||
self.widget("pool-type").set_text("")
|
||||
self.widget("pool-location").set_text("")
|
||||
self.widget("pool-state-icon").set_from_icon_name(self.ICON_SHUTOFF,
|
||||
Gtk.IconSize.BUTTON)
|
||||
self.widget("pool-state").set_text(_("Inactive"))
|
||||
self.widget("vol-list").get_model().clear()
|
||||
self.widget("pool-autostart").set_label(_("Never"))
|
||||
self.widget("pool-autostart").set_active(False)
|
||||
|
||||
self.widget("pool-delete").set_sensitive(False)
|
||||
self.widget("pool-stop").set_sensitive(False)
|
||||
self.widget("pool-start").set_sensitive(False)
|
||||
self.widget("vol-add").set_sensitive(False)
|
||||
self.widget("vol-delete").set_sensitive(False)
|
||||
self.widget("vol-list").set_sensitive(False)
|
||||
self.disable_pool_apply()
|
||||
|
||||
def vol_selected(self, src):
|
||||
model, treeiter = src.get_selected()
|
||||
ignore = model
|
||||
if treeiter is None:
|
||||
self.widget("vol-delete").set_sensitive(False)
|
||||
return
|
||||
|
||||
self.widget("vol-delete").set_sensitive(True)
|
||||
|
||||
def popup_vol_menu(self, widget_ignore, event):
|
||||
if event.button != 3:
|
||||
return
|
||||
|
||||
self.volmenu.popup(None, None, None, None, 0, event.time)
|
||||
|
||||
def copy_vol_path(self, ignore=None):
|
||||
vol = self.current_vol()
|
||||
if not vol:
|
||||
return
|
||||
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||
target_path = vol.get_target_path()
|
||||
if target_path:
|
||||
clipboard.set_text(target_path, -1)
|
||||
|
||||
|
||||
def repopulate_storage_pools(self, src=None, connkey=None):
|
||||
ignore = src
|
||||
ignore = connkey
|
||||
pool_list = self.widget("pool-list")
|
||||
populate_storage_pools(pool_list, self.conn, self.current_pool())
|
||||
|
||||
def repopulate_storage_volumes(self):
|
||||
list_widget = self.widget("vol-list")
|
||||
pool = self.current_pool()
|
||||
populate_storage_volumes(list_widget, pool, None)
|
||||
|
||||
|
||||
#############################
|
||||
# Interface manager methods #
|
||||
#############################
|
||||
@@ -1382,9 +981,7 @@ class vmmHost(vmmGObjectUI):
|
||||
chktext=_("Don't warn me again."),
|
||||
default=False):
|
||||
|
||||
if all([edit in EDIT_POOL_IDS for edit in self.active_edits]):
|
||||
self.pool_apply()
|
||||
elif all([edit in EDIT_NET_IDS for edit in self.active_edits]):
|
||||
if all([edit in EDIT_NET_IDS for edit in self.active_edits]):
|
||||
self.net_apply()
|
||||
elif all([edit in EDIT_INTERFACE_IDS
|
||||
for edit in self.active_edits]):
|
||||
@@ -1392,113 +989,3 @@ class vmmHost(vmmGObjectUI):
|
||||
|
||||
self.active_edits = []
|
||||
return True
|
||||
|
||||
def init_pool_list(self):
|
||||
pool_list = self.widget("pool-list")
|
||||
init_pool_list(pool_list, self.pool_selected)
|
||||
|
||||
sel = pool_list.get_selection()
|
||||
sel.set_select_function((lambda *x: self.confirm_changes()),
|
||||
None)
|
||||
|
||||
|
||||
# These functions are broken out, since they are used by storage browser
|
||||
# dialog.
|
||||
|
||||
def init_pool_list(pool_list, changed_func):
|
||||
poolListModel = Gtk.ListStore(str, str, bool, str)
|
||||
pool_list.set_model(poolListModel)
|
||||
|
||||
pool_list.get_selection().connect("changed", changed_func)
|
||||
|
||||
poolCol = Gtk.TreeViewColumn("Storage Pools")
|
||||
pool_txt = Gtk.CellRendererText()
|
||||
pool_per = Gtk.CellRendererText()
|
||||
poolCol.pack_start(pool_per, False)
|
||||
poolCol.pack_start(pool_txt, True)
|
||||
poolCol.add_attribute(pool_txt, 'markup', 1)
|
||||
poolCol.add_attribute(pool_txt, 'sensitive', 2)
|
||||
poolCol.add_attribute(pool_per, 'markup', 3)
|
||||
pool_list.append_column(poolCol)
|
||||
poolListModel.set_sort_column_id(1, Gtk.SortType.ASCENDING)
|
||||
|
||||
|
||||
def refresh_pool_in_list(pool_list, conn, connkey):
|
||||
for row in pool_list.get_model():
|
||||
if row[0] != connkey:
|
||||
continue
|
||||
|
||||
# Update active sensitivity and percent available for passed key
|
||||
row[3] = get_pool_size_percent(conn, connkey)
|
||||
row[2] = conn.get_pool(connkey).is_active()
|
||||
return
|
||||
|
||||
|
||||
def populate_storage_pools(pool_list, conn, curpool):
|
||||
model = pool_list.get_model()
|
||||
# Prevent events while the model is modified
|
||||
pool_list.set_model(None)
|
||||
pool_list.get_selection().unselect_all()
|
||||
model.clear()
|
||||
for pool in conn.list_pools():
|
||||
connkey = pool.get_connkey()
|
||||
per = get_pool_size_percent(conn, connkey)
|
||||
pool = conn.get_pool(connkey)
|
||||
|
||||
name = pool.get_name()
|
||||
typ = StoragePool.get_pool_type_desc(pool.get_type())
|
||||
label = "%s\n<span size='small'>%s</span>" % (name, typ)
|
||||
|
||||
model.append([connkey, label, pool.is_active(), per])
|
||||
|
||||
pool_list.set_model(model)
|
||||
uiutil.set_row_selection(pool_list,
|
||||
curpool and curpool.get_connkey() or None)
|
||||
|
||||
|
||||
def populate_storage_volumes(list_widget, pool, sensitive_cb):
|
||||
vols = pool and pool.get_volumes() or {}
|
||||
model = list_widget.get_model()
|
||||
list_widget.get_selection().unselect_all()
|
||||
model.clear()
|
||||
|
||||
for key in vols.keys():
|
||||
vol = vols[key]
|
||||
|
||||
try:
|
||||
path = vol.get_target_path()
|
||||
name = vol.get_pretty_name(pool.get_type())
|
||||
cap = vol.get_pretty_capacity()
|
||||
fmt = vol.get_format() or ""
|
||||
except:
|
||||
logging.debug("Error getting volume info for '%s', "
|
||||
"hiding it", key, exc_info=True)
|
||||
continue
|
||||
|
||||
namestr = None
|
||||
try:
|
||||
if path:
|
||||
names = VirtualDisk.path_in_use_by(vol.conn.get_backend(),
|
||||
path)
|
||||
namestr = ", ".join(names)
|
||||
if not namestr:
|
||||
namestr = None
|
||||
except:
|
||||
logging.exception("Failed to determine if storage volume in "
|
||||
"use.")
|
||||
|
||||
row = [key, name, cap, fmt, namestr]
|
||||
if sensitive_cb:
|
||||
row.append(sensitive_cb(fmt))
|
||||
model.append(row)
|
||||
|
||||
|
||||
def get_pool_size_percent(conn, connkey):
|
||||
pool = conn.get_pool(connkey)
|
||||
cap = pool.get_capacity()
|
||||
alloc = pool.get_allocation()
|
||||
if not cap or alloc is None:
|
||||
per = 0
|
||||
else:
|
||||
per = int(((float(alloc) / float(cap)) * 100))
|
||||
return "<span size='small' color='#484848'>%s%%</span>" % int(per)
|
||||
|
||||
@@ -20,13 +20,9 @@
|
||||
|
||||
import logging
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from . import host
|
||||
from .asyncjob import vmmAsyncJob
|
||||
from .createvol import vmmCreateVolume
|
||||
from .baseclass import vmmGObjectUI
|
||||
from . import uiutil
|
||||
from .baseclass import vmmGObjectUI
|
||||
from .storagelist import vmmStorageList
|
||||
|
||||
|
||||
class vmmStorageBrowser(vmmGObjectUI):
|
||||
@@ -34,43 +30,33 @@ class vmmStorageBrowser(vmmGObjectUI):
|
||||
vmmGObjectUI.__init__(self, "storagebrowse.ui", "vmm-storage-browse")
|
||||
self.conn = conn
|
||||
|
||||
self.conn_signal_ids = []
|
||||
self.can_new_volume = True
|
||||
self._first_run = False
|
||||
self._finish_cb = None
|
||||
|
||||
# Add Volume wizard
|
||||
self.addvol = None
|
||||
# Passed to browse_local
|
||||
self._browse_reason = None
|
||||
|
||||
# Name of VM we are choosing storage for, can be used to recommend
|
||||
# volume name if creating
|
||||
self.vm_name = None
|
||||
# Whether we should abide stable defaults
|
||||
self._stable_defaults = False
|
||||
|
||||
# Arguments to pass to util.browse_local for local storage
|
||||
self.browse_reason = None
|
||||
self.local_args = {}
|
||||
|
||||
self.stable_defaults = False
|
||||
self._in_refresh = False
|
||||
self.storagelist = vmmStorageList(self.conn, self.builder, self.topwin,
|
||||
self._vol_sensitive_cb)
|
||||
self._init_ui()
|
||||
|
||||
self.builder.connect_signals({
|
||||
"on_vmm_storage_browse_delete_event" : self.close,
|
||||
"on_browse_cancel_clicked" : self.close,
|
||||
"on_browse_local_clicked" : self.browse_local,
|
||||
"on_new_volume_clicked" : self.new_volume,
|
||||
"on_choose_volume_clicked" : self.finish,
|
||||
"on_pool_refresh_clicked": self.pool_refresh,
|
||||
"on_vol_delete_clicked": self.delete_vol,
|
||||
"on_vol_list_row_activated" : self.finish,
|
||||
"on_vol_list_changed": self.vol_selected,
|
||||
})
|
||||
self.bind_escape_key_close()
|
||||
|
||||
self.set_initial_state()
|
||||
|
||||
def show(self, parent, conn):
|
||||
def show(self, parent):
|
||||
logging.debug("Showing storage browser")
|
||||
self.reset_state(conn)
|
||||
if not self._first_run:
|
||||
self._first_run = True
|
||||
pool = self.conn.get_default_pool()
|
||||
uiutil.set_row_selection(self.storagelist.widget("pool-list"),
|
||||
pool and pool.get_connkey() or None)
|
||||
|
||||
self.topwin.set_transient_for(parent)
|
||||
self.topwin.present()
|
||||
self.conn.schedule_priority_tick(pollpool=True)
|
||||
@@ -78,319 +64,112 @@ class vmmStorageBrowser(vmmGObjectUI):
|
||||
def close(self, ignore1=None, ignore2=None):
|
||||
logging.debug("Closing storage browser")
|
||||
self.topwin.hide()
|
||||
if self.addvol:
|
||||
self.addvol.close()
|
||||
self.storagelist.close()
|
||||
return 1
|
||||
|
||||
def _cleanup(self):
|
||||
self.remove_conn()
|
||||
self.conn = None
|
||||
|
||||
if self.addvol:
|
||||
self.addvol.cleanup()
|
||||
self.addvol = None
|
||||
self.storagelist.cleanup()
|
||||
self.storagelist = None
|
||||
|
||||
def remove_conn(self):
|
||||
if not self.conn:
|
||||
return
|
||||
|
||||
for i in self.conn_signal_ids:
|
||||
self.conn.disconnect(i)
|
||||
##############
|
||||
# Public API #
|
||||
##############
|
||||
|
||||
def set_finish_cb(self, callback):
|
||||
self._finish_cb = callback
|
||||
|
||||
def set_browse_reason(self, reason):
|
||||
self.browse_reason = reason
|
||||
|
||||
def set_local_arg(self, arg, val):
|
||||
self.local_args[arg] = val
|
||||
|
||||
self._browse_reason = reason
|
||||
def set_vm_name(self, name):
|
||||
self.vm_name = name
|
||||
self.storagelist.set_name_hint(name)
|
||||
def set_stable_defaults(self, val):
|
||||
self._stable_defaults = val
|
||||
|
||||
def set_initial_state(self):
|
||||
pool_list = self.widget("pool-list")
|
||||
host.init_pool_list(pool_list, self.pool_selected)
|
||||
def _init_ui(self):
|
||||
self.storagelist.connect("browse-clicked", self._browse_clicked)
|
||||
self.storagelist.connect("volume-chosen", self._volume_chosen)
|
||||
self.storagelist.connect("cancel-clicked", self.close)
|
||||
|
||||
# (Key, Name, Cap, Format, Used By, sensitive)
|
||||
vol_list = self.widget("vol-list")
|
||||
volListModel = Gtk.ListStore(str, str, str, str, str, bool)
|
||||
vol_list.set_model(volListModel)
|
||||
|
||||
volCol = Gtk.TreeViewColumn(_("Name"))
|
||||
vol_txt1 = Gtk.CellRendererText()
|
||||
volCol.pack_start(vol_txt1, True)
|
||||
volCol.add_attribute(vol_txt1, 'text', 1)
|
||||
volCol.add_attribute(vol_txt1, 'sensitive', 5)
|
||||
volCol.set_sort_column_id(1)
|
||||
vol_list.append_column(volCol)
|
||||
|
||||
volSizeCol = Gtk.TreeViewColumn(_("Size"))
|
||||
vol_txt2 = Gtk.CellRendererText()
|
||||
volSizeCol.pack_start(vol_txt2, False)
|
||||
volSizeCol.add_attribute(vol_txt2, 'text', 2)
|
||||
volSizeCol.add_attribute(vol_txt2, 'sensitive', 5)
|
||||
volSizeCol.set_sort_column_id(2)
|
||||
vol_list.append_column(volSizeCol)
|
||||
|
||||
volPathCol = Gtk.TreeViewColumn(_("Format"))
|
||||
vol_txt4 = Gtk.CellRendererText()
|
||||
volPathCol.pack_start(vol_txt4, False)
|
||||
volPathCol.add_attribute(vol_txt4, 'text', 3)
|
||||
volPathCol.add_attribute(vol_txt4, 'sensitive', 5)
|
||||
volPathCol.set_sort_column_id(3)
|
||||
vol_list.append_column(volPathCol)
|
||||
|
||||
volUseCol = Gtk.TreeViewColumn(_("Used By"))
|
||||
vol_txt5 = Gtk.CellRendererText()
|
||||
volUseCol.pack_start(vol_txt5, False)
|
||||
volUseCol.add_attribute(vol_txt5, 'text', 4)
|
||||
volUseCol.add_attribute(vol_txt5, 'sensitive', 5)
|
||||
volUseCol.set_sort_column_id(4)
|
||||
vol_list.append_column(volUseCol)
|
||||
|
||||
volListModel.set_sort_column_id(1, Gtk.SortType.ASCENDING)
|
||||
|
||||
def reset_state(self, conn):
|
||||
self.remove_conn()
|
||||
self.conn = conn
|
||||
|
||||
self.repopulate_storage_pools()
|
||||
|
||||
ids = []
|
||||
ids.append(self.conn.connect("pool-added",
|
||||
self.repopulate_storage_pools))
|
||||
ids.append(self.conn.connect("pool-removed",
|
||||
self.repopulate_storage_pools))
|
||||
ids.append(self.conn.connect("pool-started",
|
||||
self.refresh_storage_pool))
|
||||
ids.append(self.conn.connect("pool-stopped",
|
||||
self.refresh_storage_pool))
|
||||
self.conn_signal_ids = ids
|
||||
|
||||
# FIXME: Need a connection specific "vol-added" function?
|
||||
# Won't be able to pick that change up from outside?
|
||||
|
||||
if not self._first_run:
|
||||
self._first_run = True
|
||||
pool = self.conn.get_default_pool()
|
||||
uiutil.set_row_selection(self.widget("pool-list"),
|
||||
pool and pool.get_connkey() or None)
|
||||
|
||||
# Manually trigger vol_selected, so buttons are in the correct state
|
||||
self.vol_selected()
|
||||
self.pool_selected()
|
||||
self.widget("storage-align").add(self.storagelist.top_box)
|
||||
self.err.set_modal_default(True)
|
||||
self.storagelist.err.set_modal_default(True)
|
||||
|
||||
tooltip = ""
|
||||
is_remote = self.conn.is_remote()
|
||||
self.widget("browse-local").set_sensitive(not is_remote)
|
||||
self.storagelist.widget("browse-local").set_sensitive(not is_remote)
|
||||
if is_remote:
|
||||
tooltip = _("Cannot use local storage on remote connection.")
|
||||
self.widget("browse-local").set_tooltip_text(tooltip)
|
||||
self.storagelist.widget("browse-local").set_tooltip_text(tooltip)
|
||||
|
||||
# Set data based on browse type
|
||||
self.local_args["dialog_type"] = None
|
||||
self.local_args["browse_reason"] = self.browse_reason
|
||||
uiutil.set_grid_row_visible(
|
||||
self.storagelist.widget("pool-autostart"), False)
|
||||
uiutil.set_grid_row_visible(
|
||||
self.storagelist.widget("pool-name-entry"), False)
|
||||
uiutil.set_grid_row_visible(
|
||||
self.storagelist.widget("pool-type"), False)
|
||||
uiutil.set_grid_row_visible(
|
||||
self.storagelist.widget("pool-state-box"), False)
|
||||
self.storagelist.widget("browse-local").set_visible(True)
|
||||
self.storagelist.widget("browse-cancel").set_visible(True)
|
||||
self.storagelist.widget("choose-volume").set_visible(True)
|
||||
self.storagelist.widget("choose-volume").set_sensitive(False)
|
||||
self.storagelist.widget("pool-apply").set_visible(False)
|
||||
|
||||
data = self.config.browse_reason_data.get(self.browse_reason)
|
||||
data = self.config.browse_reason_data.get(self._browse_reason)
|
||||
allow_create = True
|
||||
if data:
|
||||
self.topwin.set_title(data["storage_title"])
|
||||
self.local_args["dialog_name"] = data["local_title"]
|
||||
self.local_args["dialog_type"] = data.get("dialog_type")
|
||||
self.local_args["choose_button"] = data.get("choose_button")
|
||||
allow_create = data["enable_create"]
|
||||
|
||||
self.widget("new-volume").set_visible(
|
||||
self.can_new_volume and self.allow_create())
|
||||
self.storagelist.widget("vol-add").set_sensitive(allow_create)
|
||||
|
||||
|
||||
# Convenience helpers
|
||||
def allow_create(self):
|
||||
data = self.config.browse_reason_data.get(self.browse_reason)
|
||||
if not data:
|
||||
return True
|
||||
#############
|
||||
# Listeners #
|
||||
#############
|
||||
|
||||
return data["enable_create"]
|
||||
|
||||
def current_pool(self):
|
||||
row = uiutil.get_list_selection(self.widget("pool-list"), None)
|
||||
if not row:
|
||||
return
|
||||
|
||||
connkey = row[0]
|
||||
try:
|
||||
return self.conn.get_pool(connkey)
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def current_vol_row(self):
|
||||
if not self.current_pool():
|
||||
return
|
||||
return uiutil.get_list_selection(self.widget("vol-list"), None)
|
||||
|
||||
def current_vol(self):
|
||||
pool = self.current_pool()
|
||||
row = self.current_vol_row()
|
||||
if not pool or not row:
|
||||
return
|
||||
return pool.get_volume(row[0])
|
||||
|
||||
def refresh_storage_pool(self, src, connkey):
|
||||
def _browse_clicked(self, src):
|
||||
ignore = src
|
||||
return self._browse_local()
|
||||
|
||||
pool_list = self.widget("pool-list")
|
||||
host.refresh_pool_in_list(pool_list, self.conn, connkey)
|
||||
curpool = self.current_pool()
|
||||
if curpool.get_connkey() != connkey:
|
||||
return
|
||||
|
||||
# Currently selected pool changed state: force a 'pool_selected' to
|
||||
# update vol list
|
||||
self.pool_selected(self.widget("pool-list").get_selection())
|
||||
|
||||
def repopulate_storage_pools(self, src=None, connkey=None):
|
||||
def _volume_chosen(self, src, volume):
|
||||
ignore = src
|
||||
ignore = connkey
|
||||
pool_list = self.widget("pool-list")
|
||||
host.populate_storage_pools(pool_list, self.conn, self.current_pool())
|
||||
self._finish(volume.get_target_path())
|
||||
|
||||
def delete_vol(self, src_ignore):
|
||||
vol = self.current_vol()
|
||||
if vol is None:
|
||||
return
|
||||
def _vol_sensitive_cb(self, fmt):
|
||||
if ((self._browse_reason == self.config.CONFIG_DIR_FS)
|
||||
and fmt != 'dir'):
|
||||
return False
|
||||
elif self._stable_defaults:
|
||||
if fmt == "vmdk":
|
||||
return False
|
||||
return True
|
||||
|
||||
result = self.err.yes_no(_("Are you sure you want to permanently "
|
||||
"delete the volume %s?") % vol.get_name())
|
||||
if not result:
|
||||
return
|
||||
|
||||
def cb():
|
||||
vol.delete()
|
||||
def idlecb():
|
||||
self.refresh_current_pool()
|
||||
self.populate_storage_volumes()
|
||||
self.idle_add(idlecb)
|
||||
####################
|
||||
# Internal helpers #
|
||||
####################
|
||||
|
||||
logging.debug("Deleting volume '%s'", vol.get_name())
|
||||
vmmAsyncJob.simple_async_noshow(cb, [], self,
|
||||
_("Error refreshing volume '%s'") % vol.get_name())
|
||||
def _browse_local(self):
|
||||
dialog_type = None
|
||||
dialog_name = None
|
||||
choose_button = None
|
||||
|
||||
def pool_refresh(self, src_ignore):
|
||||
if self._in_refresh:
|
||||
logging.debug("Already refreshing the pool, skipping")
|
||||
return
|
||||
data = self.config.browse_reason_data.get(self._browse_reason)
|
||||
if data:
|
||||
dialog_name = data["local_title"] or None
|
||||
dialog_type = data.get("dialog_type")
|
||||
choose_button = data.get("choose_button")
|
||||
|
||||
pool = self.current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
self._in_refresh = True
|
||||
|
||||
def cb():
|
||||
try:
|
||||
pool.refresh()
|
||||
self.idle_add(self.refresh_current_pool)
|
||||
finally:
|
||||
self._in_refresh = False
|
||||
|
||||
logging.debug("Refresh pool '%s'", pool.get_name())
|
||||
vmmAsyncJob.simple_async_noshow(cb, [], self,
|
||||
_("Error refreshing pool '%s'") % pool.get_name())
|
||||
|
||||
# Listeners
|
||||
def pool_selected(self, src_ignore=None):
|
||||
pool = self.current_pool()
|
||||
|
||||
can_new_vol = False
|
||||
tt = ""
|
||||
if pool:
|
||||
pool.tick()
|
||||
can_new_vol = (pool.is_active() and
|
||||
pool.supports_volume_creation())
|
||||
if not can_new_vol:
|
||||
tt = _("Pool does not support volume creation")
|
||||
|
||||
self.widget("new-volume").set_sensitive(can_new_vol)
|
||||
self.widget("new-volume").set_tooltip_text(tt)
|
||||
|
||||
self.populate_storage_volumes()
|
||||
|
||||
def vol_selected(self, ignore=None):
|
||||
vol = self.current_vol_row()
|
||||
canchoose = bool(vol and vol[5])
|
||||
self.widget("choose-volume").set_sensitive(canchoose)
|
||||
self.widget("vol-delete").set_sensitive(canchoose)
|
||||
|
||||
def refresh_current_pool(self, createvol=None):
|
||||
cp = self.current_pool()
|
||||
if cp is None:
|
||||
return
|
||||
cp.refresh()
|
||||
|
||||
self.refresh_storage_pool(None, cp.get_connkey())
|
||||
name = createvol and createvol.vol.name or None
|
||||
|
||||
vol_list = self.widget("vol-list")
|
||||
def select_volume(model, path, it, volume_name):
|
||||
if model.get(it, 0)[0] == volume_name:
|
||||
uiutil.set_list_selection(vol_list, path)
|
||||
|
||||
vol_list.get_model().foreach(select_volume, name)
|
||||
|
||||
def new_volume(self, src_ignore):
|
||||
pool = self.current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
try:
|
||||
if self.addvol is None:
|
||||
self.addvol = vmmCreateVolume(self.conn, pool)
|
||||
self.addvol.connect("vol-created", self.refresh_current_pool)
|
||||
else:
|
||||
self.addvol.set_parent_pool(self.conn, pool)
|
||||
self.addvol.set_modal(True)
|
||||
self.addvol.set_name_hint(self.vm_name)
|
||||
self.addvol.show(self.topwin)
|
||||
except Exception, e:
|
||||
self.show_err(_("Error launching volume wizard: %s") % str(e))
|
||||
|
||||
def browse_local(self, src_ignore):
|
||||
if not self.local_args.get("dialog_name"):
|
||||
self.local_args["dialog_name"] = None
|
||||
|
||||
filename = self.err.browse_local(
|
||||
self.conn, **self.local_args)
|
||||
filename = self.err.browse_local(self.conn,
|
||||
dialog_type=dialog_type, browse_reason=self._browse_reason,
|
||||
dialog_name=dialog_name, choose_button=choose_button)
|
||||
if filename:
|
||||
self._do_finish(path=filename)
|
||||
self._finish(filename)
|
||||
|
||||
def finish(self, ignore=None, ignore1=None, ignore2=None):
|
||||
self._do_finish()
|
||||
|
||||
def _do_finish(self, path=None):
|
||||
if not path:
|
||||
path = self.current_vol().get_target_path()
|
||||
def _finish(self, path):
|
||||
if self._finish_cb:
|
||||
self._finish_cb(self, path)
|
||||
self.close()
|
||||
|
||||
|
||||
# Do stuff!
|
||||
def populate_storage_volumes(self):
|
||||
list_widget = self.widget("vol-list")
|
||||
pool = self.current_pool()
|
||||
|
||||
def sensitive_cb(fmt):
|
||||
if ((self.browse_reason == self.config.CONFIG_DIR_FS)
|
||||
and fmt != 'dir'):
|
||||
return False
|
||||
elif self.stable_defaults:
|
||||
if fmt == "vmdk":
|
||||
return False
|
||||
return True
|
||||
|
||||
host.populate_storage_volumes(list_widget, pool, sensitive_cb)
|
||||
|
||||
def show_err(self, info, details=None):
|
||||
self.err.show_err(info,
|
||||
details=details,
|
||||
modal=True)
|
||||
|
||||
712
virtManager/storagelist.py
Normal file
712
virtManager/storagelist.py
Normal file
@@ -0,0 +1,712 @@
|
||||
#
|
||||
# Copyright (C) 2015 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
|
||||
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GObject
|
||||
|
||||
from virtinst import StoragePool
|
||||
from virtinst import VirtualDisk
|
||||
|
||||
from . import uiutil
|
||||
from .asyncjob import vmmAsyncJob
|
||||
from .baseclass import vmmGObjectUI
|
||||
from .createpool import vmmCreatePool
|
||||
from .createvol import vmmCreateVolume
|
||||
|
||||
|
||||
EDIT_POOL_IDS = (
|
||||
EDIT_POOL_NAME,
|
||||
EDIT_POOL_AUTOSTART,
|
||||
) = range(2)
|
||||
|
||||
VOL_NUM_COLUMNS = 6
|
||||
(VOL_COLUMN_KEY,
|
||||
VOL_COLUMN_NAME,
|
||||
VOL_COLUMN_CAPACITY,
|
||||
VOL_COLUMN_FORMAT,
|
||||
VOL_COLUMN_INUSEBY,
|
||||
VOL_COLUMN_SENSITIVE) = range(VOL_NUM_COLUMNS)
|
||||
|
||||
POOL_NUM_COLUMNS = 4
|
||||
(POOL_COLUMN_CONNKEY,
|
||||
POOL_COLUMN_LABEL,
|
||||
POOL_COLUMN_ISACTIVE,
|
||||
POOL_COLUMN_PERCENT) = range(POOL_NUM_COLUMNS)
|
||||
|
||||
ICON_RUNNING = "state_running"
|
||||
ICON_SHUTOFF = "state_shutoff"
|
||||
|
||||
|
||||
def _get_pool_size_percent(pool):
|
||||
cap = pool.get_capacity()
|
||||
alloc = pool.get_allocation()
|
||||
if not cap or alloc is None:
|
||||
per = 0
|
||||
else:
|
||||
per = int(((float(alloc) / float(cap)) * 100))
|
||||
return "<span size='small' color='#484848'>%s%%</span>" % int(per)
|
||||
|
||||
|
||||
class vmmStorageList(vmmGObjectUI):
|
||||
__gsignals__ = {
|
||||
"browse-clicked": (GObject.SignalFlags.RUN_FIRST, None, []),
|
||||
"volume-chosen": (GObject.SignalFlags.RUN_FIRST, None, [object]),
|
||||
"cancel-clicked": (GObject.SignalFlags.RUN_FIRST, None, []),
|
||||
}
|
||||
|
||||
def __init__(self, conn, builder, topwin, vol_sensitive_cb=None):
|
||||
vmmGObjectUI.__init__(self, "storagelist.ui",
|
||||
None, builder=builder, topwin=topwin)
|
||||
self.conn = conn
|
||||
|
||||
# Callback function for setting volume row sensitivity. Used
|
||||
# by storage browser to disallow selecting certain volumes
|
||||
self._vol_sensitive_cb = vol_sensitive_cb
|
||||
|
||||
# Name hint passed to addvol. Set by storagebrowser
|
||||
self._name_hint = None
|
||||
|
||||
self._active_edits = []
|
||||
self._addpool = None
|
||||
self._addvol = None
|
||||
self._in_refresh = False
|
||||
self._volmenu = None
|
||||
self.top_box = self.widget("storage-grid")
|
||||
|
||||
self.builder.connect_signals({
|
||||
"on_pool_add_clicked" : self._pool_add,
|
||||
"on_pool_stop_clicked": self._pool_stop,
|
||||
"on_pool_start_clicked": self._pool_start,
|
||||
"on_pool_delete_clicked": self._pool_delete,
|
||||
"on_pool_refresh_clicked": self._pool_refresh,
|
||||
"on_pool_apply_clicked": (lambda *x: self._pool_apply()),
|
||||
|
||||
"on_vol_delete_clicked": self._vol_delete,
|
||||
"on_vol_list_button_press_event": self._vol_popup_menu,
|
||||
"on_vol_list_changed": self._vol_selected,
|
||||
"on_vol_add_clicked" : self._vol_add,
|
||||
|
||||
"on_browse_cancel_clicked" : self._cancel_clicked,
|
||||
"on_browse_local_clicked" : self._browse_local_clicked,
|
||||
"on_choose_volume_clicked" : self._choose_volume_clicked,
|
||||
"on_vol_list_row_activated" : self._vol_list_row_activated,
|
||||
|
||||
"on_pool_name_changed": (lambda *x:
|
||||
self._enable_pool_apply(x, EDIT_POOL_NAME)),
|
||||
"on_pool_autostart_toggled": self._pool_autostart_changed,
|
||||
})
|
||||
|
||||
self._init_ui()
|
||||
|
||||
|
||||
def _cleanup(self):
|
||||
try:
|
||||
self.conn.disconnect_by_func(self._conn_pools_changed)
|
||||
self.conn.disconnect_by_func(self._conn_pools_changed)
|
||||
self.conn.disconnect_by_func(self._reselect_pool)
|
||||
self.conn.disconnect_by_func(self._reselect_pool)
|
||||
self.conn.disconnect_by_func(self._conn_state_changed)
|
||||
except:
|
||||
pass
|
||||
|
||||
self.conn = None
|
||||
|
||||
if self._addpool:
|
||||
self._addpool.cleanup()
|
||||
self._addpool = None
|
||||
|
||||
if self._addvol:
|
||||
self._addvol.cleanup()
|
||||
self._addvol = None
|
||||
|
||||
self._volmenu.destroy()
|
||||
self._volmenu = None
|
||||
|
||||
def close(self, ignore1=None, ignore2=None):
|
||||
if self._addvol:
|
||||
self._addvol.close()
|
||||
if self._addpool:
|
||||
self._addpool.close()
|
||||
if self._volmenu:
|
||||
self._volmenu.hide()
|
||||
|
||||
|
||||
##########################
|
||||
# Initialization methods #
|
||||
##########################
|
||||
|
||||
def _init_ui(self):
|
||||
self.widget("storage-pages").set_show_tabs(False)
|
||||
|
||||
# These are enabled in storagebrowser.py
|
||||
self.widget("browse-local").set_visible(False)
|
||||
self.widget("browse-cancel").set_visible(False)
|
||||
self.widget("choose-volume").set_visible(False)
|
||||
|
||||
# Volume list popup menu
|
||||
self._volmenu = Gtk.Menu()
|
||||
volCopyPath = Gtk.ImageMenuItem.new_with_label(_("Copy Volume Path"))
|
||||
volCopyImage = Gtk.Image()
|
||||
volCopyImage.set_from_stock(Gtk.STOCK_COPY, Gtk.IconSize.MENU)
|
||||
volCopyPath.set_image(volCopyImage)
|
||||
volCopyPath.show()
|
||||
volCopyPath.connect("activate", self._vol_copy_path)
|
||||
self._volmenu.add(volCopyPath)
|
||||
|
||||
# Volume list
|
||||
# [key, name, capacity, format, in use by string, sensitive]
|
||||
volListModel = Gtk.ListStore(str, str, str, str, str, bool)
|
||||
self.widget("vol-list").set_model(volListModel)
|
||||
|
||||
volCol = Gtk.TreeViewColumn("Volumes")
|
||||
vol_txt1 = Gtk.CellRendererText()
|
||||
volCol.pack_start(vol_txt1, True)
|
||||
volCol.add_attribute(vol_txt1, 'text', VOL_COLUMN_NAME)
|
||||
volCol.add_attribute(vol_txt1, 'sensitive', VOL_COLUMN_SENSITIVE)
|
||||
volCol.set_sort_column_id(VOL_COLUMN_NAME)
|
||||
self.widget("vol-list").append_column(volCol)
|
||||
|
||||
volSizeCol = Gtk.TreeViewColumn("Size")
|
||||
vol_txt2 = Gtk.CellRendererText()
|
||||
volSizeCol.pack_start(vol_txt2, False)
|
||||
volSizeCol.add_attribute(vol_txt2, 'text', VOL_COLUMN_CAPACITY)
|
||||
volSizeCol.add_attribute(vol_txt2, 'sensitive', VOL_COLUMN_SENSITIVE)
|
||||
volSizeCol.set_sort_column_id(VOL_COLUMN_CAPACITY)
|
||||
self.widget("vol-list").append_column(volSizeCol)
|
||||
|
||||
volFormatCol = Gtk.TreeViewColumn("Format")
|
||||
vol_txt3 = Gtk.CellRendererText()
|
||||
volFormatCol.pack_start(vol_txt3, False)
|
||||
volFormatCol.add_attribute(vol_txt3, 'text', VOL_COLUMN_FORMAT)
|
||||
volFormatCol.add_attribute(vol_txt3, 'sensitive', VOL_COLUMN_SENSITIVE)
|
||||
volFormatCol.set_sort_column_id(VOL_COLUMN_FORMAT)
|
||||
self.widget("vol-list").append_column(volFormatCol)
|
||||
|
||||
volUseCol = Gtk.TreeViewColumn("Used By")
|
||||
vol_txt4 = Gtk.CellRendererText()
|
||||
volUseCol.pack_start(vol_txt4, False)
|
||||
volUseCol.add_attribute(vol_txt4, 'text', VOL_COLUMN_INUSEBY)
|
||||
volUseCol.add_attribute(vol_txt4, 'sensitive', VOL_COLUMN_SENSITIVE)
|
||||
volUseCol.set_sort_column_id(VOL_COLUMN_INUSEBY)
|
||||
self.widget("vol-list").append_column(volUseCol)
|
||||
|
||||
volListModel.set_sort_column_id(VOL_COLUMN_NAME,
|
||||
Gtk.SortType.ASCENDING)
|
||||
|
||||
# Init pool list
|
||||
# [connkey, label, pool.is_active(), percent string]
|
||||
pool_list = self.widget("pool-list")
|
||||
poolListModel = Gtk.ListStore(str, str, bool, str)
|
||||
pool_list.set_model(poolListModel)
|
||||
|
||||
poolCol = Gtk.TreeViewColumn("Storage Pools")
|
||||
pool_txt = Gtk.CellRendererText()
|
||||
pool_per = Gtk.CellRendererText()
|
||||
poolCol.pack_start(pool_per, False)
|
||||
poolCol.pack_start(pool_txt, True)
|
||||
poolCol.add_attribute(pool_txt, 'markup', POOL_COLUMN_LABEL)
|
||||
poolCol.add_attribute(pool_txt, 'sensitive', POOL_COLUMN_ISACTIVE)
|
||||
poolCol.add_attribute(pool_per, 'markup', POOL_COLUMN_PERCENT)
|
||||
pool_list.append_column(poolCol)
|
||||
poolListModel.set_sort_column_id(POOL_COLUMN_LABEL,
|
||||
Gtk.SortType.ASCENDING)
|
||||
|
||||
pool_list.get_selection().connect("changed", self._pool_selected)
|
||||
pool_list.get_selection().set_select_function(
|
||||
(lambda *x: self._confirm_changes()), None)
|
||||
|
||||
# Populate list and connect conn signals
|
||||
self._populate_pools()
|
||||
self.conn.connect("pool-added", self._conn_pools_changed)
|
||||
self.conn.connect("pool-removed", self._conn_pools_changed)
|
||||
self.conn.connect("pool-started", self._reselect_pool)
|
||||
self.conn.connect("pool-stopped", self._reselect_pool)
|
||||
self.conn.connect("state-changed", self._conn_state_changed)
|
||||
|
||||
self._conn_state_changed()
|
||||
|
||||
|
||||
###############
|
||||
# Public APIs #
|
||||
###############
|
||||
|
||||
def refresh_page(self):
|
||||
self._populate_vols()
|
||||
self.conn.schedule_priority_tick(pollpool=True)
|
||||
|
||||
def set_name_hint(self, val):
|
||||
self._name_hint = val
|
||||
|
||||
|
||||
####################
|
||||
# Internal helpers #
|
||||
####################
|
||||
|
||||
def _current_pool(self):
|
||||
connkey = uiutil.get_list_selection(self.widget("pool-list"), 0)
|
||||
try:
|
||||
return connkey and self.conn.get_pool(connkey)
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def _current_vol(self):
|
||||
pool = self._current_pool()
|
||||
if not pool:
|
||||
return None
|
||||
|
||||
connkey = uiutil.get_list_selection(self.widget("vol-list"), 0)
|
||||
try:
|
||||
return connkey and pool.get_volume(connkey)
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def _enable_pool_apply(self, *arglist):
|
||||
edittype = arglist[-1]
|
||||
self.widget("pool-apply").set_sensitive(True)
|
||||
if edittype not in self._active_edits:
|
||||
self._active_edits.append(edittype)
|
||||
|
||||
def _disable_pool_apply(self):
|
||||
for i in EDIT_POOL_IDS:
|
||||
if i in self._active_edits:
|
||||
self._active_edits.remove(i)
|
||||
|
||||
self.widget("pool-apply").set_sensitive(False)
|
||||
|
||||
def _reselect_pool(self, src, connkey):
|
||||
ignore = src
|
||||
|
||||
for row in self.widget("pool-list").get_model():
|
||||
if row[POOL_COLUMN_CONNKEY] != connkey:
|
||||
continue
|
||||
|
||||
# Update active sensitivity and percent available for passed key
|
||||
pool = self.conn.get_pool(connkey)
|
||||
row[POOL_COLUMN_ISACTIVE] = pool.is_active()
|
||||
row[POOL_COLUMN_PERCENT] = _get_pool_size_percent(pool)
|
||||
break
|
||||
|
||||
curpool = self._current_pool()
|
||||
if curpool.get_connkey() != connkey:
|
||||
return
|
||||
|
||||
# Currently selected pool changed state: force a 'pool_selected' to
|
||||
# update vol list
|
||||
self._pool_selected(self.widget("pool-list").get_selection())
|
||||
|
||||
def _reset_pool_state(self):
|
||||
self.widget("pool-details").set_sensitive(False)
|
||||
self.widget("pool-name").set_text("")
|
||||
self.widget("pool-name-entry").set_text("")
|
||||
self.widget("pool-sizes").set_markup("""<span size="large"> </span>""")
|
||||
self.widget("pool-type").set_text("")
|
||||
self.widget("pool-location").set_text("")
|
||||
self.widget("pool-state-icon").set_from_icon_name(
|
||||
ICON_SHUTOFF, Gtk.IconSize.BUTTON)
|
||||
self.widget("pool-state").set_text(_("Inactive"))
|
||||
self.widget("vol-list").get_model().clear()
|
||||
self.widget("pool-autostart").set_label(_("Never"))
|
||||
self.widget("pool-autostart").set_active(False)
|
||||
|
||||
self.widget("pool-delete").set_sensitive(False)
|
||||
self.widget("pool-stop").set_sensitive(False)
|
||||
self.widget("pool-start").set_sensitive(False)
|
||||
self.widget("vol-add").set_sensitive(False)
|
||||
self.widget("vol-delete").set_sensitive(False)
|
||||
self.widget("vol-list").set_sensitive(False)
|
||||
self._disable_pool_apply()
|
||||
|
||||
def _populate_pool_state(self, connkey):
|
||||
pool = self.conn.get_pool(connkey)
|
||||
pool.tick()
|
||||
auto = pool.get_autostart()
|
||||
active = pool.is_active()
|
||||
|
||||
# Set pool details state
|
||||
self.widget("pool-details").set_sensitive(True)
|
||||
self.widget("pool-name").set_markup("<b>%s:</b>" % pool.get_name())
|
||||
self.widget("pool-name-entry").set_text(pool.get_name())
|
||||
self.widget("pool-name-entry").set_editable(not active)
|
||||
self.widget("pool-sizes").set_markup(
|
||||
"""<span size="large">%s Free</span> / <i>%s In Use</i>""" %
|
||||
(pool.get_pretty_available(), pool.get_pretty_allocation()))
|
||||
self.widget("pool-type").set_text(
|
||||
StoragePool.get_pool_type_desc(pool.get_type()))
|
||||
self.widget("pool-location").set_text(
|
||||
pool.get_target_path())
|
||||
self.widget("pool-state-icon").set_from_icon_name(
|
||||
((active and ICON_RUNNING) or ICON_SHUTOFF),
|
||||
Gtk.IconSize.BUTTON)
|
||||
self.widget("pool-state").set_text(
|
||||
(active and _("Active")) or _("Inactive"))
|
||||
self.widget("pool-autostart").set_label(
|
||||
(auto and _("On Boot")) or _("Never"))
|
||||
self.widget("pool-autostart").set_active(auto)
|
||||
|
||||
self.widget("vol-list").set_sensitive(active)
|
||||
self._populate_vols()
|
||||
|
||||
self.widget("pool-delete").set_sensitive(not active)
|
||||
self.widget("pool-stop").set_sensitive(active)
|
||||
self.widget("pool-start").set_sensitive(not active)
|
||||
self.widget("vol-add").set_sensitive(active)
|
||||
self.widget("vol-add").set_tooltip_text(_("Create new volume"))
|
||||
self.widget("vol-delete").set_sensitive(False)
|
||||
|
||||
if active and not pool.supports_volume_creation():
|
||||
self.widget("vol-add").set_sensitive(False)
|
||||
self.widget("vol-add").set_tooltip_text(
|
||||
_("Pool does not support volume creation"))
|
||||
|
||||
def _set_storage_error_page(self, msg):
|
||||
self._reset_pool_state()
|
||||
self.widget("storage-pages").set_current_page(1)
|
||||
self.widget("storage-error-label").set_text(msg)
|
||||
|
||||
def _populate_pools(self):
|
||||
pool_list = self.widget("pool-list")
|
||||
curpool = self._current_pool()
|
||||
|
||||
# Prevent events while the model is modified
|
||||
model = pool_list.get_model()
|
||||
pool_list.set_model(None)
|
||||
pool_list.get_selection().unselect_all()
|
||||
model.clear()
|
||||
|
||||
for pool in self.conn.list_pools():
|
||||
name = pool.get_name()
|
||||
typ = StoragePool.get_pool_type_desc(pool.get_type())
|
||||
label = "%s\n<span size='small'>%s</span>" % (name, typ)
|
||||
|
||||
row = [None] * POOL_NUM_COLUMNS
|
||||
row[POOL_COLUMN_CONNKEY] = pool.get_connkey()
|
||||
row[POOL_COLUMN_LABEL] = label
|
||||
row[POOL_COLUMN_ISACTIVE] = pool.is_active()
|
||||
row[POOL_COLUMN_PERCENT] = _get_pool_size_percent(pool)
|
||||
|
||||
model.append(row)
|
||||
pool_list.set_model(model)
|
||||
|
||||
uiutil.set_row_selection(pool_list,
|
||||
curpool and curpool.get_connkey() or None)
|
||||
|
||||
def _populate_vols(self):
|
||||
list_widget = self.widget("vol-list")
|
||||
pool = self._current_pool()
|
||||
vols = pool and pool.get_volumes() or {}
|
||||
model = list_widget.get_model()
|
||||
list_widget.get_selection().unselect_all()
|
||||
model.clear()
|
||||
|
||||
for key in vols.keys():
|
||||
vol = vols[key]
|
||||
|
||||
try:
|
||||
path = vol.get_target_path()
|
||||
name = vol.get_pretty_name(pool.get_type())
|
||||
cap = vol.get_pretty_capacity()
|
||||
fmt = vol.get_format() or ""
|
||||
except:
|
||||
logging.debug("Error getting volume info for '%s', "
|
||||
"hiding it", key, exc_info=True)
|
||||
continue
|
||||
|
||||
namestr = None
|
||||
try:
|
||||
if path:
|
||||
names = VirtualDisk.path_in_use_by(vol.conn.get_backend(),
|
||||
path)
|
||||
namestr = ", ".join(names)
|
||||
if not namestr:
|
||||
namestr = None
|
||||
except:
|
||||
logging.exception("Failed to determine if storage volume in "
|
||||
"use.")
|
||||
|
||||
sensitive = True
|
||||
if self._vol_sensitive_cb:
|
||||
sensitive = self._vol_sensitive_cb(fmt)
|
||||
|
||||
row = [None] * VOL_NUM_COLUMNS
|
||||
row[VOL_COLUMN_KEY] = key
|
||||
row[VOL_COLUMN_NAME] = name
|
||||
row[VOL_COLUMN_CAPACITY] = cap
|
||||
row[VOL_COLUMN_FORMAT] = fmt
|
||||
row[VOL_COLUMN_INUSEBY] = namestr
|
||||
row[VOL_COLUMN_SENSITIVE] = sensitive
|
||||
model.append(row)
|
||||
|
||||
def _confirm_changes(self):
|
||||
if not self._active_edits:
|
||||
return True
|
||||
|
||||
if self.err.chkbox_helper(
|
||||
self.config.get_confirm_unapplied,
|
||||
self.config.set_confirm_unapplied,
|
||||
text1=(_("There are unapplied changes. "
|
||||
"Would you like to apply them now?")),
|
||||
chktext=_("Don't warn me again."),
|
||||
default=False):
|
||||
|
||||
if all([edit in EDIT_POOL_IDS for edit in self._active_edits]):
|
||||
self._pool_apply()
|
||||
|
||||
self._active_edits = []
|
||||
return True
|
||||
|
||||
|
||||
#############
|
||||
# Listeners #
|
||||
#############
|
||||
|
||||
def _conn_state_changed(self, ignore=None):
|
||||
conn_active = self.conn.is_active()
|
||||
self.widget("pool-add").set_sensitive(conn_active and
|
||||
self.conn.is_storage_capable())
|
||||
|
||||
if not conn_active:
|
||||
self._set_storage_error_page(_("Connection not active."))
|
||||
self._populate_pools()
|
||||
return
|
||||
|
||||
if not self.conn.is_storage_capable():
|
||||
self._set_storage_error_page(
|
||||
_("Libvirt connection does not support storage management."))
|
||||
|
||||
def _cancel_clicked(self, src):
|
||||
ignore = src
|
||||
self.emit("cancel-clicked")
|
||||
|
||||
def _browse_local_clicked(self, src):
|
||||
ignore = src
|
||||
self.emit("browse-clicked")
|
||||
|
||||
def _choose_volume_clicked(self, src):
|
||||
ignore = src
|
||||
self.emit("volume-chosen", self._current_vol())
|
||||
|
||||
def _vol_list_row_activated(self, src, treeiter, viewcol):
|
||||
ignore = src
|
||||
ignore = treeiter
|
||||
ignore = viewcol
|
||||
self.emit("volume-chosen", self._current_vol())
|
||||
|
||||
def _pool_selected(self, src):
|
||||
model, treeiter = src.get_selected()
|
||||
if treeiter is None:
|
||||
self._set_storage_error_page(_("No storage pool selected."))
|
||||
return
|
||||
|
||||
self.widget("storage-pages").set_current_page(0)
|
||||
connkey = model[treeiter][0]
|
||||
|
||||
try:
|
||||
self._populate_pool_state(connkey)
|
||||
except Exception, e:
|
||||
logging.exception(e)
|
||||
self._set_storage_error_page(_("Error selecting pool: %s") % e)
|
||||
self._disable_pool_apply()
|
||||
|
||||
def _reselect_current_pool(self, ignore1=None):
|
||||
cp = self._current_pool()
|
||||
if cp is None:
|
||||
return
|
||||
|
||||
cp.refresh()
|
||||
self._reselect_pool(None, cp.get_connkey())
|
||||
|
||||
def _conn_pools_changed(self, src, connkey):
|
||||
ignore = src
|
||||
ignore = connkey
|
||||
self._populate_pools()
|
||||
|
||||
def _pool_autostart_changed(self, src):
|
||||
ignore = src
|
||||
auto = self.widget("pool-autostart").get_active()
|
||||
self.widget("pool-autostart").set_label(
|
||||
auto and _("On Boot") or _("Never"))
|
||||
self._enable_pool_apply(EDIT_POOL_AUTOSTART)
|
||||
|
||||
def _vol_selected(self, src):
|
||||
model, treeiter = src.get_selected()
|
||||
self.widget("vol-delete").set_sensitive(bool(treeiter))
|
||||
|
||||
can_choose = bool(treeiter and model[treeiter][VOL_COLUMN_SENSITIVE])
|
||||
self.widget("choose-volume").set_sensitive(can_choose)
|
||||
|
||||
def _vol_popup_menu(self, widget_ignore, event):
|
||||
if event.button != 3:
|
||||
return
|
||||
|
||||
self._volmenu.popup(None, None, None, None, 0, event.time)
|
||||
|
||||
|
||||
#########################
|
||||
# Pool action listeners #
|
||||
#########################
|
||||
|
||||
def _pool_stop(self, src_ignore):
|
||||
pool = self._current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
logging.debug("Stopping pool '%s'", pool.get_name())
|
||||
vmmAsyncJob.simple_async_noshow(pool.stop, [], self,
|
||||
_("Error stopping pool '%s'") % pool.get_name())
|
||||
|
||||
def _pool_start(self, src):
|
||||
ignore = src
|
||||
pool = self._current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
logging.debug("Starting pool '%s'", pool.get_name())
|
||||
vmmAsyncJob.simple_async_noshow(pool.start, [], self,
|
||||
_("Error starting pool '%s'") % pool.get_name())
|
||||
|
||||
def _pool_add(self, src):
|
||||
ignore = src
|
||||
logging.debug("Launching 'Add Pool' wizard")
|
||||
|
||||
try:
|
||||
if self._addpool is None:
|
||||
self._addpool = vmmCreatePool(self.conn)
|
||||
self._addpool.show(self.topwin)
|
||||
except Exception, e:
|
||||
self.err.show_err(_("Error launching pool wizard: %s") % str(e))
|
||||
|
||||
def _pool_delete(self, src):
|
||||
ignore = src
|
||||
pool = self._current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
result = self.err.yes_no(_("Are you sure you want to permanently "
|
||||
"delete the pool %s?") % pool.get_name())
|
||||
if not result:
|
||||
return
|
||||
|
||||
logging.debug("Deleting pool '%s'", pool.get_name())
|
||||
vmmAsyncJob.simple_async_noshow(pool.delete, [], self,
|
||||
_("Error deleting pool '%s'") % pool.get_name())
|
||||
|
||||
def _pool_refresh(self, src):
|
||||
ignore = src
|
||||
if self._in_refresh:
|
||||
logging.debug("Already refreshing the pool, skipping")
|
||||
return
|
||||
|
||||
if not self._confirm_changes():
|
||||
return
|
||||
|
||||
pool = self._current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
self._in_refresh = True
|
||||
|
||||
def cb():
|
||||
try:
|
||||
pool.refresh()
|
||||
self.idle_add(self._reselect_current_pool)
|
||||
finally:
|
||||
self._in_refresh = False
|
||||
|
||||
logging.debug("Refresh pool '%s'", pool.get_name())
|
||||
vmmAsyncJob.simple_async_noshow(cb, [], self,
|
||||
_("Error refreshing pool '%s'") % pool.get_name())
|
||||
|
||||
def _pool_apply(self):
|
||||
pool = self._current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
logging.debug("Applying changes for pool '%s'", pool.get_name())
|
||||
try:
|
||||
if EDIT_POOL_AUTOSTART in self._active_edits:
|
||||
auto = self.widget("pool-autostart").get_active()
|
||||
pool.set_autostart(auto)
|
||||
if EDIT_POOL_NAME in self._active_edits:
|
||||
pool.define_name(self.widget("pool-name-entry").get_text())
|
||||
self.idle_add(self._populate_pools)
|
||||
except Exception, e:
|
||||
self.err.show_err(_("Error changing pool settings: %s") % str(e))
|
||||
return
|
||||
|
||||
self._disable_pool_apply()
|
||||
|
||||
|
||||
###########################
|
||||
# Volume action listeners #
|
||||
###########################
|
||||
|
||||
def _vol_copy_path(self, src):
|
||||
ignore = src
|
||||
vol = self._current_vol()
|
||||
if not vol:
|
||||
return
|
||||
|
||||
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||
target_path = vol.get_target_path()
|
||||
if target_path:
|
||||
clipboard.set_text(target_path, -1)
|
||||
|
||||
def _vol_add(self, src):
|
||||
ignore = src
|
||||
pool = self._current_pool()
|
||||
if pool is None:
|
||||
return
|
||||
|
||||
logging.debug("Launching 'Add Volume' wizard for pool '%s'",
|
||||
pool.get_name())
|
||||
try:
|
||||
if self._addvol is None:
|
||||
self._addvol = vmmCreateVolume(self.conn, pool)
|
||||
self._addvol.connect("vol-created", self._reselect_current_pool)
|
||||
else:
|
||||
self._addvol.set_parent_pool(self.conn, pool)
|
||||
self._addvol.set_modal(self.topwin.get_modal())
|
||||
self._addvol.set_name_hint(self._name_hint)
|
||||
self._addvol.show(self.topwin)
|
||||
except Exception, e:
|
||||
self.err.show_err(_("Error launching volume wizard: %s") % str(e))
|
||||
|
||||
def _vol_delete(self, src_ignore):
|
||||
vol = self._current_vol()
|
||||
if vol is None:
|
||||
return
|
||||
|
||||
result = self.err.yes_no(_("Are you sure you want to permanently "
|
||||
"delete the volume %s?") % vol.get_name())
|
||||
if not result:
|
||||
return
|
||||
|
||||
def cb():
|
||||
vol.delete()
|
||||
def idlecb():
|
||||
self._reselect_current_pool()
|
||||
self._populate_vols()
|
||||
self.idle_add(idlecb)
|
||||
|
||||
logging.debug("Deleting volume '%s'", vol.get_name())
|
||||
vmmAsyncJob.simple_async_noshow(cb, [], self,
|
||||
_("Error refreshing volume '%s'") % vol.get_name())
|
||||
Reference in New Issue
Block a user