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