[Ovirt-devel] [PATCH server] First attempt at refactoring the vm_controller using the service layer.

Scott Seago sseago at redhat.com
Mon Apr 20 15:13:13 UTC 2009


This code is based on David Lutterkort's proposal outlined in
https://www.redhat.com/archives/ovirt-devel/2009-April/msg00086.html

I've included service-layer methods for show, create, update, destroy, vm_action, and cancel_queued_tasks. These are the concrete actions that make sense as part of the API. Most of the remaining vm_controller methods are pure-WUI actions -- for things such as generating the web form for a new VM (the subsequent 'create' action _is_ part of the API).

There's a certain amount of temporary code that we need in place while only some of the controllers have been converted, as we still need permissions pre-filters for non-converted create, etc. methods -- but for the converted controllers we don't want to perform actions in before_filters that will be done as part of the services layer. Any temporary methods, etc. that will go away when the last controller is converted have been marked clearly with FIXME comments.

Signed-off-by: Scott Seago <sseago at redhat.com>
---
 src/app/controllers/application.rb               |  113 +++++++----
 src/app/controllers/hardware_controller.rb       |    3 +-
 src/app/controllers/host_controller.rb           |    4 +-
 src/app/controllers/nic_controller.rb            |    2 +-
 src/app/controllers/permission_controller.rb     |   12 +-
 src/app/controllers/pool_controller.rb           |    2 +-
 src/app/controllers/quota_controller.rb          |    4 +-
 src/app/controllers/storage_controller.rb        |    5 +-
 src/app/controllers/storage_volume_controller.rb |    6 +-
 src/app/controllers/task_controller.rb           |    9 +-
 src/app/controllers/vm_controller.rb             |  230 +++++-----------------
 src/app/models/vm.rb                             |    1 +
 src/app/services/application_service.rb          |   56 ++++++
 src/app/services/vm_service.rb                   |  189 ++++++++++++++++++
 src/app/views/vm/_form.rhtml                     |    3 +-
 15 files changed, 394 insertions(+), 245 deletions(-)
 create mode 100644 src/app/services/application_service.rb
 create mode 100644 src/app/services/vm_service.rb

diff --git a/src/app/controllers/application.rb b/src/app/controllers/application.rb
index fa88773..34283ec 100644
--- a/src/app/controllers/application.rb
+++ b/src/app/controllers/application.rb
@@ -20,19 +20,35 @@
 # Filters added to this controller apply to all controllers in the application.
 # Likewise, all the methods added will be available for all controllers.
 
+# FIXME: once all controller classes include this, remove here
+require 'services/vm_service'
+
 class ApplicationController < ActionController::Base
+  # FIXME: once all controller classes include this, remove here
+  include ApplicationService
+
   # Pick a unique cookie name to distinguish our session data from others'
   session :session_key => '_ovirt_session_id'
   init_gettext "ovirt"
   layout :choose_layout
 
+  # FIXME: once service layer is complete, the following before_filters will be
+  # removed as their functionality has been moved to the service layer
+  # pre_create
+  # pre_edit will remain only for :edit, not :update or :destroy
+  # pre_show
+  # authorize_admin will remain only for :new, :edit
   before_filter :pre_new, :only => [:new]
   before_filter :pre_create, :only => [:create]
-  before_filter :pre_edit, :only => [:edit, :update, :destroy]
+  before_filter :pre_edit, :only => [:edit]
+  # the following is to facilitate transition to service layer
+  before_filter :tmp_pre_update, :only => [:update, :destroy]
   before_filter :pre_show, :only => [:show]
-  before_filter :authorize_admin, :only => [:new, :create, :edit, :update, :destroy]
+  before_filter :authorize_admin, :only => [:new, :edit]
+  before_filter :tmp_authorize_admin, :only => [:create, :update, :destroy]
   before_filter :is_logged_in, :get_help_section
 
+
   def choose_layout
     if(params[:component_layout])
       return (ENV["RAILS_ENV"] != "production")?'components/' << params[:component_layout]:'redux'
@@ -61,62 +77,66 @@ class ApplicationController < ActionController::Base
     (ENV["RAILS_ENV"] == "production") ? session[:user] : "ovirtadmin"
   end
 
-  def set_perms(hwpool)
-    @user = get_login_user
-    @can_view = hwpool.can_view(@user)
-    @can_control_vms = hwpool.can_control_vms(@user)
-    @can_modify = hwpool.can_modify(@user)
-    @can_view_perms = hwpool.can_view_perms(@user)
-    @can_set_perms = hwpool.can_set_perms(@user)
-  end
-
   protected
   # permissions checking
 
   def pre_new
   end
