Scott Seago
2009-May-11 21:14 UTC
[Ovirt-devel] [PATCH server] converted the storage controller to use the service layer.
Signed-off-by: Scott Seago <sseago at redhat.com> --- src/app/controllers/storage_controller.rb | 205 +++++++++-------------------- src/app/services/storage_pool_service.rb | 150 +++++++++++++++++++++ 2 files changed, 210 insertions(+), 145 deletions(-) create mode 100644 src/app/services/storage_pool_service.rb diff --git a/src/app/controllers/storage_controller.rb b/src/app/controllers/storage_controller.rb index 3cd969d..4cc4953 100644 --- a/src/app/controllers/storage_controller.rb +++ b/src/app/controllers/storage_controller.rb @@ -18,14 +18,11 @@ # also available at http://www.gnu.org/copyleft/gpl.html. class StorageController < ApplicationController + include StoragePoolService EQ_ATTRIBUTES = [ :ip_addr, :export_path, :target, :hardware_pool_id ] - before_filter :pre_pool_admin, :only => [:refresh] - before_filter :pre_new2, :only => [:new2] - before_filter :pre_add, :only => [:add, :addstorage] - def index list respond_to do |format| @@ -55,106 +52,70 @@ class StorageController < ApplicationController end def show - @storage_pool = StoragePool.find(params[:id]) - set_perms(@storage_pool.hardware_pool) - if authorize_view - respond_to do |format| - format.html { render :layout => 'selection' } - format.xml { - xml_txt = @storage_pool.to_xml(:include => :storage_volumes) do |xml| - xml.type @storage_pool.class.name - end - render :xml => xml_txt - } - end + svc_show(params[:id]) + respond_to do |format| + format.html { render :layout => 'selection' } + format.xml { + xml_txt = @storage_pool.to_xml(:include => :storage_volumes) do |xml| + xml.type @storage_pool.class.name + end + render :xml => xml_txt + } end end def new + svc_load_hw_pool(params[:hardware_pool_id]) + @storage_types = StoragePool::STORAGE_TYPE_PICKLIST + render :layout => false end def new2 - @storage_pools = @storage_pool.hardware_pool.storage_volumes + svc_new(params[:hardware_pool_id], params[:storage_type]) render :layout => false end - def insert_refresh_task - @task = StorageTask.new({ :user => @user, - :task_target => @storage_pool, - :action => StorageTask::ACTION_REFRESH_POOL, - :state => Task::STATE_QUEUED}) - @task.save! - end - def refresh - begin - insert_refresh_task - storage_url = url_for(:controller => "storage", :action => "show", :id => @storage_pool) - flash[:notice] = 'Storage pool refresh was successfully scheduled.' - rescue - flash[:notice] = 'Error scheduling Storage pool refresh.' - end - redirect_to :action => 'show', :id => @storage_pool.id + alert = svc_refresh(params[:id]) + render :json => { :object => "vm", :success => true, :alert => alert } end def create - begin - StoragePool.transaction do - @storage_pool.save! - insert_refresh_task - end - respond_to do |format| - format.json { render :json => { :object => "storage_pool", - :success => true, - :alert => "Storage Pool was successfully created.", - :new_pool => @storage_pool.storage_tree_element({:filter_unavailable => false, :state => 'new'})} } - format.xml { render :xml => @storage_pool, - :status => :created, - :location => storage_pool_url(@storage_pool) - } - end - rescue => ex - # FIXME: need to distinguish pool vs. task save errors (but should mostly be pool) - respond_to do |format| - format.json { - json_hash = { :object => "storage_pool", :success => false, - :errors => @storage_pool.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_pool.errors, - :status => :unprocessable_entity } - end + pool = params[:storage_pool] + unless type = params[:storage_type] + type = pool.delete(:storage_type) end + alert = svc_create(type, pool) + respond_to do |format| + format.json { render :json => { :object => "storage_pool", + :success => true, :alert => alert, + :new_pool => @storage_pool.storage_tree_element({:filter_unavailable => false, :state => 'new'})} } + format.xml { render :xml => @storage_pool, + :status => :created, + :location => storage_pool_url(@storage_pool) + } + end end def edit + svc_modify(params[:id]) render :layout => 'popup' end def update - begin - StoragePool.transaction do - @storage_pool.update_attributes!(params[:storage_pool]) - insert_refresh_task - end - render :json => { :object => "storage_pool", :success => true, - :alert => "Storage Pool was successfully modified." } - rescue - # FIXME: need to distinguish pool vs. task save errors (but should mostly be pool) - render :json => { :object => "storage_pool", :success => false, - :errors => @storage_pool.errors.localize_error_messages.to_a } - end + alert = svc_update(params[:id], params[:storage_pool]) + render :json => { :object => "storage_pool", :success => true, + :alert => alert } end def addstorage + svc_load_hw_pool(params[:hardware_pool_id]) + @storage_types = StoragePool::STORAGE_TYPE_PICKLIST render :layout => 'popup' end def add - render :layout => false - end - - def new + svc_load_hw_pool(params[:hardware_pool_id]) render :layout => false end @@ -167,87 +128,41 @@ class StorageController < ApplicationController # in addition to the current pool (which is checked). We also need to fail # for storage that aren't currently empty def delete_pools - storage_pool_ids_str = params[:storage_pool_ids] - storage_pool_ids = storage_pool_ids_str.split(",").collect {|x| x.to_i} - - begin - StoragePool.transaction do - storage = StoragePool.find(:all, :conditions => "id in (#{storage_pool_ids.join(', ')})") - storage.each do |storage_pool| - storage_pool.destroy - end + storage_pool_ids = params[:storage_pool_ids].split(",") + successes = [] + failures = {} + storage_pool_ids.each do |storage_pool_id| + begin + svc_destroy(storage_pool_id) + successes << @storage_pool + rescue PermissionError => perm_error + failures[@storage_pool] = perm_error.message + rescue Exception => ex + failures[@storage_pool] = ex.message end - render :json => { :object => "storage_pool", :success => true, - :alert => "Storage Pools were successfully deleted." } - rescue - render :json => { :object => "storage_pool", :success => true, - :alert => "Error deleting storage pools." } end + unless failures.empty? + raise PartialSuccessError.new("Delete failed for some Storage Pools", + failures, successes) + end + render :json => { :object => "storage_pool", :success => true, + :alert => "Storage Pools were successfully deleted." } end def destroy - unless @storage_pool.movable? - @error = "Cannot delete storage with associated vms" - respond_to do |format| - format.json { render :json => { :object => "storage_pool", - :success => false, :alert => @error } } - format.xml { render :template => "errors/simple", :layout => false, - :status => :forbidden } - end - return - end - - pool = @storage_pool.hardware_pool - if @storage_pool.destroy - alert="Storage Pool was successfully deleted." - success=true - else - alert="Failed to delete storage pool." - success=false - end + alert = svc_destroy(params[:id]) respond_to do |format| format.json { render :json => { :object => "storage_pool", - :success => success, :alert => alert } } - format.xml { head(success ? :ok : :method_not_allowed) } + :success => true, :alert => alert } } + format.xml { head(:ok) } end end - def pre_new - @hardware_pool = HardwarePool.find(params[:hardware_pool_id]) - set_perms(@hardware_pool) - authorize_admin - @storage_pools = @hardware_pool.storage_volumes - @storage_types = StoragePool::STORAGE_TYPE_PICKLIST - end - - def pre_add - pre_new - end - - def pre_new2 - new_params = { :hardware_pool_id => params[:hardware_pool_id]} - if (params[:storage_type] == "iSCSI") - new_params[:port] = 3260 - end - @storage_pool = StoragePool.factory(params[:storage_type], new_params) - set_perms(@storage_pool.hardware_pool) - authorize_admin - end - def pre_create - pool = params[:storage_pool] - unless type = params[:storage_type] - type = pool.delete(:storage_type) - end - @storage_pool = StoragePool.factory(type, pool) - set_perms(@storage_pool.hardware_pool) - end - def pre_edit - @storage_pool = StoragePool.find(params[:id]) - set_perms(@storage_pool.hardware_pool) + # FIXME: remove these when service transition is complete. these are here + # to keep from running permissions checks and other setup steps twice + def tmp_pre_update end - def pre_pool_admin - pre_edit - authorize_admin + def tmp_authorize_admin end end diff --git a/src/app/services/storage_pool_service.rb b/src/app/services/storage_pool_service.rb new file mode 100644 index 0000000..dd36304 --- /dev/null +++ b/src/app/services/storage_pool_service.rb @@ -0,0 +1,150 @@ +# +# Copyright (C) 2009 Red Hat, Inc. +# Written by Scott Seago <sseago at redhat.com>, +# David Lutterkort <lutter at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. +# Mid-level API: Business logic around storage pools +module StoragePoolService + + include ApplicationService + + # Load the StoragePool with +id+ for viewing + # + # === Instance variables + # [<tt>@storage_pool</tt>] stores the Storage Pool with +id+ + # === Required permissions + # [<tt>Privilege::VIEW</tt>] on StoragePool's HardwarePool + def svc_show(id) + lookup(id,Privilege::VIEW) + end + + # Load the StoragePool with +id+ for editing + # + # === Instance variables + # [<tt>@storage_pool</tt>] stores the Storage Pool with +id+ + # === Required permissions + # [<tt>Privilege::MODIFY</tt>] on StoragePool's HardwarePool + def svc_modify(id) + lookup(id,Privilege::MODIFY) + end + + # Update attributes for the StoragePool with +id+ + # + # === Instance variables + # [<tt>@storage_pool</tt>] stores the Storage Pool with +id+ + # === Required permissions + # [<tt>Privilege::MODIFY</tt>] on StoragePool's HardwarePool + def svc_update(id, storage_pool_hash) + lookup(id,Privilege::MODIFY) + authorized!(Privilege::MODIFY, at storage_pool.hardware_pool) + StoragePool.transaction do + @storage_pool.update_attributes!(storage_pool_hash) + insert_refresh_task + end + return "Storage Pool was successfully modified." + + end + + # Refresh the StoragePool with +id+ + # + # === Instance variables + # [<tt>@storage_pool</tt>] stores the Storage Pool with +id+ + # === Required permissions + # [<tt>Privilege::MODIFY</tt>] on StoragePool's HardwarePool + def svc_refresh(id) + lookup(id,Privilege::MODIFY) + insert_refresh_task + return "Storage pool refresh was successfully scheduled." + end + + # Load a parent HardwarePool in preparation for creating/adding + # a storage pool + # + # === Instance variables + # [<tt>@hardware_pool</tt>] loads the HardwarePool as specified by + # +hardware_pool_id+ + # === Required permissions + # [<tt>Privilege::MODIFY</tt>] for the storage pool's HardwarePool as + # specified by +hardware_pool_id+ + def svc_load_hw_pool(hardware_pool_id) + @hardware_pool = HardwarePool.find(hardware_pool_id) + authorized!(Privilege::MODIFY, at hardware_pool) + end + + # Load a new StoragePool for creating + # + # === Instance variables + # [<tt>@storage_pool</tt>] loads a new StoragePool object into memory + # === Required permissions + # [<tt>Privilege::MODIFY</tt>] for the storage pool's HardwarePool as + # specified by +hardware_pool_id+ + def svc_new(hardware_pool_id, storage_type) + new_params = { :hardware_pool_id => hardware_pool_id} + if (storage_type == "iSCSI") + new_params[:port] = 3260 + end + @storage_pool = StoragePool.factory(storage_type, new_params) + authorized!(Privilege::MODIFY, at storage_pool.hardware_pool) + end + + # Create a new StoragePool + # + # === Instance variables + # [<tt>@storage_pool</tt>] the newly-created StoragePool + # === Required permissions + # [<tt>Privilege::MODIFY</tt>] for the storage pool's HardwarePool + def svc_create(storage_type, storage_pool_hash) + @storage_pool = StoragePool.factory(storage_type, storage_pool_hash) + authorized!(Privilege::MODIFY, at storage_pool.hardware_pool) + StoragePool.transaction do + @storage_pool.save! + insert_refresh_task + end + return "Storage Pool was successfully created." + end + + # Destroys for the StoragePool with +id+ + # + # === Instance variables + # [<tt>@storage_pool</tt>] stores the StoragePool with +id+ + # === Required permissions + # [<tt>Privilege::MODIFY</tt>] for the StoragePool's HardwarePool + def svc_destroy(id) + lookup(id, Privilege::MODIFY) + unless @storage_pool.movable? + raise ActionError.new("Cannot delete storage with associated vms") + end + @storage_pool.destroy + return "Storage Pool was successfully deleted." + end + + private + def lookup(id, priv) + @storage_pool = StoragePool.find(id) + authorized!(priv, at storage_pool.hardware_pool) + end + + def insert_refresh_task + @task = StorageTask.new({ :user => @user, + :task_target => @storage_pool, + :action => StorageTask::ACTION_REFRESH_POOL, + :state => Task::STATE_QUEUED}) + @task.save! + end + + +end -- 1.6.0.6
Jason Guiditta
2009-May-13 19:51 UTC
[Ovirt-devel] [PATCH server] converted the storage controller to use the service layer.
On Mon, 2009-05-11 at 21:14 +0000, Scott Seago wrote:> Signed-off-by: Scott Seago <sseago at redhat.com> > --- > src/app/controllers/storage_controller.rb | 205 +++++++++-------------------- > src/app/services/storage_pool_service.rb | 150 +++++++++++++++++++++ > 2 files changed, 210 insertions(+), 145 deletions(-) > create mode 100644 src/app/services/storage_pool_service.rb >ACK, this seems to work fine. Btw, kudos on some nice documentation on the new service layer methods. -j