Scott Seago
2009-Apr-28 22:10 UTC
[Ovirt-devel] [PATCH server] controller service layer refactoring for pools.
This is the second round of controller/service layer refactoring changes -- in
this case for the pool-related controllers (HW, VM, and Smart pools)
Signed-off-by: Scott Seago <sseago at redhat.com>
---
src/app/controllers/application.rb | 26 ++--
src/app/controllers/hardware_controller.rb | 210 ++++------------------
src/app/controllers/pool_controller.rb | 130 ++++++++++++--
src/app/controllers/resources_controller.rb | 122 ++++---------
src/app/controllers/smart_pools_controller.rb | 120 +++++-------
src/app/controllers/storage_volume_controller.rb | 39 +++--
src/app/models/hardware_pool.rb | 48 -----
src/app/models/host.rb | 4 +
src/app/models/pool.rb | 3 +
src/app/models/smart_pool.rb | 19 --
src/app/models/storage_pool.rb | 4 +
src/app/models/vm.rb | 6 +
src/app/models/vm_resource_pool.rb | 4 +
src/app/models/vm_task.rb | 8 +-
src/app/services/application_service.rb | 28 +++-
src/app/services/hardware_pool_service.rb | 125 +++++++++++++
src/app/services/pool_service.rb | 60 ++++++
src/app/services/smart_pool_service.rb | 79 ++++++++
src/app/services/vm_resource_pool_service.rb | 76 ++++++++
src/app/views/resources/quick_summary.rhtml | 2 +-
src/app/views/resources/vm_actions.rhtml | 6 +-
src/app/views/smart_pools/_form.rhtml | 2 +-
22 files changed, 671 insertions(+), 450 deletions(-)
create mode 100644 src/app/services/hardware_pool_service.rb
create mode 100644 src/app/services/pool_service.rb
create mode 100644 src/app/services/smart_pool_service.rb
create mode 100644 src/app/services/vm_resource_pool_service.rb
diff --git a/src/app/controllers/application.rb
b/src/app/controllers/application.rb
index e5f4d4b..289d950 100644
--- a/src/app/controllers/application.rb
+++ b/src/app/controllers/application.rb
@@ -118,17 +118,7 @@ class ApplicationController < ActionController::Base
def handle_auth_error(msg)
respond_to do |format|
format.html do
- @title = "Access denied"
- @errmsg = msg
- @ajax = params[:ajax]
- @nolayout = params[:nolayout]
- if @ajax
- render :template => 'layouts/popup-error', :layout =>
'tabs-and-content'
- elsif @nolayout
- render :template => 'layouts/popup-error', :layout =>
'help-and-content'
- else
- render :template => 'layouts/popup-error', :layout =>
'popup'
- end
+ html_error_page(msg)
end
format.json do
@json_hash ||= {}
@@ -139,7 +129,19 @@ class ApplicationController < ActionController::Base
format.xml { head :forbidden }
end
end
-
+ def html_error_page(msg)
+ @title = "Access denied"
+ @errmsg = msg
+ @ajax = params[:ajax]
+ @nolayout = params[:nolayout]
+ if @ajax
+ render :template => 'layouts/popup-error', :layout =>
'tabs-and-content'
+ elsif @nolayout
+ render :template => 'layouts/popup-error', :layout =>
'help-and-content'
+ else
+ render :template => 'layouts/popup-error', :layout =>
'popup'
+ end
+ end
# don't define find_opts for array inputs
def json_hash(full_items, attributes, arg_list=[], find_opts={},
id_method=:id)
page = params[:page].to_i
diff --git a/src/app/controllers/hardware_controller.rb
b/src/app/controllers/hardware_controller.rb
index 2158e08..8261cd7 100644
--- a/src/app/controllers/hardware_controller.rb
+++ b/src/app/controllers/hardware_controller.rb
@@ -17,8 +17,10 @@
# MA 02110-1301, USA. A copy of the GNU General Public License is
# also available at http://www.gnu.org/copyleft/gpl.html.
#
+require 'services/hardware_pool_service'
class HardwareController < PoolController
+ include HardwarePoolService
EQ_ATTRIBUTES = [ :name, :parent_id ]
@@ -27,10 +29,7 @@ class HardwareController < PoolController
verify :method => [:post, :delete], :only => :destroy,
:redirect_to => { :action => :list }
- before_filter :pre_modify, :only => [:add_hosts, :move_hosts,
- :add_storage, :move_storage,
- :create_storage, :delete_storage,
- :move, :removestorage]
+ before_filter :pre_modify, :only => [:move, :removestorage]
def index
if params[:path]
@@ -113,8 +112,13 @@ class HardwareController < PoolController
end
def show_storage
- @storage_tree = @pool.storage_tree(:filter_unavailable => false,
:include_used => true).to_json
- show
+ begin
+ svc_show(params[:id])
+ @storage_tree = @pool.storage_tree(:filter_unavailable => false,
:include_used => true).to_json
+ render_show
+ rescue PermissionError => perm_error
+ handle_auth_error(perm_error.message)
+ end
end
def show_tasks
@@ -124,12 +128,12 @@ class HardwareController < PoolController
def hosts_json
if params[:exclude_host]
- pre_show
+ pre_show_pool
hosts = @pool.hosts
find_opts = {:conditions => ["id != ?",
params[:exclude_host]]}
include_pool = false
elsif params[:id]
- pre_show
+ pre_show_pool
hosts = @pool.hosts
find_opts = {}
include_pool = false
@@ -154,7 +158,7 @@ class HardwareController < PoolController
def storage_pools_json
if params[:id]
- pre_show
+ pre_show_pool
storage_pools = @pool.storage_pools
find_opts = {:conditions => "type !=
'LvmStoragePool'"}
include_pool = false
@@ -190,144 +194,49 @@ class HardwareController < PoolController
super
end
- def create
- resource_type = params[:resource_type]
- resource_ids_str = params[:resource_ids]
- resource_ids = []
- resource_ids = resource_ids_str.split(",").collect {|x| x.to_i}
if resource_ids_str
- begin
- @pool.create_with_resources(@parent, resource_type, resource_ids)
- respond_to do |format|
- format.html {
- reply = { :object => "pool", :success => true,
- :alert => "Hardware Pool was successfully created." }
- reply[:resource_type] = resource_type if resource_type
- render :json => reply
- }
- format.xml {
- render :xml => @pool.to_xml(XML_OPTS),
- :status => :created,
- :location => hardware_pool_url(@pool)
- }
- end
- rescue
- respond_to do |format|
- format.json {
- render :json => { :object => "pool", :success =>
false,
- :errors => @pool.errors.localize_error_messages.to_a }
- }
- format.xml { render :xml => @pool.errors,
- :status => :unprocessable_entity }
- end
- end
- end
-
- def update
- if params[:hardware_pool]
- # FIXME: For the REST API, we allow moving hosts/storage through
- # update. It makes that operation convenient for clients, though makes
- # the implementation here somewhat ugly.
- [:hosts, :storage_pools].each do |k|
- objs = params[:hardware_pool].delete(k)
- ids = objs.reject{ |obj| obj[:hardware_pool_id] == @pool.id}.
- collect{ |obj| obj[:id] }
- if ids.size > 0
- # FIXME: use self.move_hosts/self.move_storage
- if k == :hosts
- @pool.move_hosts(ids, @pool.id)
- else
- @pool.move_storage(ids, @pool.id)
- end
- end
- end
- # FIXME: HTML views should use :hardware_pool
- params[:pool] = params.delete(:hardware_pool)
- end
-
- begin
- @pool.update_attributes!(params[:pool])
- respond_to do |format|
- format.json {
- render :json => { :object => "pool", :success =>
true,
- :alert => "Hardware Pool was successfully modified." }
- }
- format.xml {
- render :xml => @pool.to_xml(XML_OPTS),
- :status => :created,
- :location => hardware_pool_url(@pool)
- }
- end
- rescue
- respond_to do |format|
- format.json {
- render :json => { :object => "pool", :success =>
false,
- :errors => @pool.errors.localize_error_messages.to_a}
- }
- format.xml {
- render :xml => @pool.errors,
- :status => :unprocessable_entity
- }
- end
- end
+ def additional_create_params
+ {:resource_type => params[:resource_type],
+ :resource_ids => params[:resource_ids],
+ :parent_id => (params[:hardware_pool] ?
+ params[:hardware_pool][:parent_id] :
+ params[:parent_id])}
end
def add_hosts
- edit_items(Host, :move_hosts, @pool.id, :add)
+ edit_items(Host, @pool.id, :add)
end
def move_hosts
- edit_items(Host, :move_hosts, params[:target_pool_id], :move)
+ edit_items(Host, params[:target_pool_id], :move)
end
def add_storage
- edit_items(StoragePool, :move_storage, @pool.id, :add)
+ edit_items(StoragePool, @pool.id, :add)
end
def move_storage
- edit_items(StoragePool, :move_storage, params[:target_pool_id], :move)
+ edit_items(StoragePool, params[:target_pool_id], :move)
end
- #FIXME: we need permissions checks. user must have permission on src pool
- # in addition to the current pool (which is checked). We also need to fail
- # for storage that aren't currently empty
- def edit_items(item_class, item_method, target_pool_id, item_action)
- resource_ids_str = params[:resource_ids]
- resource_ids = resource_ids_str.split(",").collect {|x| x.to_i}
-
- # if user doesn't have modify permission on both source and destination
- unless @pool.can_modify(@user) and
Pool.find(target_pool_id).can_modify(@user)
- render :json => { :success => false,
- :alert => "Cannot #{item_action.to_s}
#{item_class.table_name.humanize} without admin permissions on both pools"
}
- return
- end
-
- # relay error message if movable check fails for any resource
- success = true
- failed_resources = ""
- resource_ids.each {|x|
- unless item_class.find(x).movable?
- success = false
- failed_resources += x.to_s + " "
- end
- }
- resource_ids.delete_if { |x| ! item_class.find(x).movable? }
-
+ def edit_items(item_class, target_pool_id, item_action)
begin
- @pool.transaction do
- @pool.send(item_method, resource_ids, target_pool_id)
+ if item_class == Host
+ alert = svc_move_hosts(params[:id],
params[:resource_ids].split(","), target_pool_id)
+ elsif item_class == StoragePool
+ alert = svc_move_storage(params[:id],
params[:resource_ids].split(","), target_pool_id)
+ else
+ raise "invalid class #{item_class}"
end
- rescue
- success = false
- end
-
- if success
- render :json => { :success => true,
- :alert => "#{item_action.to_s}
#{item_class.table_name.humanize} successful.",
+ render :json => { :success => true, :alert => alert,
:storage => @pool.storage_tree({:filter_unavailable => false,
:include_used => true, :state => item_action.to_s})}
- else
- render :json => { :success => false,
- :alert => "#{item_action.to_s}
#{item_class.table_name.humanize} failed" +
- (failed_resources == "" ? "." : "
for " + failed_resources) }
+ rescue PermissionError => perm_error
+ handle_auth_error(perm_error.message)
+ # If we need to give more details as to which hosts/storage succeeded,
+ # they're in the exception
+ rescue PartialSuccessError => error
+ render :json => { :success => false, :alert => error.message }
+ rescue Exception => ex
+ render :json => { :success => false, :alert => error.message }
end
end
@@ -335,33 +244,6 @@ class HardwareController < PoolController
render :layout => 'popup'
end
- def destroy
- parent = @pool.parent
- if not(parent)
- alert="You can't delete the top level Hardware pool."
- success=false
- status=:method_not_allowed
- elsif not(@pool.children.empty?)
- alert = "You can't delete a Pool without first deleting its
children."
- success=false
- status=:conflict
- else
- if @pool.move_contents_and_destroy
- alert="Hardware Pool was successfully deleted."
- success=true
- status=:ok
- else
- alert="Failed to delete hardware pool."
- success=false
- status=:internal_server_error
- end
- end
- respond_to do |format|
- format.json { render :json => { :object => "pool",
:success => success,
- :alert => alert } }
- format.xml { head status }
- end
- end
protected
#filter methods
@@ -369,25 +251,11 @@ class HardwareController < PoolController
@pool = HardwarePool.new
super
end
- def pre_create
- # FIXME: REST and browsers send params differently. Should be fixed
- # in the views
- if params[:pool]
- @pool = HardwarePool.new(params[:pool])
- else
- @pool = HardwarePool.new(params[:hardware_pool])
- end
- super
- end
def pre_edit
@pool = HardwarePool.find(params[:id])
@parent = @pool.parent
set_perms(@pool)
end
- def pre_show
- @pool = HardwarePool.find(params[:id])
- super
- end
def pre_modify
pre_edit
authorize_admin
diff --git a/src/app/controllers/pool_controller.rb
b/src/app/controllers/pool_controller.rb
index 06f8768..41c1be9 100644
--- a/src/app/controllers/pool_controller.rb
+++ b/src/app/controllers/pool_controller.rb
@@ -20,12 +20,9 @@
class PoolController < ApplicationController
- before_filter :pre_show_pool, :only => [:show_vms, :show_users,
- :show_hosts, :show_storage,
- :users_json, :show_tasks, :tasks,
+ before_filter :pre_show_pool, :only => [:users_json, :show_tasks, :tasks,
:vm_pools_json,
- :pools_json, :show_pools,
- :storage_volumes_json,
:quick_summary]
+ :pools_json, :storage_volumes_json]
XML_OPTS = {
:include => [ :storage_pools, :hosts, :quota ]
@@ -40,6 +37,15 @@ class PoolController < ApplicationController
end
def show
+ begin
+ svc_show(params[:id])
+ render_show
+ rescue PermissionError => perm_error
+ handle_auth_error(perm_error.message)
+ end
+ end
+
+ def render_show
respond_to do |format|
format.html {
render :layout => 'tabs-and-content' if params[:ajax]
@@ -49,10 +55,15 @@ class PoolController < ApplicationController
render :xml => @pool.to_xml(XML_OPTS)
}
end
- end
+ end
def quick_summary
- render :layout => 'selection'
+ begin
+ svc_show(params[:id])
+ render :layout => 'selection'
+ rescue PermissionError => perm_error
+ handle_auth_error(perm_error.message)
+ end
end
# resource's users list page
@@ -97,30 +108,111 @@ class PoolController < ApplicationController
render :layout => 'popup'
end
+ def create
+ # FIXME: REST and browsers send params differently. Should be fixed
+ # in the views
+ begin
+ alert = svc_create(params[:pool] ? params[:pool] :
params[:hardware_pool],
+ additional_create_params)
+ respond_to do |format|
+ format.html {
+ reply = { :object => "pool", :success => true,
+ :alert => alert }
+ reply[:resource_type] = params[:resource_type] if
params[:resource_type]
+ render :json => reply
+ }
+ format.xml {
+ render :xml => @pool.to_xml(XML_OPTS),
+ :status => :created,
+ :location => hardware_pool_url(@pool)
+ }
+ end
+ rescue PermissionError => perm_error
+ handle_auth_error(perm_error.message)
+ rescue Exception => ex
+ respond_to do |format|
+ format.json { json_error("pool", @pool, ex) }
+ format.xml { render :xml => @pool.errors,
+ :status => :unprocessable_entity }
+ end
+ end
+ end
+
+ def update
+ begin
+ alert = svc_update(params[:id], params[:pool] ? params[:pool] :
+ params[:hardware_pool])
+ respond_to do |format|
+ format.html {
+ reply = { :object => "pool", :success => true, :alert
=> alert }
+ render :json => reply
+ }
+ format.xml {
+ render :xml => @pool.to_xml(XML_OPTS),
+ :status => :created,
+ :location => hardware_pool_url(@pool)
+ }
+ end
+ rescue PermissionError => perm_error
+ handle_auth_error(perm_error.message)
+ rescue Exception => ex
+ respond_to do |format|
+ format.json { json_error("pool", @pool, ex) }
+ format.xml { render :xml => @pool.errors,
+ :status => :unprocessable_entity }
+ end
+ end
+ end
+
+ def additional_create_params
+ {}
+ end
+
def edit
render :layout => 'popup'
end
+ def destroy
+ alert = nil
+ success = true
+ status = :ok
+ begin
+ alert = svc_destroy(params[:id])
+ rescue ActionError => error
+ alert = error.message
+ success = false
+ status = :conflict
+ rescue PermissionError => error
+ alert = error.message
+ success = false
+ status = :forbidden
+ rescue Exception => error
+ alert = error.message
+ success = false
+ status = :method_not_allowed
+ end
+ respond_to do |format|
+ format.json { render :json => { :object => "pool",
:success => success,
+ :alert => alert } }
+ format.xml { head status }
+ end
+ end
+
protected
def pre_new
@parent = Pool.find(params[:parent_id])
set_perms(@parent)
end
- def pre_create
- #this is currently only true for the rest API for hardware pools
- if params[:hardware_pool]
- @parent = Pool.find(params[:hardware_pool][:parent_id])
- else
- @parent = Pool.find(params[:parent_id])
- end
- set_perms(@parent)
- end
def pre_show_pool
- pre_show
- end
- def pre_show
+ @pool = Pool.find(params[:id])
set_perms(@pool)
authorize_view
end
+ # 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 tmp_authorize_admin
+ end
end
diff --git a/src/app/controllers/resources_controller.rb
b/src/app/controllers/resources_controller.rb
index c61ef46..63bc98c 100644
--- a/src/app/controllers/resources_controller.rb
+++ b/src/app/controllers/resources_controller.rb
@@ -16,15 +16,15 @@
# 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.
+require 'services/vm_resource_pool_service'
class ResourcesController < PoolController
+ include VmResourcePoolService
def index
list
render :action => 'list'
end
- before_filter :pre_vm_actions, :only => [:vm_actions]
-
# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
verify :method => :post, :only => [ :destroy, :create, :update ],
:redirect_to => { :action => :list }
@@ -69,89 +69,62 @@ class ResourcesController < PoolController
end
def vms_json
- pre_show
+ pre_show_pool
super(:full_items => @pool.vms, :find_opts => {}, :include_pool =>
:true)
end
- def create
- begin
- @pool.create_with_parent(@parent)
- render :json => { :object => "vm_resource_pool", :success
=> true,
- :alert => "Virtual Machine Pool was
successfully created." }
- rescue
- render :json => { :object => "vm_resource_pool", :success
=> false,
- :errors =>
@pool.errors.localize_error_messages.to_a}
- end
- end
-
- def update
- begin
- @pool.update_attributes!(params[:pool])
- render :json => { :object => "vm_resource_pool", :success
=> true,
- :alert => "Virtual Machine Pool was
successfully modified." }
- rescue
- render :json => { :object => "vm_resource_pool", :success
=> false,
- :errors =>
@pool.errors.localize_error_messages.to_a}
- end
+ def additional_create_params
+ {:parent_id => (params[:hardware_pool] ?
+ params[:hardware_pool][:parent_id] :
+ params[:parent_id])}
end
- #FIXME: we need permissions checks. user must have permission. We also need
to fail
+ #FIXME: we need permissions checks. user must have permission. We also need
to fail
# for pools that aren't currently empty
def delete
- vm_pool_ids_str = params[:vm_pool_ids]
- vm_pool_ids = vm_pool_ids_str.split(",").collect {|x| x.to_i}
- vm_pool_names = []
- begin
- VmResourcePool.transaction do
- pools = VmResourcePool.find(:all, :conditions => "id in
(#{vm_pool_ids.join(', ')})")
- pools.each do |pool|
- vm_pool_names << pool.name
- pool.destroy
- end
+ vm_pool_ids = params[:vm_pool_ids].split(",")
+ successes = []
+ failures = {}
+ vm_pool_ids.each do |pool_id|
+ begin
+ svc_destroy(pool_id)
+ successes << @pool
+ rescue PermissionError => perm_error
+ failures[@pool] = perm_error.message
+ rescue Exception => ex
+ failures[@pool] = ex.message
end
- render :json => { :object => "vm_resource_pool", :success
=> true,
- :alert => "Virtual Machine Pools
#{vm_pool_names.join(', ')} were successfully deleted." }
- rescue
- render :json => { :object => "vm_resource_pool", :success
=> false,
- :alert => "Error in deleting Virtual Machine
Pools."}
end
- end
-
- def destroy
- if @pool.destroy
- alert="Virtual Machine Pool was successfully deleted."
- success=true
- else
- alert="Failed to delete virtual machine pool."
- success=false
+ success = failures.empty?
+ alert = ""
+ if !successes.empty?
+ alert == "Virtual Machine Pools #{successes.collect{|pool|
pool.name}.join(', ')} were successfully deleted."
+ end
+ if !failures.empty?
+ alert == " Errors in deleting VM Pools #{failures.collect{|pool,err|
"#{pool.name}: #{err}"}.join(', ')}."
end
- render :json => { :object => "vm_resource_pool", :success
=> success, :alert => alert }
+ render :json => { :object => "vm_resource_pool", :success
=> success,
+ :alert => alert }
end
def vm_actions
- @action = params[:vm_action]
- @action_label = VmTask.action_label(@action)
- vms_str = params[:vm_ids]
- @vms = vms_str.split(",").collect {|x| Vm.find(x.to_i)}
- @success_list = []
- @failure_list = []
begin
- @pool.transaction do
- @vms.each do |vm|
- if vm.queue_action(@user, @action)
- @success_list << vm
- print vm.description, vm.id, "\n"
- else
- @failure_list << vm
- end
- end
- end
- rescue
+ alert = svc_vm_actions_hosts(params[:id], params[:vm_action],
+ params[:vm_ids].split(","))
+ @success_list = @vms
+ @failures = {}
+ render :layout => 'confirmation'
+ rescue PermissionError => perm_error
+ handle_auth_error(perm_error.message)
+ rescue PartialSuccessError => error
+ @success_list = error.successes
+ @failures = error.failures
+ render :layout => 'confirmation'
+ rescue Exeption => ex
flash[:errmsg] = 'Error queueing VM actions.'
@success_list = []
@failure_list = []
end
- render :layout => 'confirmation'
end
protected
@@ -159,26 +132,11 @@ class ResourcesController < PoolController
@pool = VmResourcePool.new
super
end
- def pre_create
- @pool = VmResourcePool.new(params[:pool])
- super
- end
def pre_edit
@pool = VmResourcePool.find(params[:id])
@parent = @pool.parent
@current_pool_id=@pool.id
set_perms(@pool.parent)
end
- def pre_show
- @pool = VmResourcePool.find(params[:id])
- super
- @is_hwpool_admin = @pool.parent.can_modify(@user)
- end
- def pre_vm_actions
- @pool = VmResourcePool.find(params[:id])
- @parent = @pool.parent
- set_perms(@pool)
- authorize_user
- end
end
diff --git a/src/app/controllers/smart_pools_controller.rb
b/src/app/controllers/smart_pools_controller.rb
index fb6ccb5..bc20024 100644
--- a/src/app/controllers/smart_pools_controller.rb
+++ b/src/app/controllers/smart_pools_controller.rb
@@ -17,14 +17,12 @@
# MA 02110-1301, USA. A copy of the GNU General Public License is
# also available at http://www.gnu.org/copyleft/gpl.html.
#
+require 'services/smart_pool_service'
class SmartPoolsController < PoolController
+ include SmartPoolService
- before_filter :pre_modify, :only => [:add_hosts, :remove_hosts,
- :add_storage, :remove_storage,
- :add_vms, :remove_vms,
- :add_pools, :remove_pools,
- :add_items, :add_pool_dialog]
+ before_filter :pre_modify, :only => [:add_pool_dialog]
def show_vms
show
end
@@ -38,30 +36,19 @@ class SmartPoolsController < PoolController
end
def show_storage
- @storage_tree = @pool.storage_tree(:filter_unavailable => false,
:include_used => true).to_json
- show
- end
-
- def create
begin
- @pool.create_with_parent(@parent)
- render :json => { :object => "smart_pool", :success =>
true,
- :alert => "Smart Pool was successfully
created." }
- rescue
- render :json => { :object => "smart_pool", :success =>
false,
- :errors =>
@pool.errors.localize_error_messages.to_a}
+ svc_show(params[:id])
+ @storage_tree = @pool.storage_tree(:filter_unavailable => false,
:include_used => true).to_json
+ render_show
+ rescue PermissionError => perm_error
+ handle_auth_error(perm_error.message)
end
end
- def update
- begin
- @pool.update_attributes!(params[:smart_pool])
- render :json => { :object => "smart_pool", :success =>
true,
- :alert => "Smart Pool was successfully
modified." }
- rescue
- render :json => { :object => "smart_pool", :success =>
false,
- :errors =>
@pool.errors.localize_error_messages.to_a}
- end
+ def additional_create_params
+ {:parent_id => (params[:hardware_pool] ?
+ params[:hardware_pool][:parent_id] :
+ params[:parent_id])}
end
def add_pool_dialog
@@ -98,7 +85,7 @@ class SmartPoolsController < PoolController
def items_json_internal(item_class, item_assoc)
if params[:id]
- pre_show
+ pre_show_pool
full_items = @pool.send(item_assoc)
find_opts = {}
include_pool = false
@@ -120,80 +107,82 @@ class SmartPoolsController < PoolController
end
def add_hosts
- edit_items(Host, :add_items, :add)
+ add_or_remove_items(Host, :add)
end
def remove_hosts
- edit_items(Host, :remove_items, :remove)
+ add_or_remove_items(Host, :remove)
end
def add_storage
- edit_items(StoragePool, :add_items, :add)
+ add_or_remove_items(StoragePool, :add)
end
def remove_storage
- edit_items(StoragePool, :remove_items, :remove)
+ add_or_remove_items(StoragePool, :remove)
end
def add_vms
- edit_items(Vm, :add_items, :add)
+ add_or_remove_items(Vm, :add)
end
def remove_vms
- edit_items(Vm, :remove_items, :remove)
+ add_or_remove_items(Vm, :remove)
end
def add_pools
- edit_items(Pool, :add_items, :add)
+ add_or_remove_items(Pool, :add)
end
def remove_pools
- edit_items(Pool, :remove_items, :remove)
+ add_or_remove_items(Pool, :remove)
end
- def edit_items(item_class, item_method, item_action)
- resource_ids_str = params[:resource_ids]
- resource_ids = resource_ids_str.split(",").collect {|x| x.to_i}
+ def add_or_remove_items(item_class, item_action)
begin
- @pool.send(item_method,item_class, resource_ids)
- render :json => { :success => true,
- :alert => "#{item_action.to_s}
#{item_class.table_name.humanize} successful." }
+ alert = svc_add_remove_items(params[:id], item_class, item_action,
+ params[:resource_ids].split(","))
+ render :json => { :success => true, :alert => alert}
rescue
render :json => { :success => false,
:alert => "#{item_action.to_s}
#{item_class.table_name.humanize} failed." }
+ rescue PermissionError => perm_error
+ handle_auth_error(perm_error.message)
+ # If we need to give more details as to which hosts/storage succeeded,
+ # they're in the exception
+ rescue PartialSuccessError => error
+ render :json => { :success => false, :alert => error.message }
+ rescue Exception => ex
+ render :json => { :success => false, :alert => error.message }
end
end
def add_items
class_and_ids_str = params[:class_and_ids]
- class_and_ids = class_and_ids_str.split(",").collect {|x|
x.split("_")}
+ class_and_ids = class_and_ids_str.split(",").collect do
|class_and_id_str|
+ class_and_id = class_and_id_str.split("_")
+ class_and_id[0] = class_and_id[0].constantize
+ class_and_id[1] = class_and_id[1].to_a
+ end
begin
- @pool.transaction do
- class_and_ids.each do |class_and_id|
-
@pool.add_item(class_and_id[0].constantize.find(class_and_id[1].to_i))
- end
- end
- render :json => { :success => true,
- :alert => "Add items to smart pool successful." }
- rescue => ex
+ alert = svc_add_remove_items(params[:id], nil, :add, class_and_ids)
+ render :json => { :success => true, :alert => alert}
+ rescue
render :json => { :success => false,
- :alert => "Add items to smart pool failed: " +
ex.message }
+ :alert => "#{item_action.to_s} failed." }
+ rescue PermissionError => perm_error
+ handle_auth_error(perm_error.message)
+ # If we need to give more details as to which hosts/storage succeeded,
+ # they're in the exception
+ rescue PartialSuccessError => error
+ render :json => { :success => false, :alert => error.message }
+ rescue Exception => ex
+ render :json => { :success => false, :alert => error.message }
end
end
- def destroy
- if @pool.destroy
- alert="Smart Pool was successfully deleted."
- success=true
- else
- alert="Failed to delete Smart pool."
- success=false
- end
- render :json => { :object => "smart_pool", :success =>
success, :alert => alert }
- end
-
protected
#filter methods
def pre_new
@@ -201,20 +190,11 @@ class SmartPoolsController < PoolController
@parent = DirectoryPool.get_or_create_user_root(get_login_user)
set_perms(@parent)
end
- def pre_create
- @pool = SmartPool.new(params[:smart_pool])
- @parent = DirectoryPool.get_or_create_user_root(get_login_user)
- set_perms(@parent)
- end
def pre_edit
@pool = SmartPool.find(params[:id])
@parent = @pool.parent
set_perms(@pool)
end
- def pre_show
- @pool = SmartPool.find(params[:id])
- super
- end
def pre_modify
pre_edit
authorize_admin
diff --git a/src/app/controllers/storage_volume_controller.rb
b/src/app/controllers/storage_volume_controller.rb
index b6b0593..d4a2561 100644
--- a/src/app/controllers/storage_volume_controller.rb
+++ b/src/app/controllers/storage_volume_controller.rb
@@ -22,19 +22,16 @@ class StorageVolumeController < ApplicationController
def new
@return_to_workflow = params[:return_to_workflow] || false
if params[:storage_pool_id]
- @storage_pool = StoragePool.find(params[:storage_pool_id])
unless @storage_pool.user_subdividable
- #fixme: proper error page for popups
- redirect_to :controller => 'dashboard'
+ html_error_page("User-created storage volumes are not supported on
this pool")
return
end
- new_volume_internal(@storage_pool,
- { :storage_pool_id => params[:storage_pool_id]})
+ @storage_volume = StorageVolume.factory(@storage_pool.get_type_label,
+ { :storage_pool_id =>
+ params[:storage_pool_id]})
else
- @source_volume = StorageVolume.find(params[:source_volume_id])
unless @source_volume.supports_lvm_subdivision
- #fixme: proper error page for popups
- redirect_to :controller => 'dashboard'
+ html_error_page("LVM is not supported for this storage
volume")
return
end
lvm_pool = @source_volume.lvm_storage_pool
@@ -47,7 +44,8 @@ class StorageVolumeController < ApplicationController
lvm_pool.source_volumes << @source_volume
lvm_pool.save!
end
- new_volume_internal(lvm_pool, { :storage_pool_id => lvm_pool.id})
+ @storage_volume = StorageVolume.factory(lvm_pool.get_type_label,
+ { :storage_pool_id =>
lvm_pool.id})
@storage_volume.lv_owner_perms='0744'
@storage_volume.lv_group_perms='0744'
@storage_volume.lv_mode_perms='0744'
@@ -99,7 +97,7 @@ class StorageVolumeController < ApplicationController
format.html { render :layout => 'selection' }
format.json do
attr_list = []
- attr_list << :id if (@storage_pool.user_subdividable and
authorized?(Privilege::MODIFY)
+ attr_list << :id if (@storage_pool.user_subdividable and
authorized?(Privilege::MODIFY))
attr_list += [:display_name, :size_in_gb, :get_type_label]
json_list(@storage_pool.storage_volumes, attr_list)
end
@@ -109,8 +107,6 @@ class StorageVolumeController < ApplicationController
end
def destroy
- @storage_volume = StorageVolume.find(params[:id])
- set_perms(@storage_volume.storage_pool.hardware_pool)
unless authorized?(Privilege::MODIFY) and
@storage_volume.storage_pool.user_subdividable
handle_auth_error("You do not have permission to delete this storage
volume.")
else
@@ -123,6 +119,16 @@ class StorageVolumeController < ApplicationController
end
end
+ def pre_new
+ if params[:storage_pool_id]
+ @storage_pool = StoragePool.find(params[:storage_pool_id])
+ set_perms(@storage_pool.hardware_pool)
+ else
+ @source_volume = StorageVolume.find(params[:source_volume_id])
+ set_perms(@source_volume.storage_pool.hardware_pool)
+ end
+ end
+
def pre_create
volume = params[:storage_volume]
unless type = params[:storage_type]
@@ -132,14 +138,13 @@ class StorageVolumeController < ApplicationController
set_perms(@storage_volume.storage_pool.hardware_pool)
authorize_admin
end
-
- private
- def new_volume_internal(storage_pool, new_params)
- @storage_volume = StorageVolume.factory(storage_pool.get_type_label,
new_params)
+ # will go away w/ svc layer
+ def pre_edit
+ @storage_volume = StorageVolume.find(params[:id])
set_perms(@storage_volume.storage_pool.hardware_pool)
- authorize_admin
end
+ private
def delete_volume_internal(volume)
begin
name = volume.display_name
diff --git a/src/app/models/hardware_pool.rb b/src/app/models/hardware_pool.rb
index 7015854..0e7e745 100644
--- a/src/app/models/hardware_pool.rb
+++ b/src/app/models/hardware_pool.rb
@@ -40,54 +40,6 @@ class HardwarePool < Pool
hw_root ? hw_root.named_child(DEFAULT_POOL_NAME) : nil
end
- def create_with_resources(parent, resource_type= nil, resource_ids=[])
- create_with_parent(parent) do
- if resource_type == "hosts"
- move_hosts(resource_ids, id)
- elsif resource_type == "storage"
- move_storage(resource_ids, id)
- end
- end
- end
-
- def move_hosts(host_ids, target_pool_id)
- hosts = Host.find(:all, :conditions => "id in
(#{host_ids.join(', ')})")
- transaction do
- hosts.each do |host|
- host.hardware_pool = HardwarePool.find(target_pool_id)
- host.save!
- end
- end
- end
-
- def move_storage(storage_pool_ids, target_pool_id)
- storage_pools = StoragePool.find(:all, :conditions => "id in
(#{storage_pool_ids.join(', ')})")
- transaction do
- storage_pools.each do |storage_pool|
- storage_pool.hardware_pool_id = target_pool_id
- storage_pool.save!
- end
- end
- end
-
-
- # todo: does this method still make sense? or should we just enforce
"empty" pools?
- def move_contents_and_destroy
- transaction do
- parent_id = parent.id
- hosts.each do |host|
- host.hardware_pool_id=parent_id
- host.save
- end
- storage_pools.each do |vol|
- vol.hardware_pool_id=parent_id
- vol.save
- end
- # what about quotas -- for now they're deleted
- destroy
- end
- end
-
def total_storage_volumes
storage_pools.inject(0) { |sum, pool| sum += pool.storage_volumes.size}
end
diff --git a/src/app/models/host.rb b/src/app/models/host.rb
index 06d7388..0665c3f 100644
--- a/src/app/models/host.rb
+++ b/src/app/models/host.rb
@@ -134,6 +134,10 @@ class Host < ActiveRecord::Base
hardware_pool.search_users
end
+ def permission_obj
+ hardware_pool
+ end
+
def movable?
return vms.size == 0
end
diff --git a/src/app/models/pool.rb b/src/app/models/pool.rb
index 6f2a086..2979fcb 100644
--- a/src/app/models/pool.rb
+++ b/src/app/models/pool.rb
@@ -306,6 +306,9 @@ class Pool < ActiveRecord::Base
def class_and_id
self.class.name + "_" + self.id.to_s
end
+ def permission_obj
+ self
+ end
protected
def traverse_parents
if id
diff --git a/src/app/models/smart_pool.rb b/src/app/models/smart_pool.rb
index dba000d..1f718a8 100644
--- a/src/app/models/smart_pool.rb
+++ b/src/app/models/smart_pool.rb
@@ -53,25 +53,6 @@ class SmartPool < Pool
:tagged_id=>item.id}).destroy
end
- def add_items(item_class, item_ids)
- items = item_class.find(:all, :conditions => "id in
(#{item_ids.join(', ')})")
- transaction do
- items.each { |item| add_item(item)}
- end
- end
-
- def remove_items(item_class, item_ids)
- tags = smart_pool_tags.find(:all,
- :conditions => "tagged_id in
- (#{item_ids.join(',
')})
- and
tagged_type='#{item_class.name}'")
- transaction do
- tags.each do |tag|
- tag.destroy
- end
- end
- end
-
def self.smart_pools_for_user(user)
nested_pools = DirectoryPool.get_smart_root.full_set_nested(
:privilege => Privilege::MODIFY, :user => user,
diff --git a/src/app/models/storage_pool.rb b/src/app/models/storage_pool.rb
index fbe5954..92548bd 100644
--- a/src/app/models/storage_pool.rb
+++ b/src/app/models/storage_pool.rb
@@ -155,6 +155,10 @@ class StoragePool < ActiveRecord::Base
return_hash
end
+ def permission_obj
+ hardware_pool
+ end
+
def movable?
storage_volumes.each{ |x|
return false unless x.movable?
diff --git a/src/app/models/vm.rb b/src/app/models/vm.rb
index c62595a..6880c22 100644
--- a/src/app/models/vm.rb
+++ b/src/app/models/vm.rb
@@ -177,11 +177,17 @@ class Vm < ActiveRecord::Base
:in => EFFECTIVE_STATE.keys
+ def get_vm_pool
+ vm_resource_pool
+ end
def get_hardware_pool
pool = vm_resource_pool
pool = pool.get_hardware_pool if pool
pool
end
+ def permission_obj
+ vm_resource_pool
+ end
def storage_volume_ids
storage_volumes.collect {|x| x.id }
end
diff --git a/src/app/models/vm_resource_pool.rb
b/src/app/models/vm_resource_pool.rb
index 1ab4ab1..e41dcab 100644
--- a/src/app/models/vm_resource_pool.rb
+++ b/src/app/models/vm_resource_pool.rb
@@ -32,6 +32,10 @@ class VmResourcePool < Pool
traverse_parents { |pool| pool if pool[:type] == HardwarePool.name}
end
+ def get_vm_pool
+ self
+ end
+
def allocated_resources(exclude_vm = nil)
pending_cpus = 0
pending_memory = 0
diff --git a/src/app/models/vm_task.rb b/src/app/models/vm_task.rb
index ed33564..d7afe54 100644
--- a/src/app/models/vm_task.rb
+++ b/src/app/models/vm_task.rb
@@ -36,7 +36,7 @@ class VmTask < Task
# for migrate VM action, args provides the optional target host
ACTION_MIGRATE_VM = "migrate_vm"
- PRIV_OBJECT_VM_POOL = "vm_resource_pool"
+ PRIV_OBJECT_VM_POOL = "get_vm_pool"
PRIV_OBJECT_HW_POOL = "get_hardware_pool"
@@ -145,6 +145,12 @@ class VmTask < Task
actions
end
+ def self.action_privilege(action)
+ return ACTIONS[action][:privilege][0]
+ end
+ def self.action_privilege_object(action, obj)
+ return obj.send(ACTIONS[action][:privilege][1])
+ end
def self.action_label(action)
return ACTIONS[action][:label]
end
diff --git a/src/app/services/application_service.rb
b/src/app/services/application_service.rb
index 4dc5eba..251b37a 100644
--- a/src/app/services/application_service.rb
+++ b/src/app/services/application_service.rb
@@ -29,18 +29,34 @@
module ApplicationService
class PermissionError < RuntimeError; end
class ActionError < RuntimeError; end
+ class PartialSuccessError < RuntimeError
+ def initialize(msg, failures={}, successes=[])
+ @failures = failures
+ @successes = successes
+ end
+ def failures
+ @failures
+ end
+ def successes
+ @successes
+ end
+ end
# Including class must provide a GET_LOGIN_USER
+ def user
+ @user ||= get_login_user
+ end
+
def set_perms(perm_obj)
+ return if @perm_obj && @perm_obj.id == perm_obj.id
@perm_obj = perm_obj
@current_pool_id ||= perm_obj.id
- @user = get_login_user
- @can_view = @perm_obj.can_view(@user)
- @can_control_vms = @perm_obj.can_control_vms(@user)
- @can_modify = @perm_obj.can_modify(@user)
- @can_view_perms = @perm_obj.can_view_perms(@user)
- @can_set_perms = @perm_obj.can_set_perms(@user)
+ @can_view = @perm_obj.can_view(user)
+ @can_control_vms = @perm_obj.can_control_vms(user)
+ @can_modify = @perm_obj.can_modify(user)
+ @can_view_perms = @perm_obj.can_view_perms(user)
+ @can_set_perms = @perm_obj.can_set_perms(user)
end
def authorized?(privilege, perm_obj=nil)
diff --git a/src/app/services/hardware_pool_service.rb
b/src/app/services/hardware_pool_service.rb
new file mode 100644
index 0000000..0dbe8dc
--- /dev/null
+++ b/src/app/services/hardware_pool_service.rb
@@ -0,0 +1,125 @@
+#
+# 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 HW pools
+module HardwarePoolService
+
+ include PoolService
+
+ def svc_create(pool_hash, other_args)
+ # from before_filter
+ @pool = HardwarePool.new(pool_hash)
+ @parent = Pool.find(other_args[:parent_id])
+ authorized!(Privilege::MODIFY, at parent)
+
+ alert = "Hardware Pool was successfully created."
+ Pool.transaction do
+ @pool.create_with_parent(@parent)
+ begin
+ if other_args[:resource_type] == "hosts"
+ svc_move_hosts(@pool.id,
other_args[:resource_ids].split(","), @pool.id)
+ elsif other_args[:resource_type] == "storage"
+ svc_move_storage(@pool.id,
other_args[:resource_ids].split(","), @pool.id)
+ end
+ # wrapped in a transaction, so fail on partial success
+ rescue PartialEuccessError => ex
+ raise ActionError.new("Could not move all hosts or storage to this
pool")
+ end
+ end
+ return alert
+ end
+
+ def svc_move_hosts(pool_id, host_ids, target_pool_id)
+ svc_move_items_internal(pool_id, Host, host_ids, target_pool_id)
+ end
+ def svc_move_storage(pool_id, storage_pool_ids, target_pool_id)
+ svc_move_items_internal(pool_id, StoragePool, storage_pool_ids,
target_pool_id)
+ end
+ def svc_move_items_internal(pool_id, item_class, resource_ids,
target_pool_id)
+ # from before_filter
+ @pool = HardwarePool.find(pool_id)
+ target_pool = Pool.find(target_pool_id)
+ authorized!(Privilege::MODIFY,target_pool)
+ authorized!(Privilege::MODIFY, at pool) unless @pool == target_pool
+
+ resources = item_class.find(resource_ids)
+
+ # relay error message if movable check fails for any resource
+ success = true
+ failed_resources = {}
+ successful_resources = []
+ resources.each do |resource|
+ begin
+ if !resource.movable?
+ failed_resources[resource] = "Not Movable"
+ elsif ! resource.hardware_pool.can_modify(@user)
+ failed_resources[resource] = "Failed permission check"
+ else
+ resource.hardware_pool = target_pool
+ resource.save!
+ successful_resources << resource
+ end
+ rescue Exception => ex
+ failed_resources[resource] = ex.message
+ end
+ end
+ unless failed_resources.empty?
+ raise PartialSuccessError.new("Move
#{item_class.table_name.humanize} only partially successful",
+ failed_resources, successful_resources)
+ end
+ return "Move #{item_class.table_name.humanize} successful."
+ end
+
+ def additional_update_actions(pool, pool_hash)
+ # FIXME: For the REST API, we allow moving hosts/storage through
+ # update. It makes that operation convenient for clients, though makes
+ # the implementation here somewhat ugly.
+ begin
+ [:hosts, :storage_pools].each do |k|
+ objs = pool_hash.delete(k)
+ ids = objs.reject{ |obj| obj[:hardware_pool_id] == @pool.id}.
+ collect{ |obj| obj[:id] }
+ if ids.size > 0
+ if k == :hosts
+ svc_move_hosts(pool.id, ids, pool.id)
+ else
+ svc_move_storage(pool.id, ids, pool.id)
+ end
+ end
+ end
+ # wrapped in a transaction, so fail on partial success
+ rescue PartialEuccessError => ex
+ raise ActionError.new("Could not move all hosts or storage to this
pool")
+ end
+ end
+
+ def check_destroy_preconditions
+ msg = nil
+ if @pool == HardwarePool.get_default_pool
+ msg = "You can't delete the top level Hardware pool"
+ elsif not(@pool.children.empty?)
+ msg = "You can't delete a Pool without first deleting its
children."
+ elsif not(@pool.hosts.empty?)
+ msg = "You can't delete a Pool without first moving its
hosts."
+ elsif not(@pool.storage_pools.empty?)
+ msg = "You can't delete a Pool without first moving its
storage."
+ end
+ raise ActionError.new(msg) if msg
+ end
+end
diff --git a/src/app/services/pool_service.rb b/src/app/services/pool_service.rb
new file mode 100644
index 0000000..8073067
--- /dev/null
+++ b/src/app/services/pool_service.rb
@@ -0,0 +1,60 @@
+#
+# 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 pools
+module PoolService
+
+ include ApplicationService
+
+ def svc_show(pool_id)
+ # from before_filter
+ @pool = Pool.find(pool_id)
+ authorized!(Privilege::VIEW, at pool)
+ end
+
+ def update_perms
+ set_perms(@pool)
+ end
+ def additional_update_actions(pool, pool_hash)
+ end
+
+ def svc_update(pool_id, pool_hash)
+ # from before_filter
+ @pool = Pool.find(params[:id])
+ @parent = @pool.parent
+ update_perms
+ authorized!(Privilege::Modify)
+ Pool.transaction do
+ additional_update_actions(@pool, pool_hash)
+ @pool.update_attributes!(pool_hash)
+ end
+ end
+
+ def svc_destroy(pool_id)
+ # from before_filter
+ @pool = Pool.find(pool_id)
+ authorized!(Privilege::MODIFY, @pool)
+ check_destroy_preconditions
+ @pool.destroy
+ return "Pool was successfully deleted."
+ end
+
+ def check_destroy_preconditions
+ end
+end
diff --git a/src/app/services/smart_pool_service.rb
b/src/app/services/smart_pool_service.rb
new file mode 100644
index 0000000..344b9e8
--- /dev/null
+++ b/src/app/services/smart_pool_service.rb
@@ -0,0 +1,79 @@
+#
+# 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 smart pools
+module SmartPoolService
+
+ include PoolService
+
+ def svc_create(pool_hash, other_args)
+ # from before_filter
+ @pool = SmartPool.new(pool_hash)
+ @parent = DirectoryPool.get_or_create_user_root(get_login_user)
+ authorized!(Privilege::MODIFY, at parent)
+
+ alert = "Smart Pool was successfully created."
+ @pool.create_with_parent(@parent)
+ return alert
+ end
+
+ # if item_class is nil, resource_ids is an array of [class, id] pairs
+ def svc_add_remove_items(pool_id, item_class, item_action, resource_ids)
+ # from before_filter
+ @pool = SmartPool.find(pool_id)
+ @parent = @pool.parent
+ authorized!(Privilege::MODIFY, at pool)
+ unless [:add, :remove].include?(item_action)
+ raise ActionError.new("Invalid action #{item_action}")
+ end
+ if item_class
+ resources = item_class.find(resource_ids)
+ else
+ resources = resource_ids.collect {|the_class,id| the_class.find(id)}
+ end
+
+ # relay error message if movable check fails for any resource
+ success = true
+ failed_resources = {}
+ successful_resources = []
+ resources.each do |resource|
+ begin
+ if item_action == :add
+ if ! resource.permission_obj.can_view(@user)
+ failed_resources[resource] = "Failed permission check"
+ else
+ @pool.add_item(resource)
+ successful_resources << resource
+ end
+ elsif item_action == :remove
+ @pool.remove_item(resource)
+ successful_resources << resource
+ end
+ rescue Exception => ex
+ failed_resources[resource] = ex.message
+ end
+ end
+ unless failed_resources.empty?
+ raise PartialSuccessError.new("#{item_action.to_s}
#{item_class.table_name.humanize if item_class} only partially successful",
+ failed_resources, successful_resources)
+ end
+ return "#{item_action.to_s} #{item_class.table_name.humanize}
successful."
+ end
+
+end
diff --git a/src/app/services/vm_resource_pool_service.rb
b/src/app/services/vm_resource_pool_service.rb
new file mode 100644
index 0000000..30f7106
--- /dev/null
+++ b/src/app/services/vm_resource_pool_service.rb
@@ -0,0 +1,76 @@
+#
+# 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 VM pools
+module VmResourcePoolService
+
+ include PoolService
+
+ def svc_create(pool_hash, other_args)
+ # from before_filter
+ @pool = VmResourcePool.new(pool_hash)
+ @parent = Pool.find(other_args[:parent_id])
+ authorized!(Privilege::MODIFY, at parent)
+
+ alert = "VM Pool was successfully created."
+ @pool.create_with_parent(@parent)
+ return alert
+ end
+
+ def update_perms
+ @current_pool_id=@pool.id
+ set_perms(@pool.parent)
+ end
+
+ def svc_vm_actions(pool_id, vm_action, vm_ids)
+ # from before_filter
+ @pool = VmResourcePool.find(pool_id)
+ @parent = @pool.parent
+ @action = vm_action
+ @action_label = VmTask.action_label(@action)
+ authorized!(VmTask.action_privilege(@action),
+ VmTask.action_privilege_object(@action, at pool))
+
+ @vms = Vm.find(vm_ids)
+
+ successful_vms = []
+ failed_vms = {}
+ @vms.each do |vm|
+ begin
+ if vm.vm_resource_pool != @pool
+ failed_vms[vm] = "VM #{vm.description} does not belong to the
current pool."
+ elsif vm.queue_action(@user, @action)
+ successful_vms << vm
+ else
+ failed_vms[vm] = "unavailable action"
+ end
+ rescue Exception => ex
+ failed_vms[vm] = ex.message
+ end
+ end
+ unless failed_vms.empty?
+ raise PartialSuccessError.new("#{@action} only partially
successful",
+ failed_vms, successful_vms)
+ end
+ return "Action #{@action} successful."
+ end
+
+
+
+end
diff --git a/src/app/views/resources/quick_summary.rhtml
b/src/app/views/resources/quick_summary.rhtml
index 31c4033..70b8df6 100644
--- a/src/app/views/resources/quick_summary.rhtml
+++ b/src/app/views/resources/quick_summary.rhtml
@@ -2,7 +2,7 @@
<%=h @pool.name %> quota
<%- end -%>
<%- content_for :action_links do -%>
- <%if @is_hwpool_admin -%>
+ <%if @pool.parent.can_modify(@user) -%>
<%= link_to image_tag("icon_edit.png") + " Edit",
{:controller => 'resources', :action =>
'edit', :id => @pool},
:rel=>"facebox[.bolder]",
:class=>"selection_facebox" %>
diff --git a/src/app/views/resources/vm_actions.rhtml
b/src/app/views/resources/vm_actions.rhtml
index d556b94..b3fa1ad 100644
--- a/src/app/views/resources/vm_actions.rhtml
+++ b/src/app/views/resources/vm_actions.rhtml
@@ -25,10 +25,10 @@ Action succeeded for these VMs:
<% end %>
</ul>
-Action invalid for these VMs:
+Action failed for these VMs:
<ul>
- <% for vm in @failure_list %>
- <li><%= vm.description %></li>
+ <% for vm, msg in @failures %>
+ <li><%= vm.description %>: <%= msg %></li>
<% end %>
</ul>
</div>
diff --git a/src/app/views/smart_pools/_form.rhtml
b/src/app/views/smart_pools/_form.rhtml
index 2f2156a..c1ec6bd 100644
--- a/src/app/views/smart_pools/_form.rhtml
+++ b/src/app/views/smart_pools/_form.rhtml
@@ -1,6 +1,6 @@
<%= error_messages_for 'vm_resource_pool' %>
<!--[form:vm_resource_pool]-->
-<%= text_field_with_label "Name", 'smart_pool',
'name' %>
+<%= text_field_with_label "Name", 'pool', 'name'
%>
<!--[eoform:vm_resource_pool]-->
--
1.6.0.6
David Lutterkort
2009-Apr-29 19:22 UTC
[Ovirt-devel] [PATCH server] controller service layer refactoring for pools.
On Tue, 2009-04-28 at 22:10 +0000, Scott Seago wrote:> This is the second round of controller/service layer refactoring > changes -- in this case for the pool-related controllers (HW, VM, and > Smart pools)This looks mostly good, modulo comments below. It would also be good to include rdoc comments in the service modules to document required permissions and instance variables that get set.> diff --git a/src/app/controllers/hardware_controller.rb b/src/app/controllers/hardware_controller.rb > index 2158e08..8261cd7 100644 > --- a/src/app/controllers/hardware_controller.rb > +++ b/src/app/controllers/hardware_controller.rb> @@ -17,8 +17,10 @@ > # MA 02110-1301, USA. A copy of the GNU General Public License is > # also available at http://www.gnu.org/copyleft/gpl.html. > # > +require 'services/hardware_pool_service'This breaks class reloading in the development environment, and is not necessary at all. Should be removed> @@ -113,8 +112,13 @@ class HardwareController < PoolController > end > > def show_storage > - @storage_tree = @pool.storage_tree(:filter_unavailable => false, :include_used => true).to_json > - show > + begin > + svc_show(params[:id]) > + @storage_tree = @pool.storage_tree(:filter_unavailable => false, :include_used => true).to_json > + render_show > + rescue PermissionError => perm_error > + handle_auth_error(perm_error.message) > + end > endI know that it's just moving code around, but please avoid lines with more than 80 chars.> @@ -190,144 +194,49 @@ class HardwareController < PoolController> def add_hosts > - edit_items(Host, :move_hosts, @pool.id, :add) > + edit_items(Host, @pool.id, :add) > end > > def move_hosts > - edit_items(Host, :move_hosts, params[:target_pool_id], :move) > + edit_items(Host, params[:target_pool_id], :move) > end > > def add_storage > - edit_items(StoragePool, :move_storage, @pool.id, :add) > + edit_items(StoragePool, @pool.id, :add) > end > > def move_storage > - edit_items(StoragePool, :move_storage, params[:target_pool_id], :move) > + edit_items(StoragePool, params[:target_pool_id], :move) > end > > - #FIXME: we need permissions checks. user must have permission on src pool > - # in addition to the current pool (which is checked). We also need to fail > - # for storage that aren't currently empty > - def edit_items(item_class, item_method, target_pool_id, item_action) > - resource_ids_str = params[:resource_ids] > - resource_ids = resource_ids_str.split(",").collect {|x| x.to_i} > - > - # if user doesn't have modify permission on both source and destination > - unless @pool.can_modify(@user) and Pool.find(target_pool_id).can_modify(@user) > - render :json => { :success => false, > - :alert => "Cannot #{item_action.to_s} #{item_class.table_name.humanize} without admin permissions on both pools" } > - return > - end > - > - # relay error message if movable check fails for any resource > - success = true > - failed_resources = "" > - resource_ids.each {|x| > - unless item_class.find(x).movable? > - success = false > - failed_resources += x.to_s + " " > - end > - } > - resource_ids.delete_if { |x| ! item_class.find(x).movable? } > - > + def edit_items(item_class, target_pool_id, item_action) > begin > - @pool.transaction do > - @pool.send(item_method, resource_ids, target_pool_id) > + if item_class == Host > + alert = svc_move_hosts(params[:id], params[:resource_ids].split(","), target_pool_id) > + elsif item_class == StoragePool > + alert = svc_move_storage(params[:id], params[:resource_ids].split(","), target_pool_id) > + else > + raise "invalid class #{item_class}" > end > - rescue > - success = false > - end > - > - if success > - render :json => { :success => true, > - :alert => "#{item_action.to_s} #{item_class.table_name.humanize} successful.", > + render :json => { :success => true, :alert => alert, > :storage => @pool.storage_tree({:filter_unavailable => false, :include_used => true, :state => item_action.to_s})} > - else > - render :json => { :success => false, > - :alert => "#{item_action.to_s} #{item_class.table_name.humanize} failed" + > - (failed_resources == "" ? "." : " for " + failed_resources) } > + rescue PermissionError => perm_error > + handle_auth_error(perm_error.message) > + # If we need to give more details as to which hosts/storage succeeded, > + # they're in the exception > + rescue PartialSuccessError => error > + render :json => { :success => false, :alert => error.message } > + rescue Exception => ex > + render :json => { :success => false, :alert => error.message } > end > endThis is a good example of how our current error handling makes things more complex: we have move_hosts/move_storage actions, call a generic edit_items, and then dispatch in that method on the class of one of the parameters. First off, it would be more rubyish if edit_items took a block that has the proper calls to svc_move_hosts/svc_move_storage. Second, it seems the main point of edit_items is to factor all the error handling overhead intoa common method. For the time being, edit_items should take a block; in the future we need to look into handling more errors with rescue_with, avoiding all that error handling clutter altogether.> diff --git a/src/app/controllers/pool_controller.rb b/src/app/controllers/pool_controller.rb > index 06f8768..41c1be9 100644 > --- a/src/app/controllers/pool_controller.rb > +++ b/src/app/controllers/pool_controller.rb> @@ -40,6 +37,15 @@ class PoolController < ApplicationController > end > > def show > + begin > + svc_show(params[:id]) > + render_show > + rescue PermissionError => perm_error > + handle_auth_error(perm_error.message) > + end > + end > + > + def render_show > respond_to do |format| > format.html { > render :layout => 'tabs-and-content' if params[:ajax] > @@ -49,10 +55,15 @@ class PoolController < ApplicationController > render :xml => @pool.to_xml(XML_OPTS) > } > end > - endWhy the separate method render_show ? I don't think it simplifies things compared to inlining that method.> @@ -97,30 +108,111 @@ class PoolController < ApplicationController> + rescue Exception => ex > + respond_to do |format| > + format.json { json_error("pool", @pool, ex) } > + format.xml { render :xml => @pool.errors, > + :status => :unprocessable_entity } > + end > + endWhile I'm on a tear about error handling, I really don't like seeing 'rescue Exception' or even worse a bare rescue. It would be nice if we could finda way to report errors for json and xml with additional error templates.> + end > + > + def update > + begin > + alert = svc_update(params[:id], params[:pool] ? params[:pool] : > + params[:hardware_pool]) > + respond_to do |format| > + format.html { > + reply = { :object => "pool", :success => true, :alert => alert } > + render :json => reply > + }Is that right ? format.html and render :json ?> diff --git a/src/app/controllers/resources_controller.rb b/src/app/controllers/resources_controller.rb > index c61ef46..63bc98c 100644 > --- a/src/app/controllers/resources_controller.rb > +++ b/src/app/controllers/resources_controller.rb > @@ -16,15 +16,15 @@ > # 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. > +require 'services/vm_resource_pool_service'Not needed> @@ -69,89 +69,62 @@ class ResourcesController < PoolController...> - def destroy > - if @pool.destroy > - alert="Virtual Machine Pool was successfully deleted." > - success=true > - else > - alert="Failed to delete virtual machine pool." > - success=false > + success = failures.empty? > + alert = "" > + if !successes.empty? > + alert == "Virtual Machine Pools #{successes.collect{|pool| pool.name}.join(', ')} were successfully deleted." > + end > + if !failures.empty? > + alert == " Errors in deleting VM Pools #{failures.collect{|pool,err| "#{pool.name}: #{err}"}.join(', ')}." > ends/==/=/ The logic also deserves a comment that clobbering alert on failure is intentional.> diff --git a/src/app/controllers/smart_pools_controller.rb b/src/app/controllers/smart_pools_controller.rb > index fb6ccb5..bc20024 100644 > --- a/src/app/controllers/smart_pools_controller.rb > +++ b/src/app/controllers/smart_pools_controller.rb > @@ -17,14 +17,12 @@ > # MA 02110-1301, USA. A copy of the GNU General Public License is > # also available at http://www.gnu.org/copyleft/gpl.html. > # > +require 'services/smart_pool_service'Not needed.> diff --git a/src/app/controllers/storage_volume_controller.rb b/src/app/controllers/storage_volume_controller.rb > index b6b0593..d4a2561 100644 > --- a/src/app/controllers/storage_volume_controller.rb > +++ b/src/app/controllers/storage_volume_controller.rbThis seems tangential to the pool refactoring - could that go into a separate patch ?> diff --git a/src/app/models/host.rb b/src/app/models/host.rb > index 06d7388..0665c3f 100644 > --- a/src/app/models/host.rb > +++ b/src/app/models/host.rb > @@ -134,6 +134,10 @@ class Host < ActiveRecord::Base > hardware_pool.search_users > end > > + def permission_obj > + hardware_pool > + end > +Nice !> diff --git a/src/app/services/application_service.rb b/src/app/services/application_service.rb > index 4dc5eba..251b37a 100644 > --- a/src/app/services/application_service.rb > +++ b/src/app/services/application_service.rb > @@ -29,18 +29,34 @@ > module ApplicationService > class PermissionError < RuntimeError; end > class ActionError < RuntimeError; end > + class PartialSuccessError < RuntimeError > + def initialize(msg, failures={}, successes=[]) > + @failures = failures > + @successes = successes > + end > + def failures > + @failures > + end > + def successes > + @successes > + end > + endattr_reader :failures, :successes ?> # Including class must provide a GET_LOGIN_USER > > + def user > + @user ||= get_login_user > + end > +I don't think this is the right place to cache the user - if we want that, it should be done in get_login_user. In any event, this and the changes to set_perm_obj should be in a separate patch.> diff --git a/src/app/services/hardware_pool_service.rb b/src/app/services/hardware_pool_service.rb > new file mode 100644 > index 0000000..0dbe8dc > --- /dev/null > +++ b/src/app/services/hardware_pool_service.rb > @@ -0,0 +1,125 @@ > +# > +# 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 HW pools > +module HardwarePoolService > + > + include PoolService > + > + def svc_create(pool_hash, other_args)How about 'svc_create(parent_id, resource_types, resource_ids, pool_hash)' ? The signature feels a little WUI specific.> + # from before_filter > + @pool = HardwarePool.new(pool_hash) > + @parent = Pool.find(other_args[:parent_id]) > + authorized!(Privilege::MODIFY, at parent) > + > + alert = "Hardware Pool was successfully created." > + Pool.transaction do > + @pool.create_with_parent(@parent) > + begin > + if other_args[:resource_type] == "hosts" > + svc_move_hosts(@pool.id, other_args[:resource_ids].split(","), @pool.id) > + elsif other_args[:resource_type] == "storage" > + svc_move_storage(@pool.id, other_args[:resource_ids].split(","), @pool.id) > + end > + # wrapped in a transaction, so fail on partial success > + rescue PartialEuccessError => ex > + raise ActionError.new("Could not move all hosts or storage to this pool")Why not make throwing PartialSuccessError part of the method's signature ? Does wrapping in an ActionError really buy us anything ?> + end > + end > + return alert > + end > + > + def svc_move_hosts(pool_id, host_ids, target_pool_id) > + svc_move_items_internal(pool_id, Host, host_ids, target_pool_id) > + end > + def svc_move_storage(pool_id, storage_pool_ids, target_pool_id) > + svc_move_items_internal(pool_id, StoragePool, storage_pool_ids, target_pool_id) > + end > + def svc_move_items_internal(pool_id, item_class, resource_ids, target_pool_id) > + # from before_filter > + @pool = HardwarePool.find(pool_id) > + target_pool = Pool.find(target_pool_id) > + authorized!(Privilege::MODIFY,target_pool) > + authorized!(Privilege::MODIFY, at pool) unless @pool == target_poolDoes it even make sense to do anything if @pool == target_pool ? We should just bail.> diff --git a/src/app/services/pool_service.rb b/src/app/services/pool_service.rb > new file mode 100644 > index 0000000..8073067 > --- /dev/null > +++ b/src/app/services/pool_service.rb > @@ -0,0 +1,60 @@...> + def svc_show(pool_id) > + # from before_filter > + @pool = Pool.find(pool_id) > + authorized!(Privilege::VIEW, at pool) > + end > + > + def update_perms > + set_perms(@pool) > + endIn the host controller, I addressed the same problem with private def lookup(id, priv) @pool = Pool.find(id) authorized!(priv, @pool) end Of course, I would prefer that here, too.> + def svc_update(pool_id, pool_hash) > + # from before_filter > + @pool = Pool.find(params[:id]) > + @parent = @pool.parent > + update_perms > + authorized!(Privilege::Modify)Should be Privilege::MODIFY David
Scott Seago
2009-May-04 18:01 UTC
[Ovirt-devel] [PATCH server] controller service layer refactoring for pools.
David Lutterkort wrote:> On Tue, 2009-04-28 at 22:10 +0000, Scott Seago wrote: > >> This is the second round of controller/service layer refactoring >> changes -- in this case for the pool-related controllers (HW, VM, and >> Smart pools) >> > > This looks mostly good, modulo comments below. It would also be good to > include rdoc comments in the service modules to document required > permissions and instance variables that get set. > >Sending updated patch shortly with the fixes below:>> diff --git a/src/app/controllers/hardware_controller.rb b/src/app/controllers/hardware_controller.rb >> index 2158e08..8261cd7 100644 >> --- a/src/app/controllers/hardware_controller.rb >> +++ b/src/app/controllers/hardware_controller.rb >> > > >> @@ -17,8 +17,10 @@ >> # MA 02110-1301, USA. A copy of the GNU General Public License is >> # also available at http://www.gnu.org/copyleft/gpl.html. >> # >> +require 'services/hardware_pool_service' >> > > This breaks class reloading in the development environment, and is not > necessary at all. Should be removed > >Done>> @@ -113,8 +112,13 @@ class HardwareController < PoolController >> end >> >> def show_storage >> - @storage_tree = @pool.storage_tree(:filter_unavailable => false, :include_used => true).to_json >> - show >> + begin >> + svc_show(params[:id]) >> + @storage_tree = @pool.storage_tree(:filter_unavailable => false, :include_used => true).to_json >> + render_show >> + rescue PermissionError => perm_error >> + handle_auth_error(perm_error.message) >> + end >> end >> > > I know that it's just moving code around, but please avoid lines with > more than 80 chars. > >Done>> @@ -190,144 +194,49 @@ class HardwareController < PoolController >> > > >> def add_hosts >> - edit_items(Host, :move_hosts, @pool.id, :add) >> + edit_items(Host, @pool.id, :add) >> end >> >> def move_hosts >> - edit_items(Host, :move_hosts, params[:target_pool_id], :move) >> + edit_items(Host, params[:target_pool_id], :move) >> end >> >> def add_storage >> - edit_items(StoragePool, :move_storage, @pool.id, :add) >> + edit_items(StoragePool, @pool.id, :add) >> end >> >> def move_storage >> - edit_items(StoragePool, :move_storage, params[:target_pool_id], :move) >> + edit_items(StoragePool, params[:target_pool_id], :move) >> end >> >> - #FIXME: we need permissions checks. user must have permission on src pool >> - # in addition to the current pool (which is checked). We also need to fail >> - # for storage that aren't currently empty >> - def edit_items(item_class, item_method, target_pool_id, item_action) >> - resource_ids_str = params[:resource_ids] >> - resource_ids = resource_ids_str.split(",").collect {|x| x.to_i} >> - >> - # if user doesn't have modify permission on both source and destination >> - unless @pool.can_modify(@user) and Pool.find(target_pool_id).can_modify(@user) >> - render :json => { :success => false, >> - :alert => "Cannot #{item_action.to_s} #{item_class.table_name.humanize} without admin permissions on both pools" } >> - return >> - end >> - >> - # relay error message if movable check fails for any resource >> - success = true >> - failed_resources = "" >> - resource_ids.each {|x| >> - unless item_class.find(x).movable? >> - success = false >> - failed_resources += x.to_s + " " >> - end >> - } >> - resource_ids.delete_if { |x| ! item_class.find(x).movable? } >> - >> + def edit_items(item_class, target_pool_id, item_action) >> begin >> - @pool.transaction do >> - @pool.send(item_method, resource_ids, target_pool_id) >> + if item_class == Host >> + alert = svc_move_hosts(params[:id], params[:resource_ids].split(","), target_pool_id) >> + elsif item_class == StoragePool >> + alert = svc_move_storage(params[:id], params[:resource_ids].split(","), target_pool_id) >> + else >> + raise "invalid class #{item_class}" >> end >> - rescue >> - success = false >> - end >> - >> - if success >> - render :json => { :success => true, >> - :alert => "#{item_action.to_s} #{item_class.table_name.humanize} successful.", >> + render :json => { :success => true, :alert => alert, >> :storage => @pool.storage_tree({:filter_unavailable => false, :include_used => true, :state => item_action.to_s})} >> - else >> - render :json => { :success => false, >> - :alert => "#{item_action.to_s} #{item_class.table_name.humanize} failed" + >> - (failed_resources == "" ? "." : " for " + failed_resources) } >> + rescue PermissionError => perm_error >> + handle_auth_error(perm_error.message) >> + # If we need to give more details as to which hosts/storage succeeded, >> + # they're in the exception >> + rescue PartialSuccessError => error >> + render :json => { :success => false, :alert => error.message } >> + rescue Exception => ex >> + render :json => { :success => false, :alert => error.message } >> end >> end >> > > This is a good example of how our current error handling makes things > more complex: we have move_hosts/move_storage actions, call a generic > edit_items, and then dispatch in that method on the class of one of the > parameters. > > First off, it would be more rubyish if edit_items took a block that has > the proper calls to svc_move_hosts/svc_move_storage. Second, it seems > the main point of edit_items is to factor all the error handling > overhead intoa common method. > > For the time being, edit_items should take a block; in the future we > need to look into handling more errors with rescue_with, avoiding all > that error handling clutter altogether. > >OK so we'll revisit this when we add the rescue_with stuff, but for now I've actually changed this to take a symbol arg for the method name. Since the only thing that's different here is which method is calling, this avoids the if block with two method invocations (the args are the same in both cases) -- I started to do blocks but I realized that I'd have to repeat the method argument generation in each block, which is also rather un-rubyish.>> @@ -97,30 +108,111 @@ class PoolController < ApplicationController >> >> + end >> + >> + def update >> + begin >> + alert = svc_update(params[:id], params[:pool] ? params[:pool] : >> + params[:hardware_pool]) >> + respond_to do |format| >> + format.html { >> + reply = { :object => "pool", :success => true, :alert => alert } >> + render :json => reply >> + } >> > > Is that right ? format.html and render :json ? >fixed>> diff --git a/src/app/controllers/resources_controller.rb b/src/app/controllers/resources_controller.rb >> index c61ef46..63bc98c 100644 >> --- a/src/app/controllers/resources_controller.rb >> +++ b/src/app/controllers/resources_controller.rb >> @@ -16,15 +16,15 @@ >> # 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. >> +require 'services/vm_resource_pool_service' >> > > Not needed >Fixed>> @@ -69,89 +69,62 @@ class ResourcesController < PoolController >> > > ... > > >> - def destroy >> - if @pool.destroy >> - alert="Virtual Machine Pool was successfully deleted." >> - success=true >> - else >> - alert="Failed to delete virtual machine pool." >> - success=false >> + success = failures.empty? >> + alert = "" >> + if !successes.empty? >> + alert == "Virtual Machine Pools #{successes.collect{|pool| pool.name}.join(', ')} were successfully deleted." >> + end >> + if !failures.empty? >> + alert == " Errors in deleting VM Pools #{failures.collect{|pool,err| "#{pool.name}: #{err}"}.join(', ')}." >> end >> > > s/==/=/ > > The logic also deserves a comment that clobbering alert on failure is > intentional. > >Fixed this -- and no alert clobbering on delete either.>> diff --git a/src/app/controllers/smart_pools_controller.rb b/src/app/controllers/smart_pools_controller.rb >> index fb6ccb5..bc20024 100644 >> --- a/src/app/controllers/smart_pools_controller.rb >> +++ b/src/app/controllers/smart_pools_controller.rb >> @@ -17,14 +17,12 @@ >> # MA 02110-1301, USA. A copy of the GNU General Public License is >> # also available at http://www.gnu.org/copyleft/gpl.html. >> # >> +require 'services/smart_pool_service' >> > Not needed. >Done>> diff --git a/src/app/controllers/storage_volume_controller.rb b/src/app/controllers/storage_volume_controller.rb >> index b6b0593..d4a2561 100644 >> --- a/src/app/controllers/storage_volume_controller.rb >> +++ b/src/app/controllers/storage_volume_controller.rb >> > > This seems tangential to the pool refactoring - could that go into a > separate patch ? > >OK I'll pull this out into a later patch.>> diff --git a/src/app/services/application_service.rb b/src/app/services/application_service.rb >> index 4dc5eba..251b37a 100644 >> --- a/src/app/services/application_service.rb >> +++ b/src/app/services/application_service.rb >> @@ -29,18 +29,34 @@ >> module ApplicationService >> class PermissionError < RuntimeError; end >> class ActionError < RuntimeError; end >> + class PartialSuccessError < RuntimeError >> + def initialize(msg, failures={}, successes=[]) >> + @failures = failures >> + @successes = successes >> + end >> + def failures >> + @failures >> + end >> + def successes >> + @successes >> + end >> + end >> > > attr_reader :failures, :successes ? >Fixed this>> # Including class must provide a GET_LOGIN_USER >> >> + def user >> + @user ||= get_login_user >> + end >> + >> > > I don't think this is the right place to cache the user - if we want > that, it should be done in get_login_user. In any event, this and the > changes to set_perm_obj should be in a separate patch. > >I guess I'll un-do this for now then and later re-create this later, as I don't trust git rebase -i to be useful for me to separate commits _within_ a file.>> diff --git a/src/app/services/pool_service.rb b/src/app/services/pool_service.rb >> new file mode 100644 >> index 0000000..8073067 >> --- /dev/null >> +++ b/src/app/services/pool_service.rb >> @@ -0,0 +1,60 @@ >> + def svc_update(pool_id, pool_hash) >> + # from before_filter >> + @pool = Pool.find(params[:id]) >> + @parent = @pool.parent >> + update_perms >> + authorized!(Privilege::Modify) >> > > Should be Privilege::MODIFY > > David > > >Fixed