-  def pre_create
-  end
   def pre_edit
   end
+
+  # FIXME: remove these when service layer transition is complete
+  def tmp_pre_update
+    pre_edit
+  end
+  def tmp_authorize_admin
+    authorize_admin
+  end
+  def pre_create
+  end
   def pre_show
   end
 
+  def authorize_view(msg=nil)
+    authorize_action(Privilege::VIEW,msg)
+  end
   def authorize_user(msg=nil)
-    authorize_action(false,msg)
+    authorize_action(Privilege::VM_CONTROL,msg)
   end
   def authorize_admin(msg=nil)
-    authorize_action(true,msg)
-  end
-  def authorize_action(is_modify_action, msg=nil)
-    msg ||= 'You do not have permission to create or modify this item '
-    if @perm_obj
-      set_perms(@perm_obj)
-      unless (is_modify_action ? @can_modify : @can_control_vms)
-        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
-          end
-          format.json do
-            @json_hash ||= {}
-            @json_hash[:success] = false
-            @json_hash[:alert] = msg
-            render :json => @json_hash
-          end
-          format.xml { head :forbidden }
+    authorize_action(Privilege::MODIFY,msg)
+  end
+  def authorize_action(privilege, msg=nil)
+    msg ||= 'You have insufficient privileges to perform action.'
+    unless authorized?(privilege)
+      handle_auth_error(msg)
+      false
+    else
+      true
+    end
+  end
+  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
-        false
       end
+      format.json do
+        @json_hash ||= {}
+        @json_hash[:success] = false
+        @json_hash[:alert] = msg
+        render :json => @json_hash
+      end
+      format.xml { head :forbidden }
     end
   end
 
@@ -155,6 +175,11 @@ class ApplicationController < ActionController::Base
     render :json => json_hash(full_items, attributes, arg_list, find_opts, id_method).to_json
   end
 
-
+  def json_error(obj_type, obj, exception)
+    json_hash = { :object => obj_type, :success => false}
+    json_hash[:errors] = obj.errors.localize_error_messages.to_a if obj
+    json_hash[:alert] = exception.message if obj.errors.size == 0
+    render :json => json_hash
+  end
 
 end
diff --git a/src/app/controllers/hardware_controller.rb b/src/app/controllers/hardware_controller.rb
index 726e5bd..5615f6e 100644
--- a/src/app/controllers/hardware_controller.rb
+++ b/src/app/controllers/hardware_controller.rb
@@ -70,7 +70,8 @@ class HardwareController < PoolController
     id = params[:id]
     if id
       @pool = Pool.find(id)
-      set_perms(@pool)
+      @perm_obj = @pool
+      set_perms
       unless @pool.has_privilege(@user, privilege)
         flash[:notice] = 'You do not have permission to access this hardware pool: redirecting to top level'
         redirect_to :controller => "dashboard"
diff --git a/src/app/controllers/host_controller.rb b/src/app/controllers/host_controller.rb
index f0b8c2b..ad3bde4 100644
--- a/src/app/controllers/host_controller.rb
+++ b/src/app/controllers/host_controller.rb
@@ -53,7 +53,7 @@ class HostController < ApplicationController
 
 
   def show
-    set_perms(@perm_obj)
+    set_perms
     unless @can_view
       flash[:notice] = 'You do not have permission to view this host: redirecting to top level'
       #perm errors for ajax should be done differently
@@ -68,7 +68,7 @@ class HostController < ApplicationController
 
   def quick_summary
     pre_show
-    set_perms(@perm_obj)
+    set_perms
     unless @can_view
       flash[:notice] = 'You do not have permission to view this host: redirecting to top level'
       #perm errors for ajax should be done differently
diff --git a/src/app/controllers/nic_controller.rb b/src/app/controllers/nic_controller.rb
index 85d4315..6319dbe 100644
--- a/src/app/controllers/nic_controller.rb
+++ b/src/app/controllers/nic_controller.rb
@@ -23,7 +23,7 @@ class NicController < ApplicationController
          :redirect_to => { :controller => 'dashboard' }
 
   def show
-    set_perms(@perm_obj)
+    set_perms
     unless @can_view
       flash[:notice] = 'You do not have permission to view this NIC: redirecting to top level'
       redirect_to :controller => 'pool', :action => 'list'
diff --git a/src/app/controllers/permission_controller.rb b/src/app/controllers/permission_controller.rb
index 3bfbffa..9914fb7 100644
--- a/src/app/controllers/permission_controller.rb
+++ b/src/app/controllers/permission_controller.rb
@@ -28,7 +28,8 @@ class PermissionController < ApplicationController
 
   def show
     @permission = Permission.find(params[:id])
