Scott Seago
2008-Oct-30 23:53 UTC
[Ovirt-devel] [PATCH] lvm storage volume HW pool admin tab.
Includes 'view storage volume' facebox popup and new/delete LVM volume
functionality. Although lvm tasks are created, taskomatic doesn't yet know
what to do with them.
Signed-off-by: Scott Seago <sseago at redhat.com>
---
src/app/controllers/hardware_controller.rb | 5 +-
src/app/controllers/storage_controller.rb | 180 +++++++++++++++++++++++++-
src/app/controllers/task_controller.rb | 2 +
src/app/models/lvm_storage_pool.rb | 5 +
src/app/models/storage_volume_task.rb | 3 +-
src/app/views/storage/_lvm_volume_form.rhtml | 19 +++
src/app/views/storage/new_lvm_volume.rhtml | 42 ++++++
src/app/views/storage/show.rhtml | 63 +++++----
src/app/views/storage/show_volume.rhtml | 169 +++++++++++++++++-------
9 files changed, 404 insertions(+), 84 deletions(-)
create mode 100644 src/app/views/storage/_lvm_volume_form.rhtml
create mode 100644 src/app/views/storage/new_lvm_volume.rhtml
diff --git a/src/app/controllers/hardware_controller.rb
b/src/app/controllers/hardware_controller.rb
index 4f24cb3..8c26184 100644
--- a/src/app/controllers/hardware_controller.rb
+++ b/src/app/controllers/hardware_controller.rb
@@ -117,7 +117,8 @@ class HardwareController < PoolController
def show_tasks
@task_types = [["VM Task", "VmTask"],
["Host Task", "HostTask"],
- ["Storage Task", "StorageTask",
"break"],
+ ["Storage Task", "StorageTask"],
+ ["Storage Volume Task",
"StorageVolumeTask", "break"],
["Show All", ""]]
super
end
@@ -162,7 +163,7 @@ class HardwareController < PoolController
if params[:id]
pre_show
storage_pools = @pool.storage_pools
- find_opts = {}
+ find_opts = {:conditions => "type !=
'LvmStoragePool'"}
include_pool = false
else
# FIXME: no permissions or usage checks here yet
diff --git a/src/app/controllers/storage_controller.rb
b/src/app/controllers/storage_controller.rb
index fe524f3..75058cd 100644
--- a/src/app/controllers/storage_controller.rb
+++ b/src/app/controllers/storage_controller.rb
@@ -25,6 +25,7 @@ class StorageController < ApplicationController
before_filter :pre_pool_admin, :only => [:refresh]
before_filter :pre_new2, :only => [:new2]
before_filter :pre_json, :only => [:storage_volumes_json]
+ before_filter :pre_create_volume, :only => [:create_volume]
def index
list
@@ -90,15 +91,25 @@ class StorageController < ApplicationController
flash[:notice] = 'You do not have permission to view this storage
pool: redirecting to top level'
redirect_to :controller => 'dashboard'
end
- json_list(@storage_pool.storage_volumes,
- [:display_name, :size_in_gb, :get_type_label])
+ attr_list = []
+ attr_list << :id if (@storage_pool.user_subdividable and @can_modify)
+ attr_list += [:display_name, :size_in_gb, :get_type_label]
+ json_list(@storage_pool.storage_volumes, attr_list)
end
def show_volume
@storage_volume = StorageVolume.find(params[:id])
set_perms(@storage_volume.storage_pool.hardware_pool)
unless @can_view
flash[:notice] = 'You do not have permission to view this storage
volume: redirecting to top level'
- redirect_to :controller => 'dashboard'
+ respond_to do |format|
+ format.html { redirect_to :controller => 'dashboard' }
+ format.xml { head :forbidden }
+ end
+ else
+ respond_to do |format|
+ format.html { render :layout => 'popup' }
+ format.xml { render :xml => @storage_volume.to_xml }
+ end
end
end
@@ -110,6 +121,70 @@ class StorageController < ApplicationController
render :layout => false
end
+ def new_volume
+ new_volume_internal(StoragePool.find(params[:storage_pool_id]),
+ { :storage_pool_id => params[:storage_pool_id]})
+ render :layout => 'popup'
+ end
+
+ def new_lvm_volume
+ @source_volume = StorageVolume.find(params[:source_volume_id])
+ @return_facebox = params[:return_facebox]
+ unless @source_volume.supports_lvm_subdivision
+ #fixme: proper error page for popups
+ redirect_to :controller => 'dashboard'
+ return
+ end
+ lvm_pool = @source_volume.lvm_storage_pool
+ unless lvm_pool
+ # FIXME: what should we do about VG/LV names?
+ # for now auto-create VG name as ovirt_vg_#{@source_volume.id}
+ lvm_pool = LvmStoragePool.new(:vg_name =>
"ovirt_vg_#{@source_volume.id}",
+ :hardware_pool_id =>
@source_volume.storage_pool.hardware_pool_id)
+ lvm_pool.source_volumes << @source_volume
+ lvm_pool.save!
+ end
+ new_volume_internal(lvm_pool, { :storage_pool_id => lvm_pool.id})
+ @storage_volume.lv_owner_perms='0744'
+ @storage_volume.lv_group_perms='0744'
+ @storage_volume.lv_mode_perms='0744'
+ render :layout => 'popup'
+ end
+
+ def create_volume
+ begin
+ StorageVolume.transaction do
+ @storage_volume.save!
+ @task = StorageVolumeTask.new({ :user => @user,
+ :task_target => @storage_volume,
+ :action =>
StorageVolumeTask::ACTION_CREATE_VOLUME,
+ :state => Task::STATE_QUEUED})
+ @task.save!
+ end
+ respond_to do |format|
+ format.json { render :json => { :object =>
"storage_volume",
+ :success => true,
+ :alert => "Storage Volume was successfully created." }
}
+ format.xml { render :xml => @storage_volume,
+ :status => :created,
+ # FIXME: create storage_volume_url method if relevant
+ :location => storage_pool_url(@storage_volume)
+ }
+ end
+ rescue => ex
+ # FIXME: need to distinguish volume vs. task save errors
+ respond_to do |format|
+ format.json {
+ json_hash = { :object => "storage_volume", :success
=> false,
+ :errors => @storage_volume.errors.localize_error_messages.to_a
}
+ json_hash[:message] = ex.message if json_hash[:errors].empty?
+ render :json => json_hash }
+ format.xml { render :xml => @storage_volume.errors,
+ :status => :unprocessable_entity }
+ end
+ end
+ end
+
def insert_refresh_task
@task = StorageTask.new({ :user => @user,
:task_target => @storage_pool,
@@ -244,6 +319,67 @@ class StorageController < ApplicationController
end
end
+ def delete_volumes
+ storage_volume_ids_str = params[:storage_volume_ids]
+ storage_volume_ids = storage_volume_ids_str.split(",").collect
{|x| x.to_i}
+ alerts = []
+ status = true
+ begin
+ StorageVolume.transaction do
+ storage = StorageVolume.find(:all, :conditions => "id in
(#{storage_volume_ids.join(', ')})")
+ unless storage.empty?
+ set_perms(storage[0].storage_pool.hardware_pool)
+ unless @can_modify and storage[0].storage_pool.user_subdividable
+ respond_to do |format|
+ format.json { render :json => { :object =>
"storage_volume",
+ :success => false,
+ :alert => "You do not have permission to delete this
storage volume." } }
+ format.xml { head :forbidden }
+ end
+ else
+ storage.each do |storage_volume|
+ alert, success = delete_volume_internal(storage_volume)
+ alerts << alert
+ status = false unless success
+ end
+ respond_to do |format|
+ format.json { render :json => { :object =>
"storage_volume",
+ :success => status, :alert =>
alerts.join("\n") } }
+ format.xml { head(status ? :ok : :method_not_allowed) }
+ end
+ end
+ else
+ respond_to do |format|
+ format.json { render :json => { :object =>
"storage_volume",
+ :success => false, :alert => "no volumes
selected" } }
+ format.xml { head(status ? :ok : :method_not_allowed) }
+ end
+ end
+ end
+ end
+ end
+
+ def delete_volume
+ @storage_volume = StorageVolume.find(params[:id])
+ set_perms(@storage_volume.storage_pool.hardware_pool)
+ unless @can_modify and @storage_volume.storage_pool.user_subdividable
+ respond_to do |format|
+ format.json { render :json => { :object =>
"storage_volume",
+ :success => false,
+ :alert => "You do not have permission to delete this
storage volume." } }
+ format.xml { head :forbidden }
+ end
+ else
+ alert, success = delete_volume_internal(@storage_volume)
+ respond_to do |format|
+ format.json { render :json => { :object =>
"storage_volume",
+ :success => success, :alert => alert } }
+ format.xml { head(success ? :ok : :method_not_allowed) }
+ end
+ end
+
+ end
+
def pre_new
@hardware_pool = HardwarePool.find(params[:hardware_pool_id])
@perm_obj = @hardware_pool
@@ -274,6 +410,16 @@ class StorageController < ApplicationController
@perm_obj = @storage_pool.hardware_pool
@redir_obj = @storage_pool
end
+ def pre_create_volume
+ volume = params[:storage_volume]
+ unless type = params[:storage_type]
+ type = volume.delete(:storage_type)
+ end
+ @storage_volume = StorageVolume.factory(type, volume)
+ @perm_obj = @storage_volume.storage_pool.hardware_pool
+ @redir_controller =
@storage_volume.storage_pool.hardware_pool.get_controller
+ authorize_admin
+ end
def pre_json
pre_show
end
@@ -282,4 +428,32 @@ class StorageController < ApplicationController
authorize_admin
end
+ private
+ def new_volume_internal(storage_pool, new_params)
+ @storage_volume = StorageVolume.factory(storage_pool.get_type_label,
new_params)
+ @perm_obj = @storage_volume.storage_pool.hardware_pool
+ authorize_admin
+ end
+
+ def delete_volume_internal(volume)
+ begin
+ name = volume.display_name
+ if !volume.vms.empty?
+ vm_list = volume.vms.collect {|vm| vm.description}.join(", ")
+ ["Storage Volume #{name} must be unattached from VMs (#{vm_list})
before deleting it.",
+ false]
+ #FIXME: create delete volume task. include metadata in task
+ else
+ #FIXME: need to set volume to 'unavailable' state once we have
states
+ @task = StorageVolumeTask.new({ :user => @user,
+ :task_target => volume,
+ :action =>
StorageVolumeTask::ACTION_DELETE_VOLUME,
+ :state => Task::STATE_QUEUED})
+ @task.save!
+ ["Storage Volume #{name} deletion was successfully queued.",
true]
+ end
+ rescue => ex
+ ["Failed to delete storage volume #{name}
(#{ex.message}.",false]
+ end
+ end
end
diff --git a/src/app/controllers/task_controller.rb
b/src/app/controllers/task_controller.rb
index 73e5155..647d69c 100644
--- a/src/app/controllers/task_controller.rb
+++ b/src/app/controllers/task_controller.rb
@@ -24,6 +24,8 @@ class TaskController < ApplicationController
set_perms(@task.vm.vm_resource_pool)
elsif @task[:type] == StorageTask.name
set_perms(@task.storage_pool.hardware_pool)
+ elsif @task[:type] == StorageVolumeTask.name
+ set_perms(@task.storage_volume.storage_pool.hardware_pool)
elsif @task[:type] == HostTask.name
set_perms(@task.host.hardware_pool)
end
diff --git a/src/app/models/lvm_storage_pool.rb
b/src/app/models/lvm_storage_pool.rb
index 08b8938..84fbb28 100644
--- a/src/app/models/lvm_storage_pool.rb
+++ b/src/app/models/lvm_storage_pool.rb
@@ -44,5 +44,10 @@ class LvmStoragePool < StoragePool
kb_to_gb(size)
end
+ # FIXME: needs to take free space into account here
+ def user_subdividable
+ true
+ end
+
end
diff --git a/src/app/models/storage_volume_task.rb
b/src/app/models/storage_volume_task.rb
index 78f2895..136f1ba 100644
--- a/src/app/models/storage_volume_task.rb
+++ b/src/app/models/storage_volume_task.rb
@@ -20,7 +20,7 @@
class StorageVolumeTask < Task
ACTION_CREATE_VOLUME = "create_volume"
- ACTION_EDIT_VOLUME = "edit_volume"
+ ACTION_DELETE_VOLUME = "delete_volume"
def after_initialize
self.hardware_pool = task_target.storage_pool.hardware_pool if
self.task_target_type=="StorageVolume"
@@ -29,7 +29,6 @@ class StorageVolumeTask < Task
def task_obj
"StorageVolume;;;#{self.storage_volume.id};;;#{self.storage_volume.display_name}"
end
- end
def host
nil
end
diff --git a/src/app/views/storage/_lvm_volume_form.rhtml
b/src/app/views/storage/_lvm_volume_form.rhtml
new file mode 100644
index 0000000..823158e
--- /dev/null
+++ b/src/app/views/storage/_lvm_volume_form.rhtml
@@ -0,0 +1,19 @@
+<%= error_messages_for 'storage_volume' %>
+
+<!--[form:storage_pool]-->
+<%= hidden_field 'storage_volume', 'storage_pool_id' %>
+<%= hidden_field_tag 'storage_type', @storage_volume.get_type_label
%>
+
+<%= text_field_with_label "Size (GB):", 'storage_volume',
'size_in_gb' %>
+
+<%= text_field_with_label "LV Name:", 'storage_volume',
'lv_name' %>
+
+<%= text_field_with_label "Owner permissions:",
'storage_volume', 'lv_owner_perms' %>
+
+<%= text_field_with_label "Group permissions:",
'storage_volume', 'lv_group_perms' %>
+
+<%= text_field_with_label "Mode permissions:",
'storage_volume', 'lv_mode_perms' %>
+
+
+<!--[eoform:storage_volume]-->
+
diff --git a/src/app/views/storage/new_lvm_volume.rhtml
b/src/app/views/storage/new_lvm_volume.rhtml
new file mode 100644
index 0000000..ea51cc5
--- /dev/null
+++ b/src/app/views/storage/new_lvm_volume.rhtml
@@ -0,0 +1,42 @@
+<%- content_for :title do -%>
+ <%= _("Add New LVM Volume") %>
+<%- end -%>
+<%- content_for :description do -%>
+ Add a new LVM Storage Volume to <%= @source_volume.display_name %>.
+<%- end -%>
+<div class="panel_header"></div>
+<div class="dialog_form">
+<form method="POST" action="<%= url_for :action =>
'create_volume' %>" id="lvm_volume_form" >
+ <div class="dialog_form">
+ <div id="new_storage_pool">
+ <%= render :partial => 'lvm_volume_form' %>
+ </div>
+ </div>
+ <!-- FIXME: need to pop up the details dialog again -->
+ <%= popup_footer("$('#lvm_volume_form').submit()",
"New LVM Volume") %>
+</form>
+</div>
+<script type="text/javascript">
+function afterLvmVolume(response, status){
+ ajax_validation(response, status);
+ if (response.success) {
+ <% if @return_facebox %>
+ $('#window').fadeOut('fast');
+ $("#window").empty().load("<%= @return_facebox
%>")
+ $('#window').fadeIn('fast');
+ <% else %>
+ $(document).trigger('close.facebox');
+ <% end %>
+ }
+}
+$(function() {
+ var lvmvolumeoptions = {
+ target: '<%= url_for :action =>
'create_volume' %>', // target element to update
+ dataType: 'json',
+ success: afterLvmVolume // post-submit callback
+ };
+
+ // bind form using 'ajaxForm'
+ $('#lvm_volume_form').ajaxForm(lvmvolumeoptions);
+});
+</script>
diff --git a/src/app/views/storage/show.rhtml b/src/app/views/storage/show.rhtml
index 82c9dab..a84dc62 100644
--- a/src/app/views/storage/show.rhtml
+++ b/src/app/views/storage/show.rhtml
@@ -18,24 +18,24 @@
<div id="storage_selection_id"
style="display:none"><%= @storage_pool.id %></div>
<div class="selection_key">
- IP address: </br>
+ IP address:<br/>
<% if @storage_pool[:type] == "IscsiStoragePool" %>
- Port: </br>
- Target: </br>
+ Port:<br/>
+ Target:<br/>
<% elsif @storage_pool[:type] == "NfsStoragePool" %>
- Export path:</br>
+ Export path:<br/>
<% end %>
- Type: </br>
+ Type:<br/>
</div>
<div class="selection_value">
- <%=h @storage_pool.ip_addr %></br>
+ <%=h @storage_pool.ip_addr %><br/>
<% if @storage_pool[:type] == "IscsiStoragePool" %>
- <%=h @storage_pool.port %></br>
- <%=h @storage_pool.target %></br>
+ <%=h @storage_pool.port %><br/>
+ <%=h @storage_pool.target %><br/>
<% elsif @storage_pool[:type] == "NfsStoragePool" %>
- <%=h @storage_pool.export_path %></br>
+ <%=h @storage_pool.export_path %><br/>
<% end %>
- <%=h @storage_pool.get_type_label %></br>
+ <%=h @storage_pool.get_type_label %><br/>
</div>
<%- content_for :right do -%>
<div class="selection_right_title">Volumes</div>
@@ -54,24 +54,33 @@
<%= confirmation_dialog("confirm_delete_storage", "Are you
sure?", "delete_storage_pool()") %>
<script type="text/javascript">
- $("#storage_volumes_grid").flexigrid
- (
- {
- url: '<%= url_for :action => "storage_volumes_json", :id
=> @storage_pool.id %>',
- dataType: 'json',
- colModel : [
- {display: 'Alias', name : 'display_name', width : 180,
sortable : false, align: 'left'},
- {display: 'Size (Gb)', name : 'size', width : 80,
sortable : true, align: 'left'},
- {display: 'Type', name : 'type', width : 80, sortable : true,
align: 'left'}
- ],
- sortname: "size",
- sortorder: "desc",
- usepager: false,
- useRp: true,
- rp: 40,
- showTableToggleBtn: true,
- }
+ $("#storage_volumes_grid").flexigrid
+ (
+ {
+ url: '<%= url_for :action => "storage_volumes_json",
:id => @storage_pool.id %>',
+ dataType: 'json',
+ colModel : [
+ <%= "{display: '', width : 20, align: 'left',
process: storage_volumes_gridcheckbox}," if
(@storage_pool.user_subdividable and @can_modify) %>
+ {display: 'Alias', width : 180, sortable : false, align:
'left', process: storage_volume_details_link},
+ {display: 'Size (Gb)', name : 'size', width : 80,
sortable : true, align: 'left'},
+ {display: 'Type', name : 'type', width : 80, sortable :
true, align: 'left'}
+ ],
+ sortname: "size",
+ sortorder: "desc",
+ usepager: false,
+ useRp: true,
+ rp: 40,
+ showTableToggleBtn: true,
+ }
);
+ function storage_volumes_gridcheckbox(celDiv)
+ {
+ $(celDiv).html('<input class="grid_checkbox"
type="checkbox"
name="grid_checkbox'+$(celDiv).html()+'"
value="'+$(celDiv).html()+'"/>');
+ }
+ function storage_volume_details_link(celDiv,pid)
+ {
+ $(celDiv).html('<a href="<%= url_for :controller =>
'storage', :action => 'show_volume' %>/'+ pid
+'" rel="facebox[.bolder]">' + $(celDiv).html() +
'</a>');
+ }
</script>
<script type="text/javascript">
diff --git a/src/app/views/storage/show_volume.rhtml
b/src/app/views/storage/show_volume.rhtml
index 9aeb808..e39c10e 100644
--- a/src/app/views/storage/show_volume.rhtml
+++ b/src/app/views/storage/show_volume.rhtml
@@ -1,54 +1,123 @@
- <div id="data">
- <div class="inside">
-
- <div id="dataTableWrapper">
-
- <div class="dataTable">
- <div class="inside">
-
- <div class="data-table-column">
+<%- content_for :title do -%>
+ <%=h @storage_volume.display_name %>
+<%- end -%>
+<%- content_for :description do -%>
+ Details for Storage Volume <%=h @storage_volume.display_name %>
+<%- end -%>
- <p><b>IP address:</b> <%=h
@storage_volume.storage_pool.ip_addr %></p>
-<% if @storage_volume.storage_pool[:type] == "IscsiStoragePool"
%>
- <p><b>Port:</b> <%=h
@storage_volume.storage_pool.port %></p>
- <p><b>Target:</b> <%=h
@storage_volume.storage_pool[:target] %></p>
-<% elsif @storage_volume.storage_pool[:type] == "NfsStoragePool"
%>
- <p><b>Export path:</b> <%=h
@storage_volume.storage_pool.export_path %></p>
-<% end %>
- <p><b>Type:</b> <%=h
@storage_volume.storage_pool.get_type_label %></p>
- <p></p>
- <p><b>Path:</b> <%=h
@storage_volume.path %></p>
-<% if @storage_volume[:type] == "IscsiStorageVolume" %>
- <p><b>LUN:</b> <%=h
@storage_volume.lun %></p>
-<% elsif @storage_volume[:type] == "NfsStorageVolume" %>
- <p><b>Filename:</b> <%=h
@storage_volume.filename %></p>
+<% if @storage_volume.supports_lvm_subdivision %>
+ <div id="dialog-content-area">
+ <div id="toolbar_nav">
+ <ul>
+ <li><a href="#"
id="new_lvm_volume_button"><%=image_tag
"icon_addstorage.png", :style=>"vertical-align:middle;"
%>Add</a></li>
+ <% if (@storage_volume.lvm_storage_pool and
+ !@storage_volume.lvm_storage_pool.storage_volumes.empty?) %>
+ <li><a href="#" onClick="return
remove_lvm_volumes();"><%=image_tag "icon_remove.png"
%> Remove</a></li>
+ <% end %>
+ </ul>
+ </div>
+ </div>
<% end %>
- <p><b>Size:</b> <%=h
@storage_volume.size_in_gb %> GB</p>
-
- </div>
-
- </div> <!-- end #data-table.inside -->
- </div> <!-- end #dataTable -->
-
- </div> <!-- end #dataTableWrapper -->
-
- </div> <!-- end #data.inside -->
- </div> <!-- end #data -->
-
- </td>
- <td id="right">
- <div class="heading"> </div>
- <div id="tools">
- <h3>Actions</h3>
- <%if @can_modify -%>
- <% end -%>
- <div class="actions">
- <%= link_to "Back to #{@storage_volume.storage_pool.ip_addr}:
#{@storage_volume.storage_pool[:target]}", { :action => 'show',
:id => @storage_volume.storage_pool.id }, { :class => "" } %>
- </div>
+<div class="panel_header"></div>
+ <div class="selection_key">
+ IP address:<br/>
+ <% if @storage_volume.storage_pool[:type] ==
"IscsiStoragePool" %>
+ Port:<br/>
+ Target:<br/>
+ <% elsif @storage_volume.storage_pool[:type] ==
"NfsStoragePool" %>
+ Export path:<br/>
+ <% end %>
+ Type:<br/>
+ Path:<br/>
+ <% if @storage_volume[:type] == "IscsiStorageVolume" %>
+ LUN:<br/>
+ <% elsif @storage_volume[:type] == "NfsStorageVolume" %>
+ Filename:<br/>
+ <% end %>
+ Size:<br/>
</div>
+ <div class="selection_value">
+ <%=h @storage_volume.storage_pool.ip_addr %><br/>
+ <% if @storage_volume.storage_pool[:type] ==
"IscsiStoragePool" %>
+ <%=h @storage_volume.storage_pool.port %><br/>
+ <%=h @storage_volume.storage_pool[:target] %><br/>
+ <% elsif @storage_volume.storage_pool[:type] ==
"NfsStoragePool" %>
+ <%=h @storage_volume.storage_pool.export_path %><br/>
+ <% end %>
+ <%=h @storage_volume.storage_pool.get_type_label %><br/>
+ <%=h @storage_volume.path %><br/>
+ <% if @storage_volume[:type] == "IscsiStorageVolume" %>
+ <%=h @storage_volume.lun %><br/>
+ <% elsif @storage_volume[:type] == "NfsStorageVolume" %>
+ <%=h @storage_volume.filename %><br/>
+ <% end %>
+ <%=h @storage_volume.size_in_gb %> GB<br/>
+ </div>
+<% if @storage_volume.supports_lvm_subdivision %>
+ <div class="dialog_form">
+ <div class="form_heading">LVM Volumes</div>
+ <% if @storage_volume.lvm_storage_pool %>
+ <form id="lvm_storage_volumes_grid_form">
+ <table id="lvm_storage_volumes_grid"
style="display:none"></table>
+ </form>
+ <% end %>
+ </div>
+<% end %>
- </td>
-
-<%- content_for :title do -%>
-<%= _("Storage Volume") %>
-<%- end -%>
+<div class="facebox_timfooter">
+ <div class="button">
+ <div class="button_left_grey"></div>
+ <div class="button_middle_grey"><a
href="#"
onclick="jQuery(document).trigger('close.facebox')">Close</a></div>
+ <div class="button_right_grey"></div>
+ </div>
+</div>
+<script type="text/javascript">
+ $("#lvm_storage_volumes_grid").flexigrid
+ (
+ {
+ url: '<%= url_for :action => "storage_volumes_json",
:id => @storage_volume.lvm_storage_pool.id %>',
+ dataType: 'json',
+ colModel : [
+ <%= "{display: '', width : 20, align: 'left',
process: lvm_storage_volumes_gridcheckbox}," if @can_modify %>
+ {display: 'Alias', width : 180, sortable : false, align:
'left'},
+ {display: 'Size (Gb)', name : 'size', width : 80,
sortable : true, align: 'left'},
+ {display: 'Type', name : 'type', width : 80, sortable :
true, align: 'left'}
+ ],
+ sortname: "size",
+ sortorder: "desc",
+ usepager: false,
+ useRp: true,
+ rp: 40,
+ showTableToggleBtn: true,
+ }
+ );
+ function lvm_storage_volumes_gridcheckbox(celDiv)
+ {
+ $(celDiv).html('<input class="grid_checkbox"
type="checkbox"
name="grid_checkbox'+$(celDiv).html()+'"
value="'+$(celDiv).html()+'"/>');
+ }
+ function get_selected_lvm_volumes()
+ {
+ return get_selected_checkboxes("lvm_storage_volumes_grid_form");
+ }
+ function remove_lvm_volumes()
+ {
+ var volumes = get_selected_lvm_volumes();
+ if (validate_selected(volumes, "storage volume")) {
+ $.post('<%= url_for :controller => "storage", :action
=> "delete_volumes" %>',
+ { storage_volume_ids: volumes.toString() },
+ function(data,status){
+ $("#lvm_storage_volumes_grid").flexReload();
+ if (data.alert) {
+ $.jGrowl(data.alert);
+ }
+ }, 'json');
+ }
+ }
+$('#new_lvm_volume_button').click(function(){
+ $('#window').fadeOut('fast');
+ $("#window").empty().load("<%= url_for :controller
=> 'storage', :action => 'new_lvm_volume' %>",
+ { source_volume_id: <%=@storage_volume.id%>,
+ return_facebox: '<%= url_for :controller =>
'storage', :action => 'show_volume', :id =>
@storage_volume.id %>' });
+ $('#window').fadeIn('fast');
+});
+</script>
--
1.5.5.1
Jason Guiditta
2008-Nov-03 22:24 UTC
[Ovirt-devel] [PATCH] lvm storage volume HW pool admin tab.
On Thu, 2008-10-30 at 23:53 +0000, Scott Seago wrote:> Includes 'view storage volume' facebox popup and new/delete LVM volume functionality. Although lvm tasks are created, taskomatic doesn't yet know what to do with them. > > Signed-off-by: Scott Seago <sseago at redhat.com> > --- > src/app/controllers/hardware_controller.rb | 5 +- > src/app/controllers/storage_controller.rb | 180 +++++++++++++++++++++++++- > src/app/controllers/task_controller.rb | 2 + > src/app/models/lvm_storage_pool.rb | 5 + > src/app/models/storage_volume_task.rb | 3 +- > src/app/views/storage/_lvm_volume_form.rhtml | 19 +++ > src/app/views/storage/new_lvm_volume.rhtml | 42 ++++++ > src/app/views/storage/show.rhtml | 63 +++++---- > src/app/views/storage/show_volume.rhtml | 169 +++++++++++++++++------- > 9 files changed, 404 insertions(+), 84 deletions(-) > create mode 100644 src/app/views/storage/_lvm_volume_form.rhtml > create mode 100644 src/app/views/storage/new_lvm_volume.rhtml > > diff --git a/src/app/controllers/hardware_controller.rb b/src/app/controllers/hardware_controller.rb > index 4f24cb3..8c26184 100644 > --- a/src/app/controllers/hardware_controller.rb > +++ b/src/app/controllers/hardware_controller.rb > @@ -117,7 +117,8 @@ class HardwareController < PoolController > def show_tasks > @task_types = [["VM Task", "VmTask"], > ["Host Task", "HostTask"], > - ["Storage Task", "StorageTask", "break"], > + ["Storage Task", "StorageTask"], > + ["Storage Volume Task", "StorageVolumeTask", "break"], > ["Show All", ""]] > super > end > @@ -162,7 +163,7 @@ class HardwareController < PoolController > if params[:id] > pre_show > storage_pools = @pool.storage_pools > - find_opts = {} > + find_opts = {:conditions => "type != 'LvmStoragePool'"} > include_pool = false > else > # FIXME: no permissions or usage checks here yet > diff --git a/src/app/controllers/storage_controller.rb b/src/app/controllers/storage_controller.rb > index fe524f3..75058cd 100644 > --- a/src/app/controllers/storage_controller.rb > +++ b/src/app/controllers/storage_controller.rb > @@ -25,6 +25,7 @@ class StorageController < ApplicationController > before_filter :pre_pool_admin, :only => [:refresh] > before_filter :pre_new2, :only => [:new2] > before_filter :pre_json, :only => [:storage_volumes_json] > + before_filter :pre_create_volume, :only => [:create_volume] > > def index > list > @@ -90,15 +91,25 @@ class StorageController < ApplicationController > flash[:notice] = 'You do not have permission to view this storage pool: redirecting to top level' > redirect_to :controller => 'dashboard' > end > - json_list(@storage_pool.storage_volumes, > - [:display_name, :size_in_gb, :get_type_label]) > + attr_list = [] > + attr_list << :id if (@storage_pool.user_subdividable and @can_modify) > + attr_list += [:display_name, :size_in_gb, :get_type_label] > + json_list(@storage_pool.storage_volumes, attr_list) > end > def show_volume > @storage_volume = StorageVolume.find(params[:id]) > set_perms(@storage_volume.storage_pool.hardware_pool) > unless @can_view > flash[:notice] = 'You do not have permission to view this storage volume: redirecting to top level' > - redirect_to :controller => 'dashboard' > + respond_to do |format| > + format.html { redirect_to :controller => 'dashboard' } > + format.xml { head :forbidden } > + end > + else > + respond_to do |format| > + format.html { render :layout => 'popup' } > + format.xml { render :xml => @storage_volume.to_xml } > + end > end > end > > @@ -110,6 +121,70 @@ class StorageController < ApplicationController > render :layout => false > end > > + def new_volume > + new_volume_internal(StoragePool.find(params[:storage_pool_id]), > + { :storage_pool_id => params[:storage_pool_id]}) > + render :layout => 'popup' > + end > + > + def new_lvm_volume > + @source_volume = StorageVolume.find(params[:source_volume_id]) > + @return_facebox = params[:return_facebox] > + unless @source_volume.supports_lvm_subdivision > + #fixme: proper error page for popups > + redirect_to :controller => 'dashboard' > + return > + end > + lvm_pool = @source_volume.lvm_storage_pool > + unless lvm_pool > + # FIXME: what should we do about VG/LV names? > + # for now auto-create VG name as ovirt_vg_#{@source_volume.id} > + lvm_pool = LvmStoragePool.new(:vg_name => "ovirt_vg_#{@source_volume.id}", > + :hardware_pool_id => @source_volume.storage_pool.hardware_pool_id) > + lvm_pool.source_volumes << @source_volume > + lvm_pool.save! > + end > + new_volume_internal(lvm_pool, { :storage_pool_id => lvm_pool.id}) > + @storage_volume.lv_owner_perms='0744' > + @storage_volume.lv_group_perms='0744' > + @storage_volume.lv_mode_perms='0744' > + render :layout => 'popup' > + end > + > + def create_volume > + begin > + StorageVolume.transaction do > + @storage_volume.save! > + @task = StorageVolumeTask.new({ :user => @user, > + :task_target => @storage_volume, > + :action => StorageVolumeTask::ACTION_CREATE_VOLUME, > + :state => Task::STATE_QUEUED}) > + @task.save! > + end > + respond_to do |format| > + format.json { render :json => { :object => "storage_volume", > + :success => true, > + :alert => "Storage Volume was successfully created." } } > + format.xml { render :xml => @storage_volume, > + :status => :created, > + # FIXME: create storage_volume_url method if relevant > + :location => storage_pool_url(@storage_volume) > + } > + end > + rescue => ex > + # FIXME: need to distinguish volume vs. task save errors > + respond_to do |format| > + format.json { > + json_hash = { :object => "storage_volume", :success => false, > + :errors => @storage_volume.errors.localize_error_messages.to_a } > + json_hash[:message] = ex.message if json_hash[:errors].empty? > + render :json => json_hash } > + format.xml { render :xml => @storage_volume.errors, > + :status => :unprocessable_entity } > + end > + end > + end > + > def insert_refresh_task > @task = StorageTask.new({ :user => @user, > :task_target => @storage_pool, > @@ -244,6 +319,67 @@ class StorageController < ApplicationController > end > end > > + def delete_volumes > + storage_volume_ids_str = params[:storage_volume_ids] > + storage_volume_ids = storage_volume_ids_str.split(",").collect {|x| x.to_i} > + alerts = [] > + status = true > + begin > + StorageVolume.transaction do > + storage = StorageVolume.find(:all, :conditions => "id in (#{storage_volume_ids.join(', ')})") > + unless storage.empty? > + set_perms(storage[0].storage_pool.hardware_pool) > + unless @can_modify and storage[0].storage_pool.user_subdividable > + respond_to do |format| > + format.json { render :json => { :object => "storage_volume", > + :success => false, > + :alert => "You do not have permission to delete this storage volume." } } > + format.xml { head :forbidden } > + end > + else > + storage.each do |storage_volume| > + alert, success = delete_volume_internal(storage_volume) > + alerts << alert > + status = false unless success > + end > + respond_to do |format| > + format.json { render :json => { :object => "storage_volume", > + :success => status, :alert => alerts.join("\n") } } > + format.xml { head(status ? :ok : :method_not_allowed) } > + end > + end > + else > + respond_to do |format| > + format.json { render :json => { :object => "storage_volume", > + :success => false, :alert => "no volumes selected" } } > + format.xml { head(status ? :ok : :method_not_allowed) } > + end > + end > + end > + end > + end > + > + def delete_volume > + @storage_volume = StorageVolume.find(params[:id]) > + set_perms(@storage_volume.storage_pool.hardware_pool) > + unless @can_modify and @storage_volume.storage_pool.user_subdividable > + respond_to do |format| > + format.json { render :json => { :object => "storage_volume", > + :success => false, > + :alert => "You do not have permission to delete this storage volume." } } > + format.xml { head :forbidden } > + end > + else > + alert, success = delete_volume_internal(@storage_volume) > + respond_to do |format| > + format.json { render :json => { :object => "storage_volume", > + :success => success, :alert => alert } } > + format.xml { head(success ? :ok : :method_not_allowed) } > + end > + end > + > + end > + > def pre_new > @hardware_pool = HardwarePool.find(params[:hardware_pool_id]) > @perm_obj = @hardware_pool > @@ -274,6 +410,16 @@ class StorageController < ApplicationController > @perm_obj = @storage_pool.hardware_pool > @redir_obj = @storage_pool > end > + def pre_create_volume > + volume = params[:storage_volume] > + unless type = params[:storage_type] > + type = volume.delete(:storage_type) > + end > + @storage_volume = StorageVolume.factory(type, volume) > + @perm_obj = @storage_volume.storage_pool.hardware_pool > + @redir_controller = @storage_volume.storage_pool.hardware_pool.get_controller > + authorize_admin > + end > def pre_json > pre_show > end > @@ -282,4 +428,32 @@ class StorageController < ApplicationController > authorize_admin > end > > + private > + def new_volume_internal(storage_pool, new_params) > + @storage_volume = StorageVolume.factory(storage_pool.get_type_label, new_params) > + @perm_obj = @storage_volume.storage_pool.hardware_pool > + authorize_admin > + end > + > + def delete_volume_internal(volume) > + begin > + name = volume.display_name > + if !volume.vms.empty? > + vm_list = volume.vms.collect {|vm| vm.description}.join(", ") > + ["Storage Volume #{name} must be unattached from VMs (#{vm_list}) before deleting it.", > + false] > + #FIXME: create delete volume task. include metadata in task > + else > + #FIXME: need to set volume to 'unavailable' state once we have states > + @task = StorageVolumeTask.new({ :user => @user, > + :task_target => volume, > + :action => StorageVolumeTask::ACTION_DELETE_VOLUME, > + :state => Task::STATE_QUEUED}) > + @task.save! > + ["Storage Volume #{name} deletion was successfully queued.", true] > + end > + rescue => ex > + ["Failed to delete storage volume #{name} (#{ex.message}.",false] > + end > + end > end > diff --git a/src/app/controllers/task_controller.rb b/src/app/controllers/task_controller.rb > index 73e5155..647d69c 100644 > --- a/src/app/controllers/task_controller.rb > +++ b/src/app/controllers/task_controller.rb > @@ -24,6 +24,8 @@ class TaskController < ApplicationController > set_perms(@task.vm.vm_resource_pool) > elsif @task[:type] == StorageTask.name > set_perms(@task.storage_pool.hardware_pool) > + elsif @task[:type] == StorageVolumeTask.name > + set_perms(@task.storage_volume.storage_pool.hardware_pool) > elsif @task[:type] == HostTask.name > set_perms(@task.host.hardware_pool) > end > diff --git a/src/app/models/lvm_storage_pool.rb b/src/app/models/lvm_storage_pool.rb > index 08b8938..84fbb28 100644 > --- a/src/app/models/lvm_storage_pool.rb > +++ b/src/app/models/lvm_storage_pool.rb > @@ -44,5 +44,10 @@ class LvmStoragePool < StoragePool > kb_to_gb(size) > end > > + # FIXME: needs to take free space into account here > + def user_subdividable > + true > + end > + > > end > diff --git a/src/app/models/storage_volume_task.rb b/src/app/models/storage_volume_task.rb > index 78f2895..136f1ba 100644 > --- a/src/app/models/storage_volume_task.rb > +++ b/src/app/models/storage_volume_task.rb > @@ -20,7 +20,7 @@ > class StorageVolumeTask < Task > > ACTION_CREATE_VOLUME = "create_volume" > - ACTION_EDIT_VOLUME = "edit_volume" > + ACTION_DELETE_VOLUME = "delete_volume" > > def after_initialize > self.hardware_pool = task_target.storage_pool.hardware_pool if self.task_target_type=="StorageVolume" > @@ -29,7 +29,6 @@ class StorageVolumeTask < Task > def task_obj > "StorageVolume;;;#{self.storage_volume.id};;;#{self.storage_volume.display_name}" > end > - end > def host > nil > end > diff --git a/src/app/views/storage/_lvm_volume_form.rhtml b/src/app/views/storage/_lvm_volume_form.rhtml > new file mode 100644 > index 0000000..823158e > --- /dev/null > +++ b/src/app/views/storage/_lvm_volume_form.rhtml > @@ -0,0 +1,19 @@ > +<%= error_messages_for 'storage_volume' %> > + > +<!--[form:storage_pool]--> > +<%= hidden_field 'storage_volume', 'storage_pool_id' %> > +<%= hidden_field_tag 'storage_type', @storage_volume.get_type_label %> > + > +<%= text_field_with_label "Size (GB):", 'storage_volume', 'size_in_gb' %> > + > +<%= text_field_with_label "LV Name:", 'storage_volume', 'lv_name' %> > + > +<%= text_field_with_label "Owner permissions:", 'storage_volume', 'lv_owner_perms' %> > + > +<%= text_field_with_label "Group permissions:", 'storage_volume', 'lv_group_perms' %> > + > +<%= text_field_with_label "Mode permissions:", 'storage_volume', 'lv_mode_perms' %> > + > + > +<!--[eoform:storage_volume]--> > + > diff --git a/src/app/views/storage/new_lvm_volume.rhtml b/src/app/views/storage/new_lvm_volume.rhtml > new file mode 100644 > index 0000000..ea51cc5 > --- /dev/null > +++ b/src/app/views/storage/new_lvm_volume.rhtml > @@ -0,0 +1,42 @@ > +<%- content_for :title do -%> > + <%= _("Add New LVM Volume") %> > +<%- end -%> > +<%- content_for :description do -%> > + Add a new LVM Storage Volume to <%= @source_volume.display_name %>. > +<%- end -%> > +<div class="panel_header"></div> > +<div class="dialog_form"> > +<form method="POST" action="<%= url_for :action => 'create_volume' %>" id="lvm_volume_form" > > + <div class="dialog_form"> > + <div id="new_storage_pool"> > + <%= render :partial => 'lvm_volume_form' %> > + </div> > + </div> > + <!-- FIXME: need to pop up the details dialog again --> > + <%= popup_footer("$('#lvm_volume_form').submit()", "New LVM Volume") %> > +</form> > +</div> > +<script type="text/javascript"> > +function afterLvmVolume(response, status){ > + ajax_validation(response, status); > + if (response.success) { > + <% if @return_facebox %> > + $('#window').fadeOut('fast'); > + $("#window").empty().load("<%= @return_facebox %>") > + $('#window').fadeIn('fast'); > + <% else %> > + $(document).trigger('close.facebox'); > + <% end %> > + } > +} > +$(function() { > + var lvmvolumeoptions = { > + target: '<%= url_for :action => 'create_volume' %>', // target element to update > + dataType: 'json', > + success: afterLvmVolume // post-submit callback > + }; > + > + // bind form using 'ajaxForm' > + $('#lvm_volume_form').ajaxForm(lvmvolumeoptions); > +}); > +</script> > diff --git a/src/app/views/storage/show.rhtml b/src/app/views/storage/show.rhtml > index 82c9dab..a84dc62 100644 > --- a/src/app/views/storage/show.rhtml > +++ b/src/app/views/storage/show.rhtml > @@ -18,24 +18,24 @@ > > <div id="storage_selection_id" style="display:none"><%= @storage_pool.id %></div> > <div class="selection_key"> > - IP address: </br> > + IP address:<br/> > <% if @storage_pool[:type] == "IscsiStoragePool" %> > - Port: </br> > - Target: </br> > + Port:<br/> > + Target:<br/> > <% elsif @storage_pool[:type] == "NfsStoragePool" %> > - Export path:</br> > + Export path:<br/> > <% end %> > - Type: </br> > + Type:<br/> > </div> > <div class="selection_value"> > - <%=h @storage_pool.ip_addr %></br> > + <%=h @storage_pool.ip_addr %><br/> > <% if @storage_pool[:type] == "IscsiStoragePool" %> > - <%=h @storage_pool.port %></br> > - <%=h @storage_pool.target %></br> > + <%=h @storage_pool.port %><br/> > + <%=h @storage_pool.target %><br/> > <% elsif @storage_pool[:type] == "NfsStoragePool" %> > - <%=h @storage_pool.export_path %></br> > + <%=h @storage_pool.export_path %><br/> > <% end %> > - <%=h @storage_pool.get_type_label %></br> > + <%=h @storage_pool.get_type_label %><br/> > </div> > <%- content_for :right do -%> > <div class="selection_right_title">Volumes</div> > @@ -54,24 +54,33 @@ > > <%= confirmation_dialog("confirm_delete_storage", "Are you sure?", "delete_storage_pool()") %> > <script type="text/javascript"> > - $("#storage_volumes_grid").flexigrid > - ( > - { > - url: '<%= url_for :action => "storage_volumes_json", :id => @storage_pool.id %>', > - dataType: 'json', > - colModel : [ > - {display: 'Alias', name : 'display_name', width : 180, sortable : false, align: 'left'}, > - {display: 'Size (Gb)', name : 'size', width : 80, sortable : true, align: 'left'}, > - {display: 'Type', name : 'type', width : 80, sortable : true, align: 'left'} > - ], > - sortname: "size", > - sortorder: "desc", > - usepager: false, > - useRp: true, > - rp: 40, > - showTableToggleBtn: true, > - } > + $("#storage_volumes_grid").flexigrid > + ( > + { > + url: '<%= url_for :action => "storage_volumes_json", :id => @storage_pool.id %>', > + dataType: 'json', > + colModel : [ > + <%= "{display: '', width : 20, align: 'left', process: storage_volumes_gridcheckbox}," if (@storage_pool.user_subdividable and @can_modify) %> > + {display: 'Alias', width : 180, sortable : false, align: 'left', process: storage_volume_details_link}, > + {display: 'Size (Gb)', name : 'size', width : 80, sortable : true, align: 'left'}, > + {display: 'Type', name : 'type', width : 80, sortable : true, align: 'left'} > + ], > + sortname: "size", > + sortorder: "desc", > + usepager: false, > + useRp: true, > + rp: 40, > + showTableToggleBtn: true, > + } > ); > + function storage_volumes_gridcheckbox(celDiv) > + { > + $(celDiv).html('<input class="grid_checkbox" type="checkbox" name="grid_checkbox'+$(celDiv).html()+'" value="'+$(celDiv).html()+'"/>'); > + } > + function storage_volume_details_link(celDiv,pid) > + { > + $(celDiv).html('<a href="<%= url_for :controller => 'storage', :action => 'show_volume' %>/'+ pid +'" rel="facebox[.bolder]">' + $(celDiv).html() + '</a>'); > + } > </script> > <script type="text/javascript"> > > diff --git a/src/app/views/storage/show_volume.rhtml b/src/app/views/storage/show_volume.rhtml > index 9aeb808..e39c10e 100644 > --- a/src/app/views/storage/show_volume.rhtml > +++ b/src/app/views/storage/show_volume.rhtml > @@ -1,54 +1,123 @@ > - <div id="data"> > - <div class="inside"> > - > - <div id="dataTableWrapper"> > - > - <div class="dataTable"> > - <div class="inside"> > - > - <div class="data-table-column"> > +<%- content_for :title do -%> > + <%=h @storage_volume.display_name %> > +<%- end -%> > +<%- content_for :description do -%> > + Details for Storage Volume <%=h @storage_volume.display_name %> > +<%- end -%> > > - <p><b>IP address:</b> <%=h @storage_volume.storage_pool.ip_addr %></p> > -<% if @storage_volume.storage_pool[:type] == "IscsiStoragePool" %> > - <p><b>Port:</b> <%=h @storage_volume.storage_pool.port %></p> > - <p><b>Target:</b> <%=h @storage_volume.storage_pool[:target] %></p> > -<% elsif @storage_volume.storage_pool[:type] == "NfsStoragePool" %> > - <p><b>Export path:</b> <%=h @storage_volume.storage_pool.export_path %></p> > -<% end %> > - <p><b>Type:</b> <%=h @storage_volume.storage_pool.get_type_label %></p> > - <p></p> > - <p><b>Path:</b> <%=h @storage_volume.path %></p> > -<% if @storage_volume[:type] == "IscsiStorageVolume" %> > - <p><b>LUN:</b> <%=h @storage_volume.lun %></p> > -<% elsif @storage_volume[:type] == "NfsStorageVolume" %> > - <p><b>Filename:</b> <%=h @storage_volume.filename %></p> > +<% if @storage_volume.supports_lvm_subdivision %> > + <div id="dialog-content-area"> > + <div id="toolbar_nav"> > + <ul> > + <li><a href="#" id="new_lvm_volume_button"><%=image_tag "icon_addstorage.png", :style=>"vertical-align:middle;" %>Add</a></li> > + <% if (@storage_volume.lvm_storage_pool and > + !@storage_volume.lvm_storage_pool.storage_volumes.empty?) %> > + <li><a href="#" onClick="return remove_lvm_volumes();"><%=image_tag "icon_remove.png" %> Remove</a></li> > + <% end %> > + </ul> > + </div> > + </div> > <% end %> > - <p><b>Size:</b> <%=h @storage_volume.size_in_gb %> GB</p> > - > - </div> > - > - </div> <!-- end #data-table.inside --> > - </div> <!-- end #dataTable --> > - > - </div> <!-- end #dataTableWrapper --> > - > - </div> <!-- end #data.inside --> > - </div> <!-- end #data --> > - > - </td> > - <td id="right"> > - <div class="heading"> </div> > - <div id="tools"> > - <h3>Actions</h3> > - <%if @can_modify -%> > - <% end -%> > - <div class="actions"> > - <%= link_to "Back to #{@storage_volume.storage_pool.ip_addr}: #{@storage_volume.storage_pool[:target]}", { :action => 'show', :id => @storage_volume.storage_pool.id }, { :class => "" } %> > - </div> > +<div class="panel_header"></div> > + <div class="selection_key"> > + IP address:<br/> > + <% if @storage_volume.storage_pool[:type] == "IscsiStoragePool" %> > + Port:<br/> > + Target:<br/> > + <% elsif @storage_volume.storage_pool[:type] == "NfsStoragePool" %> > + Export path:<br/> > + <% end %> > + Type:<br/> > + Path:<br/> > + <% if @storage_volume[:type] == "IscsiStorageVolume" %> > + LUN:<br/> > + <% elsif @storage_volume[:type] == "NfsStorageVolume" %> > + Filename:<br/> > + <% end %> > + Size:<br/> > </div> > + <div class="selection_value"> > + <%=h @storage_volume.storage_pool.ip_addr %><br/> > + <% if @storage_volume.storage_pool[:type] == "IscsiStoragePool" %> > + <%=h @storage_volume.storage_pool.port %><br/> > + <%=h @storage_volume.storage_pool[:target] %><br/> > + <% elsif @storage_volume.storage_pool[:type] == "NfsStoragePool" %> > + <%=h @storage_volume.storage_pool.export_path %><br/> > + <% end %> > + <%=h @storage_volume.storage_pool.get_type_label %><br/> > + <%=h @storage_volume.path %><br/> > + <% if @storage_volume[:type] == "IscsiStorageVolume" %> > + <%=h @storage_volume.lun %><br/> > + <% elsif @storage_volume[:type] == "NfsStorageVolume" %> > + <%=h @storage_volume.filename %><br/> > + <% end %> > + <%=h @storage_volume.size_in_gb %> GB<br/> > + </div> > +<% if @storage_volume.supports_lvm_subdivision %> > + <div class="dialog_form"> > + <div class="form_heading">LVM Volumes</div> > + <% if @storage_volume.lvm_storage_pool %> > + <form id="lvm_storage_volumes_grid_form"> > + <table id="lvm_storage_volumes_grid" style="display:none"></table> > + </form> > + <% end %> > + </div> > +<% end %> > > - </td> > - > -<%- content_for :title do -%> > -<%= _("Storage Volume") %> > -<%- end -%> > +<div class="facebox_timfooter"> > + <div class="button"> > + <div class="button_left_grey"></div> > + <div class="button_middle_grey"><a href="#" onclick="jQuery(document).trigger('close.facebox')">Close</a></div> > + <div class="button_right_grey"></div> > + </div> > +</div> > +<script type="text/javascript"> > + $("#lvm_storage_volumes_grid").flexigrid > + ( > + { > + url: '<%= url_for :action => "storage_volumes_json", :id => @storage_volume.lvm_storage_pool.id %>', > + dataType: 'json', > + colModel : [ > + <%= "{display: '', width : 20, align: 'left', process: lvm_storage_volumes_gridcheckbox}," if @can_modify %> > + {display: 'Alias', width : 180, sortable : false, align: 'left'}, > + {display: 'Size (Gb)', name : 'size', width : 80, sortable : true, align: 'left'}, > + {display: 'Type', name : 'type', width : 80, sortable : true, align: 'left'} > + ], > + sortname: "size", > + sortorder: "desc", > + usepager: false, > + useRp: true, > + rp: 40, > + showTableToggleBtn: true, > + } > + ); > + function lvm_storage_volumes_gridcheckbox(celDiv) > + { > + $(celDiv).html('<input class="grid_checkbox" type="checkbox" name="grid_checkbox'+$(celDiv).html()+'" value="'+$(celDiv).html()+'"/>'); > + } > + function get_selected_lvm_volumes() > + { > + return get_selected_checkboxes("lvm_storage_volumes_grid_form"); > + } > + function remove_lvm_volumes() > + { > + var volumes = get_selected_lvm_volumes(); > + if (validate_selected(volumes, "storage volume")) { > + $.post('<%= url_for :controller => "storage", :action => "delete_volumes" %>', > + { storage_volume_ids: volumes.toString() }, > + function(data,status){ > + $("#lvm_storage_volumes_grid").flexReload(); > + if (data.alert) { > + $.jGrowl(data.alert); > + } > + }, 'json'); > + } > + } > +$('#new_lvm_volume_button').click(function(){ > + $('#window').fadeOut('fast'); > + $("#window").empty().load("<%= url_for :controller => 'storage', :action => 'new_lvm_volume' %>", > + { source_volume_id: <%=@storage_volume.id%>, > + return_facebox: '<%= url_for :controller => 'storage', :action => 'show_volume', :id => @storage_volume.id %>' }); > + $('#window').fadeIn('fast'); > +}); > +</script>ACK, with one fix- dutils/active_record_env.rb needs: require 'models/storage_volume_task.rb' added around line 78. Also, could you put a FIXME or something around line 57 suggesting we pull in the requires with a splat of some sort? Then we don't have to remember to bring in every model and it will keep the code smaller (I didn't have time to try it, just seems like it would be worth doing).
Chris Lalancette
2008-Nov-05 15:18 UTC
[Ovirt-devel] [PATCH] lvm storage volume HW pool admin tab.
Scott Seago wrote:> Includes 'view storage volume' facebox popup and new/delete LVM volume > functionality. Although lvm tasks are created, taskomatic doesn't yet know > what to do with them. > > Signed-off-by: Scott Seago <sseago at redhat.com>Committed. -- Chris Lalancette