Project

General

Profile

Download (9.17 KB) Statistics
| Branch: | Tag: | Revision:
module ForemanXen
class Xenserver < ComputeResource
validates_presence_of :url, :user, :password

def provided_attributes
super.merge(
{ :uuid => :reference,
:mac => :mac
})
end

def capabilities
[:build]
end

def find_vm_by_uuid(ref)
client.servers.get(ref)
rescue Fog::XenServer::RequestFailed => e
raise(ActiveRecord::RecordNotFound) if e.message.include?('HANDLE_INVALID')
raise(ActiveRecord::RecordNotFound) if e.message.include?('VM.get_record: ["SESSION_INVALID"')
raise e
end

# we default to destroy the VM's storage as well.
def destroy_vm(ref, args = {})
find_vm_by_uuid(ref).destroy
rescue ActiveRecord::RecordNotFound
true
end

def self.model_name
ComputeResource.model_name
end

def max_cpu_count
## 16 is a max number of cpus per vm according to XenServer doc
[hypervisor.host_cpus.size, 16].min
end

def max_memory
xenServerMaxDoc = 128*1024*1024*1024
[hypervisor.metrics.memory_total.to_i, xenServerMaxDoc].min
rescue => e
logger.debug "unable to figure out free memory, guessing instead due to:#{e}"
16*1024*1024*1024
end

def test_connection(options = {})
super
errors[:url].empty? and hypervisor
rescue => e
disconnect rescue nil
errors[:base] << e.message
end

def new_nic(attr={})
client.networks.new attr
end

def new_volume(attr={})
client.storage_repositories.new attr
end

def storage_pools
storages = client.storage_repositories.select { |sr| sr.type!= 'udev' && sr.type!= 'iso'} rescue []
storages.sort { |a, b| a.name <=> b.name }
end

def interfaces
client.interfaces rescue []
end

def networks
networks = client.networks rescue []
networks.sort { |a, b| a.name <=> b.name }
end

def templates
client.servers.templates rescue []
end

def custom_templates
tmps = client.servers.custom_templates.select { |t| !t.is_a_snapshot } rescue []
tmps.sort { |a, b| a.name <=> b.name }
end

def builtin_templates
tmps = client.servers.builtin_templates.select { |t| !t.is_a_snapshot } rescue []
tmps.sort { |a, b| a.name <=> b.name }
end

def associated_host(vm)
ips = []
begin
if vm.tools_installed?
vm.guest_metrics.networks.each do |k,v|
ips << v
end
end
rescue => e
logger.error("Error retrieving Network via Guest Metrics: #{e.message}")
end

Host.authorized(:view_hosts, Host).where(:ip => ips).first
end

def new_vm(attr={})

test_connection
return unless errors.empty?
opts = vm_instance_defaults.merge(attr.to_hash).symbolize_keys

[:networks, :volumes].each do |collection|
nested_attrs = opts.delete("#{collection}_attributes".to_sym)
opts[collection] = nested_attributes_for(collection, nested_attrs) if nested_attrs
end
opts.reject! { |_, v| v.nil? }
client.servers.new opts
end

def create_vm(args = {})
custom_template_name = args[:custom_template_name]
builtin_template_name = args[:builtin_template_name]
raise 'you can select at most one template type' if builtin_template_name != '' and custom_template_name != ''
begin
vm = nil
if custom_template_name != ''
vm = create_vm_from_custom args
else
vm = create_vm_from_builtin args
end
vm.set_attribute('name_description', 'Provisioned by Foreman')
cpus = args[:vcpus_max]
if vm.vcpus_max.to_i < cpus.to_i
vm.set_attribute('VCPUs_max', cpus)
vm.set_attribute('VCPUs_at_startup', cpus)
else
vm.set_attribute('VCPUs_at_startup', cpus)
vm.set_attribute('VCPUs_max', cpus)
end
vm.refresh
return vm
rescue => e
logger.info e
logger.info e.backtrace.join("\n")
return false
end
end

def create_vm_from_custom(args)
mem_max = args[:memory_max]
mem_min = args[:memory_min]

raise 'Memory max cannot be lower than Memory min' if mem_min.to_i > mem_max.to_i
vm = client.servers.new :name => args[:name],
:template_name => args[:custom_template_name]

vm.save :auto_start => false

vm.provision

vm.vifs.first.destroy rescue nil