-    set_perms(@permission.pool)
+    @perm_obj = @permission.pool
+    set_perms
     # admin permission required to view permissions
     unless @can_view_perms
       flash[:notice] = 'You do not have permission to view this permission record'
@@ -43,7 +44,8 @@ class PermissionController < ApplicationController
     filter = @pool.permissions.collect{ |permission| permission.uid }
     @users = Account.names(filter)
     @roles = Role.find(:all).collect{ |role| [role.name, role.id] }
-    set_perms(@permission.pool)
+    @perm_obj = @permission.pool
+    set_perms
     # admin permission required to view permissions
     unless @can_set_perms
       flash[:notice] = 'You do not have permission to create this permission record'
@@ -55,7 +57,8 @@ class PermissionController < ApplicationController
 
   def create
     @permission = Permission.new(params[:permission])
-    set_perms(@permission.pool)
+    @perm_obj = @permission.pool
+    set_perms
     unless @can_set_perms
       # FIXME: need to handle proper error messages w/ ajax
       flash[:notice] = 'You do not have permission to create this permission record'
@@ -117,7 +120,8 @@ class PermissionController < ApplicationController
 
   def destroy
     @permission = Permission.find(params[:id])
-    set_perms(@permission.pool)
+    @perm_obj = @permission.pool
+    set_perms
     unless @can_set_perms
       flash[:notice] = 'You do not have permission to delete this permission record'
       redirect_to_parent
diff --git a/src/app/controllers/pool_controller.rb b/src/app/controllers/pool_controller.rb
index 22cf1a4..018f2cd 100644
--- a/src/app/controllers/pool_controller.rb
+++ b/src/app/controllers/pool_controller.rb
@@ -123,7 +123,7 @@ class PoolController < ApplicationController
   def pre_show
     @perm_obj = @pool
     @current_pool_id=@pool.id
-    set_perms(@perm_obj)
+    set_perms
     unless @can_view
       flash[:notice] = 'You do not have permission to view this pool: redirecting to top level'
       respond_to do |format|
diff --git a/src/app/controllers/quota_controller.rb b/src/app/controllers/quota_controller.rb
index 17fdc20..220792b 100644
--- a/src/app/controllers/quota_controller.rb
+++ b/src/app/controllers/quota_controller.rb
@@ -28,7 +28,7 @@ class QuotaController < ApplicationController
 
   def show
     @quota = Quota.find(params[:id])
-    set_perms(@quota.pool)
+    set_perms
 
     unless @can_view
       flash[:notice] = 'You do not have permission to view this quota: redirecting to top level'
@@ -91,7 +91,7 @@ class QuotaController < ApplicationController
   end
   def pre_show
     @quota = Quota.find(params[:id])
-    @perm_obj = @quota
+    @perm_obj = @quota.pool
   end
   def pre_edit
     @quota = Quota.find(params[:id])
diff --git a/src/app/controllers/storage_controller.rb b/src/app/controllers/storage_controller.rb
index 98ba0f6..2232388 100644
--- a/src/app/controllers/storage_controller.rb
+++ b/src/app/controllers/storage_controller.rb
@@ -46,7 +46,7 @@ class StorageController < ApplicationController
     @attach_to_pool=params[:attach_to_pool]
     if @attach_to_pool
       pool = HardwarePool.find(@attach_to_pool)
-      set_perms(pool)
+      @perm_obj = pool
       unless @can_view
         flash[:notice] = 'You do not have permission to view this storage pool list: redirecting to top level'
         redirect_to :controller => 'dashboard'
@@ -70,7 +70,8 @@ class StorageController < ApplicationController
 
   def show
     @storage_pool = StoragePool.find(params[:id])
-    set_perms(@storage_pool.hardware_pool)
+    @perm_obj = @storage_pool.hardware_pool
+    set_perms
     unless @can_view
       flash[:notice] = 'You do not have permission to view this storage pool: redirecting to top level'
       respond_to do |format|
diff --git a/src/app/controllers/storage_volume_controller.rb b/src/app/controllers/storage_volume_controller.rb
index 9b0b9e0..f78071f 100644
--- a/src/app/controllers/storage_volume_controller.rb
+++ b/src/app/controllers/storage_volume_controller.rb
@@ -92,7 +92,8 @@ class StorageVolumeController < ApplicationController
 
   def show
     @storage_volume = StorageVolume.find(params[:id])
-    set_perms(@storage_volume.storage_pool.hardware_pool)
+    @perm_obj = @storage_volume.storage_pool.hardware_pool
+    set_perms
     @storage_pool = @storage_volume.storage_pool
     unless @can_view
       flash[:notice] = 'You do not have permission to view this storage volume: redirecting to top level'
