This series of patches makes various changes needed for the API and the command line client. In detail, they do: 1/8: small bugfix 2/8: remove the client/ directory; everything there will go into a new ovirt-client git repo. I'll send out patches for that shortly 3/8 and 4/8: add additional info to the XML available through the API 5/8 and 6/8: Create a new StorageVolumeController and move handling of storage volumes out of the StorageController. Remove dead code 7/8: report an error with a human-readable error message through the API (yay) 8/8: route API calls for storage volumes The main patch of this series is 5/8, the others are all pretty trivial. David
David Lutterkort
2009-Jan-26 21:11 UTC
[Ovirt-devel] [PATCH server 1/8] Wrap generation of error message in respond_to block
--- src/app/controllers/storage_controller.rb | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/controllers/storage_controller.rb b/src/app/controllers/storage_controller.rb index e4b72f1..a3de2cd 100644 --- a/src/app/controllers/storage_controller.rb +++ b/src/app/controllers/storage_controller.rb @@ -311,10 +311,12 @@ class StorageController < ApplicationController def destroy unless @storage_pool.movable? - format.json { render :json => { :object => "storage_pool", - :success => false, - :alert => "Cannot delete storage with associated vms" } } - return + respond_to do |format| + format.json { render :json => { :object => "storage_pool", + :success => false, + :alert => "Cannot delete storage with associated vms" } } + end + return end pool = @storage_pool.hardware_pool -- 1.6.0.6
David Lutterkort
2009-Jan-26 21:11 UTC
[Ovirt-devel] [PATCH server 2/8] Remove client/ directory, it will live in its own repo
--- client/README | 17 ------- client/examples/script.rb | 101 -------------------------------------------- client/lib/ovirt.rb | 102 --------------------------------------------- 3 files changed, 0 insertions(+), 220 deletions(-) delete mode 100644 client/README delete mode 100755 client/examples/script.rb delete mode 100644 client/lib/ovirt.rb diff --git a/client/README b/client/README deleted file mode 100644 index ee1db15..0000000 --- a/client/README +++ /dev/null @@ -1,17 +0,0 @@ -This is a very simple client library for accessing the OVirt API from Ruby. - -The file examples/script.rb contains a script that shows how this is done -in some detail. - -You must have ActiveResource installed, e.g. with 'yum install -rubygem-activeresource' - -The server is specified with a URL of the form - http://USER:PASSWORD at HOST/ovirt - -This requires that the server is configured to allow HTTP authentication, -since there are no mechanisms in the API to forward krb5 tickets. - -Before calling any other method on the API, you need to call - OVirt::Base::site = "http://USER:PASSWORD at HOST/ovirt" - OVirt::Base::login diff --git a/client/examples/script.rb b/client/examples/script.rb deleted file mode 100755 index 1485535..0000000 --- a/client/examples/script.rb +++ /dev/null @@ -1,101 +0,0 @@ -#! /usr/bin/ruby - -# Sample script that shows how to use the OVirt API - -require 'pp' -require 'rubygems' -require 'activeresource' -require 'optparse' - -require 'ovirt' - -def move_random_host(hosts, pool) - host = hosts[rand(hosts.size)] - puts "Move #{host.hostname} to #{pool.name}" - pool.hosts << host - pool.save -end - -def element_path(obj) - "[#{obj.class.element_path(obj.id)}]" -end - -def print_pool(pool) - puts "\n\nPool #{pool.name}: #{pool.hosts.size} hosts, #{pool.storage_pools.size} storage pools #{element_path(pool)} " - puts "=" * 75 - pool.hosts.each do |h| - printf "%-36s %s\n", h.hostname, element_path(h) - end - pool.storage_pools.each do |sp| - type = sp.nfs? ? "NFS" : "iSCSI" - printf "%-5s %-30s %s\n", type, sp.label, element_path(sp) - end - puts "-" * 75 -end - -# Plumbing so we can find the OVirt server -# "http://ovirt.watzmann.net:3000/ovirt/rest" -PROGNAME=File::basename($0) -OVirt::Base.site = ENV["OVIRT_SERVER"] -opts = OptionParser.new("#{PROGNAME} GLOBAL_OPTS") -opts.separator(" Run some things against an OVirt server. The server is specified with") -opts.separator(" the -s option as a URL of the form http://USER:PASSWORD at SERVER/ovirt") -opts.separator("") -opts.separator "Global options:" -opts.on("-s", "--server=URL", "The OVirt server. Since there is no auth\n" + - "#{" "*37}yet, must be the mongrel server port.\n" + - "#{" "*37}Overrides env var OVIRT_SERVER") do |val| - OVirt::Base.site = val -end - -opts.order(ARGV) - -unless OVirt::Base.site - $stderr.puts <<EOF -You must specify the OVirt server to connect to, either with the ---server option or through the OVIRT_SERVER environment variable -EOF - exit 1 -end - -OVirt::Base.login - -# Get a single host by name -host = OVirt::Host.find_by_hostname("node3.priv.ovirt.org") -puts "#{host.uuid} has id #{host.id}" - -# What's in the default pool -defpool = OVirt::HardwarePool.default_pool -print_pool(defpool) - -# Create a new hardware pool -mypool = OVirt::HardwarePool.find_by_path("/default/mypool") -unless mypool - puts "Create mypool" - mypool = OVirt::HardwarePool.create( { :parent_id => defpool.id, - :name => "mypool" } ) -end - -# Move some hosts around -puts -if defpool.hosts.size > 1 - move_random_host(defpool.hosts, mypool) -elsif mypool.hosts.size > 0 - move_random_host(mypool.hosts, defpool) -end - -# Delete all storage pools for mypool and add a new one -mypool.storage_pools.each do |sp| - puts "Delete storage pool #{sp.id}" - sp.destroy -end - -storage_pool = OVirt::StoragePool.create( { :storage_type => "NFS", - :hardware_pool_id => mypool.id, - :ip_addr => "192.168.122.50", - :export_path => "/exports/pool1" } ) -puts "Created storage pool #{storage_pool.id}" - -# For some reason, mypool.reload doesn't work here -mypool = OVirt::HardwarePool.find_by_path("/default/mypool") -print_pool(mypool) diff --git a/client/lib/ovirt.rb b/client/lib/ovirt.rb deleted file mode 100644 index 15dc467..0000000 --- a/client/lib/ovirt.rb +++ /dev/null @@ -1,102 +0,0 @@ -require 'pp' -require 'rubygems' -require 'activeresource' - -class ActiveResource::Connection - attr_accessor :session - - alias_method :old_default_header, :default_header - - def default_header - old_default_header - @default_header ||= {} - if session - @default_header["Cookie"] = session - end - @default_header - end -end - -module OVirt - class Base < ActiveResource::Base - LOGIN_PATH = "login/login" - - def self.login - response = nil - begin - response = connection.get(prefix + LOGIN_PATH) - rescue ActiveResource::Redirection => e - response = e.response - end - unless connection.session = session_cookie(response) - raise "Authentication failed" - end - end - - private - def self.session_cookie(response) - if cookies = response.get_fields("Set-Cookie") - cookies.find { |cookie| - cookie.split(";")[0].split("=")[0] == "_ovirt_session_id" - } - end - end - - end - - class HardwarePool < Base - def self.find_by_path(path) - find(:first, :params => { :path => path }) - end - - def self.default_pool - find(:first, :params => { :path => "/default" }) - end - end - - class StoragePool < Base - def iscsi? - attributes["type"] == "IscsiStoragePool" - end - - def nfs? - attributes["type"] == "NfsStoragePool" - end - - def label - if iscsi? - "#{ip_addr}:#{port}:#{target}" - elsif nfs? - "#{ip_addr}:#{export_path}" - else - raise "Unknown type #{attributes["type"]}" - end - end - end - - class IscsiStoragePool < StoragePool - def initialize(attributes = {}) - super(attributes.update( "type" => "IscsiStoragePool" )) - end - end - - class NfsStoragePool < StoragePool - def initialize(attributes = {}) - super(attributes.update( "type" => "NfsStoragePool" )) - end - end - - class Host < Base - def self.find_by_uuid(uuid) - find(:first, :params => { :uuid => uuid }) - end - - def self.find_by_hostname(hostname) - find(:first, :params => { :hostname => hostname }) - end - - def hardware_pool - HardwarePool.find(hardware_pool_id) - end - end -end -- 1.6.0.6
David Lutterkort
2009-Jan-26 21:12 UTC
[Ovirt-devel] [PATCH server 3/8] Include CPU's in host information
--- src/app/controllers/hardware_controller.rb | 7 ++++++- src/app/controllers/host_controller.rb | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/app/controllers/hardware_controller.rb b/src/app/controllers/hardware_controller.rb index 5c14eec..30e80b2 100644 --- a/src/app/controllers/hardware_controller.rb +++ b/src/app/controllers/hardware_controller.rb @@ -50,7 +50,12 @@ class HardwareController < PoolController end respond_to do |format| - format.xml { render :xml => @pools.to_xml(XML_OPTS) } + format.xml { + opts = XML_OPTS.dup + opts[:include] = opts[:include].inject({}) { |m, k| m[k] = {}; m } + opts[:include][:hosts] = { :include => :cpus } + render :xml => @pools.to_xml(opts) + } end end diff --git a/src/app/controllers/host_controller.rb b/src/app/controllers/host_controller.rb index da630f7..8cf2aed 100644 --- a/src/app/controllers/host_controller.rb +++ b/src/app/controllers/host_controller.rb @@ -60,7 +60,7 @@ class HostController < ApplicationController else respond_to do |format| format.html { render :layout => 'selection' } - format.xml { render :xml => @host.to_xml } + format.xml { render :xml => @host.to_xml(:include => [ :cpus ] ) } end end end -- 1.6.0.6
David Lutterkort
2009-Jan-26 21:12 UTC
[Ovirt-devel] [PATCH server 4/8] API: include storage_volumes; indicate type for individual pools
--- src/app/controllers/storage_controller.rb | 9 +++++++-- 1 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/app/controllers/storage_controller.rb b/src/app/controllers/storage_controller.rb index a3de2cd..f7b53c5 100644 --- a/src/app/controllers/storage_controller.rb +++ b/src/app/controllers/storage_controller.rb @@ -31,7 +31,7 @@ class StorageController < ApplicationController list respond_to do |format| format.html { render :action => 'list' } - format.xml { render :xml => @storage_pools.to_xml } + format.xml { render :xml => @storage_pools.to_xml( :include => :storage_volumes) } end end @@ -79,7 +79,12 @@ class StorageController < ApplicationController else respond_to do |format| format.html { render :layout => 'selection' } - format.xml { render :xml => @storage_pool.to_xml } + format.xml { + xml_txt = @storage_pool.to_xml(:include => :storage_volumes) do |xml| + xml.type @storage_pool.class.name + end + render :xml => xml_txt + } end end end -- 1.6.0.6
David Lutterkort
2009-Jan-26 21:12 UTC
[Ovirt-devel] [PATCH server 5/8] Factor StorageVolume functionality out of StorageController
Storage volumes can now be accessed through their own controller. This is needed to expose them in the API. --- src/app/controllers/search_controller.rb | 12 +- src/app/controllers/storage_controller.rb | 203 ----------------- src/app/controllers/storage_volume_controller.rb | 228 ++++++++++++++++++++ src/app/views/storage/_list_volumes.rhtml | 2 +- src/app/views/storage/_new_volume_form.rhtml | 24 -- src/app/views/storage/new_volume.rhtml | 47 ---- src/app/views/storage/show.rhtml | 2 +- src/app/views/storage/show_volume.rhtml | 87 -------- .../views/storage_volume/_new_volume_form.rhtml | 24 ++ src/app/views/storage_volume/new.rhtml | 47 ++++ src/app/views/storage_volume/show.rhtml | 87 ++++++++ 11 files changed, 394 insertions(+), 369 deletions(-) create mode 100644 src/app/controllers/storage_volume_controller.rb delete mode 100644 src/app/views/storage/_new_volume_form.rhtml delete mode 100644 src/app/views/storage/new_volume.rhtml delete mode 100644 src/app/views/storage/show_volume.rhtml create mode 100644 src/app/views/storage_volume/_new_volume_form.rhtml create mode 100644 src/app/views/storage_volume/new.rhtml create mode 100644 src/app/views/storage_volume/show.rhtml diff --git a/src/app/controllers/search_controller.rb b/src/app/controllers/search_controller.rb index 7551242..0fb6456 100644 --- a/src/app/controllers/search_controller.rb +++ b/src/app/controllers/search_controller.rb @@ -37,14 +37,14 @@ class SearchController < ApplicationController "NfsStoragePool" => {:controller => "storage", :show_action => "show", :searched => true}, - "IscsiStorageVolume" => {:controller => "storage", - :show_action => "show_volume", + "IscsiStorageVolume" => {:controller => "storage_volume", + :show_action => "show", :searched => false}, - "NfsStorageVolume" => {:controller => "storage", - :show_action => "show_volume", + "NfsStorageVolume" => {:controller => "storage_volume", + :show_action => "show", :searched => false}, - "LvmStorageVolume" => {:controller => "storage", - :show_action => "show_volume", + "LvmStorageVolume" => {:controller => "storage_volume", + :show_action => "show", :searched => false}} MULTI_TYPE_MODELS = {"StoragePool" => ["IscsiStoragePool", "NfsStoragePool"]} diff --git a/src/app/controllers/storage_controller.rb b/src/app/controllers/storage_controller.rb index f7b53c5..9674125 100644 --- a/src/app/controllers/storage_controller.rb +++ b/src/app/controllers/storage_controller.rb @@ -24,8 +24,6 @@ class StorageController < ApplicationController before_filter :pre_pool_admin, :only => [:refresh] before_filter :pre_new2, :only => [:new2] - before_filter :pre_json, :only => [:storage_volumes_json] - before_filter :pre_create_volume, :only => [:create_volume] def index list @@ -89,35 +87,6 @@ class StorageController < ApplicationController end end - def storage_volumes_json - @storage_pool = StoragePool.find(params[:id]) - set_perms(@storage_pool.hardware_pool) - unless @can_view - flash[:notice] = 'You do not have permission to view this storage pool: redirecting to top level' - redirect_to :controller => 'dashboard' - end - attr_list = [] - attr_list << :id if (@storage_pool.user_subdividable and @can_modify) - attr_list += [:display_name, :size_in_gb, :get_type_label] - json_list(@storage_pool.storage_volumes, attr_list) - end - def show_volume - @storage_volume = StorageVolume.find(params[:id]) - set_perms(@storage_volume.storage_pool.hardware_pool) - unless @can_view - flash[:notice] = 'You do not have permission to view this storage volume: redirecting to top level' - respond_to do |format| - format.html { redirect_to :controller => 'dashboard' } - format.xml { head :forbidden } - end - else - respond_to do |format| - format.html { render :layout => 'selection' } - format.xml { render :xml => @storage_volume.to_xml } - end - end - end - def new end @@ -126,76 +95,6 @@ class StorageController < ApplicationController render :layout => false end - def new_volume - @return_facebox = params[:return_facebox] - 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' - return - end - new_volume_internal(@storage_pool, - { :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' - return - end - lvm_pool = @source_volume.lvm_storage_pool - unless lvm_pool - # FIXME: what should we do about VG/LV names? - # for now auto-create VG name as ovirt_vg_#{@source_volume.id} - new_params = { :vg_name => "ovirt_vg_#{@source_volume.id}", - :hardware_pool_id => @source_volume.storage_pool.hardware_pool_id} - lvm_pool = StoragePool.factory(StoragePool::LVM, new_params) - lvm_pool.source_volumes << @source_volume - lvm_pool.save! - end - new_volume_internal(lvm_pool, { :storage_pool_id => lvm_pool.id}) - @storage_volume.lv_owner_perms='0744' - @storage_volume.lv_group_perms='0744' - @storage_volume.lv_mode_perms='0744' - end - render :layout => 'popup' - end - - def create_volume - begin - StorageVolume.transaction do - @storage_volume.save! - @task = StorageVolumeTask.new({ :user => @user, - :task_target => @storage_volume, - :action => StorageVolumeTask::ACTION_CREATE_VOLUME, - :state => Task::STATE_QUEUED}) - @task.save! - end - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => true, - :alert => "Storage Volume was successfully created." } } - format.xml { render :xml => @storage_volume, - :status => :created, - # FIXME: create storage_volume_url method if relevant - :location => storage_pool_url(@storage_volume) - } - end - rescue => ex - # FIXME: need to distinguish volume vs. task save errors - respond_to do |format| - format.json { - json_hash = { :object => "storage_volume", :success => false, - :errors => @storage_volume.errors.localize_error_messages.to_a } - json_hash[:message] = ex.message if json_hash[:errors].empty? - render :json => json_hash } - format.xml { render :xml => @storage_volume.errors, - :status => :unprocessable_entity } - end - end - end - def insert_refresh_task @task = StorageTask.new({ :user => @user, :task_target => @storage_pool, @@ -339,67 +238,6 @@ class StorageController < ApplicationController end end - def delete_volumes - storage_volume_ids_str = params[:storage_volume_ids] - storage_volume_ids = storage_volume_ids_str.split(",").collect {|x| x.to_i} - alerts = [] - status = true - begin - StorageVolume.transaction do - storage = StorageVolume.find(:all, :conditions => "id in (#{storage_volume_ids.join(', ')})") - unless storage.empty? - set_perms(storage[0].storage_pool.hardware_pool) - unless @can_modify and storage[0].storage_pool.user_subdividable - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => false, - :alert => "You do not have permission to delete this storage volume." } } - format.xml { head :forbidden } - end - else - storage.each do |storage_volume| - alert, success = delete_volume_internal(storage_volume) - alerts << alert - status = false unless success - end - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => status, :alert => alerts.join("\n") } } - format.xml { head(status ? :ok : :method_not_allowed) } - end - end - else - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => false, :alert => "no volumes selected" } } - format.xml { head(status ? :ok : :method_not_allowed) } - end - end - end - end - end - - def delete_volume - @storage_volume = StorageVolume.find(params[:id]) - set_perms(@storage_volume.storage_pool.hardware_pool) - unless @can_modify and @storage_volume.storage_pool.user_subdividable - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => false, - :alert => "You do not have permission to delete this storage volume." } } - format.xml { head :forbidden } - end - else - alert, success = delete_volume_internal(@storage_volume) - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => success, :alert => alert } } - format.xml { head(success ? :ok : :method_not_allowed) } - end - end - - end - def pre_new @hardware_pool = HardwarePool.find(params[:hardware_pool_id]) @perm_obj = @hardware_pool @@ -430,50 +268,9 @@ class StorageController < ApplicationController @perm_obj = @storage_pool.hardware_pool @redir_obj = @storage_pool end - def pre_create_volume - volume = params[:storage_volume] - unless type = params[:storage_type] - type = volume.delete(:storage_type) - end - @storage_volume = StorageVolume.factory(type, volume) - @perm_obj = @storage_volume.storage_pool.hardware_pool - @redir_controller = @storage_volume.storage_pool.hardware_pool.get_controller - authorize_admin - end - def pre_json - pre_show - end def pre_pool_admin pre_edit authorize_admin end - private - def new_volume_internal(storage_pool, new_params) - @storage_volume = StorageVolume.factory(storage_pool.get_type_label, new_params) - @perm_obj = @storage_volume.storage_pool.hardware_pool - authorize_admin - end - - def delete_volume_internal(volume) - begin - name = volume.display_name - if !volume.vms.empty? - vm_list = volume.vms.collect {|vm| vm.description}.join(", ") - ["Storage Volume #{name} must be unattached from VMs (#{vm_list}) before deleting it.", - false] - else - volume.state=StorageVolume::STATE_PENDING_DELETION - volume.save! - @task = StorageVolumeTask.new({ :user => @user, - :task_target => volume, - :action => StorageVolumeTask::ACTION_DELETE_VOLUME, - :state => Task::STATE_QUEUED}) - @task.save! - ["Storage Volume #{name} deletion was successfully queued.", true] - end - rescue => ex - ["Failed to delete storage volume #{name} (#{ex.message}.",false] - end - end end diff --git a/src/app/controllers/storage_volume_controller.rb b/src/app/controllers/storage_volume_controller.rb new file mode 100644 index 0000000..197864a --- /dev/null +++ b/src/app/controllers/storage_volume_controller.rb @@ -0,0 +1,228 @@ +# +# Copyright (C) 2009 Red Hat, Inc. +# Written by Scott Seago <sseago 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. + +class StorageVolumeController < ApplicationController + + before_filter :pre_create, :only => [:create] + + def new + @return_facebox = params[:return_facebox] + 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' + return + end + new_volume_internal(@storage_pool, + { :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' + return + end + lvm_pool = @source_volume.lvm_storage_pool + unless lvm_pool + # FIXME: what should we do about VG/LV names? + # for now auto-create VG name as ovirt_vg_#{@source_volume.id} + new_params = { :vg_name => "ovirt_vg_#{@source_volume.id}", + :hardware_pool_id => @source_volume.storage_pool.hardware_pool_id} + lvm_pool = StoragePool.factory(StoragePool::LVM, new_params) + lvm_pool.source_volumes << @source_volume + lvm_pool.save! + end + new_volume_internal(lvm_pool, { :storage_pool_id => lvm_pool.id}) + @storage_volume.lv_owner_perms='0744' + @storage_volume.lv_group_perms='0744' + @storage_volume.lv_mode_perms='0744' + end + render :layout => 'popup' + end + + def create + begin + StorageVolume.transaction do + @storage_volume.save! + @task = StorageVolumeTask.new({ :user => @user, + :task_target => @storage_volume, + :action => StorageVolumeTask::ACTION_CREATE_VOLUME, + :state => Task::STATE_QUEUED}) + @task.save! + end + respond_to do |format| + format.json { render :json => { :object => "storage_volume", + :success => true, + :alert => "Storage Volume was successfully created." } } + format.xml { render :xml => @storage_volume, + :status => :created, + # FIXME: create storage_volume_url method if relevant + :location => storage_pool_url(@storage_volume) + } + end + rescue => ex + # FIXME: need to distinguish volume vs. task save errors + respond_to do |format| + format.json { + json_hash = { :object => "storage_volume", :success => false, + :errors => @storage_volume.errors.localize_error_messages.to_a } + json_hash[:message] = ex.message if json_hash[:errors].empty? + render :json => json_hash } + format.xml { render :xml => @storage_volume.errors, + :status => :unprocessable_entity } + end + end + end + + def show + @storage_volume = StorageVolume.find(params[:id]) + set_perms(@storage_volume.storage_pool.hardware_pool) + @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' + respond_to do |format| + format.html { redirect_to :controller => 'dashboard' } + format.json { redirect_to :controller => 'dashboard' } + format.xml { head :forbidden } + end + else + respond_to do |format| + format.html { render :layout => 'selection' } + format.json do + attr_list = [] + attr_list << :id if (@storage_pool.user_subdividable and @can_modify) + attr_list += [:display_name, :size_in_gb, :get_type_label] + json_list(@storage_pool.storage_volumes, attr_list) + end + format.xml { render :xml => @storage_volume.to_xml } + end + end + end + + def destroy + if params[:id] + delete_volume + else + delete_volumes + end + end + + def delete_volumes + storage_volume_ids_str = params[:storage_volume_ids] + storage_volume_ids = storage_volume_ids_str.split(",").collect {|x| x.to_i} + alerts = [] + status = true + begin + StorageVolume.transaction do + storage = StorageVolume.find(:all, :conditions => "id in (#{storage_volume_ids.join(', ')})") + unless storage.empty? + set_perms(storage[0].storage_pool.hardware_pool) + unless @can_modify and storage[0].storage_pool.user_subdividable + respond_to do |format| + format.json { render :json => { :object => "storage_volume", + :success => false, + :alert => "You do not have permission to delete this storage volume." } } + format.xml { head :forbidden } + end + else + storage.each do |storage_volume| + alert, success = delete_volume_internal(storage_volume) + alerts << alert + status = false unless success + end + respond_to do |format| + format.json { render :json => { :object => "storage_volume", + :success => status, :alert => alerts.join("\n") } } + format.xml { head(status ? :ok : :method_not_allowed) } + end + end + else + respond_to do |format| + format.json { render :json => { :object => "storage_volume", + :success => false, :alert => "no volumes selected" } } + format.xml { head(status ? :ok : :method_not_allowed) } + end + end + end + end + end + + def delete_volume + @storage_volume = StorageVolume.find(params[:id]) + set_perms(@storage_volume.storage_pool.hardware_pool) + unless @can_modify and @storage_volume.storage_pool.user_subdividable + respond_to do |format| + format.json { render :json => { :object => "storage_volume", + :success => false, + :alert => "You do not have permission to delete this storage volume." } } + format.xml { head :forbidden } + end + else + alert, success = delete_volume_internal(@storage_volume) + respond_to do |format| + format.json { render :json => { :object => "storage_volume", + :success => success, :alert => alert } } + format.xml { head(success ? :ok : :method_not_allowed) } + end + end + + end + + def pre_create + volume = params[:storage_volume] + unless type = params[:storage_type] + type = volume.delete(:storage_type) + end + @storage_volume = StorageVolume.factory(type, volume) + @perm_obj = @storage_volume.storage_pool.hardware_pool + @redir_controller = @storage_volume.storage_pool.hardware_pool.get_controller + authorize_admin + end + + private + def new_volume_internal(storage_pool, new_params) + @storage_volume = StorageVolume.factory(storage_pool.get_type_label, new_params) + @perm_obj = @storage_volume.storage_pool.hardware_pool + authorize_admin + end + + def delete_volume_internal(volume) + begin + name = volume.display_name + if !volume.vms.empty? + vm_list = volume.vms.collect {|vm| vm.description}.join(", ") + ["Storage Volume #{name} must be unattached from VMs (#{vm_list}) before deleting it.", + false] + else + volume.state=StorageVolume::STATE_PENDING_DELETION + volume.save! + @task = StorageVolumeTask.new({ :user => @user, + :task_target => volume, + :action => StorageVolumeTask::ACTION_DELETE_VOLUME, + :state => Task::STATE_QUEUED}) + @task.save! + ["Storage Volume #{name} deletion was successfully queued.", true] + end + rescue => ex + ["Failed to delete storage volume #{name} (#{ex.message}.",false] + end + end + +end diff --git a/src/app/views/storage/_list_volumes.rhtml b/src/app/views/storage/_list_volumes.rhtml index 212720e..ab4f623 100644 --- a/src/app/views/storage/_list_volumes.rhtml +++ b/src/app/views/storage/_list_volumes.rhtml @@ -19,7 +19,7 @@ <tbody> <% for storage_volume in type_volumes %> <tr class="<%= cycle('odd','even', :name => type_volumes) %>"> - <% vol_hash = { :controller => 'storage', :action => 'show_volume', :id => storage_volume } + <% vol_hash = { :controller => 'storage_volume', :action => 'show', :id => storage_volume } vol_hash[:vm_id] = vm_id if defined? vm_id %> <td style="text-align:left;"><%= link_to storage_volume.storage_pool.ip_addr, vol_hash, { :class => "show" } %> diff --git a/src/app/views/storage/_new_volume_form.rhtml b/src/app/views/storage/_new_volume_form.rhtml deleted file mode 100644 index ae65e18..0000000 --- a/src/app/views/storage/_new_volume_form.rhtml +++ /dev/null @@ -1,24 +0,0 @@ -<%= error_messages_for 'storage_volume' %> - -<!--[form:storage_pool]--> -<%= hidden_field 'storage_volume', 'storage_pool_id' %> -<%= hidden_field_tag 'storage_type', @storage_volume.get_type_label %> - -<%= text_field_with_label "Size (GB):", 'storage_volume', 'size_in_gb' %> - -<%if @storage_volume.get_type_label==StoragePool::LVM -%> - <%= text_field_with_label "LV Name:", 'storage_volume', 'lv_name' %> - - <%= text_field_with_label "Owner permissions:", 'storage_volume', 'lv_owner_perms' %> - - <%= text_field_with_label "Group permissions:", 'storage_volume', 'lv_group_perms' %> - - <%= text_field_with_label "Mode permissions:", 'storage_volume', 'lv_mode_perms' %> -<%- end -%> -<%= text_field_with_label "LUN:", 'storage_volume', 'lun' if @storage_volume.get_type_label==StoragePool::ISCSI %> - -<%= text_field_with_label "Filename:", 'storage_volume', 'filename' if @storage_volume.get_type_label==StoragePool::NFS %> - - -<!--[eoform:storage_volume]--> - diff --git a/src/app/views/storage/new_volume.rhtml b/src/app/views/storage/new_volume.rhtml deleted file mode 100644 index 958c463..0000000 --- a/src/app/views/storage/new_volume.rhtml +++ /dev/null @@ -1,47 +0,0 @@ -<%- content_for :title do -%> - <%= _("Add New Volume") %> -<%- end -%> -<%- content_for :description do -%> - Add a new Storage Volume to - <%= if @storage_volume.get_type_label==StoragePool::LVM - @source_volume.display_name - else - @storage_pool.display_name - end %>. -<%- end -%> -<div class="panel_header"></div> -<div class="dialog_form"> -<form method="POST" action="<%= url_for :action => 'create_volume' %>" id="storage_volume_form" > - <div class="dialog_form"> - <div id="new_storage_pool"> - <%= render :partial => 'new_volume_form' %> - </div> - </div> - <!-- FIXME: need to pop up the details dialog again --> - <%= popup_footer("$('#storage_volume_form').submit()", "New Storage Volume") %> -</form> -</div> -<script type="text/javascript"> -function afterStorageVolume(response, status){ - ajax_validation(response, status); - if (response.success) { - <% if @return_facebox %> - $('#window').fadeOut('fast'); - $("#window").empty().load("<%= @return_facebox %>") - $('#window').fadeIn('fast'); - <% else %> - $(document).trigger('close.facebox'); - <% end %> - } -} -$(function() { - var storagevolumeoptions = { - target: '<%= url_for :action => 'create_volume' %>', // target element to update - dataType: 'json', - success: afterStorageVolume // post-submit callback - }; - - // bind form using 'ajaxForm' - $('#storage_volume_form').ajaxForm(storagevolumeoptions); -}); -</script> diff --git a/src/app/views/storage/show.rhtml b/src/app/views/storage/show.rhtml index 7e02f32..dd52d79 100644 --- a/src/app/views/storage/show.rhtml +++ b/src/app/views/storage/show.rhtml @@ -12,7 +12,7 @@ </a> <%if @storage_pool.user_subdividable -%> <%= link_to image_tag("icon_addstorage.png") + " Add new Volume", - {:controller => 'storage', :action => 'new_volume', :storage_pool_id => @storage_pool.id}, + {:controller => 'storage_volume', :action => 'new', :storage_pool_id => @storage_pool.id}, :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> <% end %> <a href="#confirm_delete_storage" rel="facebox[.bolder]"> diff --git a/src/app/views/storage/show_volume.rhtml b/src/app/views/storage/show_volume.rhtml deleted file mode 100644 index f85feaa..0000000 --- a/src/app/views/storage/show_volume.rhtml +++ /dev/null @@ -1,87 +0,0 @@ -<%- content_for :title do -%> - <%=h @storage_volume.display_name %> -<%- end -%> - -<%- content_for :action_links do -%> - <%if @can_modify -%> - <%if @storage_volume.supports_lvm_subdivision and @storage_volume.vms.empty? -%> - <%= link_to image_tag("icon_addstorage.png") + " Add new Volume", - {:controller => 'storage', :action => 'new_volume', :source_volume_id => @storage_volume.id}, - :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> - <% end %> - <%if @storage_volume.deletable -%> - <a href="#confirm_delete" rel="facebox[.bolder]"> - <%= image_tag "icon_x.png" %> Delete - </a> - <%- end -%> - <%- end -%> -<%- end -%> -<%= confirmation_dialog("confirm_delete", "Are you sure?", "delete_volume()") %> - - <div class="selection_key"> - <% unless @storage_volume.storage_pool[:type] == "LvmStoragePool" %> - IP address:<br/> - <% end %> - <% if @storage_volume.storage_pool[:type] == "IscsiStoragePool" %> - Port:<br/> - Target:<br/> - <% elsif @storage_volume.storage_pool[:type] == "NfsStoragePool" %> - Export path:<br/> - <% end %> - Type:<br/> - State:<br/> - Path:<br/> - <% if @storage_volume[:type] == "IscsiStorageVolume" %> - LUN:<br/> - <% elsif @storage_volume[:type] == "NfsStorageVolume" %> - Filename:<br/> - <% elsif @storage_volume[:type] == "LvmStorageVolume" %> - Volume Group:<br/> - Logical Volume:<br/> - Permissions (owner/group/mode):<br/> - <% end %> - Size:<br/> - </div> - <div class="selection_value"> - <% unless @storage_volume.storage_pool[:type] == "LvmStoragePool" %> - <%=h @storage_volume.storage_pool.ip_addr %><br/> - <% end %> - <% if @storage_volume.storage_pool[:type] == "IscsiStoragePool" %> - <%=h @storage_volume.storage_pool.port %><br/> - <%=h @storage_volume.storage_pool[:target] %><br/> - <% elsif @storage_volume.storage_pool[:type] == "NfsStoragePool" %> - <%=h @storage_volume.storage_pool.export_path %><br/> - <% end %> - <%=h @storage_volume.storage_pool.get_type_label %><br/> - <%=h @storage_volume.state %><br/> - <%=h @storage_volume.path %><br/> - <% if @storage_volume[:type] == "IscsiStorageVolume" %> - <%=h @storage_volume.lun %><br/> - <% elsif @storage_volume[:type] == "NfsStorageVolume" %> - <%=h @storage_volume.filename %><br/> - <% elsif @storage_volume[:type] == "LvmStorageVolume" %> - <%=h @storage_volume.storage_pool.vg_name %><br/> - <%=h @storage_volume.lv_name %><br/> - <%=h @storage_volume.lv_owner_perms %>/<%=h @storage_volume.lv_group_perms %>/<%=h @storage_volume.lv_mode_perms %><br/> - <% end %> - <%=h @storage_volume.size_in_gb %> GB<br/> - </div> -<%- content_for :right do -%> - -<%- end -%> - -<script type="text/javascript"> - function delete_volume() - { - $(document).trigger('close.facebox'); - $.post('<%= url_for :controller => "storage", :action => "delete_volumes" %>', - { storage_volume_ids: <%= @storage_volume.id %> }, - function(data,status) { - // FIXME: reload tree - if (data.alert) { - $.jGrowl(data.alert); - } - empty_summary('storage_selection', 'Storage Pool or Volume'); - }, 'json'); - } -</script> diff --git a/src/app/views/storage_volume/_new_volume_form.rhtml b/src/app/views/storage_volume/_new_volume_form.rhtml new file mode 100644 index 0000000..ae65e18 --- /dev/null +++ b/src/app/views/storage_volume/_new_volume_form.rhtml @@ -0,0 +1,24 @@ +<%= error_messages_for 'storage_volume' %> + +<!--[form:storage_pool]--> +<%= hidden_field 'storage_volume', 'storage_pool_id' %> +<%= hidden_field_tag 'storage_type', @storage_volume.get_type_label %> + +<%= text_field_with_label "Size (GB):", 'storage_volume', 'size_in_gb' %> + +<%if @storage_volume.get_type_label==StoragePool::LVM -%> + <%= text_field_with_label "LV Name:", 'storage_volume', 'lv_name' %> + + <%= text_field_with_label "Owner permissions:", 'storage_volume', 'lv_owner_perms' %> + + <%= text_field_with_label "Group permissions:", 'storage_volume', 'lv_group_perms' %> + + <%= text_field_with_label "Mode permissions:", 'storage_volume', 'lv_mode_perms' %> +<%- end -%> +<%= text_field_with_label "LUN:", 'storage_volume', 'lun' if @storage_volume.get_type_label==StoragePool::ISCSI %> + +<%= text_field_with_label "Filename:", 'storage_volume', 'filename' if @storage_volume.get_type_label==StoragePool::NFS %> + + +<!--[eoform:storage_volume]--> + diff --git a/src/app/views/storage_volume/new.rhtml b/src/app/views/storage_volume/new.rhtml new file mode 100644 index 0000000..4993642 --- /dev/null +++ b/src/app/views/storage_volume/new.rhtml @@ -0,0 +1,47 @@ +<%- content_for :title do -%> + <%= _("Add New Volume") %> +<%- end -%> +<%- content_for :description do -%> + Add a new Storage Volume to + <%= if @storage_volume.get_type_label==StoragePool::LVM + @source_volume.display_name + else + @storage_pool.display_name + end %>. +<%- end -%> +<div class="panel_header"></div> +<div class="dialog_form"> +<form method="POST" action="<%= url_for :action => 'create' %>" id="storage_volume_form" > + <div class="dialog_form"> + <div id="new_storage_pool"> + <%= render :partial => 'new_volume_form' %> + </div> + </div> + <!-- FIXME: need to pop up the details dialog again --> + <%= popup_footer("$('#storage_volume_form').submit()", "New Storage Volume") %> +</form> +</div> +<script type="text/javascript"> +function afterStorageVolume(response, status){ + ajax_validation(response, status); + if (response.success) { + <% if @return_facebox %> + $('#window').fadeOut('fast'); + $("#window").empty().load("<%= @return_facebox %>") + $('#window').fadeIn('fast'); + <% else %> + $(document).trigger('close.facebox'); + <% end %> + } +} +$(function() { + var storagevolumeoptions = { + target: '<%= url_for :action => 'create' %>', // target element to update + dataType: 'json', + success: afterStorageVolume // post-submit callback + }; + + // bind form using 'ajaxForm' + $('#storage_volume_form').ajaxForm(storagevolumeoptions); +}); +</script> diff --git a/src/app/views/storage_volume/show.rhtml b/src/app/views/storage_volume/show.rhtml new file mode 100644 index 0000000..c2434aa --- /dev/null +++ b/src/app/views/storage_volume/show.rhtml @@ -0,0 +1,87 @@ +<%- content_for :title do -%> + <%=h @storage_volume.display_name %> +<%- end -%> + +<%- content_for :action_links do -%> + <%if @can_modify -%> + <%if @storage_volume.supports_lvm_subdivision and @storage_volume.vms.empty? -%> + <%= link_to image_tag("icon_addstorage.png") + " Add new Volume", + {:controller => 'storage_volume', :action => 'new', :source_volume_id => @storage_volume.id}, + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> + <% end %> + <%if @storage_volume.deletable -%> + <a href="#confirm_delete" rel="facebox[.bolder]"> + <%= image_tag "icon_x.png" %> Delete + </a> + <%- end -%> + <%- end -%> +<%- end -%> +<%= confirmation_dialog("confirm_delete", "Are you sure?", "delete_volume()") %> + + <div class="selection_key"> + <% unless @storage_volume.storage_pool[:type] == "LvmStoragePool" %> + IP address:<br/> + <% end %> + <% if @storage_volume.storage_pool[:type] == "IscsiStoragePool" %> + Port:<br/> + Target:<br/> + <% elsif @storage_volume.storage_pool[:type] == "NfsStoragePool" %> + Export path:<br/> + <% end %> + Type:<br/> + State:<br/> + Path:<br/> + <% if @storage_volume[:type] == "IscsiStorageVolume" %> + LUN:<br/> + <% elsif @storage_volume[:type] == "NfsStorageVolume" %> + Filename:<br/> + <% elsif @storage_volume[:type] == "LvmStorageVolume" %> + Volume Group:<br/> + Logical Volume:<br/> + Permissions (owner/group/mode):<br/> + <% end %> + Size:<br/> + </div> + <div class="selection_value"> + <% unless @storage_volume.storage_pool[:type] == "LvmStoragePool" %> + <%=h @storage_volume.storage_pool.ip_addr %><br/> + <% end %> + <% if @storage_volume.storage_pool[:type] == "IscsiStoragePool" %> + <%=h @storage_volume.storage_pool.port %><br/> + <%=h @storage_volume.storage_pool[:target] %><br/> + <% elsif @storage_volume.storage_pool[:type] == "NfsStoragePool" %> + <%=h @storage_volume.storage_pool.export_path %><br/> + <% end %> + <%=h @storage_volume.storage_pool.get_type_label %><br/> + <%=h @storage_volume.state %><br/> + <%=h @storage_volume.path %><br/> + <% if @storage_volume[:type] == "IscsiStorageVolume" %> + <%=h @storage_volume.lun %><br/> + <% elsif @storage_volume[:type] == "NfsStorageVolume" %> + <%=h @storage_volume.filename %><br/> + <% elsif @storage_volume[:type] == "LvmStorageVolume" %> + <%=h @storage_volume.storage_pool.vg_name %><br/> + <%=h @storage_volume.lv_name %><br/> + <%=h @storage_volume.lv_owner_perms %>/<%=h @storage_volume.lv_group_perms %>/<%=h @storage_volume.lv_mode_perms %><br/> + <% end %> + <%=h @storage_volume.size_in_gb %> GB<br/> + </div> +<%- content_for :right do -%> + +<%- end -%> + +<script type="text/javascript"> + function delete_volume() + { + $(document).trigger('close.facebox'); + $.post('<%= url_for :controller => "storage_volume", :action => "destroy" %>', + { storage_volume_ids: <%= @storage_volume.id %> }, + function(data,status) { + // FIXME: reload tree + if (data.alert) { + $.jGrowl(data.alert); + } + empty_summary('storage_selection', 'Storage Pool or Volume'); + }, 'json'); + } +</script> -- 1.6.0.6
David Lutterkort
2009-Jan-26 21:12 UTC
[Ovirt-devel] [PATCH server 6/8] Remove delete_volumes, since it was not used
--- src/app/controllers/storage_volume_controller.rb | 49 ---------------------- src/app/views/storage_volume/show.rhtml | 2 +- 2 files changed, 1 insertions(+), 50 deletions(-) diff --git a/src/app/controllers/storage_volume_controller.rb b/src/app/controllers/storage_volume_controller.rb index 197864a..93eb68c 100644 --- a/src/app/controllers/storage_volume_controller.rb +++ b/src/app/controllers/storage_volume_controller.rb @@ -117,54 +117,6 @@ class StorageVolumeController < ApplicationController end def destroy - if params[:id] - delete_volume - else - delete_volumes - end - end - - def delete_volumes - storage_volume_ids_str = params[:storage_volume_ids] - storage_volume_ids = storage_volume_ids_str.split(",").collect {|x| x.to_i} - alerts = [] - status = true - begin - StorageVolume.transaction do - storage = StorageVolume.find(:all, :conditions => "id in (#{storage_volume_ids.join(', ')})") - unless storage.empty? - set_perms(storage[0].storage_pool.hardware_pool) - unless @can_modify and storage[0].storage_pool.user_subdividable - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => false, - :alert => "You do not have permission to delete this storage volume." } } - format.xml { head :forbidden } - end - else - storage.each do |storage_volume| - alert, success = delete_volume_internal(storage_volume) - alerts << alert - status = false unless success - end - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => status, :alert => alerts.join("\n") } } - format.xml { head(status ? :ok : :method_not_allowed) } - end - end - else - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => false, :alert => "no volumes selected" } } - format.xml { head(status ? :ok : :method_not_allowed) } - end - end - end - end - end - - def delete_volume @storage_volume = StorageVolume.find(params[:id]) set_perms(@storage_volume.storage_pool.hardware_pool) unless @can_modify and @storage_volume.storage_pool.user_subdividable @@ -182,7 +134,6 @@ class StorageVolumeController < ApplicationController format.xml { head(success ? :ok : :method_not_allowed) } end end - end def pre_create diff --git a/src/app/views/storage_volume/show.rhtml b/src/app/views/storage_volume/show.rhtml index c2434aa..3963a3c 100644 --- a/src/app/views/storage_volume/show.rhtml +++ b/src/app/views/storage_volume/show.rhtml @@ -75,7 +75,7 @@ { $(document).trigger('close.facebox'); $.post('<%= url_for :controller => "storage_volume", :action => "destroy" %>', - { storage_volume_ids: <%= @storage_volume.id %> }, + { id: <%= @storage_volume.id %> }, function(data,status) { // FIXME: reload tree if (data.alert) { -- 1.6.0.6
David Lutterkort
2009-Jan-26 21:12 UTC
[Ovirt-devel] [PATCH server 7/8] Produce a human-readable error when deleting a pool fails
--- src/app/controllers/storage_controller.rb | 6 ++++-- src/app/views/errors/simple.xml.builder | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 src/app/views/errors/simple.xml.builder diff --git a/src/app/controllers/storage_controller.rb b/src/app/controllers/storage_controller.rb index 9674125..721cfc0 100644 --- a/src/app/controllers/storage_controller.rb +++ b/src/app/controllers/storage_controller.rb @@ -215,10 +215,12 @@ class StorageController < ApplicationController def destroy unless @storage_pool.movable? + @error = "Cannot delete storage with associated vms" respond_to do |format| format.json { render :json => { :object => "storage_pool", - :success => false, - :alert => "Cannot delete storage with associated vms" } } + :success => false, :alert => @error } } + format.xml { render :template => "errors/simple", :layout => false, + :status => :forbidden } end return end diff --git a/src/app/views/errors/simple.xml.builder b/src/app/views/errors/simple.xml.builder new file mode 100644 index 0000000..d8d3c5b --- /dev/null +++ b/src/app/views/errors/simple.xml.builder @@ -0,0 +1,3 @@ +xml.instruct! + +xml.error @error -- 1.6.0.6
David Lutterkort
2009-Jan-26 21:12 UTC
[Ovirt-devel] [PATCH server 8/8] REST route for storage volumes
--- src/config/routes.rb | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/src/config/routes.rb b/src/config/routes.rb index 132072e..168ded5 100644 --- a/src/config/routes.rb +++ b/src/config/routes.rb @@ -53,6 +53,7 @@ ActionController::Routing::Routes.draw do |map| # REST work out of the box, and use these as the default routes map.resources :hosts, :controller => 'host' map.resources :storage_pools, :controller => 'storage' + map.resources :storage_volumes, :controller => 'storage_volume' map.resources :hardware_pools, :controller => 'hardware' do |hardware_pools| hardware_pools.resources :hosts, :controller => 'host' hardware_pools.resources :storage_pools, :controller => 'storage' -- 1.6.0.6
This is a rebase of the patches onto the current HEAD of next
David Lutterkort
2009-Feb-02 20:35 UTC
[Ovirt-devel] [PATCH server 1/8] Wrap generation of error message in respond_to block
--- src/app/controllers/storage_controller.rb | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/controllers/storage_controller.rb b/src/app/controllers/storage_controller.rb index 1920e27..160d22e 100644 --- a/src/app/controllers/storage_controller.rb +++ b/src/app/controllers/storage_controller.rb @@ -302,10 +302,12 @@ class StorageController < ApplicationController def destroy unless @storage_pool.movable? - format.json { render :json => { :object => "storage_pool", - :success => false, - :alert => "Cannot delete storage with associated vms" } } - return + respond_to do |format| + format.json { render :json => { :object => "storage_pool", + :success => false, + :alert => "Cannot delete storage with associated vms" } } + end + return end pool = @storage_pool.hardware_pool -- 1.6.0.6
David Lutterkort
2009-Feb-02 20:35 UTC
[Ovirt-devel] [PATCH server 2/8] Remove client/ directory, it will live in its own repo
--- client/README | 17 ------- client/examples/script.rb | 101 -------------------------------------------- client/lib/ovirt.rb | 102 --------------------------------------------- 3 files changed, 0 insertions(+), 220 deletions(-) delete mode 100644 client/README delete mode 100755 client/examples/script.rb delete mode 100644 client/lib/ovirt.rb diff --git a/client/README b/client/README deleted file mode 100644 index ee1db15..0000000 --- a/client/README +++ /dev/null @@ -1,17 +0,0 @@ -This is a very simple client library for accessing the OVirt API from Ruby. - -The file examples/script.rb contains a script that shows how this is done -in some detail. - -You must have ActiveResource installed, e.g. with 'yum install -rubygem-activeresource' - -The server is specified with a URL of the form - http://USER:PASSWORD at HOST/ovirt - -This requires that the server is configured to allow HTTP authentication, -since there are no mechanisms in the API to forward krb5 tickets. - -Before calling any other method on the API, you need to call - OVirt::Base::site = "http://USER:PASSWORD at HOST/ovirt" - OVirt::Base::login diff --git a/client/examples/script.rb b/client/examples/script.rb deleted file mode 100755 index 1485535..0000000 --- a/client/examples/script.rb +++ /dev/null @@ -1,101 +0,0 @@ -#! /usr/bin/ruby - -# Sample script that shows how to use the OVirt API - -require 'pp' -require 'rubygems' -require 'activeresource' -require 'optparse' - -require 'ovirt' - -def move_random_host(hosts, pool) - host = hosts[rand(hosts.size)] - puts "Move #{host.hostname} to #{pool.name}" - pool.hosts << host - pool.save -end - -def element_path(obj) - "[#{obj.class.element_path(obj.id)}]" -end - -def print_pool(pool) - puts "\n\nPool #{pool.name}: #{pool.hosts.size} hosts, #{pool.storage_pools.size} storage pools #{element_path(pool)} " - puts "=" * 75 - pool.hosts.each do |h| - printf "%-36s %s\n", h.hostname, element_path(h) - end - pool.storage_pools.each do |sp| - type = sp.nfs? ? "NFS" : "iSCSI" - printf "%-5s %-30s %s\n", type, sp.label, element_path(sp) - end - puts "-" * 75 -end - -# Plumbing so we can find the OVirt server -# "http://ovirt.watzmann.net:3000/ovirt/rest" -PROGNAME=File::basename($0) -OVirt::Base.site = ENV["OVIRT_SERVER"] -opts = OptionParser.new("#{PROGNAME} GLOBAL_OPTS") -opts.separator(" Run some things against an OVirt server. The server is specified with") -opts.separator(" the -s option as a URL of the form http://USER:PASSWORD at SERVER/ovirt") -opts.separator("") -opts.separator "Global options:" -opts.on("-s", "--server=URL", "The OVirt server. Since there is no auth\n" + - "#{" "*37}yet, must be the mongrel server port.\n" + - "#{" "*37}Overrides env var OVIRT_SERVER") do |val| - OVirt::Base.site = val -end - -opts.order(ARGV) - -unless OVirt::Base.site - $stderr.puts <<EOF -You must specify the OVirt server to connect to, either with the ---server option or through the OVIRT_SERVER environment variable -EOF - exit 1 -end - -OVirt::Base.login - -# Get a single host by name -host = OVirt::Host.find_by_hostname("node3.priv.ovirt.org") -puts "#{host.uuid} has id #{host.id}" - -# What's in the default pool -defpool = OVirt::HardwarePool.default_pool -print_pool(defpool) - -# Create a new hardware pool -mypool = OVirt::HardwarePool.find_by_path("/default/mypool") -unless mypool - puts "Create mypool" - mypool = OVirt::HardwarePool.create( { :parent_id => defpool.id, - :name => "mypool" } ) -end - -# Move some hosts around -puts -if defpool.hosts.size > 1 - move_random_host(defpool.hosts, mypool) -elsif mypool.hosts.size > 0 - move_random_host(mypool.hosts, defpool) -end - -# Delete all storage pools for mypool and add a new one -mypool.storage_pools.each do |sp| - puts "Delete storage pool #{sp.id}" - sp.destroy -end - -storage_pool = OVirt::StoragePool.create( { :storage_type => "NFS", - :hardware_pool_id => mypool.id, - :ip_addr => "192.168.122.50", - :export_path => "/exports/pool1" } ) -puts "Created storage pool #{storage_pool.id}" - -# For some reason, mypool.reload doesn't work here -mypool = OVirt::HardwarePool.find_by_path("/default/mypool") -print_pool(mypool) diff --git a/client/lib/ovirt.rb b/client/lib/ovirt.rb deleted file mode 100644 index 15dc467..0000000 --- a/client/lib/ovirt.rb +++ /dev/null @@ -1,102 +0,0 @@ -require 'pp' -require 'rubygems' -require 'activeresource' - -class ActiveResource::Connection - attr_accessor :session - - alias_method :old_default_header, :default_header - - def default_header - old_default_header - @default_header ||= {} - if session - @default_header["Cookie"] = session - end - @default_header - end -end - -module OVirt - class Base < ActiveResource::Base - LOGIN_PATH = "login/login" - - def self.login - response = nil - begin - response = connection.get(prefix + LOGIN_PATH) - rescue ActiveResource::Redirection => e - response = e.response - end - unless connection.session = session_cookie(response) - raise "Authentication failed" - end - end - - private - def self.session_cookie(response) - if cookies = response.get_fields("Set-Cookie") - cookies.find { |cookie| - cookie.split(";")[0].split("=")[0] == "_ovirt_session_id" - } - end - end - - end - - class HardwarePool < Base - def self.find_by_path(path) - find(:first, :params => { :path => path }) - end - - def self.default_pool - find(:first, :params => { :path => "/default" }) - end - end - - class StoragePool < Base - def iscsi? - attributes["type"] == "IscsiStoragePool" - end - - def nfs? - attributes["type"] == "NfsStoragePool" - end - - def label - if iscsi? - "#{ip_addr}:#{port}:#{target}" - elsif nfs? - "#{ip_addr}:#{export_path}" - else - raise "Unknown type #{attributes["type"]}" - end - end - end - - class IscsiStoragePool < StoragePool - def initialize(attributes = {}) - super(attributes.update( "type" => "IscsiStoragePool" )) - end - end - - class NfsStoragePool < StoragePool - def initialize(attributes = {}) - super(attributes.update( "type" => "NfsStoragePool" )) - end - end - - class Host < Base - def self.find_by_uuid(uuid) - find(:first, :params => { :uuid => uuid }) - end - - def self.find_by_hostname(hostname) - find(:first, :params => { :hostname => hostname }) - end - - def hardware_pool - HardwarePool.find(hardware_pool_id) - end - end -end -- 1.6.0.6
David Lutterkort
2009-Feb-02 20:35 UTC
[Ovirt-devel] [PATCH server 3/8] Include CPU's in host information
--- src/app/controllers/hardware_controller.rb | 7 ++++++- src/app/controllers/host_controller.rb | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/app/controllers/hardware_controller.rb b/src/app/controllers/hardware_controller.rb index fc16a27..0f9cceb 100644 --- a/src/app/controllers/hardware_controller.rb +++ b/src/app/controllers/hardware_controller.rb @@ -51,7 +51,12 @@ class HardwareController < PoolController end respond_to do |format| - format.xml { render :xml => @pools.to_xml(XML_OPTS) } + format.xml { + opts = XML_OPTS.dup + opts[:include] = opts[:include].inject({}) { |m, k| m[k] = {}; m } + opts[:include][:hosts] = { :include => :cpus } + render :xml => @pools.to_xml(opts) + } end end diff --git a/src/app/controllers/host_controller.rb b/src/app/controllers/host_controller.rb index 02ad8c9..f0b8c2b 100644 --- a/src/app/controllers/host_controller.rb +++ b/src/app/controllers/host_controller.rb @@ -61,7 +61,7 @@ class HostController < ApplicationController else respond_to do |format| format.html { render :layout => 'selection' } - format.xml { render :xml => @host.to_xml } + format.xml { render :xml => @host.to_xml(:include => [ :cpus ] ) } end end end -- 1.6.0.6
David Lutterkort
2009-Feb-02 20:35 UTC
[Ovirt-devel] [PATCH server 4/8] API: include storage_volumes; indicate type for individual pools
--- src/app/controllers/storage_controller.rb | 9 +++++++-- 1 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/app/controllers/storage_controller.rb b/src/app/controllers/storage_controller.rb index 160d22e..ee9f116 100644 --- a/src/app/controllers/storage_controller.rb +++ b/src/app/controllers/storage_controller.rb @@ -32,7 +32,7 @@ class StorageController < ApplicationController list respond_to do |format| format.html { render :action => 'list' } - format.xml { render :xml => @storage_pools.to_xml } + format.xml { render :xml => @storage_pools.to_xml( :include => :storage_volumes) } end end @@ -80,7 +80,12 @@ class StorageController < ApplicationController else respond_to do |format| format.html { render :layout => 'selection' } - format.xml { render :xml => @storage_pool.to_xml } + format.xml { + xml_txt = @storage_pool.to_xml(:include => :storage_volumes) do |xml| + xml.type @storage_pool.class.name + end + render :xml => xml_txt + } end end end -- 1.6.0.6
David Lutterkort
2009-Feb-02 20:35 UTC
[Ovirt-devel] [PATCH server 5/8] Factor StorageVolume functionality out of StorageController
Storage volumes can now be accessed through their own controller. This is needed to expose them in the API. --- src/app/controllers/search_controller.rb | 12 +- src/app/controllers/storage_controller.rb | 204 ----------------- src/app/controllers/storage_volume_controller.rb | 228 ++++++++++++++++++++ src/app/views/storage/_list_volumes.rhtml | 2 +- src/app/views/storage/_new_volume_form.rhtml | 24 -- src/app/views/storage/new_volume.rhtml | 53 ----- src/app/views/storage/show.rhtml | 2 +- src/app/views/storage/show_volume.rhtml | 87 -------- .../views/storage_volume/_new_volume_form.rhtml | 24 ++ src/app/views/storage_volume/new.rhtml | 53 +++++ src/app/views/storage_volume/show.rhtml | 87 ++++++++ 11 files changed, 400 insertions(+), 376 deletions(-) create mode 100644 src/app/controllers/storage_volume_controller.rb delete mode 100644 src/app/views/storage/_new_volume_form.rhtml delete mode 100644 src/app/views/storage/new_volume.rhtml delete mode 100644 src/app/views/storage/show_volume.rhtml create mode 100644 src/app/views/storage_volume/_new_volume_form.rhtml create mode 100644 src/app/views/storage_volume/new.rhtml create mode 100644 src/app/views/storage_volume/show.rhtml diff --git a/src/app/controllers/search_controller.rb b/src/app/controllers/search_controller.rb index 7551242..0fb6456 100644 --- a/src/app/controllers/search_controller.rb +++ b/src/app/controllers/search_controller.rb @@ -37,14 +37,14 @@ class SearchController < ApplicationController "NfsStoragePool" => {:controller => "storage", :show_action => "show", :searched => true}, - "IscsiStorageVolume" => {:controller => "storage", - :show_action => "show_volume", + "IscsiStorageVolume" => {:controller => "storage_volume", + :show_action => "show", :searched => false}, - "NfsStorageVolume" => {:controller => "storage", - :show_action => "show_volume", + "NfsStorageVolume" => {:controller => "storage_volume", + :show_action => "show", :searched => false}, - "LvmStorageVolume" => {:controller => "storage", - :show_action => "show_volume", + "LvmStorageVolume" => {:controller => "storage_volume", + :show_action => "show", :searched => false}} MULTI_TYPE_MODELS = {"StoragePool" => ["IscsiStoragePool", "NfsStoragePool"]} diff --git a/src/app/controllers/storage_controller.rb b/src/app/controllers/storage_controller.rb index ee9f116..3579967 100644 --- a/src/app/controllers/storage_controller.rb +++ b/src/app/controllers/storage_controller.rb @@ -24,8 +24,6 @@ class StorageController < ApplicationController before_filter :pre_pool_admin, :only => [:refresh] before_filter :pre_new2, :only => [:new2] - before_filter :pre_json, :only => [:storage_volumes_json] - before_filter :pre_create_volume, :only => [:create_volume] before_filter :pre_add, :only => [:add, :addstorage] def index @@ -90,35 +88,6 @@ class StorageController < ApplicationController end end - def storage_volumes_json - @storage_pool = StoragePool.find(params[:id]) - set_perms(@storage_pool.hardware_pool) - unless @can_view - flash[:notice] = 'You do not have permission to view this storage pool: redirecting to top level' - redirect_to :controller => 'dashboard' - end - attr_list = [] - attr_list << :id if (@storage_pool.user_subdividable and @can_modify) - attr_list += [:display_name, :size_in_gb, :get_type_label] - json_list(@storage_pool.storage_volumes, attr_list) - end - def show_volume - @storage_volume = StorageVolume.find(params[:id]) - set_perms(@storage_volume.storage_pool.hardware_pool) - unless @can_view - flash[:notice] = 'You do not have permission to view this storage volume: redirecting to top level' - respond_to do |format| - format.html { redirect_to :controller => 'dashboard' } - format.xml { head :forbidden } - end - else - respond_to do |format| - format.html { render :layout => 'selection' } - format.xml { render :xml => @storage_volume.to_xml } - end - end - end - def new end @@ -127,78 +96,6 @@ class StorageController < ApplicationController render :layout => false end - def new_volume - @return_to_workflow = params[:return_to_workflow] - @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' - return - end - new_volume_internal(@storage_pool, - { :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' - return - end - lvm_pool = @source_volume.lvm_storage_pool - unless lvm_pool - # FIXME: what should we do about VG/LV names? - # for now auto-create VG name as ovirt_vg_#{@source_volume.id} - new_params = { :vg_name => "ovirt_vg_#{@source_volume.id}", - :hardware_pool_id => @source_volume.storage_pool.hardware_pool_id} - lvm_pool = StoragePool.factory(StoragePool::LVM, new_params) - lvm_pool.source_volumes << @source_volume - lvm_pool.save! - end - new_volume_internal(lvm_pool, { :storage_pool_id => lvm_pool.id}) - @storage_volume.lv_owner_perms='0744' - @storage_volume.lv_group_perms='0744' - @storage_volume.lv_mode_perms='0744' - end - render :layout => 'popup' - end - - def create_volume - begin - StorageVolume.transaction do - @storage_volume.save! - @task = StorageVolumeTask.new({ :user => @user, - :task_target => @storage_volume, - :action => StorageVolumeTask::ACTION_CREATE_VOLUME, - :state => Task::STATE_QUEUED}) - @task.save! - end - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => true, - :alert => "Storage Volume was successfully created.", - :new_volume => @storage_volume.storage_tree_element({:filter_unavailable => false, :state => 'new'})} } - format.xml { render :xml => @storage_volume, - :status => :created, - # FIXME: create storage_volume_url method if relevant - :location => storage_pool_url(@storage_volume) - } - end - rescue => ex - # FIXME: need to distinguish volume vs. task save errors - respond_to do |format| - format.json { - json_hash = { :object => "storage_volume", :success => false, - :errors => @storage_volume.errors.localize_error_messages.to_a } - json_hash[:message] = ex.message if json_hash[:errors].empty? - render :json => json_hash } - format.xml { render :xml => @storage_volume.errors, - :status => :unprocessable_entity } - end - end - end - def insert_refresh_task @task = StorageTask.new({ :user => @user, :task_target => @storage_pool, @@ -330,67 +227,6 @@ class StorageController < ApplicationController end end - def delete_volumes - storage_volume_ids_str = params[:storage_volume_ids] - storage_volume_ids = storage_volume_ids_str.split(",").collect {|x| x.to_i} - alerts = [] - status = true - begin - StorageVolume.transaction do - storage = StorageVolume.find(:all, :conditions => "id in (#{storage_volume_ids.join(', ')})") - unless storage.empty? - set_perms(storage[0].storage_pool.hardware_pool) - unless @can_modify and storage[0].storage_pool.user_subdividable - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => false, - :alert => "You do not have permission to delete this storage volume." } } - format.xml { head :forbidden } - end - else - storage.each do |storage_volume| - alert, success = delete_volume_internal(storage_volume) - alerts << alert - status = false unless success - end - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => status, :alert => alerts.join("\n") } } - format.xml { head(status ? :ok : :method_not_allowed) } - end - end - else - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => false, :alert => "no volumes selected" } } - format.xml { head(status ? :ok : :method_not_allowed) } - end - end - end - end - end - - def delete_volume - @storage_volume = StorageVolume.find(params[:id]) - set_perms(@storage_volume.storage_pool.hardware_pool) - unless @can_modify and @storage_volume.storage_pool.user_subdividable - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => false, - :alert => "You do not have permission to delete this storage volume." } } - format.xml { head :forbidden } - end - else - alert, success = delete_volume_internal(@storage_volume) - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => success, :alert => alert } } - format.xml { head(success ? :ok : :method_not_allowed) } - end - end - - end - def pre_new @hardware_pool = HardwarePool.find(params[:hardware_pool_id]) @perm_obj = @hardware_pool @@ -424,49 +260,9 @@ class StorageController < ApplicationController @storage_pool = StoragePool.find(params[:id]) @perm_obj = @storage_pool.hardware_pool end - def pre_create_volume - volume = params[:storage_volume] - unless type = params[:storage_type] - type = volume.delete(:storage_type) - end - @storage_volume = StorageVolume.factory(type, volume) - @perm_obj = @storage_volume.storage_pool.hardware_pool - authorize_admin - end - def pre_json - pre_show - end def pre_pool_admin pre_edit authorize_admin end - private - def new_volume_internal(storage_pool, new_params) - @storage_volume = StorageVolume.factory(storage_pool.get_type_label, new_params) - @perm_obj = @storage_volume.storage_pool.hardware_pool - authorize_admin - end - - def delete_volume_internal(volume) - begin - name = volume.display_name - if !volume.vms.empty? - vm_list = volume.vms.collect {|vm| vm.description}.join(", ") - ["Storage Volume #{name} must be unattached from VMs (#{vm_list}) before deleting it.", - false] - else - volume.state=StorageVolume::STATE_PENDING_DELETION - volume.save! - @task = StorageVolumeTask.new({ :user => @user, - :task_target => volume, - :action => StorageVolumeTask::ACTION_DELETE_VOLUME, - :state => Task::STATE_QUEUED}) - @task.save! - ["Storage Volume #{name} deletion was successfully queued.", true] - end - rescue => ex - ["Failed to delete storage volume #{name} (#{ex.message}.",false] - end - end end diff --git a/src/app/controllers/storage_volume_controller.rb b/src/app/controllers/storage_volume_controller.rb new file mode 100644 index 0000000..ba486d2 --- /dev/null +++ b/src/app/controllers/storage_volume_controller.rb @@ -0,0 +1,228 @@ +# +# Copyright (C) 2009 Red Hat, Inc. +# Written by Scott Seago <sseago 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. + +class StorageVolumeController < ApplicationController + + before_filter :pre_create, :only => [:create] + + 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' + return + end + new_volume_internal(@storage_pool, + { :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' + return + end + lvm_pool = @source_volume.lvm_storage_pool + unless lvm_pool + # FIXME: what should we do about VG/LV names? + # for now auto-create VG name as ovirt_vg_#{@source_volume.id} + new_params = { :vg_name => "ovirt_vg_#{@source_volume.id}", + :hardware_pool_id => @source_volume.storage_pool.hardware_pool_id} + lvm_pool = StoragePool.factory(StoragePool::LVM, new_params) + lvm_pool.source_volumes << @source_volume + lvm_pool.save! + end + new_volume_internal(lvm_pool, { :storage_pool_id => lvm_pool.id}) + @storage_volume.lv_owner_perms='0744' + @storage_volume.lv_group_perms='0744' + @storage_volume.lv_mode_perms='0744' + end + render :layout => 'popup' + end + + def create + begin + StorageVolume.transaction do + @storage_volume.save! + @task = StorageVolumeTask.new({ :user => @user, + :task_target => @storage_volume, + :action => StorageVolumeTask::ACTION_CREATE_VOLUME, + :state => Task::STATE_QUEUED}) + @task.save! + end + respond_to do |format| + format.json { render :json => { :object => "storage_volume", + :success => true, + :alert => "Storage Volume was successfully created." , + :new_volume => @storage_volume.storage_tree_element({:filter_unavailable => false, :state => 'new'})} } + format.xml { render :xml => @storage_volume, + :status => :created, + # FIXME: create storage_volume_url method if relevant + :location => storage_pool_url(@storage_volume) + } + end + rescue => ex + # FIXME: need to distinguish volume vs. task save errors + respond_to do |format| + format.json { + json_hash = { :object => "storage_volume", :success => false, + :errors => @storage_volume.errors.localize_error_messages.to_a } + json_hash[:message] = ex.message if json_hash[:errors].empty? + render :json => json_hash } + format.xml { render :xml => @storage_volume.errors, + :status => :unprocessable_entity } + end + end + end + + def show + @storage_volume = StorageVolume.find(params[:id]) + set_perms(@storage_volume.storage_pool.hardware_pool) + @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' + respond_to do |format| + format.html { redirect_to :controller => 'dashboard' } + format.json { redirect_to :controller => 'dashboard' } + format.xml { head :forbidden } + end + else + respond_to do |format| + format.html { render :layout => 'selection' } + format.json do + attr_list = [] + attr_list << :id if (@storage_pool.user_subdividable and @can_modify) + attr_list += [:display_name, :size_in_gb, :get_type_label] + json_list(@storage_pool.storage_volumes, attr_list) + end + format.xml { render :xml => @storage_volume.to_xml } + end + end + end + + def destroy + if params[:id] + delete_volume + else + delete_volumes + end + end + + def delete_volumes + storage_volume_ids_str = params[:storage_volume_ids] + storage_volume_ids = storage_volume_ids_str.split(",").collect {|x| x.to_i} + alerts = [] + status = true + begin + StorageVolume.transaction do + storage = StorageVolume.find(:all, :conditions => "id in (#{storage_volume_ids.join(', ')})") + unless storage.empty? + set_perms(storage[0].storage_pool.hardware_pool) + unless @can_modify and storage[0].storage_pool.user_subdividable + respond_to do |format| + format.json { render :json => { :object => "storage_volume", + :success => false, + :alert => "You do not have permission to delete this storage volume." } } + format.xml { head :forbidden } + end + else + storage.each do |storage_volume| + alert, success = delete_volume_internal(storage_volume) + alerts << alert + status = false unless success + end + respond_to do |format| + format.json { render :json => { :object => "storage_volume", + :success => status, :alert => alerts.join("\n") } } + format.xml { head(status ? :ok : :method_not_allowed) } + end + end + else + respond_to do |format| + format.json { render :json => { :object => "storage_volume", + :success => false, :alert => "no volumes selected" } } + format.xml { head(status ? :ok : :method_not_allowed) } + end + end + end + end + end + + def delete_volume + @storage_volume = StorageVolume.find(params[:id]) + set_perms(@storage_volume.storage_pool.hardware_pool) + unless @can_modify and @storage_volume.storage_pool.user_subdividable + respond_to do |format| + format.json { render :json => { :object => "storage_volume", + :success => false, + :alert => "You do not have permission to delete this storage volume." } } + format.xml { head :forbidden } + end + else + alert, success = delete_volume_internal(@storage_volume) + respond_to do |format| + format.json { render :json => { :object => "storage_volume", + :success => success, :alert => alert } } + format.xml { head(success ? :ok : :method_not_allowed) } + end + end + + end + + def pre_create + volume = params[:storage_volume] + unless type = params[:storage_type] + type = volume.delete(:storage_type) + end + @storage_volume = StorageVolume.factory(type, volume) + @perm_obj = @storage_volume.storage_pool.hardware_pool + authorize_admin + end + + private + def new_volume_internal(storage_pool, new_params) + @storage_volume = StorageVolume.factory(storage_pool.get_type_label, new_params) + @perm_obj = @storage_volume.storage_pool.hardware_pool + authorize_admin + end + + def delete_volume_internal(volume) + begin + name = volume.display_name + if !volume.vms.empty? + vm_list = volume.vms.collect {|vm| vm.description}.join(", ") + ["Storage Volume #{name} must be unattached from VMs (#{vm_list}) before deleting it.", + false] + else + volume.state=StorageVolume::STATE_PENDING_DELETION + volume.save! + @task = StorageVolumeTask.new({ :user => @user, + :task_target => volume, + :action => StorageVolumeTask::ACTION_DELETE_VOLUME, + :state => Task::STATE_QUEUED}) + @task.save! + ["Storage Volume #{name} deletion was successfully queued.", true] + end + rescue => ex + ["Failed to delete storage volume #{name} (#{ex.message}.",false] + end + end + +end diff --git a/src/app/views/storage/_list_volumes.rhtml b/src/app/views/storage/_list_volumes.rhtml index 212720e..ab4f623 100644 --- a/src/app/views/storage/_list_volumes.rhtml +++ b/src/app/views/storage/_list_volumes.rhtml @@ -19,7 +19,7 @@ <tbody> <% for storage_volume in type_volumes %> <tr class="<%= cycle('odd','even', :name => type_volumes) %>"> - <% vol_hash = { :controller => 'storage', :action => 'show_volume', :id => storage_volume } + <% vol_hash = { :controller => 'storage_volume', :action => 'show', :id => storage_volume } vol_hash[:vm_id] = vm_id if defined? vm_id %> <td style="text-align:left;"><%= link_to storage_volume.storage_pool.ip_addr, vol_hash, { :class => "show" } %> diff --git a/src/app/views/storage/_new_volume_form.rhtml b/src/app/views/storage/_new_volume_form.rhtml deleted file mode 100644 index ae65e18..0000000 --- a/src/app/views/storage/_new_volume_form.rhtml +++ /dev/null @@ -1,24 +0,0 @@ -<%= error_messages_for 'storage_volume' %> - -<!--[form:storage_pool]--> -<%= hidden_field 'storage_volume', 'storage_pool_id' %> -<%= hidden_field_tag 'storage_type', @storage_volume.get_type_label %> - -<%= text_field_with_label "Size (GB):", 'storage_volume', 'size_in_gb' %> - -<%if @storage_volume.get_type_label==StoragePool::LVM -%> - <%= text_field_with_label "LV Name:", 'storage_volume', 'lv_name' %> - - <%= text_field_with_label "Owner permissions:", 'storage_volume', 'lv_owner_perms' %> - - <%= text_field_with_label "Group permissions:", 'storage_volume', 'lv_group_perms' %> - - <%= text_field_with_label "Mode permissions:", 'storage_volume', 'lv_mode_perms' %> -<%- end -%> -<%= text_field_with_label "LUN:", 'storage_volume', 'lun' if @storage_volume.get_type_label==StoragePool::ISCSI %> - -<%= text_field_with_label "Filename:", 'storage_volume', 'filename' if @storage_volume.get_type_label==StoragePool::NFS %> - - -<!--[eoform:storage_volume]--> - diff --git a/src/app/views/storage/new_volume.rhtml b/src/app/views/storage/new_volume.rhtml deleted file mode 100644 index 2e49d16..0000000 --- a/src/app/views/storage/new_volume.rhtml +++ /dev/null @@ -1,53 +0,0 @@ -<%- content_for :title do -%> - <%= _("Add New Volume") %> -<%- end -%> -<%- content_for :description do -%> - Add a new Storage Volume to - <%= if @storage_volume.get_type_label==StoragePool::LVM - @source_volume.display_name - else - @storage_pool.display_name - end %>. -<%- end -%> -<div class="panel_header"></div> -<div class="dialog_form"> -<form method="POST" action="<%= url_for :action => 'create_volume' %>" id="storage_volume_form" > - <div class="dialog_form"> - <div id="new_storage_pool"> - <%= render :partial => 'new_volume_form' %> - </div> - </div> - <!-- FIXME: need to pop up the details dialog again --> - <% if @return_to_workflow %> - <%# TODO: update this method in application_helper to take an array, so we can include - a callback or trigger to to go previous step in flow. %> - <%= popup_footer("$('#storage_volume_form').submit()", "New Storage Volume") %> - <% else %> - <%= popup_footer("$('#storage_volume_form').submit()", "New Storage Volume") %> - <% end %> -</form> -</div> -<script type="text/javascript"> -function afterStorageVolume(response, status){ - ajax_validation(response, status); - if (response.success) { - //this is where I want to publish to... - //$(document).trigger('STORAGE_VOLUME', [response.new_volume]); - //but it only picks up correctly right now if I push it here, so this needs to change later - $('ul.ovirt-tree').trigger('STORAGE_VOLUME', [response.new_volume]); - <% unless @return_to_workflow -%> - $(document).trigger('close.facebox'); - <% end -%> - } -} -$(function() { - var storagevolumeoptions = { - target: '<%= url_for :action => 'create_volume' %>', // target element to update - dataType: 'json', - success: afterStorageVolume // post-submit callback - }; - - // bind form using 'ajaxForm' - $('#storage_volume_form').ajaxForm(storagevolumeoptions); -}); -</script> diff --git a/src/app/views/storage/show.rhtml b/src/app/views/storage/show.rhtml index 7e02f32..dd52d79 100644 --- a/src/app/views/storage/show.rhtml +++ b/src/app/views/storage/show.rhtml @@ -12,7 +12,7 @@ </a> <%if @storage_pool.user_subdividable -%> <%= link_to image_tag("icon_addstorage.png") + " Add new Volume", - {:controller => 'storage', :action => 'new_volume', :storage_pool_id => @storage_pool.id}, + {:controller => 'storage_volume', :action => 'new', :storage_pool_id => @storage_pool.id}, :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> <% end %> <a href="#confirm_delete_storage" rel="facebox[.bolder]"> diff --git a/src/app/views/storage/show_volume.rhtml b/src/app/views/storage/show_volume.rhtml deleted file mode 100644 index f85feaa..0000000 --- a/src/app/views/storage/show_volume.rhtml +++ /dev/null @@ -1,87 +0,0 @@ -<%- content_for :title do -%> - <%=h @storage_volume.display_name %> -<%- end -%> - -<%- content_for :action_links do -%> - <%if @can_modify -%> - <%if @storage_volume.supports_lvm_subdivision and @storage_volume.vms.empty? -%> - <%= link_to image_tag("icon_addstorage.png") + " Add new Volume", - {:controller => 'storage', :action => 'new_volume', :source_volume_id => @storage_volume.id}, - :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> - <% end %> - <%if @storage_volume.deletable -%> - <a href="#confirm_delete" rel="facebox[.bolder]"> - <%= image_tag "icon_x.png" %> Delete - </a> - <%- end -%> - <%- end -%> -<%- end -%> -<%= confirmation_dialog("confirm_delete", "Are you sure?", "delete_volume()") %> - - <div class="selection_key"> - <% unless @storage_volume.storage_pool[:type] == "LvmStoragePool" %> - IP address:<br/> - <% end %> - <% if @storage_volume.storage_pool[:type] == "IscsiStoragePool" %> - Port:<br/> - Target:<br/> - <% elsif @storage_volume.storage_pool[:type] == "NfsStoragePool" %> - Export path:<br/> - <% end %> - Type:<br/> - State:<br/> - Path:<br/> - <% if @storage_volume[:type] == "IscsiStorageVolume" %> - LUN:<br/> - <% elsif @storage_volume[:type] == "NfsStorageVolume" %> - Filename:<br/> - <% elsif @storage_volume[:type] == "LvmStorageVolume" %> - Volume Group:<br/> - Logical Volume:<br/> - Permissions (owner/group/mode):<br/> - <% end %> - Size:<br/> - </div> - <div class="selection_value"> - <% unless @storage_volume.storage_pool[:type] == "LvmStoragePool" %> - <%=h @storage_volume.storage_pool.ip_addr %><br/> - <% end %> - <% if @storage_volume.storage_pool[:type] == "IscsiStoragePool" %> - <%=h @storage_volume.storage_pool.port %><br/> - <%=h @storage_volume.storage_pool[:target] %><br/> - <% elsif @storage_volume.storage_pool[:type] == "NfsStoragePool" %> - <%=h @storage_volume.storage_pool.export_path %><br/> - <% end %> - <%=h @storage_volume.storage_pool.get_type_label %><br/> - <%=h @storage_volume.state %><br/> - <%=h @storage_volume.path %><br/> - <% if @storage_volume[:type] == "IscsiStorageVolume" %> - <%=h @storage_volume.lun %><br/> - <% elsif @storage_volume[:type] == "NfsStorageVolume" %> - <%=h @storage_volume.filename %><br/> - <% elsif @storage_volume[:type] == "LvmStorageVolume" %> - <%=h @storage_volume.storage_pool.vg_name %><br/> - <%=h @storage_volume.lv_name %><br/> - <%=h @storage_volume.lv_owner_perms %>/<%=h @storage_volume.lv_group_perms %>/<%=h @storage_volume.lv_mode_perms %><br/> - <% end %> - <%=h @storage_volume.size_in_gb %> GB<br/> - </div> -<%- content_for :right do -%> - -<%- end -%> - -<script type="text/javascript"> - function delete_volume() - { - $(document).trigger('close.facebox'); - $.post('<%= url_for :controller => "storage", :action => "delete_volumes" %>', - { storage_volume_ids: <%= @storage_volume.id %> }, - function(data,status) { - // FIXME: reload tree - if (data.alert) { - $.jGrowl(data.alert); - } - empty_summary('storage_selection', 'Storage Pool or Volume'); - }, 'json'); - } -</script> diff --git a/src/app/views/storage_volume/_new_volume_form.rhtml b/src/app/views/storage_volume/_new_volume_form.rhtml new file mode 100644 index 0000000..ae65e18 --- /dev/null +++ b/src/app/views/storage_volume/_new_volume_form.rhtml @@ -0,0 +1,24 @@ +<%= error_messages_for 'storage_volume' %> + +<!--[form:storage_pool]--> +<%= hidden_field 'storage_volume', 'storage_pool_id' %> +<%= hidden_field_tag 'storage_type', @storage_volume.get_type_label %> + +<%= text_field_with_label "Size (GB):", 'storage_volume', 'size_in_gb' %> + +<%if @storage_volume.get_type_label==StoragePool::LVM -%> + <%= text_field_with_label "LV Name:", 'storage_volume', 'lv_name' %> + + <%= text_field_with_label "Owner permissions:", 'storage_volume', 'lv_owner_perms' %> + + <%= text_field_with_label "Group permissions:", 'storage_volume', 'lv_group_perms' %> + + <%= text_field_with_label "Mode permissions:", 'storage_volume', 'lv_mode_perms' %> +<%- end -%> +<%= text_field_with_label "LUN:", 'storage_volume', 'lun' if @storage_volume.get_type_label==StoragePool::ISCSI %> + +<%= text_field_with_label "Filename:", 'storage_volume', 'filename' if @storage_volume.get_type_label==StoragePool::NFS %> + + +<!--[eoform:storage_volume]--> + diff --git a/src/app/views/storage_volume/new.rhtml b/src/app/views/storage_volume/new.rhtml new file mode 100644 index 0000000..46d379e --- /dev/null +++ b/src/app/views/storage_volume/new.rhtml @@ -0,0 +1,53 @@ +<%- content_for :title do -%> + <%= _("Add New Volume") %> +<%- end -%> +<%- content_for :description do -%> + Add a new Storage Volume to + <%= if @storage_volume.get_type_label==StoragePool::LVM + @source_volume.display_name + else + @storage_pool.display_name + end %>. +<%- end -%> +<div class="panel_header"></div> +<div class="dialog_form"> +<form method="POST" action="<%= url_for :action => 'create' %>" id="storage_volume_form" > + <div class="dialog_form"> + <div id="new_storage_pool"> + <%= render :partial => 'new_volume_form' %> + </div> + </div> + <!-- FIXME: need to pop up the details dialog again --> + <% if @return_to_workflow %> + <%# TODO: update this method in application_helper to take an array, so we can include + a callback or trigger to to go previous step in flow. %> + <%= popup_footer("$('#storage_volume_form').submit()", "New Storage Volume") %> + <% else %> + <%= popup_footer("$('#storage_volume_form').submit()", "New Storage Volume") %> + <% end %> +</form> +</div> +<script type="text/javascript"> +function afterStorageVolume(response, status){ + ajax_validation(response, status); + if (response.success) { + //this is where I want to publish to... + //$(document).trigger('STORAGE_VOLUME', [response.new_volume]); + //but it only picks up correctly right now if I push it here, so this needs to change later + $('ul.ovirt-tree').trigger('STORAGE_VOLUME', [response.new_volume]); + <% unless @return_to_workflow -%> + $(document).trigger('close.facebox'); + <% end -%> + } +} +$(function() { + var storagevolumeoptions = { + target: '<%= url_for :action => 'create' %>', // target element to update + dataType: 'json', + success: afterStorageVolume // post-submit callback + }; + + // bind form using 'ajaxForm' + $('#storage_volume_form').ajaxForm(storagevolumeoptions); +}); +</script> diff --git a/src/app/views/storage_volume/show.rhtml b/src/app/views/storage_volume/show.rhtml new file mode 100644 index 0000000..c2434aa --- /dev/null +++ b/src/app/views/storage_volume/show.rhtml @@ -0,0 +1,87 @@ +<%- content_for :title do -%> + <%=h @storage_volume.display_name %> +<%- end -%> + +<%- content_for :action_links do -%> + <%if @can_modify -%> + <%if @storage_volume.supports_lvm_subdivision and @storage_volume.vms.empty? -%> + <%= link_to image_tag("icon_addstorage.png") + " Add new Volume", + {:controller => 'storage_volume', :action => 'new', :source_volume_id => @storage_volume.id}, + :rel=>"facebox[.bolder]", :class=>"selection_facebox" %> + <% end %> + <%if @storage_volume.deletable -%> + <a href="#confirm_delete" rel="facebox[.bolder]"> + <%= image_tag "icon_x.png" %> Delete + </a> + <%- end -%> + <%- end -%> +<%- end -%> +<%= confirmation_dialog("confirm_delete", "Are you sure?", "delete_volume()") %> + + <div class="selection_key"> + <% unless @storage_volume.storage_pool[:type] == "LvmStoragePool" %> + IP address:<br/> + <% end %> + <% if @storage_volume.storage_pool[:type] == "IscsiStoragePool" %> + Port:<br/> + Target:<br/> + <% elsif @storage_volume.storage_pool[:type] == "NfsStoragePool" %> + Export path:<br/> + <% end %> + Type:<br/> + State:<br/> + Path:<br/> + <% if @storage_volume[:type] == "IscsiStorageVolume" %> + LUN:<br/> + <% elsif @storage_volume[:type] == "NfsStorageVolume" %> + Filename:<br/> + <% elsif @storage_volume[:type] == "LvmStorageVolume" %> + Volume Group:<br/> + Logical Volume:<br/> + Permissions (owner/group/mode):<br/> + <% end %> + Size:<br/> + </div> + <div class="selection_value"> + <% unless @storage_volume.storage_pool[:type] == "LvmStoragePool" %> + <%=h @storage_volume.storage_pool.ip_addr %><br/> + <% end %> + <% if @storage_volume.storage_pool[:type] == "IscsiStoragePool" %> + <%=h @storage_volume.storage_pool.port %><br/> + <%=h @storage_volume.storage_pool[:target] %><br/> + <% elsif @storage_volume.storage_pool[:type] == "NfsStoragePool" %> + <%=h @storage_volume.storage_pool.export_path %><br/> + <% end %> + <%=h @storage_volume.storage_pool.get_type_label %><br/> + <%=h @storage_volume.state %><br/> + <%=h @storage_volume.path %><br/> + <% if @storage_volume[:type] == "IscsiStorageVolume" %> + <%=h @storage_volume.lun %><br/> + <% elsif @storage_volume[:type] == "NfsStorageVolume" %> + <%=h @storage_volume.filename %><br/> + <% elsif @storage_volume[:type] == "LvmStorageVolume" %> + <%=h @storage_volume.storage_pool.vg_name %><br/> + <%=h @storage_volume.lv_name %><br/> + <%=h @storage_volume.lv_owner_perms %>/<%=h @storage_volume.lv_group_perms %>/<%=h @storage_volume.lv_mode_perms %><br/> + <% end %> + <%=h @storage_volume.size_in_gb %> GB<br/> + </div> +<%- content_for :right do -%> + +<%- end -%> + +<script type="text/javascript"> + function delete_volume() + { + $(document).trigger('close.facebox'); + $.post('<%= url_for :controller => "storage_volume", :action => "destroy" %>', + { storage_volume_ids: <%= @storage_volume.id %> }, + function(data,status) { + // FIXME: reload tree + if (data.alert) { + $.jGrowl(data.alert); + } + empty_summary('storage_selection', 'Storage Pool or Volume'); + }, 'json'); + } +</script> -- 1.6.0.6
David Lutterkort
2009-Feb-02 20:35 UTC
[Ovirt-devel] [PATCH server 6/8] Remove delete_volumes, since it was not used
--- src/app/controllers/storage_volume_controller.rb | 49 ---------------------- src/app/views/storage_volume/show.rhtml | 2 +- 2 files changed, 1 insertions(+), 50 deletions(-) diff --git a/src/app/controllers/storage_volume_controller.rb b/src/app/controllers/storage_volume_controller.rb index ba486d2..9cde09b 100644 --- a/src/app/controllers/storage_volume_controller.rb +++ b/src/app/controllers/storage_volume_controller.rb @@ -118,54 +118,6 @@ class StorageVolumeController < ApplicationController end def destroy - if params[:id] - delete_volume - else - delete_volumes - end - end - - def delete_volumes - storage_volume_ids_str = params[:storage_volume_ids] - storage_volume_ids = storage_volume_ids_str.split(",").collect {|x| x.to_i} - alerts = [] - status = true - begin - StorageVolume.transaction do - storage = StorageVolume.find(:all, :conditions => "id in (#{storage_volume_ids.join(', ')})") - unless storage.empty? - set_perms(storage[0].storage_pool.hardware_pool) - unless @can_modify and storage[0].storage_pool.user_subdividable - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => false, - :alert => "You do not have permission to delete this storage volume." } } - format.xml { head :forbidden } - end - else - storage.each do |storage_volume| - alert, success = delete_volume_internal(storage_volume) - alerts << alert - status = false unless success - end - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => status, :alert => alerts.join("\n") } } - format.xml { head(status ? :ok : :method_not_allowed) } - end - end - else - respond_to do |format| - format.json { render :json => { :object => "storage_volume", - :success => false, :alert => "no volumes selected" } } - format.xml { head(status ? :ok : :method_not_allowed) } - end - end - end - end - end - - def delete_volume @storage_volume = StorageVolume.find(params[:id]) set_perms(@storage_volume.storage_pool.hardware_pool) unless @can_modify and @storage_volume.storage_pool.user_subdividable @@ -183,7 +135,6 @@ class StorageVolumeController < ApplicationController format.xml { head(success ? :ok : :method_not_allowed) } end end - end def pre_create diff --git a/src/app/views/storage_volume/show.rhtml b/src/app/views/storage_volume/show.rhtml index c2434aa..3963a3c 100644 --- a/src/app/views/storage_volume/show.rhtml +++ b/src/app/views/storage_volume/show.rhtml @@ -75,7 +75,7 @@ { $(document).trigger('close.facebox'); $.post('<%= url_for :controller => "storage_volume", :action => "destroy" %>', - { storage_volume_ids: <%= @storage_volume.id %> }, + { id: <%= @storage_volume.id %> }, function(data,status) { // FIXME: reload tree if (data.alert) { -- 1.6.0.6
David Lutterkort
2009-Feb-02 20:35 UTC
[Ovirt-devel] [PATCH server 7/8] Produce a human-readable error when deleting a pool fails
--- src/app/controllers/storage_controller.rb | 6 ++++-- src/app/views/errors/simple.xml.builder | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 src/app/views/errors/simple.xml.builder diff --git a/src/app/controllers/storage_controller.rb b/src/app/controllers/storage_controller.rb index 3579967..a951656 100644 --- a/src/app/controllers/storage_controller.rb +++ b/src/app/controllers/storage_controller.rb @@ -204,10 +204,12 @@ class StorageController < ApplicationController def destroy unless @storage_pool.movable? + @error = "Cannot delete storage with associated vms" respond_to do |format| format.json { render :json => { :object => "storage_pool", - :success => false, - :alert => "Cannot delete storage with associated vms" } } + :success => false, :alert => @error } } + format.xml { render :template => "errors/simple", :layout => false, + :status => :forbidden } end return end diff --git a/src/app/views/errors/simple.xml.builder b/src/app/views/errors/simple.xml.builder new file mode 100644 index 0000000..d8d3c5b --- /dev/null +++ b/src/app/views/errors/simple.xml.builder @@ -0,0 +1,3 @@ +xml.instruct! + +xml.error @error -- 1.6.0.6
David Lutterkort
2009-Feb-02 20:35 UTC
[Ovirt-devel] [PATCH server 8/8] REST route for storage volumes
--- src/config/routes.rb | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/src/config/routes.rb b/src/config/routes.rb index 132072e..168ded5 100644 --- a/src/config/routes.rb +++ b/src/config/routes.rb @@ -53,6 +53,7 @@ ActionController::Routing::Routes.draw do |map| # REST work out of the box, and use these as the default routes map.resources :hosts, :controller => 'host' map.resources :storage_pools, :controller => 'storage' + map.resources :storage_volumes, :controller => 'storage_volume' map.resources :hardware_pools, :controller => 'hardware' do |hardware_pools| hardware_pools.resources :hosts, :controller => 'host' hardware_pools.resources :storage_pools, :controller => 'storage' -- 1.6.0.6