Scott Seago
2008-Jul-15 15:28 UTC
[Ovirt-devel] [PATCH] added VM-level migration support to the WUI. Still no back end bits here, though.
Signed-off-by: Scott Seago <sseago at redhat.com> --- wui/src/app/controllers/hardware_controller.rb | 18 ++++-- wui/src/app/controllers/host_controller.rb | 11 +++ wui/src/app/controllers/resources_controller.rb | 25 ------- wui/src/app/controllers/vm_controller.rb | 13 +++- wui/src/app/models/vm.rb | 31 ++++++--- wui/src/app/models/vm_task.rb | 70 +++++++++++++++----- wui/src/app/views/hardware/show_hosts.rhtml | 5 +- wui/src/app/views/hardware/show_storage.rhtml | 2 +- wui/src/app/views/host/_grid.rhtml | 15 +++-- wui/src/app/views/host/addhost.html.erb | 5 +- wui/src/app/views/host/quick_summary.rhtml | 1 + wui/src/app/views/storage/_grid.rhtml | 4 +- wui/src/app/views/storage/add.rhtml | 2 +- wui/src/app/views/vm/migrate.rhtml | 81 +++++++++++++++++++++++ wui/src/app/views/vm/show.rhtml | 15 +++- wui/src/public/javascripts/flexigrid.js | 4 +- wui/src/public/javascripts/ovirt.js | 2 +- 17 files changed, 230 insertions(+), 74 deletions(-) create mode 100644 wui/src/app/views/host/quick_summary.rhtml create mode 100644 wui/src/app/views/vm/migrate.rhtml diff --git a/wui/src/app/controllers/hardware_controller.rb b/wui/src/app/controllers/hardware_controller.rb index 4175fd8..926b6c8 100644 --- a/wui/src/app/controllers/hardware_controller.rb +++ b/wui/src/app/controllers/hardware_controller.rb @@ -112,7 +112,12 @@ class HardwareController < ApplicationController end def hosts_json - if params[:id] + if params[:exclude_host] + pre_json + hosts = @pool.hosts + find_opts = {:conditions => ["id != ?", params[:exclude_host]]} + include_pool = false + elsif params[:id] pre_json hosts = @pool.hosts find_opts = {} @@ -120,14 +125,17 @@ class HardwareController < ApplicationController else # FIXME: no permissions or usage checks here yet # filtering on which pool to exclude - id = params[:exclude_id] + id = params[:exclude_pool] hosts = Host find_opts = {:include => :hardware_pool, :conditions => ["pools.id != ?", id]} include_pool = true end - attr_list = [:id, :hostname, :uuid, :hypervisor_type, :num_cpus, :cpu_speed, :arch, :memory_in_mb, :status_str, :id] - attr_list.insert(2, [:hardware_pool, :name]) if include_pool + attr_list = [] + attr_list << :id if params[:checkboxes] + attr_list << :hostname + attr_list << [:hardware_pool, :name] if include_pool + attr_list += [:uuid, :hypervisor_type, :num_cpus, :cpu_speed, :arch, :memory_in_mb, :status_str, :id] json_list(hosts, attr_list, [:all], find_opts) end @@ -152,7 +160,7 @@ class HardwareController < ApplicationController else # FIXME: no permissions or usage checks here yet # filtering on which pool to exclude - id = params[:exclude_id] + id = params[:exclude_pool] storage_pools = StoragePool find_opts = {:include => :hardware_pool, :conditions => ["pools.id != ?", id]} diff --git a/wui/src/app/controllers/host_controller.rb b/wui/src/app/controllers/host_controller.rb index 20571d7..4e4375a 100644 --- a/wui/src/app/controllers/host_controller.rb +++ b/wui/src/app/controllers/host_controller.rb @@ -44,6 +44,17 @@ class HostController < ApplicationController render :layout => 'selection' end + def quick_summary + pre_show + set_perms(@perm_obj) + 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 + redirect_to :controller => 'dashboard', :action => 'list' + end + render :layout => false + end + # retrieves data used by snapshot graphs def snapshot_graph end diff --git a/wui/src/app/controllers/resources_controller.rb b/wui/src/app/controllers/resources_controller.rb index 694ef74..ca10960 100644 --- a/wui/src/app/controllers/resources_controller.rb +++ b/wui/src/app/controllers/resources_controller.rb @@ -184,31 +184,6 @@ class ResourcesController < ApplicationController @failure_list = [] end render :layout => 'confirmation' - - #if params[:vm_actions][:vms] - # vms = params[:vm_actions][:vms] - # if params[:vm_actions][VmTask::ACTION_START_VM] - # flash[:notice] = "Starting Machines #{vms.join(',')}." - # elsif params[:vm_actions][VmTask::ACTION_SHUTDOWN_VM] - # flash[:notice] = "Stopping Machines #{vms.join(',')}." - # elsif params[:vm_actions][:other_actions] - # case params[:vm_actions][:other_actions] - # when VmTask::ACTION_SHUTDOWN_VM then flash[:notice] = "Stopping Machines #{vms.join(',')}." - # when VmTask::ACTION_START_VM then flash[:notice] = "Starting Machines #{vms.join(',')}." - # when VmTask::ACTION_SUSPEND_VM then flash[:notice] = "Suspending Machines #{vms.join(',')}." - # when VmTask::ACTION_RESUME_VM then flash[:notice] = "Resuming Machines #{vms.join(',')}." - # when VmTask::ACTION_SAVE_VM then flash[:notice] = "Saving Machines #{vms.join(',')}." - # when VmTask::ACTION_RESTORE_VM then flash[:notice] = "Restoring Machines #{vms.join(',')}." - # when "destroy" then flash[:notice] = "Destroying Machines #{vms.join(',')}." - # else - # flash[:notice] = 'No Action Chosen.' - # end - # else - # flash[:notice] = 'No Action Chosen.' - # end - #else - # flash[:notice] = 'No Virtual Machines Selected.' - #end end protected diff --git a/wui/src/app/controllers/vm_controller.rb b/wui/src/app/controllers/vm_controller.rb index b9156d2..4f9962d 100644 --- a/wui/src/app/controllers/vm_controller.rb +++ b/wui/src/app/controllers/vm_controller.rb @@ -26,7 +26,7 @@ class VmController < ApplicationController def show set_perms(@perm_obj) - @actions = @vm.get_action_and_label_list + @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' @@ -149,8 +149,9 @@ class VmController < ApplicationController def vm_action vm_action = params[:vm_action] + data = params[:vm_action_data] begin - if @vm.queue_action(get_login_user, vm_action) + 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." } @@ -171,6 +172,14 @@ class VmController < ApplicationController end end + def migrate + @vm = Vm.find(params[:id]) + @perm_obj = @vm.get_hardware_pool + @redir_obj = @vm + @current_pool_id=@vm.vm_resource_pool.id + authorize_admin + render :layout => 'popup' + end def console @show_vnc_error = "Console is unavailable for VM #{@vm.description}" unless @vm.has_console diff --git a/wui/src/app/models/vm.rb b/wui/src/app/models/vm.rb index 617512e..b607886 100644 --- a/wui/src/app/models/vm.rb +++ b/wui/src/app/models/vm.rb @@ -22,7 +22,7 @@ require 'util/ovirt' class Vm < ActiveRecord::Base belongs_to :vm_resource_pool belongs_to :host - has_many :tasks, :class_name => "VmTask", :dependent => :destroy, :order => "id DESC" do + has_many :tasks, :class_name => "VmTask", :dependent => :destroy, :order => "id ASC" do def queued find(:all, :conditions=>{:state=>Task::STATE_QUEUED}) end @@ -58,6 +58,9 @@ class Vm < ActiveRecord::Base STATE_SAVING = "saving" STATE_SAVED = "saved" STATE_RESTORING = "restoring" + + STATE_MIGRATING = "migrating" + STATE_CREATE_FAILED = "create_failed" STATE_INVALID = "invalid" @@ -68,7 +71,8 @@ class Vm < ActiveRecord::Base STATE_SUSPENDING, STATE_RESUMING, STATE_SAVING, - STATE_RESTORING] + STATE_RESTORING, + STATE_MIGRATING] EFFECTIVE_STATE = { STATE_PENDING => STATE_PENDING, STATE_UNREACHABLE => STATE_UNREACHABLE, @@ -83,9 +87,15 @@ class Vm < ActiveRecord::Base STATE_SAVING => STATE_SAVED, STATE_SAVED => STATE_SAVED, STATE_RESTORING => STATE_RUNNING, + STATE_MIGRATING => STATE_RUNNING, STATE_CREATE_FAILED => STATE_CREATE_FAILED} TASK_STATE_TRANSITIONS = [] + def get_hardware_pool + pool = vm_resource_pool + pool = pool.get_hardware_pool if pool + pool + end def storage_volume_ids storage_volumes.collect {|x| x.id } end @@ -126,9 +136,9 @@ class Vm < ActiveRecord::Base RUNNING_STATES.include?(get_pending_state) end - def get_action_list + def get_action_list(user=nil) # return empty list rather than nil - return_val = VmTask::VALID_ACTIONS_PER_VM_STATE[get_pending_state] || [] + return_val = VmTask.valid_actions_for_vm_state(get_pending_state, self, user) || [] # filter actions based on quota unless resources_for_start? return_val = return_val - [VmTask::ACTION_START_VM, VmTask::ACTION_RESTORE_VM] @@ -136,10 +146,12 @@ class Vm < ActiveRecord::Base return_val end - def get_action_and_label_list - get_action_list.collect do |action| - VmTask.label_and_action(action) + def get_action_hash(user=nil) + actions = {} + get_action_list(user).each do |action| + actions[action] = VmTask::ACTIONS[action] end + actions end # these resource checks are made at VM start/restore time @@ -158,11 +170,12 @@ class Vm < ActiveRecord::Base return return_val end - def queue_action(user, action) + def queue_action(user, action, data = nil) return false unless get_action_list.include?(action) task = VmTask.new({ :user => user, :vm_id => id, - :action => action, + :action => action, + :args => data, :state => Task::STATE_QUEUED}) task.save! return true diff --git a/wui/src/app/models/vm_task.rb b/wui/src/app/models/vm_task.rb index aa12903..3f52478 100644 --- a/wui/src/app/models/vm_task.rb +++ b/wui/src/app/models/vm_task.rb @@ -33,59 +33,97 @@ class VmTask < Task ACTION_UPDATE_STATE_VM = "update_state_vm" + # for migrate VM action, args provides the optional target host + ACTION_MIGRATE_VM = "migrate_vm" + + PRIV_OBJECT_VM_POOL = "vm_resource_pool" + PRIV_OBJECT_HW_POOL = "get_hardware_pool" + + # a hash of task actions which point to a hash which define valid state transitions ACTIONS = { ACTION_CREATE_VM => { :label => "Create", :icon => "icon_start.png", :start => Vm::STATE_PENDING, :running => Vm::STATE_CREATING, :success => Vm::STATE_STOPPED, - :failure => Vm::STATE_CREATE_FAILED}, + :failure => Vm::STATE_CREATE_FAILED, + :privilege => [Permission::PRIV_MODIFY, + PRIV_OBJECT_VM_POOL]}, ACTION_START_VM => { :label => "Start", :icon => "icon_start.png", :start => Vm::STATE_STOPPED, :running => Vm::STATE_STARTING, :success => Vm::STATE_RUNNING, - :failure => Vm::STATE_STOPPED}, + :failure => Vm::STATE_STOPPED, + :privilege => [Permission::PRIV_VM_CONTROL, + PRIV_OBJECT_VM_POOL]}, ACTION_SHUTDOWN_VM => { :label => "Shutdown", :icon => "icon_x.png", :start => Vm::STATE_RUNNING, :running => Vm::STATE_STOPPING, :success => Vm::STATE_STOPPED, - :failure => Vm::STATE_RUNNING}, + :failure => Vm::STATE_RUNNING, + :privilege => [Permission::PRIV_VM_CONTROL, + PRIV_OBJECT_VM_POOL]}, ACTION_SUSPEND_VM => { :label => "Suspend", :icon => "icon_suspend.png", :start => Vm::STATE_RUNNING, :running => Vm::STATE_SUSPENDING, :success => Vm::STATE_SUSPENDED, - :failure => Vm::STATE_RUNNING}, + :failure => Vm::STATE_RUNNING, + :privilege => [Permission::PRIV_VM_CONTROL, + PRIV_OBJECT_VM_POOL]}, ACTION_RESUME_VM => { :label => "Resume", :icon => "icon_start.png", :start => Vm::STATE_SUSPENDED, :running => Vm::STATE_RESUMING, :success => Vm::STATE_RUNNING, - :failure => Vm::STATE_SUSPENDED}, + :failure => Vm::STATE_SUSPENDED, + :privilege => [Permission::PRIV_VM_CONTROL, + PRIV_OBJECT_VM_POOL]}, ACTION_SAVE_VM => { :label => "Save", :icon => "icon_save.png", :start => Vm::STATE_RUNNING, :running => Vm::STATE_SAVING, :success => Vm::STATE_SAVED, - :failure => Vm::STATE_RUNNING}, + :failure => Vm::STATE_RUNNING, + :privilege => [Permission::PRIV_VM_CONTROL, + PRIV_OBJECT_VM_POOL]}, ACTION_RESTORE_VM => { :label => "Restore", :icon => "icon_restore.png", :start => Vm::STATE_SAVED, :running => Vm::STATE_RESTORING, :success => Vm::STATE_RUNNING, - :failure => Vm::STATE_SAVED} } + :failure => Vm::STATE_SAVED, + :privilege => [Permission::PRIV_VM_CONTROL, + PRIV_OBJECT_VM_POOL]}, + ACTION_MIGRATE_VM => { :label => "Migrate", + :icon => "icon_restore.png", + :start => Vm::STATE_RUNNING, + :running => Vm::STATE_MIGRATING, + :success => Vm::STATE_RUNNING, + :failure => Vm::STATE_RUNNING, + :privilege => [Permission::PRIV_MODIFY, + PRIV_OBJECT_HW_POOL], + :popup_action => 'migrate'} } - VALID_ACTIONS_PER_VM_STATE = { Vm::STATE_PENDING => [ACTION_CREATE_VM], - Vm::STATE_RUNNING => [ACTION_SHUTDOWN_VM, - ACTION_SUSPEND_VM, - ACTION_SAVE_VM], - Vm::STATE_STOPPED => [ACTION_START_VM], - Vm::STATE_SUSPENDED => [ACTION_RESUME_VM], - Vm::STATE_SAVED => [ACTION_RESTORE_VM], - Vm::STATE_CREATE_FAILED => [], - Vm::STATE_INVALID => []} + def self.valid_actions_for_vm_state(state, vm=nil, user=nil) + actions = [] + ACTIONS.each do |action, hash| + if hash[:start] == state + add_action = true + print "vm: #{vm}\n user: #{user}\n" + if (vm and user) + pool = vm.send(hash[:privilege][1]) + print "pool: #{pool}\n privilege: #{hash[:privilege][1]}\n" + add_action = pool ? pool.has_privilege(user, hash[:privilege][0]) : false + end + print "add_action: #{add_action}\n" + actions << action if add_action + end + end + actions + end def self.action_label(action) return ACTIONS[action][:label] diff --git a/wui/src/app/views/hardware/show_hosts.rhtml b/wui/src/app/views/hardware/show_hosts.rhtml index aaf28b6..167c601 100644 --- a/wui/src/app/views/hardware/show_hosts.rhtml +++ b/wui/src/app/views/hardware/show_hosts.rhtml @@ -60,7 +60,10 @@ <div class="data_section"> <%= render :partial => "/host/grid", :locals => { :table_id => "hosts_grid", :hwpool => @pool, - :exclude_id => nil, + :exclude_pool => nil, + :exclude_host => nil, + :show_pool => false, + :checkboxes => true, :on_select => "hosts_select", :on_deselect => "load_widget_deselect", :on_hover => "load_widget_hover", diff --git a/wui/src/app/views/hardware/show_storage.rhtml b/wui/src/app/views/hardware/show_storage.rhtml index ec1a82d..dc1460a 100644 --- a/wui/src/app/views/hardware/show_storage.rhtml +++ b/wui/src/app/views/hardware/show_storage.rhtml @@ -66,7 +66,7 @@ <div class="data_section"> <%= render :partial => "/storage/grid", :locals => { :table_id => "storage_grid", :hwpool => @pool, - :exclude_id => nil, + :exclude_pool => nil, :on_select => "storage_select" } %> </div> diff --git a/wui/src/app/views/host/_grid.rhtml b/wui/src/app/views/host/_grid.rhtml index 98a8af4..d4d4d11 100644 --- a/wui/src/app/views/host/_grid.rhtml +++ b/wui/src/app/views/host/_grid.rhtml @@ -2,20 +2,25 @@ <% hosts_per_page = 40 %> <div id="<%= table_id %>_div"> -<form id="<%= table_id %>_form"> +<%= '<form id="#{table_id}_form">' if checkboxes %> <table id="<%= table_id %>" style="display:none"></table> -</form> +<%= '</form>' if checkboxes %> </div> <script type="text/javascript"> $("#<%= table_id %>").flexigrid ( { - url: '<%= url_for :controller => "hardware", :action => "hosts_json", :id => (hwpool.nil? ? nil : hwpool.id), :exclude_id => exclude_id %>', + url: '<%= url_for :controller => "hardware", + :action => "hosts_json", + :id => (hwpool.nil? ? nil : hwpool.id), + :exclude_pool => exclude_pool, + :exclude_host => exclude_host, + :checkboxes => checkboxes %>', dataType: 'json', colModel : [ - {display: '', width : 20, align: 'left', process: <%= table_id %>checkbox}, + <%= "{display: '', width : 20, align: 'left', process: #{table_id}checkbox}," if checkboxes %> {display: 'Hostname', name : 'hostname', width : 60, align: 'left'}, - <%= "{display: 'Hardware Pool', name : 'pools.name', width : 100, align: 'left'}," if exclude_id %> + <%= "{display: 'Hardware Pool', name : 'pools.name', width : 100, align: 'left'}," if exclude_pool %> {display: 'UUID', name : 'uuid', width : 180, align: 'left'}, {display: 'Hypervisor', name : 'hypervisor_type', width : 60, align: 'left'}, {display: 'CPUs', name : 'num_cpus', width : 30, align: 'left'}, diff --git a/wui/src/app/views/host/addhost.html.erb b/wui/src/app/views/host/addhost.html.erb index cfcc43b..41b6213 100644 --- a/wui/src/app/views/host/addhost.html.erb +++ b/wui/src/app/views/host/addhost.html.erb @@ -8,7 +8,10 @@ <div class="panel_header"></div> <div class="dialog_body"> <%= render :partial => "/host/grid", :locals => { :table_id => "addhosts_grid", - :hwpool => nil, :exclude_id => @hardware_pool.id, + :hwpool => nil, + :exclude_pool => @hardware_pool.id, + :exclude_host => nil, + :checkboxes => true, :on_select => "load_widget_select", :on_deselect => "load_widget_deselect", :on_hover => "load_widget_hover", diff --git a/wui/src/app/views/host/quick_summary.rhtml b/wui/src/app/views/host/quick_summary.rhtml new file mode 100644 index 0000000..eceb561 --- /dev/null +++ b/wui/src/app/views/host/quick_summary.rhtml @@ -0,0 +1 @@ + <%=h @host.hostname %> diff --git a/wui/src/app/views/storage/_grid.rhtml b/wui/src/app/views/storage/_grid.rhtml index 8b10aaa..3bdf407 100644 --- a/wui/src/app/views/storage/_grid.rhtml +++ b/wui/src/app/views/storage/_grid.rhtml @@ -9,12 +9,12 @@ $("#<%= table_id %>").flexigrid ( { - url: '<%= url_for :controller => "hardware", :action => "storage_pools_json", :id => (hwpool.nil? ? nil : hwpool.id), :exclude_id => exclude_id %>', + url: '<%= url_for :controller => "hardware", :action => "storage_pools_json", :id => (hwpool.nil? ? nil : hwpool.id), :exclude_pool => exclude_pool %>', dataType: 'json', colModel : [ {display: '', width : 20, align: 'left', process: <%= table_id %>checkbox}, {display: 'Alias', width : 180, align: 'left'}, - <%= "{display: 'Hardware Pool', name : 'pools.name', width : 100, align: 'left'}," if exclude_id %> + <%= "{display: 'Hardware Pool', name : 'pools.name', width : 100, align: 'left'}," if exclude_pool %> {display: 'IP', name : 'ip_addr', width : 80, align: 'left'}, {display: 'Type', name : 'storage_pools.type', width : 80, align: 'left'} ], diff --git a/wui/src/app/views/storage/add.rhtml b/wui/src/app/views/storage/add.rhtml index 5dd70e6..11cda06 100644 --- a/wui/src/app/views/storage/add.rhtml +++ b/wui/src/app/views/storage/add.rhtml @@ -3,7 +3,7 @@ <div class="dialog_body_small"> <div class="panel_header"></div> <%= render :partial => "/storage/grid", :locals => { :table_id => "addstorage_grid", - :hwpool => nil, :exclude_id => @hardware_pool.id, + :hwpool => nil, :exclude_pool => @hardware_pool.id, :on_select => "false" } %> </div> <%= popup_footer("add_storage('#{url_for :controller => 'hardware', diff --git a/wui/src/app/views/vm/migrate.rhtml b/wui/src/app/views/vm/migrate.rhtml new file mode 100644 index 0000000..f94723a --- /dev/null +++ b/wui/src/app/views/vm/migrate.rhtml @@ -0,0 +1,81 @@ +<%- content_for :title do -%> + Migrate Virtual Machine +<%- end -%> +<%- content_for :description do -%> + Please choose migration destination. Leave the selection blank to allow oVirt to choose the most appropriate destination host. +<%- end -%> +<script type="text/javascript"> +<%= popup_footer("migrate_vm()", "Migrate") %> + function migrate_vm_select(selected_rows) + { + var selected_ids = new Array(); + for(i=0; i<selected_rows.length; i++) { + load_widget_select(selected_rows[i]); + selected_ids[i] = selected_rows[i].id; + } + if (selected_ids.length == 1) + { + $('#selected_migration_target').load('<%= url_for :controller => "host", :action => "quick_summary" %>', + { id: parseInt(selected_ids[0].substring(3))}); + $('#vm_action_data').val(selected_ids[0].substring(3)); + } + } + function migrate_vm_deselect(selected_rows) + { + var selected_ids = new Array() + for(i=0; i<selected_rows.length; i++) { + load_widget_deselect(selected_rows[i]); + selected_ids[i] = selected_rows[i].id; + } + refresh_summary_static('selected_migration_target', '<div class="selection_left"> \ + <div>No migration target selected.</div> \ + </div>') + $('#vm_action_data').val('') + } +</script> +<div class="panel_header"></div> + + +<form method="POST" id="migrate_vm_form" action="<%= url_for :action => 'vm_action' %>" > +<div class="dialog_form"> + <%= error_messages_for 'migrate_vm' %> + + <%= render :partial => "/host/grid", :locals => { :table_id => "migrate_vm_grid", + :hwpool => @vm.get_hardware_pool, + :exclude_pool => nil, + :exclude_host => @vm.host_id, + :checkboxes => false, + :on_select => "migrate_vm_select", + :on_deselect => "migrate_vm_deselect", + :on_hover => "load_widget_hover", + :on_unhover => "load_widget_unhover" } %> + + <% form_tag do %> + <!--[form:migrate_vm]--> + <%= hidden_field_tag 'id', @vm.id %> + <%= hidden_field_tag 'vm_action', VmTask::ACTION_MIGRATE_VM %> + Selected Migration Target: + <div id='selected_migration_target'> + <div class="selection_left"> + <div>No migration target selected.</div> + </div> + </div> + <%= hidden_field_tag 'vm_action_data', "" %> + <% end %> +</div> +<%= popup_footer("$('#migrate_vm_form').submit()", "Migrate Virtual Machine") %> +</form> +<script type="text/javascript"> +$(function() { + var vmoptions = { + target: '<%= url_for :action => 'create' %>', // target element to update + //beforeSubmit: showStorageRequest, // pre-submit callback + dataType: 'json', + success: afterVm // post-submit callback + }; + + // bind form using 'ajaxForm' + $('#migrate_vm_form').ajaxForm(vmoptions); +}); +</script> + diff --git a/wui/src/app/views/vm/show.rhtml b/wui/src/app/views/vm/show.rhtml index 8b0a760..fe671ef 100644 --- a/wui/src/app/views/vm/show.rhtml +++ b/wui/src/app/views/vm/show.rhtml @@ -17,10 +17,17 @@ <%= link_to image_tag("icon_edit.png") + " Edit", {:controller => 'vm', :action => 'edit', :id => @vm}, :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> - <% for action in @actions %> - <a href="#" onClick="single_vm_action('<%= action[1] %>')"> - <%= image_tag action[2] %> <%= action[0] %> - </a> + <% for name, action in @actions %> + <% if action[:popup_action] -%> + <%= link_to image_tag(action[:icon]) + action[:label], + {:controller => 'vm', + :action => action[:popup_action], :id => @vm}, + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> + <% else -%> + <a href="#" onClick="single_vm_action('<%= name %>')"> + <%= image_tag action[:icon] %> <%= action[:label] %> + </a> + <% end -%> <% end %> <a href="#" onClick="cancel_queued_tasks()"> <%= image_tag "icon_x.png" %> Cancel queued tasks diff --git a/wui/src/public/javascripts/flexigrid.js b/wui/src/public/javascripts/flexigrid.js index d7b6416..3715e6f 100644 --- a/wui/src/public/javascripts/flexigrid.js +++ b/wui/src/public/javascripts/flexigrid.js @@ -719,7 +719,9 @@ } $(this).toggleClass('trSelected'); if($(this).hasClass('trSelected')){ - if (p.onSelect) p.onSelect($(t).find("tr.trSelected")); + if (p.onSelect) p.onSelect($(t).find("tr.trSelected")); + } else { + if (p.onDeselect) p.onDeselect(this); } } ) diff --git a/wui/src/public/javascripts/ovirt.js b/wui/src/public/javascripts/ovirt.js index c776458..e0aa222 100644 --- a/wui/src/public/javascripts/ovirt.js +++ b/wui/src/public/javascripts/ovirt.js @@ -74,7 +74,7 @@ function ajax_validation(response, status) if (response.object) { $(".fieldWithErrors").removeClass("fieldWithErrors"); $("div.errorExplanation").remove(); - if (!response.success) { + if (!response.success && response.errors ) { for(i=0; i<response.errors.length; i++) { var element = $("div.form_field:has(#"+response.object + "_" + response.errors[i][0]+")"); if (element) { -- 1.5.5.1
Jason Guiditta
2008-Jul-15 22:04 UTC
[Ovirt-devel] [PATCH] added VM-level migration support to the WUI. Still no back end bits here, though.
Scott, could you give me a quick scenario to test against this so I can see it does what you meant it to? Comments based just on the code inline. On Tue, 2008-07-15 at 11:28 -0400, Scott Seago wrote:> Signed-off-by: Scott Seago <sseago at redhat.com> > --- > wui/src/app/controllers/hardware_controller.rb | 18 ++++-- > wui/src/app/controllers/host_controller.rb | 11 +++ > wui/src/app/controllers/resources_controller.rb | 25 ------- > wui/src/app/controllers/vm_controller.rb | 13 +++- > wui/src/app/models/vm.rb | 31 ++++++--- > wui/src/app/models/vm_task.rb | 70 +++++++++++++++----- > wui/src/app/views/hardware/show_hosts.rhtml | 5 +- > wui/src/app/views/hardware/show_storage.rhtml | 2 +- > wui/src/app/views/host/_grid.rhtml | 15 +++-- > wui/src/app/views/host/addhost.html.erb | 5 +- > wui/src/app/views/host/quick_summary.rhtml | 1 + > wui/src/app/views/storage/_grid.rhtml | 4 +- > wui/src/app/views/storage/add.rhtml | 2 +- > wui/src/app/views/vm/migrate.rhtml | 81 +++++++++++++++++++++++ > wui/src/app/views/vm/show.rhtml | 15 +++- > wui/src/public/javascripts/flexigrid.js | 4 +- > wui/src/public/javascripts/ovirt.js | 2 +- > 17 files changed, 230 insertions(+), 74 deletions(-) > create mode 100644 wui/src/app/views/host/quick_summary.rhtml > create mode 100644 wui/src/app/views/vm/migrate.rhtml > > diff --git a/wui/src/app/controllers/hardware_controller.rb b/wui/src/app/controllers/hardware_controller.rb > index 4175fd8..926b6c8 100644 > --- a/wui/src/app/controllers/hardware_controller.rb > +++ b/wui/src/app/controllers/hardware_controller.rb > @@ -112,7 +112,12 @@ class HardwareController < ApplicationController > end > > def hosts_json > - if params[:id] > + if params[:exclude_host] > + pre_json > + hosts = @pool.hosts > + find_opts = {:conditions => ["id != ?", params[:exclude_host]]} > + include_pool = false > + elsif params[:id] > pre_json > hosts = @pool.hosts > find_opts = {} > @@ -120,14 +125,17 @@ class HardwareController < ApplicationController > else > # FIXME: no permissions or usage checks here yet > # filtering on which pool to exclude > - id = params[:exclude_id] > + id = params[:exclude_pool] > hosts = Host > find_opts = {:include => :hardware_pool, > :conditions => ["pools.id != ?", id]} > include_pool = true > end > - attr_list = [:id, :hostname, :uuid, :hypervisor_type, :num_cpus, :cpu_speed, :arch, :memory_in_mb, :status_str, :id] > - attr_list.insert(2, [:hardware_pool, :name]) if include_pool > + attr_list = [] > + attr_list << :id if params[:checkboxes] > + attr_list << :hostname > + attr_list << [:hardware_pool, :name] if include_pool > + attr_list += [:uuid, :hypervisor_type, :num_cpus, :cpu_speed, :arch, :memory_in_mb, :status_str, :id] > json_list(hosts, attr_list, [:all], find_opts) > end > > @@ -152,7 +160,7 @@ class HardwareController < ApplicationController > else > # FIXME: no permissions or usage checks here yet > # filtering on which pool to exclude > - id = params[:exclude_id] > + id = params[:exclude_pool] > storage_pools = StoragePool > find_opts = {:include => :hardware_pool, > :conditions => ["pools.id != ?", id]} > diff --git a/wui/src/app/controllers/host_controller.rb b/wui/src/app/controllers/host_controller.rb > index 20571d7..4e4375a 100644 > --- a/wui/src/app/controllers/host_controller.rb > +++ b/wui/src/app/controllers/host_controller.rb > @@ -44,6 +44,17 @@ class HostController < ApplicationController > render :layout => 'selection' > end > > + def quick_summary > + pre_show > + set_perms(@perm_obj) > + 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 differentlyConsidering it is almost all ajax now, we should figure out what 'differently' is asap> + redirect_to :controller => 'dashboard', :action => 'list' > + end > + render :layout => false > + end > + > # retrieves data used by snapshot graphs > def snapshot_graph > end > diff --git a/wui/src/app/controllers/resources_controller.rb b/wui/src/app/controllers/resources_controller.rb > index 694ef74..ca10960 100644 > --- a/wui/src/app/controllers/resources_controller.rb > +++ b/wui/src/app/controllers/resources_controller.rb > @@ -184,31 +184,6 @@ class ResourcesController < ApplicationController > @failure_list = [] > end > render :layout => 'confirmation' > - > - #if params[:vm_actions][:vms] > - # vms = params[:vm_actions][:vms] > - # if params[:vm_actions][VmTask::ACTION_START_VM] > - # flash[:notice] = "Starting Machines #{vms.join(',')}." > - # elsif params[:vm_actions][VmTask::ACTION_SHUTDOWN_VM] > - # flash[:notice] = "Stopping Machines #{vms.join(',')}." > - # elsif params[:vm_actions][:other_actions] > - # case params[:vm_actions][:other_actions] > - # when VmTask::ACTION_SHUTDOWN_VM then flash[:notice] = "Stopping Machines #{vms.join(',')}." > - # when VmTask::ACTION_START_VM then flash[:notice] = "Starting Machines #{vms.join(',')}." > - # when VmTask::ACTION_SUSPEND_VM then flash[:notice] = "Suspending Machines #{vms.join(',')}." > - # when VmTask::ACTION_RESUME_VM then flash[:notice] = "Resuming Machines #{vms.join(',')}." > - # when VmTask::ACTION_SAVE_VM then flash[:notice] = "Saving Machines #{vms.join(',')}." > - # when VmTask::ACTION_RESTORE_VM then flash[:notice] = "Restoring Machines #{vms.join(',')}." > - # when "destroy" then flash[:notice] = "Destroying Machines #{vms.join(',')}." > - # else > - # flash[:notice] = 'No Action Chosen.' > - # end > - # else > - # flash[:notice] = 'No Action Chosen.' > - # end > - #else > - # flash[:notice] = 'No Virtual Machines Selected.' > - #end > end > > protected > diff --git a/wui/src/app/controllers/vm_controller.rb b/wui/src/app/controllers/vm_controller.rb > index b9156d2..4f9962d 100644 > --- a/wui/src/app/controllers/vm_controller.rb > +++ b/wui/src/app/controllers/vm_controller.rb > @@ -26,7 +26,7 @@ class VmController < ApplicationController > > def show > set_perms(@perm_obj) > - @actions = @vm.get_action_and_label_list > + @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' > @@ -149,8 +149,9 @@ class VmController < ApplicationController > > def vm_action > vm_action = params[:vm_action] > + data = params[:vm_action_data] > begin > - if @vm.queue_action(get_login_user, vm_action) > + 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." } > @@ -171,6 +172,14 @@ class VmController < ApplicationController > end > end > > + def migrate > + @vm = Vm.find(params[:id]) > + @perm_obj = @vm.get_hardware_pool > + @redir_obj = @vm > + @current_pool_id=@vm.vm_resource_pool.id > + authorize_admin > + render :layout => 'popup' > + end > > def console > @show_vnc_error = "Console is unavailable for VM #{@vm.description}" unless @vm.has_console > diff --git a/wui/src/app/models/vm.rb b/wui/src/app/models/vm.rb > index 617512e..b607886 100644 > --- a/wui/src/app/models/vm.rb > +++ b/wui/src/app/models/vm.rb > @@ -22,7 +22,7 @@ require 'util/ovirt' > class Vm < ActiveRecord::Base > belongs_to :vm_resource_pool > belongs_to :host > - has_many :tasks, :class_name => "VmTask", :dependent => :destroy, :order => "id DESC" do > + has_many :tasks, :class_name => "VmTask", :dependent => :destroy, :order => "id ASC" do > def queued > find(:all, :conditions=>{:state=>Task::STATE_QUEUED}) > end > @@ -58,6 +58,9 @@ class Vm < ActiveRecord::Base > STATE_SAVING = "saving" > STATE_SAVED = "saved" > STATE_RESTORING = "restoring" > + > + STATE_MIGRATING = "migrating" > + > STATE_CREATE_FAILED = "create_failed" > STATE_INVALID = "invalid" > > @@ -68,7 +71,8 @@ class Vm < ActiveRecord::Base > STATE_SUSPENDING, > STATE_RESUMING, > STATE_SAVING, > - STATE_RESTORING] > + STATE_RESTORING, > + STATE_MIGRATING] > > EFFECTIVE_STATE = { STATE_PENDING => STATE_PENDING, > STATE_UNREACHABLE => STATE_UNREACHABLE, > @@ -83,9 +87,15 @@ class Vm < ActiveRecord::Base > STATE_SAVING => STATE_SAVED, > STATE_SAVED => STATE_SAVED, > STATE_RESTORING => STATE_RUNNING, > + STATE_MIGRATING => STATE_RUNNING, > STATE_CREATE_FAILED => STATE_CREATE_FAILED} > TASK_STATE_TRANSITIONS = [] > > + def get_hardware_pool > + pool = vm_resource_pool > + pool = pool.get_hardware_pool if pool > + pool > + end > def storage_volume_ids > storage_volumes.collect {|x| x.id } > end > @@ -126,9 +136,9 @@ class Vm < ActiveRecord::Base > RUNNING_STATES.include?(get_pending_state) > end > > - def get_action_list > + def get_action_list(user=nil)You do this in several places below, and I must be misunderstanding - what is the purpose of having this user var if it is always nil?> # return empty list rather than nil > - return_val = VmTask::VALID_ACTIONS_PER_VM_STATE[get_pending_state] || [] > + return_val = VmTask.valid_actions_for_vm_state(get_pending_state, self, user) || [] > # filter actions based on quota > unless resources_for_start? > return_val = return_val - [VmTask::ACTION_START_VM, VmTask::ACTION_RESTORE_VM] > @@ -136,10 +146,12 @@ class Vm < ActiveRecord::Base > return_val > end > > - def get_action_and_label_list > - get_action_list.collect do |action| > - VmTask.label_and_action(action) > + def get_action_hash(user=nil) > + actions = {} > + get_action_list(user).each do |action| > + actions[action] = VmTask::ACTIONS[action] > end > + actions > end > > # these resource checks are made at VM start/restore time > @@ -158,11 +170,12 @@ class Vm < ActiveRecord::Base > return return_val > end > > - def queue_action(user, action) > + def queue_action(user, action, data = nil) > return false unless get_action_list.include?(action) > task = VmTask.new({ :user => user, > :vm_id => id, > - :action => action, > + :action => action, > + :args => data, > :state => Task::STATE_QUEUED}) > task.save! > return true > diff --git a/wui/src/app/models/vm_task.rb b/wui/src/app/models/vm_task.rb > index aa12903..3f52478 100644 > --- a/wui/src/app/models/vm_task.rb > +++ b/wui/src/app/models/vm_task.rb > @@ -33,59 +33,97 @@ class VmTask < Task > > ACTION_UPDATE_STATE_VM = "update_state_vm" > > + # for migrate VM action, args provides the optional target host > + ACTION_MIGRATE_VM = "migrate_vm" > + > + PRIV_OBJECT_VM_POOL = "vm_resource_pool" > + PRIV_OBJECT_HW_POOL = "get_hardware_pool" > + > +Perhaps this is less a comment on the patch and more on the way we are doing this. It feels vaguely wrong to me to do it this way. I see a lot of repeated attributes, which to me says it should be more encapsulated in some way. Would it make more sense to put these 'contants' in the database? Or even a fixture of some sort?> # a hash of task actions which point to a hash which define valid state transitions > ACTIONS = { ACTION_CREATE_VM => { :label => "Create", > :icon => "icon_start.png", > :start => Vm::STATE_PENDING, > :running => Vm::STATE_CREATING, > :success => Vm::STATE_STOPPED, > - :failure => Vm::STATE_CREATE_FAILED}, > + :failure => Vm::STATE_CREATE_FAILED, > + :privilege => [Permission::PRIV_MODIFY, > + PRIV_OBJECT_VM_POOL]}, > ACTION_START_VM => { :label => "Start", > :icon => "icon_start.png", > :start => Vm::STATE_STOPPED, > :running => Vm::STATE_STARTING, > :success => Vm::STATE_RUNNING, > - :failure => Vm::STATE_STOPPED}, > + :failure => Vm::STATE_STOPPED, > + :privilege => [Permission::PRIV_VM_CONTROL, > + PRIV_OBJECT_VM_POOL]}, > ACTION_SHUTDOWN_VM => { :label => "Shutdown", > :icon => "icon_x.png", > :start => Vm::STATE_RUNNING, > :running => Vm::STATE_STOPPING, > :success => Vm::STATE_STOPPED, > - :failure => Vm::STATE_RUNNING}, > + :failure => Vm::STATE_RUNNING, > + :privilege => [Permission::PRIV_VM_CONTROL, > + PRIV_OBJECT_VM_POOL]}, > ACTION_SUSPEND_VM => { :label => "Suspend", > :icon => "icon_suspend.png", > :start => Vm::STATE_RUNNING, > :running => Vm::STATE_SUSPENDING, > :success => Vm::STATE_SUSPENDED, > - :failure => Vm::STATE_RUNNING}, > + :failure => Vm::STATE_RUNNING, > + :privilege => [Permission::PRIV_VM_CONTROL, > + PRIV_OBJECT_VM_POOL]}, > ACTION_RESUME_VM => { :label => "Resume", > :icon => "icon_start.png", > :start => Vm::STATE_SUSPENDED, > :running => Vm::STATE_RESUMING, > :success => Vm::STATE_RUNNING, > - :failure => Vm::STATE_SUSPENDED}, > + :failure => Vm::STATE_SUSPENDED, > + :privilege => [Permission::PRIV_VM_CONTROL, > + PRIV_OBJECT_VM_POOL]}, > ACTION_SAVE_VM => { :label => "Save", > :icon => "icon_save.png", > :start => Vm::STATE_RUNNING, > :running => Vm::STATE_SAVING, > :success => Vm::STATE_SAVED, > - :failure => Vm::STATE_RUNNING}, > + :failure => Vm::STATE_RUNNING, > + :privilege => [Permission::PRIV_VM_CONTROL, > + PRIV_OBJECT_VM_POOL]}, > ACTION_RESTORE_VM => { :label => "Restore", > :icon => "icon_restore.png", > :start => Vm::STATE_SAVED, > :running => Vm::STATE_RESTORING, > :success => Vm::STATE_RUNNING, > - :failure => Vm::STATE_SAVED} } > + :failure => Vm::STATE_SAVED, > + :privilege => [Permission::PRIV_VM_CONTROL, > + PRIV_OBJECT_VM_POOL]}, > + ACTION_MIGRATE_VM => { :label => "Migrate", > + :icon => "icon_restore.png", > + :start => Vm::STATE_RUNNING, > + :running => Vm::STATE_MIGRATING, > + :success => Vm::STATE_RUNNING, > + :failure => Vm::STATE_RUNNING, > + :privilege => [Permission::PRIV_MODIFY, > + PRIV_OBJECT_HW_POOL], > + :popup_action => 'migrate'} } > > - VALID_ACTIONS_PER_VM_STATE = { Vm::STATE_PENDING => [ACTION_CREATE_VM], > - Vm::STATE_RUNNING => [ACTION_SHUTDOWN_VM, > - ACTION_SUSPEND_VM, > - ACTION_SAVE_VM], > - Vm::STATE_STOPPED => [ACTION_START_VM], > - Vm::STATE_SUSPENDED => [ACTION_RESUME_VM], > - Vm::STATE_SAVED => [ACTION_RESTORE_VM], > - Vm::STATE_CREATE_FAILED => [], > - Vm::STATE_INVALID => []} > + def self.valid_actions_for_vm_state(state, vm=nil, user=nil) > + actions = [] > + ACTIONS.each do |action, hash| > + if hash[:start] == state > + add_action = true > + print "vm: #{vm}\n user: #{user}\n" > + if (vm and user) > + pool = vm.send(hash[:privilege][1]) > + print "pool: #{pool}\n privilege: #{hash[:privilege][1]}\n" > + add_action = pool ? pool.has_privilege(user, hash[:privilege][0]) : false > + end > + print "add_action: #{add_action}\n" > + actions << action if add_action > + end > + end > + actions > + end > > def self.action_label(action) > return ACTIONS[action][:label] > diff --git a/wui/src/app/views/hardware/show_hosts.rhtml b/wui/src/app/views/hardware/show_hosts.rhtml > index aaf28b6..167c601 100644 > --- a/wui/src/app/views/hardware/show_hosts.rhtml > +++ b/wui/src/app/views/hardware/show_hosts.rhtml > @@ -60,7 +60,10 @@ > <div class="data_section"> > <%= render :partial => "/host/grid", :locals => { :table_id => "hosts_grid", > :hwpool => @pool, > - :exclude_id => nil, > + :exclude_pool => nil, > + :exclude_host => nil, > + :show_pool => false, > + :checkboxes => true, > :on_select => "hosts_select", > :on_deselect => "load_widget_deselect", > :on_hover => "load_widget_hover", > diff --git a/wui/src/app/views/hardware/show_storage.rhtml b/wui/src/app/views/hardware/show_storage.rhtml > index ec1a82d..dc1460a 100644 > --- a/wui/src/app/views/hardware/show_storage.rhtml > +++ b/wui/src/app/views/hardware/show_storage.rhtml > @@ -66,7 +66,7 @@ > <div class="data_section"> > <%= render :partial => "/storage/grid", :locals => { :table_id => "storage_grid", > :hwpool => @pool, > - :exclude_id => nil, > + :exclude_pool => nil, > :on_select => "storage_select" } %> > </div> > > diff --git a/wui/src/app/views/host/_grid.rhtml b/wui/src/app/views/host/_grid.rhtml > index 98a8af4..d4d4d11 100644 > --- a/wui/src/app/views/host/_grid.rhtml > +++ b/wui/src/app/views/host/_grid.rhtml > @@ -2,20 +2,25 @@ > > <% hosts_per_page = 40 %> > <div id="<%= table_id %>_div"> > -<form id="<%= table_id %>_form"> > +<%= '<form id="#{table_id}_form">' if checkboxes %> > <table id="<%= table_id %>" style="display:none"></table> > -</form> > +<%= '</form>' if checkboxes %> > </div> > <script type="text/javascript"> > $("#<%= table_id %>").flexigrid > ( > { > - url: '<%= url_for :controller => "hardware", :action => "hosts_json", :id => (hwpool.nil? ? nil : hwpool.id), :exclude_id => exclude_id %>', > + url: '<%= url_for :controller => "hardware", > + :action => "hosts_json", > + :id => (hwpool.nil? ? nil : hwpool.id), > + :exclude_pool => exclude_pool, > + :exclude_host => exclude_host, > + :checkboxes => checkboxes %>', > dataType: 'json', > colModel : [ > - {display: '', width : 20, align: 'left', process: <%= table_id %>checkbox}, > + <%= "{display: '', width : 20, align: 'left', process: #{table_id}checkbox}," if checkboxes %> > {display: 'Hostname', name : 'hostname', width : 60, align: 'left'}, > - <%= "{display: 'Hardware Pool', name : 'pools.name', width : 100, align: 'left'}," if exclude_id %> > + <%= "{display: 'Hardware Pool', name : 'pools.name', width : 100, align: 'left'}," if exclude_pool %> > {display: 'UUID', name : 'uuid', width : 180, align: 'left'}, > {display: 'Hypervisor', name : 'hypervisor_type', width : 60, align: 'left'}, > {display: 'CPUs', name : 'num_cpus', width : 30, align: 'left'}, > diff --git a/wui/src/app/views/host/addhost.html.erb b/wui/src/app/views/host/addhost.html.erb > index cfcc43b..41b6213 100644 > --- a/wui/src/app/views/host/addhost.html.erb > +++ b/wui/src/app/views/host/addhost.html.erb > @@ -8,7 +8,10 @@ > <div class="panel_header"></div> > <div class="dialog_body"> > <%= render :partial => "/host/grid", :locals => { :table_id => "addhosts_grid", > - :hwpool => nil, :exclude_id => @hardware_pool.id, > + :hwpool => nil, > + :exclude_pool => @hardware_pool.id, > + :exclude_host => nil, > + :checkboxes => true, > :on_select => "load_widget_select", > :on_deselect => "load_widget_deselect", > :on_hover => "load_widget_hover", > diff --git a/wui/src/app/views/host/quick_summary.rhtml b/wui/src/app/views/host/quick_summary.rhtml > new file mode 100644 > index 0000000..eceb561 > --- /dev/null > +++ b/wui/src/app/views/host/quick_summary.rhtml > @@ -0,0 +1 @@ > + <%=h @host.hostname %> > diff --git a/wui/src/app/views/storage/_grid.rhtml b/wui/src/app/views/storage/_grid.rhtml > index 8b10aaa..3bdf407 100644 > --- a/wui/src/app/views/storage/_grid.rhtml > +++ b/wui/src/app/views/storage/_grid.rhtml > @@ -9,12 +9,12 @@ > $("#<%= table_id %>").flexigrid > ( > { > - url: '<%= url_for :controller => "hardware", :action => "storage_pools_json", :id => (hwpool.nil? ? nil : hwpool.id), :exclude_id => exclude_id %>', > + url: '<%= url_for :controller => "hardware", :action => "storage_pools_json", :id => (hwpool.nil? ? nil : hwpool.id), :exclude_pool => exclude_pool %>', > dataType: 'json', > colModel : [ > {display: '', width : 20, align: 'left', process: <%= table_id %>checkbox}, > {display: 'Alias', width : 180, align: 'left'}, > - <%= "{display: 'Hardware Pool', name : 'pools.name', width : 100, align: 'left'}," if exclude_id %> > + <%= "{display: 'Hardware Pool', name : 'pools.name', width : 100, align: 'left'}," if exclude_pool %> > {display: 'IP', name : 'ip_addr', width : 80, align: 'left'}, > {display: 'Type', name : 'storage_pools.type', width : 80, align: 'left'} > ], > diff --git a/wui/src/app/views/storage/add.rhtml b/wui/src/app/views/storage/add.rhtml > index 5dd70e6..11cda06 100644 > --- a/wui/src/app/views/storage/add.rhtml > +++ b/wui/src/app/views/storage/add.rhtml > @@ -3,7 +3,7 @@ > <div class="dialog_body_small"> > <div class="panel_header"></div> > <%= render :partial => "/storage/grid", :locals => { :table_id => "addstorage_grid", > - :hwpool => nil, :exclude_id => @hardware_pool.id, > + :hwpool => nil, :exclude_pool => @hardware_pool.id, > :on_select => "false" } %> > </div> > <%= popup_footer("add_storage('#{url_for :controller => 'hardware', > diff --git a/wui/src/app/views/vm/migrate.rhtml b/wui/src/app/views/vm/migrate.rhtml > new file mode 100644 > index 0000000..f94723a > --- /dev/null > +++ b/wui/src/app/views/vm/migrate.rhtml > @@ -0,0 +1,81 @@ > +<%- content_for :title do -%> > + Migrate Virtual Machine > +<%- end -%> > +<%- content_for :description do -%> > + Please choose migration destination. Leave the selection blank to allow oVirt to choose the most appropriate destination host. > +<%- end -%> > +<script type="text/javascript"> > +<%= popup_footer("migrate_vm()", "Migrate") %> > + function migrate_vm_select(selected_rows) > + { > + var selected_ids = new Array(); > + for(i=0; i<selected_rows.length; i++) { > + load_widget_select(selected_rows[i]); > + selected_ids[i] = selected_rows[i].id; > + } > + if (selected_ids.length == 1) > + { > + $('#selected_migration_target').load('<%= url_for :controller => "host", :action => "quick_summary" %>', > + { id: parseInt(selected_ids[0].substring(3))}); > + $('#vm_action_data').val(selected_ids[0].substring(3)); > + } > + } > + function migrate_vm_deselect(selected_rows) > + { > + var selected_ids = new Array() > + for(i=0; i<selected_rows.length; i++) { > + load_widget_deselect(selected_rows[i]); > + selected_ids[i] = selected_rows[i].id; > + } > + refresh_summary_static('selected_migration_target', '<div class="selection_left"> \ > + <div>No migration target selected.</div> \ > + </div>') > + $('#vm_action_data').val('') > + } > +</script> > +<div class="panel_header"></div> > + > + > +<form method="POST" id="migrate_vm_form" action="<%= url_for :action => 'vm_action' %>" > > +<div class="dialog_form"> > + <%= error_messages_for 'migrate_vm' %> > + > + <%= render :partial => "/host/grid", :locals => { :table_id => "migrate_vm_grid", > + :hwpool => @vm.get_hardware_pool, > + :exclude_pool => nil, > + :exclude_host => @vm.host_id, > + :checkboxes => false, > + :on_select => "migrate_vm_select", > + :on_deselect => "migrate_vm_deselect", > + :on_hover => "load_widget_hover", > + :on_unhover => "load_widget_unhover" } %> > + > + <% form_tag do %> > + <!--[form:migrate_vm]--> > + <%= hidden_field_tag 'id', @vm.id %> > + <%= hidden_field_tag 'vm_action', VmTask::ACTION_MIGRATE_VM %> > + Selected Migration Target: > + <div id='selected_migration_target'> > + <div class="selection_left"> > + <div>No migration target selected.</div> > + </div> > + </div> > + <%= hidden_field_tag 'vm_action_data', "" %> > + <% end %> > +</div> > +<%= popup_footer("$('#migrate_vm_form').submit()", "Migrate Virtual Machine") %> > +</form> > +<script type="text/javascript"> > +$(function() { > + var vmoptions = { > + target: '<%= url_for :action => 'create' %>', // target element to update > + //beforeSubmit: showStorageRequest, // pre-submit callback > + dataType: 'json', > + success: afterVm // post-submit callback > + }; > + > + // bind form using 'ajaxForm' > + $('#migrate_vm_form').ajaxForm(vmoptions); > +}); > +</script> > + > diff --git a/wui/src/app/views/vm/show.rhtml b/wui/src/app/views/vm/show.rhtml > index 8b0a760..fe671ef 100644 > --- a/wui/src/app/views/vm/show.rhtml > +++ b/wui/src/app/views/vm/show.rhtml > @@ -17,10 +17,17 @@ > <%= link_to image_tag("icon_edit.png") + " Edit", > {:controller => 'vm', :action => 'edit', :id => @vm}, > :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> > - <% for action in @actions %> > - <a href="#" onClick="single_vm_action('<%= action[1] %>')"> > - <%= image_tag action[2] %> <%= action[0] %> > - </a> > + <% for name, action in @actions %> > + <% if action[:popup_action] -%> > + <%= link_to image_tag(action[:icon]) + action[:label], > + {:controller => 'vm', > + :action => action[:popup_action], :id => @vm}, > + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> > + <% else -%> > + <a href="#" onClick="single_vm_action('<%= name %>')"> > + <%= image_tag action[:icon] %> <%= action[:label] %> > + </a> > + <% end -%> > <% end %> > <a href="#" onClick="cancel_queued_tasks()"> > <%= image_tag "icon_x.png" %> Cancel queued tasksI had trouble applying this (manually dropped in the 4 lines), but it seems to have worked for steve, so hopefully it is just me> diff --git a/wui/src/public/javascripts/flexigrid.js b/wui/src/public/javascripts/flexigrid.js > index d7b6416..3715e6f 100644 > --- a/wui/src/public/javascripts/flexigrid.js > +++ b/wui/src/public/javascripts/flexigrid.js > @@ -719,7 +719,9 @@ > } > $(this).toggleClass('trSelected'); > if($(this).hasClass('trSelected')){ > - if (p.onSelect) p.onSelect($(t).find("tr.trSelected")); > + if (p.onSelect) p.onSelect($(t).find("tr.trSelected")); > + } else { > + if (p.onDeselect) p.onDeselect(this); > } > } > ) > diff --git a/wui/src/public/javascripts/ovirt.js b/wui/src/public/javascripts/ovirt.js > index c776458..e0aa222 100644 > --- a/wui/src/public/javascripts/ovirt.js > +++ b/wui/src/public/javascripts/ovirt.js > @@ -74,7 +74,7 @@ function ajax_validation(response, status) > if (response.object) { > $(".fieldWithErrors").removeClass("fieldWithErrors"); > $("div.errorExplanation").remove(); > - if (!response.success) { > + if (!response.success && response.errors ) { > for(i=0; i<response.errors.length; i++) { > var element = $("div.form_field:has(#"+response.object + "_" + response.errors[i][0]+")"); > if (element) {