@@ -117,7 +118,8 @@ class StorageVolumeController < ApplicationController
 
   def destroy
     @storage_volume = StorageVolume.find(params[:id])
-    set_perms(@storage_volume.storage_pool.hardware_pool)
+    @perm_obj = @storage_volume.storage_pool.hardware_pool
+    set_perms
     unless @can_modify and @storage_volume.storage_pool.user_subdividable
       respond_to do |format|
         format.json { render :json => { :object => "storage_volume",
diff --git a/src/app/controllers/task_controller.rb b/src/app/controllers/task_controller.rb
index 647d69c..618856c 100644
--- a/src/app/controllers/task_controller.rb
+++ b/src/app/controllers/task_controller.rb
@@ -21,14 +21,15 @@ class TaskController < ApplicationController
   def show
     @task = Task.find(params[:id])
     if @task[:type] == VmTask.name
-      set_perms(@task.vm.vm_resource_pool)
+      @perm_obj = @task.vm.vm_resource_pool
     elsif @task[:type] == StorageTask.name 
-      set_perms(@task.storage_pool.hardware_pool)
+      @perm_obj = @task.storage_pool.hardware_pool
     elsif @task[:type] == StorageVolumeTask.name
-      set_perms(@task.storage_volume.storage_pool.hardware_pool)
+      @perm_obj = @task.storage_volume.storage_pool.hardware_pool
     elsif @task[:type] == HostTask.name 
-      set_perms(@task.host.hardware_pool)
+      @perm_obj = @task.host.hardware_pool
     end
+    set_perms
     unless @can_view
       flash[:notice] = 'You do not have permission to view this task: redirecting to top level'
       redirect_to :controller => 'dashboard'
diff --git a/src/app/controllers/vm_controller.rb b/src/app/controllers/vm_controller.rb
index a81f6b5..fc55d92 100644
--- a/src/app/controllers/vm_controller.rb
+++ b/src/app/controllers/vm_controller.rb
@@ -17,13 +17,16 @@
 # MA  02110-1301, USA.  A copy of the GNU General Public License is
 # also available at http://www.gnu.org/copyleft/gpl.html.
 require 'socket'
+require 'services/vm_service'
 
 class VmController < ApplicationController
+  include VmService
+
   # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
   verify :method => :post, :only => [ :destroy, :create, :update ],
          :redirect_to => { :controller => 'dashboard' }
 
-  before_filter :pre_vm_action, :only => [:vm_action, :cancel_queued_tasks, :console]
+  before_filter :pre_console, :only => [:console]
 
   def index
     @vms = Vm.find(:all,
@@ -38,13 +41,13 @@ class VmController < ApplicationController
   end
 
   def show
-    set_perms(@perm_obj)
-    @actions = @vm.get_action_hash(@user)
-    unless @can_view
-      flash[:notice] = 'You do not have permission to view this vm: redirecting to top level'
-      redirect_to :controller => 'resources', :controller => 'dashboard'
+    begin
+      svc_show(params[:id])
+      @actions = @vm.get_action_hash(@user)
+      render :layout => 'selection'
+    rescue PermissionError => perm_error
+      handle_auth_error(perm_error.message)
     end
-    render :layout => 'selection'
   end
 
   def add_to_smart_pool
@@ -58,38 +61,15 @@ class VmController < ApplicationController
   end
 
   def create
+    params[:vm][:forward_vnc] = params[:forward_vnc]
     begin
-      Vm.transaction do
-        @vm.save!
-        _setup_vm_provision(params)
-        @task = VmTask.new({ :user        => @user,
-                             :task_target => @vm,
-                             :action      => VmTask::ACTION_CREATE_VM,
-                             :state       => Task::STATE_QUEUED})
-        @task.save!
-      end
-      start_now = params[:start_now]
-      if (start_now)
-        if @vm.get_action_list.include?(VmTask::ACTION_START_VM)
-          @task = VmTask.new({ :user        => @user,
-                               :task_target => @vm,
-                               :action      => VmTask::ACTION_START_VM,
-                               :state       => Task::STATE_QUEUED})
-          @task.save!
-          alert = "VM was successfully created. VM Start action queued."
-        else
-          alert = "VM was successfully created. Resources are not available to start VM now."
-        end
-      else
-        alert = "VM was successfully created."
-      end
+      alert = svc_create(params[:vm], params[:start_now])
       render :json => { :object => "vm", :success => true, :alert => alert  }
+    rescue PermissionError => perm_error
+      handle_auth_error(perm_error.message)
     rescue Exception => error
-      # FIXME: need to distinguish vm vs. task save errors (but should mostly be vm)
-      render :json => { :object => "vm", :success => false,
-                        :errors => @vm.errors.localize_error_messages.to_a }
+      json_error("vm", @vm, error)
     end
-
   end
 
   def edit
@@ -98,58 +78,20 @@ class VmController < ApplicationController
   end
 
   def update
+    params[:vm][:forward_vnc] = params[:forward_vnc]
     begin
-      #needs restart if certain fields are changed (since those will only take effect the next startup)
-      needs_restart = false
-      unless @vm.get_pending_state == Vm::STATE_STOPPED
-        Vm::NEEDS_RESTART_FIELDS.each do |field|
-          unless @vm[field].to_s == params[:vm][field]
-            needs_restart = true
-            break
-          end
-        end
-        current_storage_ids = @vm.storage_volume_ids.sort
-        new_storage_ids = params[:vm][:storage_volume_ids]
-        new_storage_ids = [] unless new_storage_ids
-        new_storage_ids = new_storage_ids.sort.collect {|x| x.to_i }
-        needs_restart = true unless current_storage_ids == new_storage_ids
-      end
-
-      params[:vm][:forward_vnc] = params[:forward_vnc]
-      params[:vm][:needs_restart] = 1 if needs_restart
-      @vm.update_attributes!(params[:vm])
-      _setup_vm_provision(params)
-
-      if (params[:start_now] and @vm.get_action_list.include?(VmTask::ACTION_START_VM) )
-        @task = VmTask.new({ :user        => @user,
-                             :task_target => @vm,
-                             :action      => VmTask::ACTION_START_VM,
-                             :state       => Task::STATE_QUEUED})
-        @task.save!
-      elsif ( params[:restart_now] and @vm.get_action_list.include?(VmTask::ACTION_SHUTDOWN_VM) )
-        @task = VmTask.new({ :user        => @user,
-                             :task_target => @vm,
-                             :action      => VmTask::ACTION_SHUTDOWN_VM,
-                             :state       => Task::STATE_QUEUED})
-        @task.save!
-        @task = VmTask.new({ :user    => @user,
-                             :task_target => @vm,
-                             :action  => VmTask::ACTION_START_VM,
-                             :state   => Task::STATE_QUEUED})
-        @task.save!
-      end
-
-
-      render :json => { :object => "vm", :success => true,
-                        :alert => 'Vm was successfully updated.'  }
-    rescue
+      alert = svc_update(params[:id], params[:vm], params[:start_now],
+                         params[:restart_now])
+      render :json => { :object => "vm", :success => true, :alert => alert  }
+    rescue Exception => error
       # FIXME: need to distinguish vm vs. task save errors (but should mostly be vm)
-      render :json => { :object => "vm", :success => false,
-                        :errors => @vm.errors.localize_error_messages.to_a }
+      json_error("vm", @vm, error)
     end
   end
 
   #FIXME: we need permissions checks. user must have permission. Also state checks
+  # this should probably be implemented as an action on the containing VM pool once
+  # that service module is defined
   def delete
     vm_ids_str = params[:vm_ids]
     vm_ids = vm_ids_str.split(",").collect {|x| x.to_i}
@@ -181,15 +123,13 @@ class VmController < ApplicationController
   end
 
   def destroy
-    vm_resource_pool = @vm.vm_resource_pool_id
-    if (@vm.is_destroyable?)
-      destroy_cobbler_system(@vm)
-      @vm.destroy
-      render :json => { :object => "vm", :success => true,
-        :alert => "Virtual Machine was successfully deleted." }
-    else
-      render :json => { :object => "vm", :success => false,
-        :alert => "Vm must be stopped to delete it." }
+    begin
+      alert = svc_destroy(params[:id])
+      render :json => { :object => "vm", :success => true, :alert => alert  }
+    rescue ActionError => error
+      json_error("vm", @vm, error)
+    rescue Exception => error
+      json_error("vm", @vm, error)
     end
   end
 
@@ -204,27 +144,23 @@ class VmController < ApplicationController
   end
 
   def vm_action
-    vm_action = params[:vm_action]
-    data = params[:vm_action_data]
     begin
-      if @vm.queue_action(get_login_user, vm_action, data)
-        render :json => { :object => "vm", :success => true, :alert => "#{vm_action} was successfully queued." }
-      else
-        render :json => { :object => "vm", :success => false, :alert => "#{vm_action} is an invalid action." }
-      end
-    rescue
-      render :json => { :object => "vm", :success => false, :alert => "Error in queueing #{vm_action}." }
+      alert = svc_vm_action(params[:id], params[:vm_action],
+                            params[:vm_action_data])
+      render :json => { :object => "vm", :success => true, :alert => alert  }
+    rescue ActionError => error
+      json_error("vm", @vm, error)
+    rescue Exception => error
+      json_error("vm", @vm, error)
     end
   end
 
   def cancel_queued_tasks
     begin
-      Task.transaction do
-        @vm.tasks.queued.each { |task| task.cancel}
-      end
-      render :json => { :object => "vm", :success => true, :alert => "queued tasks were canceled." }
-    rescue
-      render :json => { :object => "vm", :success => true, :alert => "queued tasks cancel failed." }
+      alert = svc_cancel_queued_tasks(params[:id])
+      render :json => { :object => "vm", :success => true, :alert => alert  }
+    rescue Exception => error
+      json_error("vm", @vm, error)
     end
   end
 
@@ -268,53 +204,10 @@ class VmController < ApplicationController
     end
   end
 
-  # FIXME: move this to an edit_vm task in taskomatic
-  def _setup_vm_provision(params)
-    # spaces are invalid in the cobbler name
-    name = params[:vm][:uuid]
-    mac = params[:vm][:vnic_mac_addr]
-    provision = params[:vm][:provisioning_and_boot_settings]
-    # determine what type of provisioning was selected for the VM
-    provisioning_type = :pxe_or_hd_type
-    provisioning_type = :image_type  if provision.index "#{Vm::IMAGE_PREFIX}@#{Vm::COBBLER_PREFIX}"
-    provisioning_type = :system_type if provision.index "#{Vm::PROFILE_PREFIX}@#{Vm::COBBLER_PREFIX}"
-
-    unless provisioning_type == :pxe_or_hd_type
-      cobbler_name = provision.slice(/(.*):(.*)/, 2)
-      system = Cobbler::System.find_one(name)
-      unless system
-        nic = Cobbler::NetworkInterface.new({'mac_address' => mac})
-
-        case provisioning_type
-        when :image_type:
-            system = Cobbler::System.new("name" => name, "image"    => cobbler_name)
-        when :system_type:
-            system = Cobbler::System.new("name" => name, "profile" => cobbler_name)
-        end
-
-        system.interfaces = [nic]
-        system.save
-      end
-    end
-  end
-
   def pre_new
-    # if no vm_resource_pool is passed in, find (or auto-create) it based on hardware_pool_id
     unless params[:vm_resource_pool_id]
-      unless params[:hardware_pool_id]
-        flash[:notice] = "VM Resource Pool or Hardware Pool is required."
-        redirect_to :controller => 'dashboard'
-      end
-      @hardware_pool = HardwarePool.find(params[:hardware_pool_id])
-      @user = get_login_user
-      vm_resource_pool = @hardware_pool.sub_vm_resource_pools.select {|pool| pool.name == @user}.first
-      if vm_resource_pool
-        params[:vm_resource_pool_id] = vm_resource_pool.id
-      else
-        @vm_resource_pool = VmResourcePool.new({:name => vm_resource_pool})
-        @vm_resource_pool.tmp_parent = @hardware_pool
-        @vm_resource_pool_name = @user
-      end
+      flash[:notice] = "VM Resource Pool is required."
+      redirect_to :controller => 'dashboard'
     end
 
     # random MAC
@@ -336,26 +229,6 @@ class VmController < ApplicationController
     @networks = Network.find(:all).collect{ |net| [net.name, net.id] }
     _setup_provisioning_options
   end
-  def pre_create
-    params[:vm][:state] = Vm::STATE_PENDING
-    vm_resource_pool_name = params[:vm_resource_pool_name]
-    hardware_pool_id = params[:hardware_pool_id]
-    if vm_resource_pool_name and hardware_pool_id
-      hardware_pool = HardwarePool.find(hardware_pool_id)
-      vm_resource_pool = VmResourcePool.new({:name => vm_resource_pool_name})
-      vm_resource_pool.create_with_parent(hardware_pool)
-      params[:vm][:vm_resource_pool_id] = vm_resource_pool.id
-    end
-    params[:vm][:forward_vnc] = params[:forward_vnc]
-    @vm = Vm.new(params[:vm])
-    @perm_obj = @vm.vm_resource_pool
-    @current_pool_id=@perm_obj.id
-  end
-  def pre_show
-    @vm = Vm.find(params[:id])
-    @perm_obj = @vm.vm_resource_pool
-    @current_pool_id=@perm_obj.id
-  end
   def pre_edit
     @vm = Vm.find(params[:id])
     @perm_obj = @vm.vm_resource_pool
@@ -363,18 +236,15 @@ class VmController < ApplicationController
     @networks = Network.find(:all).collect{ |net| [net.name, net.id] }
     _setup_provisioning_options
   end
-  def pre_vm_action
+  def pre_console
     pre_edit
     authorize_user
   end
-
-  private
-
-  def destroy_cobbler_system(vm)
-    # Destroy the Cobbler system first if it's defined
-    if vm.uses_cobbler?
-      system = Cobbler::System.find_one(vm.cobbler_system_name)
-      system.remove if system
-    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/models/vm.rb b/src/app/models/vm.rb
index c62595a..4c7925c 100644
--- a/src/app/models/vm.rb
+++ b/src/app/models/vm.rb
@@ -220,6 +220,7 @@ class Vm < ActiveRecord::Base
       self[:boot_device]= BOOT_DEV_HD
       self[:provisioning]= nil
     else
+      # FIXME: Should this case trigger an error instead?
       self[:boot_device]= BOOT_DEV_NETWORK
       self[:provisioning]= settings
     end
diff --git a/src/app/services/application_service.rb b/src/app/services/application_service.rb
new file mode 100644
index 0000000..04f5196
--- /dev/null
+++ b/src/app/services/application_service.rb
@@ -0,0 +1,56 @@
+#
+# 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.
+#
+# Common infrastructure for business logic for WUI and QMF
+#
+# We call objects in the mid-level API 'Service' for lack of a better name.
+# The Service layer is all in modules that are included by the classes that
+# use them in the WUI and the QMF controllers. They set instance variables,
+# which automatically become instance variables on the controllers that use
+# the Service modules
+
+module ApplicationService
+  class PermissionError < RuntimeError; end
+  class ActionError < RuntimeError; end
+
+  # Including class must provide a GET_LOGIN_USER
+
+  def set_perms
+    @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)
+  end
+
+  def authorized?(privilege)
+    return false unless @perm_obj
+    set_perms
+    return @perm_obj.has_privilege(get_login_user, privilege)
+  end
+  def authorized!(privilege)
+    unless authorized?(privilege)
+      raise PermissionError.new(
+               'You have insufficient privileges to perform action.')
+    end
+  end
+
+end
diff --git a/src/app/services/vm_service.rb b/src/app/services/vm_service.rb
new file mode 100644
index 0000000..0c964de
--- /dev/null
+++ b/src/app/services/vm_service.rb
@@ -0,0 +1,189 @@
+#
+# 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 individual VM's
+module VmService
+
+  include ApplicationService
+
+  def svc_show(vm_id)
+    # from before_filter
+    @vm = Vm.find(vm_id)
+    @perm_obj = @vm.vm_resource_pool
+    @current_pool_id=@perm_obj.id
+    authorized!(Privilege::VIEW)
+  end
+
+  def svc_create(vm_hash, start_now)
+    # from before_filter
+    vm_hash[:state] = Vm::STATE_PENDING
+    @vm = Vm.new(params[:vm])
+    @perm_obj = @vm.vm_resource_pool
+    @current_pool_id=@perm_obj.id
+    authorized!(Privilege::MODIFY)
+
+    alert = "VM was successfully created."
+    Vm.transaction do
+      @vm.save!
+      vm_provision
+      @task = VmTask.new({ :user        => @user,
+                           :task_target => @vm,
+                           :action      => VmTask::ACTION_CREATE_VM,
+                           :state       => Task::STATE_QUEUED})
+      @task.save!
+      if start_now
+        if @vm.get_action_list.include?(VmTask::ACTION_START_VM)
+          @task = VmTask.new({ :user        => @user,
+                               :task_target => @vm,
+                               :action      => VmTask::ACTION_START_VM,
+                               :state       => Task::STATE_QUEUED})
+          @task.save!
+          alert += " VM Start action queued."
+        else
+          alert += " Resources are not available to start VM now."
+        end
+      end
+    end
+    return alert
+  end
+
+  def svc_update(vm_id, vm_hash, start_now, restart_now)
+    # from before_filter
+    @vm = Vm.find(vm_id)
+    @perm_obj = @vm.vm_resource_pool
+    @current_pool_id=@perm_obj.id
+    authorized!(Privilege::MODIFY)
+
+    #needs restart if certain fields are changed
+    # (since those will only take effect the next startup)
+    needs_restart = false
+    unless @vm.get_pending_state == Vm::STATE_STOPPED
+      Vm::NEEDS_RESTART_FIELDS.each do |field|
+        unless @vm[field].to_s == vm_hash[field]
+          needs_restart = true
+          break
+        end
+      end
+      current_storage_ids = @vm.storage_volume_ids.sort
+      new_storage_ids = vm_hash[:storage_volume_ids]
+      new_storage_ids = [] unless new_storage_ids
+      new_storage_ids = new_storage_ids.sort.collect {|x| x.to_i }
+      needs_restart = true unless current_storage_ids == new_storage_ids
+    end
+    vm_hash[:needs_restart] = 1 if needs_restart
+
+
+    alert = "VM was successfully updated."
+    Vm.transaction do
+      @vm.update_attributes!(vm_hash)
+      vm_provision
+      if start_now
+        if @vm.get_action_list.include?(VmTask::ACTION_START_VM)
+          @task = VmTask.new({ :user        => @user,
+                               :task_target => @vm,
+                               :action      => VmTask::ACTION_START_VM,
+                               :state       => Task::STATE_QUEUED})
+          @task.save!
+          alert += " VM Start action queued."
+        else
+          alert += " Resources are not available to start VM now."
+        end
+      elsif restart_now
+        if @vm.get_action_list.include?(VmTask::ACTION_SHUTDOWN_VM)
+          @task = VmTask.new({ :user        => @user,
+                               :task_target => @vm,
+                               :action      => VmTask::ACTION_SHUTDOWN_VM,
+                               :state       => Task::STATE_QUEUED})
+          @task.save!
+          @task = VmTask.new({ :user    => @user,
+                               :task_target => @vm,
+                               :action  => VmTask::ACTION_START_VM,
+                               :state   => Task::STATE_QUEUED})
+          @task.save!
+          alert += " VM Restart action queued."
+        else
+          alert += " Restart action was not available."
+        end
+      end
+    end
+    return alert
+  end
+
+  def svc_destroy(vm_id)
+    # from before_filter
+    @vm = Vm.find(vm_id)
+    @perm_obj = @vm.vm_resource_pool
+    @current_pool_id=@perm_obj.id
+    authorized!(Privilege::MODIFY)
+
+    unless @vm.is_destroyable?
+      raise ActionError.new("Virtual Machine must be stopped to delete it")
+    end
+    destroy_cobbler_system(@vm)
+    @vm.destroy
+    return "Virtual Machine wa ssuccessfully deleted."
+  end
+
+  def svc_vm_action(vm_id, vm_action, action_args)
+    @vm = Vm.find(vm_id)
+    @perm_obj = @vm.vm_resource_pool
+    @current_pool_id=@perm_obj.id
+    authorized!(Privilege::MODIFY)
+    unless @vm.queue_action(@user, vm_action, action_args)
+      raise ActionError.new("#{vm_action} is an invalid action.")
+    end
+    return "#{vm_action} was successfully queued."
+  end
+
+  def svc_cancel_queued_tasks(vm_id)
+    @vm = Vm.find(vm_id)
+    @perm_obj = @vm.vm_resource_pool
+    @current_pool_id=@perm_obj.id
+    authorized!(Privilege::MODIFY)
+
+    Task.transaction do
+      @vm.tasks.queued.each { |task| task.cancel}
+    end
+    return "Queued tasks were successfully canceled."
+  end
+
+  def vm_provision
+    if @vm.uses_cobbler?
+      # spaces are invalid in the cobbler name
+      name = @vm.uuid
+      system = Cobbler::System.find_one(name)
+      unless system
+        system = Cobbler::System.new("name" => name,
+                                     @vm.cobbler_type => @vm.cobbler_name)
+        system.interfaces = [Cobbler::NetworkInterface.new(
+                                    {'mac_address' => @vm.vnic_mac_addr})]
+        system.save
+      end
+    end
+  end
+
+  def destroy_cobbler_system(vm)
+    # Destroy the Cobbler system first if it's defined
+    if vm.uses_cobbler?
+      system = Cobbler::System.find_one(vm.cobbler_system_name)
+      system.remove if system
+    end
+  end
+
+end
diff --git a/src/app/views/vm/_form.rhtml b/src/app/views/vm/_form.rhtml
index 58a3d61..610f2bc 100644
--- a/src/app/views/vm/_form.rhtml
+++ b/src/app/views/vm/_form.rhtml
@@ -3,8 +3,7 @@
 <%  start_resources = @vm.vm_resource_pool.available_resources_for_vm(@vm) %>
 
 <!--[form:vm]-->
-<%= hidden_field 'vm', 'vm_resource_pool_id' unless @vm_resource_pool_name %>
-<%= hidden_field_tag 'vm_resource_pool_name', @vm_resource_pool_name if @vm_resource_pool_name %>
+<%= hidden_field 'vm', 'vm_resource_pool_id' %>
 <%= hidden_field_tag 'hardware_pool_id', @hardware_pool.id if @hardware_pool %>
 
     <%= text_field_with_label "Name:", "vm", "description", {:style=>"width:250px;"}  %>
-- 
1.6.0.6




More information about the ovirt-devel mailing list