create_network(vm, args)

args['xenstore']['vm-data']['ifs']['0']['mac'] = vm.vifs.first.mac
xenstore_data = xenstore_hash_flatten(args['xenstore'])

vm.set_attribute('xenstore_data', xenstore_data)
if vm.memory_static_max.to_i < mem_max.to_i
vm.set_attribute('memory_static_max', mem_max)
vm.set_attribute('memory_dynamic_max', mem_max)
vm.set_attribute('memory_dynamic_min', mem_min)
vm.set_attribute('memory_static_min', mem_min)
else
vm.set_attribute('memory_static_min', mem_min)
vm.set_attribute('memory_dynamic_min', mem_min)
vm.set_attribute('memory_dynamic_max', mem_max)
vm.set_attribute('memory_static_max', mem_max)
end

disks = vm.vbds.select { |vbd| vbd.type == 'Disk' }
disks.sort! { |a, b| a.userdevice <=> b.userdevice }
i = 0
disks.each do |vbd|
vbd.vdi.set_attribute('name-label', "#{args[:name]}_#{i}")
i+=1
end
vm
end

def create_vm_from_builtin(args)

host = client.hosts.first
storage_repository = client.storage_repositories.find { |sr| sr.name == "#{args[:VBDs][:print]}" }

gb = 1073741824 #1gb in bytes
size = args[:VBDs][:physical_size].to_i * gb
vdi = client.vdis.create :name => "#{args[:name]}-disk1",
:storage_repository => storage_repository,
:description => "#{args[:name]}-disk_1",
:virtual_size => size.to_s

mem_max = args[:memory_max]
mem_min = args[:memory_min]
other_config = {}
if args[:builtin_template_name] != ''
template = client.servers.builtin_templates.find { |tmp| tmp.name == args[:builtin_template_name] }
other_config = template.other_config
other_config.delete 'disks'
other_config.delete 'default_template'
end
vm = client.servers.new :name => args[:name],
:affinity => host,
:pv_bootloader => '',
:hvm_boot_params => { :order => 'dnc' },
:other_config => other_config,
:memory_static_max => mem_max,
:memory_static_min => mem_min,
:memory_dynamic_max => mem_max,
:memory_dynamic_min => mem_min

vm.save :auto_start => false
client.vbds.create :server => vm, :vdi => vdi

create_network(vm, args)

vm.provision
vm.set_attribute('HVM_boot_policy', 'BIOS order')
vm.refresh
vm
end

def console uuid
vm = find_vm_by_uuid(uuid)
raise 'VM is not running!' unless vm.ready?

console = vm.service.consoles.find { |c| c.__vm == vm.reference && c.protocol == 'rfb' }
raise "No console fore vm #{vm.name}" if console == nil

session_ref = (vm.service.instance_variable_get :@connection).instance_variable_get :@credentials
fullURL = "#{console.location}&session_id=#{session_ref}"
tunnel = VNCTunnel.new fullURL
tunnel.start
logger.info 'VNCTunnel started'
WsProxy.start(:host => tunnel.host, :host_port => tunnel.port, :password => '').merge(:type => 'vnc', :name => vm.name)

rescue Error => e
logger.warn e
raise e
end

def hypervisor
client.hosts.first
end

protected

def client
@client ||= ::Fog::Compute.new({ :provider => 'XenServer', :xenserver_url => url, :xenserver_username => user, :xenserver_password => password })
end

def disconnect
client.terminate if @client
@client = nil
end

def vm_instance_defaults
super.merge({})
end


private

def create_network(vm, args)
net = client.networks.find { |n| n.name == args[:VIFs][:print] }
net_config = {
'MAC_autogenerated' => 'True',
'VM' => vm.reference,
'network' => net.reference,
'MAC' => '',
'device' => '0',
'MTU' => '0',
'other_config' => {},
'qos_algorithm_type' => 'ratelimit',
'qos_algorithm_params' => {}
}
client.create_vif_custom net_config
vm.reload
end

def xenstore_hash_flatten(nested_hash, key=nil, keychain=nil, out_hash={})
nested_hash.each do |k, v|
if v.is_a? Hash
xenstore_hash_flatten(v, k, "#{keychain}#{k}/", out_hash)
else
out_hash["#{keychain}#{k}"] = v
end
end
out_hash
# @key = key
end
end
end
    (1-1/1)