diff --git a/lib/fog/bin.rb b/lib/fog/bin.rb index 341128780..a8b978933 100644 --- a/lib/fog/bin.rb +++ b/lib/fog/bin.rb @@ -82,4 +82,5 @@ require 'fog/bin/virtual_box' require 'fog/bin/vmfusion' require 'fog/bin/vsphere' require 'fog/bin/voxel' +require 'fog/bin/xenserver' require 'fog/bin/zerigo' diff --git a/lib/fog/bin/xenserver.rb b/lib/fog/bin/xenserver.rb new file mode 100644 index 000000000..1b560962a --- /dev/null +++ b/lib/fog/bin/xenserver.rb @@ -0,0 +1,31 @@ +class XenServer < Fog::Bin + class << self + + def class_for(key) + case key + when :compute + Fog::Compute::XenServer + else + raise ArgumentError, "Unrecognized service: #{key}" + end + end + + def [](service) + @@connections ||= Hash.new do |hash, key| + hash[key] = case key + when :compute + Fog::Logger.warning("XenServer[:compute] is not recommended, use Compute[:xenserver] for portability") + Fog::Compute.new(:provider => 'XenServer') + else + raise ArgumentError, "Unrecognized service: #{key.inspect}" + end + end + @@connections[service] + end + + def services + Fog::XenServer.services + end + + end +end diff --git a/lib/fog/compute.rb b/lib/fog/compute.rb index 11ef0e481..ea6ca3fbb 100644 --- a/lib/fog/compute.rb +++ b/lib/fog/compute.rb @@ -85,6 +85,9 @@ module Fog when :vsphere require 'fog/vsphere/compute' Fog::Compute::Vsphere.new(attributes) + when :xenserver + require 'fog/xenserver/compute' + Fog::Compute::XenServer.new(attributes) else raise ArgumentError.new("#{provider} is not a recognized compute provider") end diff --git a/lib/fog/providers.rb b/lib/fog/providers.rb index 39f3d569f..895a8c01a 100644 --- a/lib/fog/providers.rb +++ b/lib/fog/providers.rb @@ -27,4 +27,5 @@ require 'fog/virtual_box' require 'fog/vmfusion' require 'fog/vsphere' require 'fog/voxel' +require 'fog/xenserver' require 'fog/zerigo' diff --git a/lib/fog/xenserver.rb b/lib/fog/xenserver.rb new file mode 100644 index 000000000..985cf76ba --- /dev/null +++ b/lib/fog/xenserver.rb @@ -0,0 +1,52 @@ +module Fog + module XenServer + extend Fog::Provider + + service(:compute, 'xenserver/compute', 'Compute') + + class Connection + require 'xmlrpc/client' + + def initialize(host) + @factory = XMLRPC::Client.new(host, '/') + @factory.set_parser(XMLRPC::XMLParser::REXMLStreamParser.new) + end + + def authenticate( username, password ) + begin + response = @factory.call('session.login_with_password', username, password ) + raise Fog::XenServer::InvalidLogin unless response["Status"] =~ /Success/ + end + @credentials = response["Value"] + end + + def request(options, *params) + begin + parser = options.delete(:parser) + method = options.delete(:method) + + if params.empty? + response = @factory.call(method, @credentials) + else + if params.length.eql?(1) and params.first.is_a?(Hash) + response = @factory.call(method, @credentials, params.first) + else + response = eval("@factory.call('#{method}', '#{@credentials}', #{params.map {|p| p.is_a?(String) ? "'#{p}'" : p}.join(',')})") + end + end + #puts "RESPONSE: #{response}" + + if parser + parser.parse( response["Value"] ) + response = parser.response + end + + response + end + end + end + + end +end + + diff --git a/lib/fog/xenserver/compute.rb b/lib/fog/xenserver/compute.rb new file mode 100644 index 000000000..ccbd205f5 --- /dev/null +++ b/lib/fog/xenserver/compute.rb @@ -0,0 +1,124 @@ +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'xenserver')) +require 'fog/compute' + +module Fog + module Compute + class XenServer < Fog::Service + + require 'fog/xenserver/utilities' + require 'fog/xenserver/parser' + + requires :xenserver_username + requires :xenserver_password + requires :xenserver_url + recognizes :xenserver_defaults + + model_path 'fog/xenserver/models/compute' + model :server + collection :servers + model :host + collection :hosts + collection :vifs + model :vif + collection :storage_repositories + model :storage_repository + collection :pools + model :pool + collection :vbds + model :vbd + collection :vdis + model :vdi + collection :networks + model :network + collection :pifs + model :pif + collection :pbds + model :pbd + + request_path 'fog/xenserver/requests/compute' + request :create_server + request :create_vif + request :create_vm + request :destroy_server + request :destroy_vbd + request :destroy_vdi + request :shutdown_server + request :get_hosts + request :get_network + request :get_networks + request :get_storage_repository + request :get_storage_repositories + request :get_vifs + request :get_vms + request :get_vm + request :start_vm + request :start_server + request :get_pool + request :get_pools + request :get_record + request :get_records + request :set_affinity + request :reboot_server + + class Real + + attr_accessor :default_template + + def initialize(options={}) + @host = options[:xenserver_url] + @username = options[:xenserver_username] + @password = options[:xenserver_password] + @defaults = options[:xenserver_defaults] || {} + @connection = Fog::XenServer::Connection.new(@host) + @connection.authenticate(@username, @password) + end + + def reload + @connection.authenticate(@username, @password) + end + + def default_template=(name) + @defaults[:template] = name + end + + def default_template + return nil if @defaults[:template].nil? + data = get_vm( @defaults[:template] ) + return nil if data[:reference].nil? + servers.get data[:reference] + end + + def default_network + Fog::XenServer::Network.new( get_network( @defaults[:network] ) ) if @defaults[:network] + end + + end + + class Mock + + def self.data + @data ||= Hash.new do |hash, key| + hash[key] = {} + end + end + + def self.reset_data(keys=data.keys) + for key in [*keys] + data.delete(key) + end + end + + def initialize(options={}) + @host = options[:xenserver_pool_master] + @username = options[:xenserver_username] + @password = options[:xenserver_password] + @connection = Fog::Connection.new(@host) + @connection.authenticate(@username, @password) + end + + end + end + end +end + + diff --git a/lib/fog/xenserver/models/compute/host.rb b/lib/fog/xenserver/models/compute/host.rb new file mode 100644 index 000000000..95782050b --- /dev/null +++ b/lib/fog/xenserver/models/compute/host.rb @@ -0,0 +1,46 @@ +require 'fog/core/model' + +module Fog + module Compute + class XenServer + + class Host < Fog::Model + # API Reference here: + # http://docs.vmd.citrix.com/XenServer/5.6.0/1.0/en_gb/api/?c=host + + identity :reference + + attribute :name, :aliases => :name_label + attribute :uuid + attribute :address + attribute :allowed_operations + attribute :enabled + attribute :hostname + attribute :metrics + attribute :name_description + attribute :other_config + attribute :__pbds, :aliases => :PBDs + attribute :__pifs, :aliases => :PIFs + attribute :__resident_vms, :aliases => :resident_VMs + + def pifs + __pifs.collect { |pif| connection.pifs.get pif } + end + + def pbds + __pbds.collect { |pbd| connection.pbds.get pbd } + end + + def resident_servers + __resident_vms.collect { |ref| connection.servers.get ref } + end + + def resident_vms + resident_servers + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/hosts.rb b/lib/fog/xenserver/models/compute/hosts.rb new file mode 100644 index 000000000..273804c5a --- /dev/null +++ b/lib/fog/xenserver/models/compute/hosts.rb @@ -0,0 +1,29 @@ +require 'fog/core/collection' +require 'fog/xenserver/models/compute/host' + +module Fog + module Compute + class XenServer + + class Hosts < Fog::Collection + + model Fog::Compute::XenServer::Host + + def all + data = connection.get_hosts + load(data) + end + + def get( host_ref ) + if host_ref && host = connection.get_record( host_ref, 'host' ) + new(host) + else + nil + end + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/network.rb b/lib/fog/xenserver/models/compute/network.rb new file mode 100644 index 000000000..fb4f277fc --- /dev/null +++ b/lib/fog/xenserver/models/compute/network.rb @@ -0,0 +1,58 @@ +require 'fog/core/model' + +module Fog + module Compute + class XenServer + + class Network < Fog::Model + # API Reference here: + # http://docs.vmd.citrix.com/XenServer/5.6.0/1.0/en_gb/api/?c=network + + identity :reference + + attribute :uuid + attribute :__vifs, :aliases => :VIFs + attribute :tags + attribute :mtu, :aliases => :MTU + attribute :bridge + attribute :description, :aliases => :name_description + attribute :name, :aliases => :name_label + attribute :other_config + attribute :__pifs, :aliases => :PIFs + attribute :allowed_operations + attribute :current_operations + attribute :blobs + + def refresh + data = connection.get_record( reference, 'network' ) + merge_attributes( data ) + true + end + + # + # Return the list of network related PIFs + # + def pifs + p = [] + __pifs.each do |pif| + p << connection.pifs.get(pif) + end + p + end + + # + # Return the list of network related VIFs + # + def vifs + v = [] + __vifs.each do |vif| + v << connection.vifs.get(vif) + end + v + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/networks.rb b/lib/fog/xenserver/models/compute/networks.rb new file mode 100644 index 000000000..90d2f4089 --- /dev/null +++ b/lib/fog/xenserver/models/compute/networks.rb @@ -0,0 +1,33 @@ +require 'fog/core/collection' +require 'fog/xenserver/models/compute/network' + +module Fog + module Compute + class XenServer + + class Networks < Fog::Collection + + model Fog::Compute::XenServer::Network + + def initialize(attributes) + super + end + + def all(options = {}) + data = connection.get_records 'network' + load(data) + end + + def get( ref ) + if ref && obj = connection.get_record( ref, 'network' ) + new(obj) + end + rescue Fog::XenServer::NotFound + nil + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/pbd.rb b/lib/fog/xenserver/models/compute/pbd.rb new file mode 100644 index 000000000..3358407da --- /dev/null +++ b/lib/fog/xenserver/models/compute/pbd.rb @@ -0,0 +1,33 @@ +require 'fog/core/model' + +module Fog + module Compute + class XenServer + + class PBD < Fog::Model + # API Reference here: + # http://docs.vmd.citrix.com/XenServer/5.6.0/1.0/en_gb/api/?c=PBD + + identity :reference + + attribute :uuid + attribute :__host, :aliases => :host + attribute :__sr, :aliases => :SR + + def sr + connection.storage_repositories.get __sr + end + + def storage_repository + sr + end + + def host + connection.hosts.get __host + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/pbds.rb b/lib/fog/xenserver/models/compute/pbds.rb new file mode 100644 index 000000000..0c97907ac --- /dev/null +++ b/lib/fog/xenserver/models/compute/pbds.rb @@ -0,0 +1,33 @@ +require 'fog/core/collection' +require 'fog/xenserver/models/compute/pbd' + +module Fog + module Compute + class XenServer + + class Pbds < Fog::Collection + + model Fog::Compute::XenServer::PBD + + def initialize(attributes) + super + end + + def all(options = {}) + data = connection.get_records 'PBD' + load(data) + end + + def get( ref ) + if ref && obj = connection.get_record( ref, 'PBD' ) + new(obj) + else + nil + end + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/pif.rb b/lib/fog/xenserver/models/compute/pif.rb new file mode 100644 index 000000000..c3cb9654d --- /dev/null +++ b/lib/fog/xenserver/models/compute/pif.rb @@ -0,0 +1,46 @@ +require 'fog/core/model' + +module Fog + module Compute + class XenServer + + class PIF < Fog::Model + # API Reference here: + # http://docs.vmd.citrix.com/XenServer/5.6.0/1.0/en_gb/api/?c=PIF + + identity :reference + + attribute :uuid + attribute :physical + attribute :mac, :aliases => :MAC + attribute :currently_attached + attribute :device + attribute :device_name + attribute :metrics + attribute :dns, :aliases => :DNS + attribute :gateway + attribute :ip, :aliases => :IP + attribute :ip_configuration_mode + attribute :mtu, :aliases => :MTU + attribute :__network, :aliases => :network + attribute :netmask + attribute :status_code + attribute :status_detail + attribute :management + attribute :vlan, :aliases => :VLAN + attribute :other_config + attribute :__host, :aliases => :host + + def network + connection.networks.get __network + end + + def host + connection.hosts.get __host + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/pifs.rb b/lib/fog/xenserver/models/compute/pifs.rb new file mode 100644 index 000000000..448022de0 --- /dev/null +++ b/lib/fog/xenserver/models/compute/pifs.rb @@ -0,0 +1,33 @@ +require 'fog/core/collection' +require 'fog/xenserver/models/compute/pif' + +module Fog + module Compute + class XenServer + + class Pifs < Fog::Collection + + model Fog::Compute::XenServer::PIF + + def initialize(attributes) + super + end + + def all(options = {}) + data = connection.get_records 'PIF' + load(data) + end + + def get( ref ) + if ref && obj = connection.get_record( ref, 'PIF' ) + new(obj) + end + rescue Fog::XenServer::NotFound + nil + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/pool.rb b/lib/fog/xenserver/models/compute/pool.rb new file mode 100644 index 000000000..e7aabbe03 --- /dev/null +++ b/lib/fog/xenserver/models/compute/pool.rb @@ -0,0 +1,34 @@ +require 'fog/core/model' + +module Fog + module Compute + class XenServer + + class Pool < Fog::Model + # API Reference here: + # http://docs.vmd.citrix.com/XenServer/5.6.0/1.0/en_gb/api/?c=pool + + identity :reference + + attribute :name_label + attribute :uuid + attribute :__default_sr, :aliases => :default_SR + + def initialize(attributes={}) + @uuid ||= 0 + super + end + + def default_sr + connection.get_storage_repository_by_ref __default_sr + end + + def default_storage_repository + connection.get_storage_repository_by_ref __default_sr + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/pools.rb b/lib/fog/xenserver/models/compute/pools.rb new file mode 100644 index 000000000..e82ad3105 --- /dev/null +++ b/lib/fog/xenserver/models/compute/pools.rb @@ -0,0 +1,33 @@ +require 'fog/core/collection' +require 'fog/xenserver/models/compute/pool' + +module Fog + module Compute + class XenServer + + class Pools < Fog::Collection + + model Fog::Compute::XenServer::Pool + + def initialize(attributes) + super + end + + def all(options = {}) + data = connection.get_pools + load(data) + end + + def get( pool_ref ) + if pool_ref && pool = connection.get_pool_by_ref( pool_ref ) + new(pool) + end + rescue Fog::XenServer::NotFound + nil + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/server.rb b/lib/fog/xenserver/models/compute/server.rb new file mode 100644 index 000000000..678102a0f --- /dev/null +++ b/lib/fog/xenserver/models/compute/server.rb @@ -0,0 +1,157 @@ +require 'fog/compute/models/server' + +module Fog + module Compute + class XenServer + + class Server < Fog::Compute::Server + # API Reference here: + # http://docs.vmd.citrix.com/XenServer/5.6.0/1.0/en_gb/api/?c=VM + + identity :reference + + attribute :uuid + attribute :name, :aliases => :name_label + attribute :affinity + attribute :allowed_operations + attribute :consoles + attribute :domarch + attribute :domid + attribute :guest_metrics + attribute :is_a_snapshot + attribute :is_a_template + attribute :is_control_domain + attribute :memory_dynamic_max + attribute :memory_dynamic_min + attribute :memory_static_max + attribute :memory_static_min + attribute :memory_target + attribute :metrics + attribute :name_description + attribute :other_config + attribute :power_state + attribute :pv_args, :aliases => :PV_args + attribute :resident_on + # Virtual Block Devices + attribute :__vbds, :aliases => :VBDs + # Virtual CPUs + attribute :vcpus_at_startup, :aliases => :VCPUs_at_startup + attribute :vcpus_max, :aliases => :VCPUs_max + # Virtual Interfaces (NIC) + attribute :__vifs, :aliases => :VIFs + attribute :template_name + + #ignore_attributes :HVM_boot_params, :HVM_boot_policy, :HVM_shadow_multiplier, :PCI_bus, :PV_bootloader, + # :PV_bootloader_args, :PV_kernel, :PV_legacy_args, :PV_ramdisk, :VCPUs_params, :VTPMs, + # :actions_after_crash, :actions_after_reboot, :actions_after_shutdown, :blobs, + # :blocked_operations, :crash_dumps, :current_operations, :ha_always_run, :ha_restart_priority, + # :last_boot_CPU_flags, :last_booted_record, :platform, :recommendations, :snapshot_time, + # :snapshots, :snapshot_of, :suspend_VDI, :tags, :transportable_snapshot_id, :user_version, + # :xenstore_data, :memory_overhead, :children, :bios_strings, :parent, :snapshot_metadata, + # :snapshot_info + + def initialize(attributes={}) + super + end + + def vbds + __vbds.collect {|vbd| connection.vbds.get vbd } + end + + def destroy + stop('hard') if running? + vbds.each do |vbd| + connection.destroy_vdi( vbd.vdi.reference ) if vbd.type == "Disk" + end + connection.destroy_server( reference ) + true + end + + def refresh + data = connection.get_record( reference, 'VM' ) + merge_attributes( data ) + true + end + + def vifs + networks + end + + # associations + def networks + __vifs.collect {|vif| Fog::Compute::XenServer::VIF.new(connection.get_record( vif, 'VIF' ))} + end + + def running_on + Fog::Compute::XenServer::Host.new(connection.get_record( resident_on, 'host' )) + end + + def home_hypervisor + connection.hosts.first + end + + def mac_address + networks.first.MAC + end + + def running? + power_state =~ /Running/ + end + + def halted? + power_state =~ /Halted/ + end + + # operations + def start + return false if running? + connection.start_server( reference ) + true + end + + def save(params = {}) + requires :name + new_vm = connection.create_server( name, template_name, nil) + merge_attributes(new_vm.attributes) + true + end + + def reboot(stype = 'clean') + connection.reboot_server(reference, stype) + true + end + + def hard_reboot + reboot 'hard' + end + + def clean_reboot + reboot 'clean' + end + + def stop(stype = 'clean') + return false if !running? + connection.shutdown_server( reference, stype ) + wait_for { !running? } + true + end + + def hard_shutdown + stop 'hard' + end + + def clean_shutdown + stop 'clean' + end + + # def snapshot + # requires :reference, :name_label + # data = connection.snapshot_server(@reference, @name_label) + # merge_attributes(data.body) + # true + # end + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/servers.rb b/lib/fog/xenserver/models/compute/servers.rb new file mode 100644 index 000000000..60b7da7f9 --- /dev/null +++ b/lib/fog/xenserver/models/compute/servers.rb @@ -0,0 +1,57 @@ +require 'fog/core/collection' +require 'fog/xenserver/models/compute/server' + +module Fog + module Compute + class XenServer + + class Servers < Fog::Collection + + model Fog::Compute::XenServer::Server + + def initialize(attributes) + super + end + + def all(options = {}) + data = connection.get_vms + # Exclude templates + data.delete_if { |vm| + vm[:is_a_template] and (!options[:include_templates] and !options[:include_custom_templates]) + } + data.delete_if { |vm| + # VM is a custom template + if vm[:is_a_template] and vm[:allowed_operations].include?("destroy") + !options[:include_custom_templates] + end + } + data.delete_if { |vm| + # VM is a built-in template + if vm[:is_a_template] and !vm[:allowed_operations].include?("destroy") + !options[:include_templates] + end + } + data.delete_if { |vm| vm[:is_control_domain] } + data.delete_if { |vm| vm[:is_a_snapshot] and !options[:include_snapshots] } + data.delete_if { |vm| options[:name_matches] and (vm[:name_label] !~ /#{Regexp.escape(options[:name_matches])}/i ) } + data.delete_if { |vm| options[:name_equals] and (vm[:name_label] != options[:name_equals] ) } + load(data) + end + + def get_by_name( name ) + all(:name_equals => name).first + end + + def get( vm_ref ) + if vm_ref && vm = connection.get_vm_by_ref( vm_ref ) + new(vm) + end + rescue Fog::XenServer::NotFound + nil + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/storage_repositories.rb b/lib/fog/xenserver/models/compute/storage_repositories.rb new file mode 100644 index 000000000..023814a84 --- /dev/null +++ b/lib/fog/xenserver/models/compute/storage_repositories.rb @@ -0,0 +1,31 @@ +require 'fog/core/collection' +require 'fog/xenserver/models/compute/storage_repository' + +module Fog + module Compute + class XenServer + + class StorageRepositories < Fog::Collection + + model Fog::Compute::XenServer::StorageRepository + + def all + data = connection.get_storage_repositories + #data.delete_if {|sr| sr[:shared].eql?(false)} + #data.delete_if {|sr| sr[:content_type].eql?('iso')} + load(data) + end + + def get( sr_ref ) + if sr_ref && sr = connection.get_record( sr_ref, 'SR' ) + new(sr) + else + nil + end + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/storage_repository.rb b/lib/fog/xenserver/models/compute/storage_repository.rb new file mode 100644 index 000000000..43486038a --- /dev/null +++ b/lib/fog/xenserver/models/compute/storage_repository.rb @@ -0,0 +1,36 @@ +require 'fog/core/model' + +module Fog + module Compute + class XenServer + + class StorageRepository < Fog::Model + # API Reference here: + # http://docs.vmd.citrix.com/XenServer/5.6.0/1.0/en_gb/api/?c=SR + + identity :reference + + attribute :name_label + attribute :uuid + attribute :allowed_operations + attribute :content_type + attribute :name_description + attribute :other_config + attribute :PBDs + attribute :shared + attribute :type + attribute :VDIs + + ignore_attributes :blobs, :current_operations, :physical_size, :physical_utilisation, :sm_config, :tags, + :virtual_allocation + + def initialize(attributes={}) + @uuid ||= 0 + super + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/vbd.rb b/lib/fog/xenserver/models/compute/vbd.rb new file mode 100644 index 000000000..0a5b06f99 --- /dev/null +++ b/lib/fog/xenserver/models/compute/vbd.rb @@ -0,0 +1,39 @@ +require 'fog/core/model' + +module Fog + module Compute + class XenServer + + class VBD < Fog::Model + # API Reference here: + # http://docs.vmd.citrix.com/XenServer/5.6.0/1.0/en_gb/api/?c=VBD + + identity :reference + + attribute :uuid + attribute :currently_attached + attribute :reserved + attribute :__vdi, :aliases => :VDI + attribute :vm, :aliases => :VM + attribute :device + attribute :status_detail + attribute :type + attribute :userdevice + + #ignore_attributes :current_operations, :qos_supported_algorithms, :qos_algorithm_params, :qos_algorithm_type, :other_config, + # :runtime_properties + + def initialize(attributes={}) + super + end + + def vdi + #Fog::Compute::XenServer::VDI.new(connection.get_record( __vdi, 'VDI' )) + connection.vdis.get __vdi + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/vbds.rb b/lib/fog/xenserver/models/compute/vbds.rb new file mode 100644 index 000000000..edb439e63 --- /dev/null +++ b/lib/fog/xenserver/models/compute/vbds.rb @@ -0,0 +1,34 @@ +require 'fog/core/collection' +require 'fog/xenserver/models/compute/vbd' + +module Fog + module Compute + class XenServer + + class Vbds < Fog::Collection + + model Fog::Compute::XenServer::VBD + + def initialize(attributes) + super + end + + def all(options = {}) + data = connection.get_records 'VBD' + #data.delete_if { |vm| vm[:is_a_template] and !options[:include_templates] } + load(data) + end + + def get( vbd_ref ) + if vbd_ref && vbd = connection.get_record( vbd_ref, 'VBD' ) + new(vbd) + end + rescue Fog::XenServer::NotFound + nil + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/vdi.rb b/lib/fog/xenserver/models/compute/vdi.rb new file mode 100644 index 000000000..2d5976747 --- /dev/null +++ b/lib/fog/xenserver/models/compute/vdi.rb @@ -0,0 +1,33 @@ +require 'fog/core/model' + +module Fog + module Compute + class XenServer + + class VDI < Fog::Model + # API Reference here: + # http://docs.vmd.citrix.com/XenServer/5.6.0/1.0/en_gb/api/?c=VDI + + identity :reference + + attribute :uuid + attribute :is_a_snapshot + attribute :name, :aliases => :name_label + attribute :description, :aliases => :name_description + attribute :__parent, :aliases => :parent + attribute :virtual_size, :aliases => :parent + attribute :__vbds, :aliases => :VBDs + attribute :__sr, :aliases => :SR + attribute :sharable + attribute :readonly + + def initialize(attributes={}) + @uuid ||= 0 + super + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/vdis.rb b/lib/fog/xenserver/models/compute/vdis.rb new file mode 100644 index 000000000..3bf5c0b7b --- /dev/null +++ b/lib/fog/xenserver/models/compute/vdis.rb @@ -0,0 +1,34 @@ +require 'fog/core/collection' +require 'fog/xenserver/models/compute/vdi' + +module Fog + module Compute + class XenServer + + class Vdis < Fog::Collection + + model Fog::Compute::XenServer::VDI + + def initialize(attributes) + super + end + + def all(options = {}) + data = connection.get_records 'VDI' + #data.delete_if { |vm| vm[:is_a_template] and !options[:include_templates] } + load(data) + end + + def get( vdi_ref ) + if vdi_ref && vdi = connection.get_record( vdi_ref, 'VDI' ) + new(vdi) + end + rescue Fog::XenServer::NotFound + nil + end + + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/vif.rb b/lib/fog/xenserver/models/compute/vif.rb new file mode 100644 index 000000000..dda8a0043 --- /dev/null +++ b/lib/fog/xenserver/models/compute/vif.rb @@ -0,0 +1,37 @@ +require 'fog/core/model' + +module Fog + module Compute + class XenServer + + class VIF < Fog::Model + # API Reference here: + # http://docs.vmd.citrix.com/XenServer/5.6.0/1.0/en_gb/api/?c=VIF + + identity :reference + + attribute :mac, :aliases => :MAC + attribute :uuid + attribute :allowed_operations + attribute :currently_attached + attribute :device + attribute :mac_autogenerated, :aliases => :MAC_autogenerated + attribute :metrics + attribute :mtu, :aliases => :MTU + attribute :network + attribute :status_code + attribute :status_detail + attribute :vm, :aliases => :VM + + #ignore_attributes :current_operations, :qos_supported_algorithms, :qos_algorithm_params, :qos_algorithm_type, :other_config, + # :runtime_properties + + def initialize(attributes={}) + @uuid ||= 0 + super + end + end + + end + end +end diff --git a/lib/fog/xenserver/models/compute/vifs.rb b/lib/fog/xenserver/models/compute/vifs.rb new file mode 100644 index 000000000..d7e6821dd --- /dev/null +++ b/lib/fog/xenserver/models/compute/vifs.rb @@ -0,0 +1,29 @@ +require 'fog/core/collection' +require 'fog/xenserver/models/compute/vif' + +module Fog + module Compute + class XenServer + + class Vifs < Fog::Collection + + model Fog::Compute::XenServer::VIF + + def all(options = {}) + data = connection.get_records 'VIF' + load(data) + end + + def get( ref ) + if ref && obj = connection.get_record( ref, 'VIF' ) + new(obj) + end + rescue Fog::XenServer::NotFound + nil + end + + end + + end + end +end diff --git a/lib/fog/xenserver/parser.rb b/lib/fog/xenserver/parser.rb new file mode 100644 index 000000000..95a3488d4 --- /dev/null +++ b/lib/fog/xenserver/parser.rb @@ -0,0 +1,29 @@ +module Fog + module Parsers + module XenServer + class Base + + attr_reader :response + + def initialize + reset + end + + def reset + @response = {} + end + + def parse( data ) + if data.is_a? Hash + @response = data.symbolize_keys! + elsif data.is_a? Array + @response = data.first + end + + @response + end + + end + end + end +end \ No newline at end of file diff --git a/lib/fog/xenserver/parsers/get_hosts.rb b/lib/fog/xenserver/parsers/get_hosts.rb new file mode 100644 index 000000000..c36fc0e0a --- /dev/null +++ b/lib/fog/xenserver/parsers/get_hosts.rb @@ -0,0 +1,19 @@ +module Fog + module Parsers + module XenServer + class GetHosts < Fog::Parsers::XenServer::Base + + def reset + @response = [] + end + + def parse( data ) + parser = Fog::Parsers::XenServer::Base.new + data.each_pair {|reference, host_hash| @response << parser.parse( host_hash ).merge(:reference => reference) } + end + + end + + end + end +end diff --git a/lib/fog/xenserver/parsers/get_networks.rb b/lib/fog/xenserver/parsers/get_networks.rb new file mode 100644 index 000000000..707001cca --- /dev/null +++ b/lib/fog/xenserver/parsers/get_networks.rb @@ -0,0 +1,19 @@ +module Fog + module Parsers + module XenServer + class GetNetworks < Fog::Parsers::XenServer::Base + + def reset + @response = [] + end + + def parse( data ) + parser = Fog::Parsers::XenServer::Base.new + data.each_pair {|reference, network_hash| @response << parser.parse( network_hash ).merge(:reference => reference) } + end + + end + + end + end +end diff --git a/lib/fog/xenserver/parsers/get_pools.rb b/lib/fog/xenserver/parsers/get_pools.rb new file mode 100644 index 000000000..6f09d3cef --- /dev/null +++ b/lib/fog/xenserver/parsers/get_pools.rb @@ -0,0 +1,19 @@ +module Fog + module Parsers + module XenServer + class GetPools < Fog::Parsers::XenServer::Base + + def reset + @response = [] + end + + def parse( data ) + parser = Fog::Parsers::XenServer::Base.new + data.each_pair {|reference, pool_hash| @response << parser.parse( pool_hash ).merge(:reference => reference) } + end + + end + + end + end +end diff --git a/lib/fog/xenserver/parsers/get_records.rb b/lib/fog/xenserver/parsers/get_records.rb new file mode 100644 index 000000000..e3fc5a6a6 --- /dev/null +++ b/lib/fog/xenserver/parsers/get_records.rb @@ -0,0 +1,19 @@ +module Fog + module Parsers + module XenServer + class GetRecords < Fog::Parsers::XenServer::Base + + def reset + @response = [] + end + + def parse( data ) + parser = Fog::Parsers::XenServer::Base.new + data.each_pair {|reference, hash| @response << parser.parse( hash ).merge(:reference => reference) } + end + + end + + end + end +end diff --git a/lib/fog/xenserver/parsers/get_storage_repositories.rb b/lib/fog/xenserver/parsers/get_storage_repositories.rb new file mode 100644 index 000000000..437f54c7a --- /dev/null +++ b/lib/fog/xenserver/parsers/get_storage_repositories.rb @@ -0,0 +1,19 @@ +module Fog + module Parsers + module XenServer + class GetStorageRepositories < Fog::Parsers::XenServer::Base + + def reset + @response = [] + end + + def parse( data ) + parser = Fog::Parsers::XenServer::Base.new + data.each_pair {|reference, sr_hash| @response << parser.parse( sr_hash ).merge(:reference => reference) } + end + + end + + end + end +end diff --git a/lib/fog/xenserver/parsers/get_vbds.rb b/lib/fog/xenserver/parsers/get_vbds.rb new file mode 100644 index 000000000..29ae02c91 --- /dev/null +++ b/lib/fog/xenserver/parsers/get_vbds.rb @@ -0,0 +1,19 @@ +module Fog + module Parsers + module XenServer + class GetVBDs < Fog::Parsers::XenServer::Base + + def reset + @response = [] + end + + def parse( data ) + parser = Fog::Parsers::XenServer::Base.new + data.each_pair {|reference, hash| @response << parser.parse( hash ).merge(:reference => reference) } + end + + end + + end + end +end diff --git a/lib/fog/xenserver/parsers/get_vifs.rb b/lib/fog/xenserver/parsers/get_vifs.rb new file mode 100644 index 000000000..0341d1695 --- /dev/null +++ b/lib/fog/xenserver/parsers/get_vifs.rb @@ -0,0 +1,19 @@ +module Fog + module Parsers + module XenServer + class GetVIFs < Fog::Parsers::XenServer::Base + + def reset + @response = [] + end + + def parse( data ) + parser = Fog::Parsers::XenServer::Base.new + data.each_pair {|reference, vif_hash| @response << parser.parse( vif_hash ).merge(:reference => reference) } + end + + end + + end + end +end diff --git a/lib/fog/xenserver/parsers/get_vms.rb b/lib/fog/xenserver/parsers/get_vms.rb new file mode 100644 index 000000000..26053b618 --- /dev/null +++ b/lib/fog/xenserver/parsers/get_vms.rb @@ -0,0 +1,19 @@ +module Fog + module Parsers + module XenServer + class GetVms < Fog::Parsers::XenServer::Base + + def reset + @response = [] + end + + def parse( data ) + parser = Fog::Parsers::XenServer::Base.new + data.each_pair {|reference, vm_hash| @response << parser.parse( vm_hash ).merge(:reference => reference) } + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/create_server.rb b/lib/fog/xenserver/requests/compute/create_server.rb new file mode 100644 index 000000000..855946f1c --- /dev/null +++ b/lib/fog/xenserver/requests/compute/create_server.rb @@ -0,0 +1,22 @@ +module Fog + module Compute + class XenServer + + class Real + + def create_server( name_label, template = nil, network = nil, extra_args = {}) + create_vm( name_label, template, network, extra_args) + end + + end + + class Mock + + def create_server() + Fog::Mock.not_implemented + end + + end + end + end +end diff --git a/lib/fog/xenserver/requests/compute/create_vif.rb b/lib/fog/xenserver/requests/compute/create_vif.rb new file mode 100644 index 000000000..3eeffe4d7 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/create_vif.rb @@ -0,0 +1,36 @@ +module Fog + module Compute + class XenServer + + class Real + + def create_vif( vm_ref, network_ref ) + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'VIF.create'}, default_vif_config(vm_ref, network_ref) ) + end + + def default_vif_config( vm_ref, network_ref, device_number = '0' ) + { + 'MAC_autogenerated' => 'True', + 'VM' => vm_ref, + 'network' => network_ref, + 'device' => device_number, + 'MAC' => '', + 'MTU' => '0', + 'other_config' => {}, + 'qos_algorithm_type' => 'ratelimit', + 'qos_algorithm_params' => {} + } + end + end + + class Mock + + def create_vm() + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/create_vm.rb b/lib/fog/xenserver/requests/compute/create_vm.rb new file mode 100644 index 000000000..3ab01f42f --- /dev/null +++ b/lib/fog/xenserver/requests/compute/create_vm.rb @@ -0,0 +1,66 @@ +module Fog + module Compute + class XenServer + class Real + + def create_vm( name_label, template = nil, network = nil, extra_args = {}) + template ||= default_template + network ||= default_network + + if template.nil? + raise "Invalid template" + end + + if template.kind_of? String + data = get_vm( template ) + template_string = template + template = Fog::Compute::XenServer::Server.new(data) + end + + begin + #FIXME: need to check that template exist actually + raise "Template #{template_string} does not exist" if template.allowed_operations.nil? + raise 'Clone Operation not Allowed' unless template.allowed_operations.include?('clone') + + # Clone the VM template + @connection.request( + {:parser => Fog::Parsers::XenServer::Base.new, :method => 'VM.clone'}, + template.reference, name_label + ) + new_vm = Fog::Compute::XenServer::Server.new get_vm( name_label ) + + # Not required + #@connection.request( + # {:parser => Fog::Parsers::XenServer::Base.new, :method => 'VM.set_affinity'}, + # new_vm.reference, hosts.all.first.reference + #) + + # + # Create VIF + #create_vif( new_vm.reference, network.reference ) + + # raise Fog::XenServer::OperationFailed unless new_vm.allowed_operations.include?('provision') + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'VM.provision'}, new_vm.reference) + start_vm( new_vm.reference ) unless extra_args[:auto_start] == false + + new_vm + rescue => e + puts e.message + puts e.backtrace + puts "ERROR" + end + end + + end + + class Mock + + def create_vm() + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/destroy_server.rb b/lib/fog/xenserver/requests/compute/destroy_server.rb new file mode 100644 index 000000000..45c111165 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/destroy_server.rb @@ -0,0 +1,22 @@ +module Fog + module Compute + class XenServer + + class Real + + def destroy_server( vm_ref , extra_args = {}) + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'VM.destroy'}, vm_ref) + end + + end + + class Mock + + def destroy_server() + Fog::Mock.not_implemented + end + + end + end + end +end diff --git a/lib/fog/xenserver/requests/compute/destroy_vbd.rb b/lib/fog/xenserver/requests/compute/destroy_vbd.rb new file mode 100644 index 000000000..39071e7d3 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/destroy_vbd.rb @@ -0,0 +1,22 @@ +module Fog + module Compute + class XenServer + + class Real + + def destroy_vbd( vbd_ref, extra_args = {}) + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'VBD.destroy'}, vbd_ref) + end + + end + + class Mock + + def destroy_vbd() + Fog::Mock.not_implemented + end + + end + end + end +end diff --git a/lib/fog/xenserver/requests/compute/destroy_vdi.rb b/lib/fog/xenserver/requests/compute/destroy_vdi.rb new file mode 100644 index 000000000..ac4f428fb --- /dev/null +++ b/lib/fog/xenserver/requests/compute/destroy_vdi.rb @@ -0,0 +1,22 @@ +module Fog + module Compute + class XenServer + + class Real + + def destroy_vdi( vdi_ref, extra_args = {}) + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'VDI.destroy'}, vdi_ref) + end + + end + + class Mock + + def destroy_vdi() + Fog::Mock.not_implemented + end + + end + end + end +end diff --git a/lib/fog/xenserver/requests/compute/get_hosts.rb b/lib/fog/xenserver/requests/compute/get_hosts.rb new file mode 100644 index 000000000..e6d06e657 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/get_hosts.rb @@ -0,0 +1,27 @@ +module Fog + module Compute + class XenServer + + class Real + + require 'fog/xenserver/parsers/get_hosts' + + def get_hosts( options = {} ) + options[:sort] ||= 'name_label' + result = @connection.request(:parser => Fog::Parsers::XenServer::GetHosts.new, :method => 'host.get_all_records') + result.sort! {|a,b| a[ options[:sort].to_sym ] <=> b[ options[:sort].to_sym ]} + end + + end + + class Mock + + def get_hosts + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/get_network.rb b/lib/fog/xenserver/requests/compute/get_network.rb new file mode 100644 index 000000000..4e0541b2b --- /dev/null +++ b/lib/fog/xenserver/requests/compute/get_network.rb @@ -0,0 +1,26 @@ +module Fog + module XenServer + class Real + + require 'fog/xenserver/parser' + + def get_network( name_label ) + network_ref = @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'network.get_by_name_label'}, name_label) + get_network_by_ref( network_ref ) + end + + def get_network_by_ref( network_ref ) + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'network.get_record'}, network_ref).merge(:reference => network_ref) + end + + end + + class Mock + + def get_network( uuid ) + Fog::Mock.not_implemented + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/get_networks.rb b/lib/fog/xenserver/requests/compute/get_networks.rb new file mode 100644 index 000000000..43c5ab582 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/get_networks.rb @@ -0,0 +1,23 @@ +module Fog + module XenServer + class Real + + require 'fog/xenserver/parsers/get_networks' + + def get_networks( options = {} ) + options[:sort] ||= 'name_label' + result = @connection.request(:parser => Fog::Parsers::XenServer::GetNetworks.new, :method => 'network.get_all_records') + result.sort! {|a,b| a[ options[:sort].to_sym ] <=> b[ options[:sort].to_sym ]} + end + + end + + class Mock + + def get_networks + Fog::Mock.not_implemented + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/get_pool.rb b/lib/fog/xenserver/requests/compute/get_pool.rb new file mode 100644 index 000000000..9f183b7fb --- /dev/null +++ b/lib/fog/xenserver/requests/compute/get_pool.rb @@ -0,0 +1,24 @@ +module Fog + module Compute + class XenServer + + class Real + + require 'fog/xenserver/parser' + + def get_pool_by_ref( pool_ref ) + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'VM.get_record'}, pool_ref).merge(:reference => pool_ref) + end + end + + class Mock + + def get_pool( uuid ) + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/get_pools.rb b/lib/fog/xenserver/requests/compute/get_pools.rb new file mode 100644 index 000000000..4daa6cdcd --- /dev/null +++ b/lib/fog/xenserver/requests/compute/get_pools.rb @@ -0,0 +1,27 @@ +module Fog + module Compute + class XenServer + + class Real + + require 'fog/xenserver/parsers/get_pools' + + def get_pools( options = {} ) + options[:sort] ||= 'name_label' + result = @connection.request(:parser => Fog::Parsers::XenServer::GetPools.new, :method => 'pool.get_all_records') + result.sort! {|a,b| a[ options[:sort].to_sym ] <=> b[ options[:sort].to_sym ]} + end + + end + + class Mock + + def get_pools + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/get_record.rb b/lib/fog/xenserver/requests/compute/get_record.rb new file mode 100644 index 000000000..78af8fcee --- /dev/null +++ b/lib/fog/xenserver/requests/compute/get_record.rb @@ -0,0 +1,29 @@ +module Fog + module Compute + class XenServer + + class Real + + require 'fog/xenserver/parser' + + def get_record( ref, klass, options = {} ) + get_record_by_ref ref, klass, options + end + + def get_record_by_ref( ref, klass, options = {} ) + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => "#{klass}.get_record"}, ref).merge(:reference => ref) + end + + end + + class Mock + + def get_record_by_ref + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/get_records.rb b/lib/fog/xenserver/requests/compute/get_records.rb new file mode 100644 index 000000000..1f576d5e5 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/get_records.rb @@ -0,0 +1,25 @@ +module Fog + module Compute + class XenServer + + class Real + + require 'fog/xenserver/parsers/get_records' + + def get_records( klass, options = {} ) + @connection.request(:parser => Fog::Parsers::XenServer::GetRecords.new, :method => "#{klass}.get_all_records") + end + + end + + class Mock + + def get_vms + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/get_storage_repositories.rb b/lib/fog/xenserver/requests/compute/get_storage_repositories.rb new file mode 100644 index 000000000..7119d9798 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/get_storage_repositories.rb @@ -0,0 +1,26 @@ +module Fog + module Compute + class XenServer + class Real + + require 'fog/xenserver/parsers/get_storage_repositories' + + def get_storage_repositories( options = {} ) + options[:sort] ||= 'name_label' + result = @connection.request(:parser => Fog::Parsers::XenServer::GetStorageRepositories.new, :method => 'SR.get_all_records') + result.sort! {|a,b| a[ options[:sort].to_sym ] <=> b[ options[:sort].to_sym ]} + end + + end + + class Mock + + def get_storage_repositories + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/get_storage_repository.rb b/lib/fog/xenserver/requests/compute/get_storage_repository.rb new file mode 100644 index 000000000..455528522 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/get_storage_repository.rb @@ -0,0 +1,33 @@ +module Fog + module Compute + class XenServer + + class Real + + require 'fog/xenserver/parser' + + def get_storage_repository( name_label ) + sr_ref = @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'SR.get_by_name_label'}, name_label) + get_storage_repository_by_ref( sr_ref ) + end + + def get_storage_repository_by_ref( sr_ref ) + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'SR.get_record'}, sr_ref).merge(:reference => sr_ref) + end + end + + class Mock + + def get_storage_repository( uuid ) + Fog::Mock.not_implemented + end + + def get_storage_repository_by_ref( sr_ref ) + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/get_vbds.rb b/lib/fog/xenserver/requests/compute/get_vbds.rb new file mode 100644 index 000000000..75387541c --- /dev/null +++ b/lib/fog/xenserver/requests/compute/get_vbds.rb @@ -0,0 +1,25 @@ +module Fog + module Compute + class XenServer + + class Real + + require 'fog/xenserver/parsers/get_records' + + def get_vbds( options = {} ) + @connection.request(:parser => Fog::Parsers::XenServer::GetRecords.new, :method => 'VBD.get_all_records') + end + + end + + class Mock + + def get_vbds + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/get_vifs.rb b/lib/fog/xenserver/requests/compute/get_vifs.rb new file mode 100644 index 000000000..97382a254 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/get_vifs.rb @@ -0,0 +1,27 @@ +module Fog + module Compute + class XenServer + + class Real + + require 'fog/xenserver/parsers/get_vifs' + + def get_vifs( options = {} ) + options[:sort] ||= 'name_label' + result = @connection.request(:parser => Fog::Parsers::XenServer::GetVIFs.new, :method => 'VIF.get_all_records') + result.sort! {|a,b| a[ options[:sort].to_sym ] <=> b[ options[:sort].to_sym ]} + end + + end + + class Mock + + def get_vifs + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/get_vm.rb b/lib/fog/xenserver/requests/compute/get_vm.rb new file mode 100644 index 000000000..681584f4d --- /dev/null +++ b/lib/fog/xenserver/requests/compute/get_vm.rb @@ -0,0 +1,29 @@ +module Fog + module Compute + class XenServer + + class Real + + require 'fog/xenserver/parser' + + def get_vm( name_label ) + vm_ref = @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'VM.get_by_name_label'}, name_label) + get_vm_by_ref( vm_ref ) + end + + def get_vm_by_ref( vm_ref ) + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'VM.get_record'}, vm_ref).merge(:reference => vm_ref) + end + end + + class Mock + + def get_vm( uuid ) + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/get_vms.rb b/lib/fog/xenserver/requests/compute/get_vms.rb new file mode 100644 index 000000000..9272da6c1 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/get_vms.rb @@ -0,0 +1,27 @@ +module Fog + module Compute + class XenServer + + class Real + + require 'fog/xenserver/parsers/get_vms' + + def get_vms( options = {} ) + options[:sort] ||= 'name_label' + result = @connection.request(:parser => Fog::Parsers::XenServer::GetVms.new, :method => 'VM.get_all_records') + result.sort! {|a,b| a[ options[:sort].to_sym ] <=> b[ options[:sort].to_sym ]} + end + + end + + class Mock + + def get_vms + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/reboot_server.rb b/lib/fog/xenserver/requests/compute/reboot_server.rb new file mode 100644 index 000000000..77b912ef9 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/reboot_server.rb @@ -0,0 +1,23 @@ +module Fog + module Compute + class XenServer + + class Real + + def reboot_server( ref, stype = 'clean' ) + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => "VM.#{stype}_shutdown"}, ref) + end + + end + + class Mock + + def reboot_server( ref, stype ) + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/set_affinity.rb b/lib/fog/xenserver/requests/compute/set_affinity.rb new file mode 100644 index 000000000..cce80d5d2 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/set_affinity.rb @@ -0,0 +1,25 @@ +module Fog + module Compute + class XenServer + + class Real + + require 'fog/xenserver/parser' + + def set_affinity( host_ref ) + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'VM.set_affinity'}, host_ref) + end + + end + + class Mock + + def set_affinity( uuid ) + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/shutdown_server.rb b/lib/fog/xenserver/requests/compute/shutdown_server.rb new file mode 100644 index 000000000..d5017f532 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/shutdown_server.rb @@ -0,0 +1,23 @@ +module Fog + module Compute + class XenServer + + class Real + + def shutdown_server( vm_ref, stype = 'clean' ) + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => "VM.#{stype}_shutdown"}, vm_ref) + end + + end + + class Mock + + def shutdown_server( vm_ref ) + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/start_server.rb b/lib/fog/xenserver/requests/compute/start_server.rb new file mode 100644 index 000000000..8cdb857d9 --- /dev/null +++ b/lib/fog/xenserver/requests/compute/start_server.rb @@ -0,0 +1,23 @@ +module Fog + module Compute + + class XenServer + class Real + + def start_server( vm_ref ) + start_vm( vm_ref ) + end + + end + + class Mock + + def start_server( vm_ref ) + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/requests/compute/start_vm.rb b/lib/fog/xenserver/requests/compute/start_vm.rb new file mode 100644 index 000000000..e3e0a45ad --- /dev/null +++ b/lib/fog/xenserver/requests/compute/start_vm.rb @@ -0,0 +1,25 @@ +module Fog + module Compute + class XenServer + + class Real + + # http://bit.ly/8ZPyCN + # VM.start( session, VM_ref, start_paused, force) + def start_vm( vm_ref ) + @connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'VM.start'}, vm_ref, false, false) + end + + end + + class Mock + + def start_vm( vm_ref ) + Fog::Mock.not_implemented + end + + end + + end + end +end diff --git a/lib/fog/xenserver/utilities.rb b/lib/fog/xenserver/utilities.rb new file mode 100644 index 000000000..f681a1ad1 --- /dev/null +++ b/lib/fog/xenserver/utilities.rb @@ -0,0 +1,8 @@ +class Hash + def symbolize_keys! + keys.each do |key| + self[(key.to_sym rescue key)] = delete(key) if key.respond_to?(:to_sym) && !key.is_a?(Fixnum) + end + self + end +end \ No newline at end of file diff --git a/tests/xenserver/README b/tests/xenserver/README new file mode 100644 index 000000000..754c639a3 --- /dev/null +++ b/tests/xenserver/README @@ -0,0 +1,21 @@ +# Running the XenServer tests. + +Tweak the helper.rb file in tests/xenserver/helper.rb to fit your needs. + +Fog credentials in $HOME/.fog file are used to run the tests. In particular: + + :default: + :xenserver_url: xenserver-test + :xenserver_username: root + :xenserver_password: secret + +We need a working template named squeeze-test available in the XenServer +to perform the tests. Change the template name to fit your needs +or create a working template named squeeze-test. + +The tests creates a few VMs during the flight. Make sure you have enough RAM and +Storage available in the hypervisor. + +Run the XenServer provider tests issuing: + + shindo tests/xenserver diff --git a/tests/xenserver/compute_tests.rb b/tests/xenserver/compute_tests.rb new file mode 100644 index 000000000..186c310bc --- /dev/null +++ b/tests/xenserver/compute_tests.rb @@ -0,0 +1,44 @@ +Shindo.tests('Fog::Compute[:xenserver]', ['xenserver']) do + + compute = Fog::Compute[:xenserver] + + tests("Compute attributes") do + %w{ default_template }.each do |attr| + test("it should respond to #{attr}") { compute.respond_to? attr } + end + end + + tests("Compute collections") do + %w{ pifs vifs hosts storage_repositories servers networks vbds vdis pools }.each do |collection| + test("it should respond to #{collection}") { compute.respond_to? collection } + test("it should respond to #{collection}.all") { eval("compute.#{collection}").respond_to? 'all' } + test("it should respond to #{collection}.get") { eval("compute.#{collection}").respond_to? 'get' } + end + # This will fail if there are no PIFs + # (not sure if that's gonna be a real scenario though) + tests("PIFs collection") do + test("should have at least one PIF") { compute.pifs.size >= 1 } + end + + # This will fail if there are no VIFs + # (not sure if that's gonna be a real scenario though) + tests("VIFs collection") do + test("should have at least one VIF") { compute.vifs.size >= 1 } + end + + # This will fail if there are no VIFs + # (not sure if that's gonna be a real scenario though) + tests("Networks collection") do + test("should have at least one Network") { compute.networks.size >= 1 } + end + end + + tests "Default template" do + test("it should NOT have a default template") { compute.default_template.nil? } + # This template exists in our XenServer + compute.default_template = 'squeeze-test' + test("it should have a default template if template exists") { compute.default_template.name == 'squeeze-test' } + test("default template must be a Fog::Compute::XenServer::Server") { compute.default_template.is_a? Fog::Compute::XenServer::Server } + end +end + diff --git a/tests/xenserver/helper.rb b/tests/xenserver/helper.rb new file mode 100644 index 000000000..cd3ea2835 --- /dev/null +++ b/tests/xenserver/helper.rb @@ -0,0 +1,8 @@ +def test_template_name + 'squeeze-test' +end + +def test_ephemeral_vm_name + 'fog-test-server-shindo' +end + diff --git a/tests/xenserver/models/compute/host_tests.rb b/tests/xenserver/models/compute/host_tests.rb new file mode 100644 index 000000000..cd5cf1262 --- /dev/null +++ b/tests/xenserver/models/compute/host_tests.rb @@ -0,0 +1,67 @@ + +Shindo.tests('Fog::Compute[:xenserver] | host model', ['xenserver']) do + + hosts = Fog::Compute[:xenserver].hosts + host = hosts.first + + tests('The host model should') do + tests('have the action') do + test('reload') { host.respond_to? 'reload' } + end + + tests('have attributes') do + model_attribute_hash = host.attributes + attributes = [ + :reference, + :uuid, + :name, + :address, + :allowed_operations, + :enabled, + :hostname, + :metrics, + :name_description, + :other_config, + :__pbds, + :__pifs, + :__resident_vms + ] + tests("The host model should respond to") do + attributes.each do |attribute| + test("#{attribute}") { host.respond_to? attribute } + end + end + tests("The attributes hash should have key") do + attributes.each do |attribute| + test("#{attribute}") { model_attribute_hash.has_key? attribute } + end + end + end + + test('be a kind of Fog::Compute::XenServer::Host') { host.kind_of? Fog::Compute::XenServer::Host } + + end + + tests("A real host should") do + tests("return valid PIFs") do + test("as an array") { host.pifs.kind_of? Array } + host.pifs.each { |i| + test("and each PIF should be a Fog::Compute::XenServer::PIF") { i.kind_of? Fog::Compute::XenServer::PIF} + } + end + tests("return valid PBDs") do + test("as an array") { host.pbds.kind_of? Array } + host.pbds.each { |i| + test("and each PBD should be a Fog::Compute::XenServer::PBD") { i.kind_of? Fog::Compute::XenServer::PBD} + } + end + tests("return valid resident servers") do + test("as an array") { host.resident_servers.kind_of? Array } + host.resident_servers.each { |i| + test("and each Server should be a Fog::Compute::XenServer::Server") { i.kind_of? Fog::Compute::XenServer::Server} + } + end + + end + +end diff --git a/tests/xenserver/models/compute/hosts_tests.rb b/tests/xenserver/models/compute/hosts_tests.rb new file mode 100644 index 000000000..aacada156 --- /dev/null +++ b/tests/xenserver/models/compute/hosts_tests.rb @@ -0,0 +1,22 @@ +Shindo.tests('Fog::Compute[:xenserver] | hosts collection', ['xenserver']) do + + conn = Fog::Compute[:xenserver] + + tests('The hosts collection') do + hosts = conn.hosts.all + + test('should not be empty') { !hosts.empty? } + + test('should be a kind of Fog::Compute::XenServer::Hosts') { hosts.kind_of? Fog::Compute::XenServer::Hosts } + + tests('should be able to reload itself').succeeds { hosts.reload } + + tests('should be able to get a model') do + tests('by reference').succeeds { + hosts.get(hosts.first.reference).is_a? Fog::Compute::XenServer::Host + } + end + + end + +end diff --git a/tests/xenserver/models/compute/network_tests.rb b/tests/xenserver/models/compute/network_tests.rb new file mode 100644 index 000000000..d2e14477f --- /dev/null +++ b/tests/xenserver/models/compute/network_tests.rb @@ -0,0 +1,67 @@ +Shindo.tests('Fog::Compute[:network] | network model', ['network']) do + + require 'pp' + networks = Fog::Compute[:xenserver].networks + network = networks.first + + tests('The network model should') do + tests('have the action') do + test('reload') { network.respond_to? 'reload' } + #%w{ refresh stop clean_shutdown hard_shutdown start destroy reboot hard_reboot clean_reboot }.each do |action| + # test(action) { server.respond_to? action } + # #test("#{action} returns successfully") { server.send(action.to_sym) ? true : false } + #end + end + tests('have attributes') do + model_attribute_hash = network.attributes + attributes = [ + :reference, + :uuid, + :__vifs, + :tags, + :mtu, + :bridge, + :name, + :other_config, + :__pifs, + :allowed_operations, + :current_operations, + :blobs + ] + tests("The network model should respond to") do + attributes.each do |attribute| + test("#{attribute}") { network.respond_to? attribute } + end + end + tests("The attributes hash should have key") do + attributes.each do |attribute| + test("#{attribute}") { model_attribute_hash.has_key? attribute } + end + end + end + + test('be a kind of Fog::Compute::XenServer::Network') { network.kind_of? Fog::Compute::XenServer::Network } + + end + + tests("A real network should") do + tests("return valid vifs") do + test("as an array") { network.vifs.kind_of? Array } + network.vifs.each { |i| + test("and each VIF should be a Fog::Compute::XenServer::VIF") { i.kind_of? Fog::Compute::XenServer::VIF } + } + end + tests("return valid PIFs") do + networks.each do |network| + test("as an array") { network.pifs.kind_of? Array } + network.pifs.each { |i| + test("and each PIF should be a Fog::Compute::XenServer::PIF") { i.kind_of? Fog::Compute::XenServer::PIF} + } + end + end + + test("be able to refresh itself") { network.refresh } + + end + +end diff --git a/tests/xenserver/models/compute/pbd_tests.rb b/tests/xenserver/models/compute/pbd_tests.rb new file mode 100644 index 000000000..15933a654 --- /dev/null +++ b/tests/xenserver/models/compute/pbd_tests.rb @@ -0,0 +1,44 @@ +Shindo.tests('Fog::Compute[:network] | network model', ['network']) do + + pbds = Fog::Compute[:xenserver].pbds + pbd = pbds.first + + tests('The PBD model should') do + tests('have the action') do + test('reload') { pbd.respond_to? 'reload' } + end + tests('have attributes') do + model_attribute_hash = pbd.attributes + attributes = [ + :reference, + :uuid, + :__host, + :__sr + ] + tests("The PBD model should respond to") do + attributes.each do |attribute| + test("#{attribute}") { pbd.respond_to? attribute } + end + end + tests("The attributes hash should have key") do + attributes.each do |attribute| + test("#{attribute}") { model_attribute_hash.has_key? attribute } + end + end + end + + test('be a kind of Fog::Compute::XenServer::PBD') { pbd.kind_of? Fog::Compute::XenServer::PBD} + + end + + tests("A real PBD should") do + tests("return valid host") do + test("should be a Fog::Compute::XenServer::Host") { pbd.host.kind_of? Fog::Compute::XenServer::Host } + end + tests("return valid storage repository") do + test("should be a Fog::Compute::XenServer::StorageRepository") { pbd.storage_repository.kind_of? Fog::Compute::XenServer::StorageRepository } + end + + end + +end diff --git a/tests/xenserver/models/compute/pbds_tests.rb b/tests/xenserver/models/compute/pbds_tests.rb new file mode 100644 index 000000000..2d87f98ce --- /dev/null +++ b/tests/xenserver/models/compute/pbds_tests.rb @@ -0,0 +1,22 @@ +Shindo.tests('Fog::Compute[:xenserver] | pbds collection', ['xenserver']) do + + conn = Fog::Compute[:xenserver] + + tests('The pbds collection') do + pbds = conn.pbds.all + + test('should not be empty') { !pbds.empty? } + + test('should be a kind of Fog::Compute::XenServer::Pbds') { pbds.kind_of? Fog::Compute::XenServer::Pbds } + + tests('should be able to reload itself').succeeds { pbds.reload } + + tests('should be able to get a model') do + tests('by reference').succeeds { + pbds.get(pbds.first.reference).is_a? Fog::Compute::XenServer::PBD + } + end + + end + +end diff --git a/tests/xenserver/models/compute/pif_tests.rb b/tests/xenserver/models/compute/pif_tests.rb new file mode 100644 index 000000000..4a056f082 --- /dev/null +++ b/tests/xenserver/models/compute/pif_tests.rb @@ -0,0 +1,58 @@ +Shindo.tests('Fog::Compute[:network] | network model', ['network']) do + + pifs = Fog::Compute[:xenserver].pifs + pif = pifs.first + + tests('The PIF model should') do + tests('have the action') do + test('reload') { pif.respond_to? 'reload' } + end + tests('have attributes') do + model_attribute_hash = pif.attributes + attributes = [ + :reference, + :uuid, + :physical, + :mac, + :currently_attached, + :device, + :metrics, + :dns, + :gateway, + :ip, + :ip_configuration_mode, + :mtu, + :__network, + :netmask, + :management, + :vlan, + :other_config, + :__host + ] + tests("The PIF model should respond to") do + attributes.each do |attribute| + test("#{attribute}") { pif.respond_to? attribute } + end + end + tests("The attributes hash should have key") do + attributes.each do |attribute| + test("#{attribute}") { model_attribute_hash.has_key? attribute } + end + end + end + + test('be a kind of Fog::Compute::XenServer::PIF') { pif.kind_of? Fog::Compute::XenServer::PIF} + + end + + tests("A real PIF should") do + tests("return a valid network") do + test("should be a Fog::Compute::XenServer::Network") { pif.network.kind_of? Fog::Compute::XenServer::Network } + end + tests("return valid host") do + test("should be a Fog::Compute::XenServer::Host") { pif.host.kind_of? Fog::Compute::XenServer::Host } + end + + end + +end diff --git a/tests/xenserver/models/compute/server_tests.rb b/tests/xenserver/models/compute/server_tests.rb new file mode 100644 index 000000000..e5f133d7a --- /dev/null +++ b/tests/xenserver/models/compute/server_tests.rb @@ -0,0 +1,109 @@ + +Shindo.tests('Fog::Compute[:xenserver] | server model', ['xenserver']) do + + servers = Fog::Compute[:xenserver].servers + # pre-flight cleanup + (servers.all :name_matches => test_ephemeral_vm_name).each do |s| + s.destroy + end + + server = Fog::Compute[:xenserver].servers.create(:name => test_ephemeral_vm_name, + :template_name => test_template_name) + + tests('The server model should') do + tests('have the action') do + test('reload') { server.respond_to? 'reload' } + %w{ refresh stop clean_shutdown hard_shutdown start destroy reboot hard_reboot clean_reboot }.each do |action| + test(action) { server.respond_to? action } + #test("#{action} returns successfully") { server.send(action.to_sym) ? true : false } + end + end + tests('have attributes') do + model_attribute_hash = server.attributes + attributes = [ + :reference, + :uuid, + :is_a_template, + :affinity, + :allowed_operations, + :consoles, + :domarch, + :domid, + :guest_metrics, + :is_a_snapshot, + :is_a_template, + :is_control_domain, + :memory_dynamic_max, + :memory_dynamic_min, + :memory_static_max, + :memory_static_min, + :memory_target, + :metrics, + :name_description, + :other_config, + :power_state, + :pv_args, + :resident_on, + :__vbds, + :vcpus_at_startup, + :vcpus_max, + :__vifs + ] + tests("The server model should respond to") do + attributes.each do |attribute| + test("#{attribute}") { server.respond_to? attribute } + end + end + tests("The attributes hash should have key") do + attributes.each do |attribute| + test("#{attribute}") { model_attribute_hash.has_key? attribute } + end + end + end + + test('be a kind of Fog::Compute::XenServer::Server') { server.kind_of? Fog::Compute::XenServer::Server } + + end + + tests("A real server should") do + tests("return valid vbds") do + test("as an array") { server.vbds.kind_of? Array } + server.vbds.each { |i| + test("and each VBD should be a Fog::Compute::XenServer::VBD") { i.kind_of? Fog::Compute::XenServer::VBD } + } + end + + test("be able to refresh itself") { server.refresh } + + test("always stop when I say stop('hard')") do + server.stop 'hard' + end + + # shutdown is async apparently, we wait + test("not be running when it's stopped") do + server.wait_for { !server.running? } + true + end + + test("do nothing when I say stop('hard') but it's not running") do + server.stop('hard') == false + end + + test("always start when I say start") do + server.start + end + + # start is async apparently, we wait + test("be running if I started the server") do + server.wait_for { server.running? } + true + end + + test("be able to be destroyed!") do + server.destroy + servers.get_by_name('fog-test-server-shindo') == nil + end + + end + +end diff --git a/tests/xenserver/models/compute/servers_tests.rb b/tests/xenserver/models/compute/servers_tests.rb new file mode 100644 index 000000000..027ffc978 --- /dev/null +++ b/tests/xenserver/models/compute/servers_tests.rb @@ -0,0 +1,30 @@ +Shindo.tests('Fog::Compute[:xenserver] | servers collection', ['xenserver']) do + + #require 'pp' + conn = Fog::Compute[:xenserver] + # pre-flight cleanup + (conn.servers.all :name_matches => test_ephemeral_vm_name).each do |s| + s.destroy + end + # Create some test data + server = conn.servers.create(:name => test_ephemeral_vm_name, + :template_name => test_template_name) + server.wait_for { running? } + + tests('The servers collection') do + servers = conn.servers.all + + test('should not be empty') { !servers.empty? } + + test('should be a kind of Fog::Compute::XenServer::Servers') { servers.kind_of? Fog::Compute::XenServer::Servers } + + tests('should be able to reload itself').succeeds { servers.reload } + + tests('should be able to get a model') do + tests('by name').succeeds { servers.get_by_name test_ephemeral_vm_name } + tests('by instance uuid').succeeds { servers.get(servers.get_by_name(test_ephemeral_vm_name).reference) } + end + + end + +end