Project

General

Profile

Download (6.48 KB) Statistics
| Branch: | Tag: | Revision:
334d0359 Amos Benari
module Foreman::Model
class Libvirt < ComputeResource
553a0beb Joseph Magen
include ComputeResourceConsoleCommon

f2c78d4a Joseph Magen
validates :url, :format => { :with => URI.regexp }
7e031001 Ohad Levy
b760d48d Greg Sutcliffe
# Some getters/setters for the attrs Hash
def display_type
self.attrs[:display].present? ? self.attrs[:display] : 'vnc'
end

def display_type=(display)
self.attrs[:display] = display
end

dd42df0a Ohad Levy
def provided_attributes
super.merge({:mac => :mac})
end

43c4bd72 Marek Hulan
def interfaces_attrs_name
1fa8dcfb Daniel Lobato
:nics
43c4bd72 Marek Hulan
end

dd42df0a Ohad Levy
def capabilities
3d03e334 Dominic Cleal
[:build, :image]
dd42df0a Ohad Levy
end

5f029ed6 Daniel Lobato
def find_vm_by_uuid(uuid)
4269abbd Tomas Strachota
super
a6a6b703 David Swift
rescue ::Libvirt::RetrieveError => e
01e78260 Ivan Nečas
Foreman::Logging.exception("Failed retrieving libvirt vm by uuid #{ uuid }", e)
2312cccf Daniel Lobato
raise ActiveRecord::RecordNotFound
a6a6b703 David Swift
end

f37934af Ohad Levy
# we default to destroy the VM's storage as well.
5f029ed6 Daniel Lobato
def destroy_vm(uuid, args = { })
f37934af Ohad Levy
find_vm_by_uuid(uuid).destroy({ :destroy_volumes => true }.merge(args))
dd42df0a Ohad Levy
rescue ActiveRecord::RecordNotFound
f37934af Ohad Levy
true
334d0359 Amos Benari
end

def self.model_name
ComputeResource.model_name
end

def max_cpu_count
f37934af Ohad Levy
hypervisor.cpus
334d0359 Amos Benari
end

f37934af Ohad Levy
# libvirt reports in KB
334d0359 Amos Benari
def max_memory
02cf7a53 Tomer Brisker
hypervisor.memory * Foreman::SIZE[:kilo]
f37934af Ohad Levy
rescue => e
logger.debug "unable to figure out free memory, guessing instead due to:#{e}"
02cf7a53 Tomer Brisker
16*Foreman::SIZE[:giga]
334d0359 Amos Benari
end

5f029ed6 Daniel Lobato
def test_connection(options = {})
f37934af Ohad Levy
super
errors[:url].empty? and hypervisor
rescue => e
disconnect rescue nil
errors[:base] << e.message
end
334d0359 Amos Benari
5f029ed6 Daniel Lobato
def new_nic(attr = { })
f37934af Ohad Levy
client.nics.new attr
end

6d05514a Tomas Strachota
def new_interface(attr = {})
# fog compatibility
new_nic(attr)
end

5f029ed6 Daniel Lobato
def new_volume(attr = { })
41fb208d Lukas Zapletal
client.volumes.new(attrs.merge(:allocation => '0G'))
f37934af Ohad Levy
end

def storage_pools
98987690 Ohad Levy
client.pools rescue []
f37934af Ohad Levy
end

c5c84034 Ohad Levy
def interfaces
98987690 Ohad Levy
client.interfaces rescue []
f37934af Ohad Levy
end

c5c84034 Ohad Levy
def networks
client.networks rescue []
end

3d03e334 Dominic Cleal
def template(id)
template = client.volumes.get(id)
raise Foreman::Exception.new(N_("Unable to find template %s"), id) unless template.persisted?
template
end

5f029ed6 Daniel Lobato
def new_vm(attr = { })
95be0963 Amos Benari
test_connection
return unless errors.empty?
3059cea1 Tom Caspy
opts = vm_instance_defaults.merge(attr.to_hash).deep_symbolize_keys
f37934af Ohad Levy
# convert rails nested_attributes into a plain hash
[:nics, :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! { |k, v| v.nil? }

3d03e334 Dominic Cleal
opts[:boot_order] = %w[hd]
opts[:boot_order].unshift 'network' unless attr[:image_id]

1c81c2b9 Ohad Levy
vm = client.servers.new opts
vm.memory = opts[:memory] if opts[:memory]
vm
f37934af Ohad Levy
end

5f029ed6 Daniel Lobato
def create_vm(args = { })
f37934af Ohad Levy
vm = new_vm(args)
3d03e334 Dominic Cleal
create_volumes :prefix => vm.name, :volumes => vm.volumes, :backing_id => args[:image_id]
f37934af Ohad Levy
vm.save
rescue Fog::Errors::Error => e
01e78260 Ivan Nečas
Foreman::Logging.exception("Unhandled Libvirt error", e)
c67f9c5e Greg Sutcliffe
destroy_vm vm.id if vm
raise e
f37934af Ohad Levy
end

5f029ed6 Daniel Lobato
def console(uuid)
f37934af Ohad Levy
vm = find_vm_by_uuid(uuid)
dab82c90 Dmitri Dolguikh
raise Foreman::Exception.new(N_("VM is not running!")) unless vm.ready?
f37934af Ohad Levy
password = random_password
710dfa86 Ryan Davies
# Listen address cannot be updated while the guest is running
# When we update the display password, we pass the existing listen address
b760d48d Greg Sutcliffe
vm.update_display(:password => password, :listen => vm.display[:listen], :type => vm.display[:type])
8ffd9aee Ohad Levy
WsProxy.start(:host => hypervisor.hostname, :host_port => vm.display[:port], :password => password).merge(:type => vm.display[:type].downcase, :name=> vm.name)
f37934af Ohad Levy
rescue ::Libvirt::Error => e
if e.message =~ /cannot change listen address/
logger.warn e
dab82c90 Dmitri Dolguikh
Foreman::Exception.new(N_("Unable to change VM display listen address, make sure the display is not attached to localhost only"))
f37934af Ohad Levy
else
raise e
end
334d0359 Amos Benari
end

def hypervisor
client.nodes.first
end

805358df Jason Montleon
def associated_host(vm)
81a02cde Tom Caspy
associate_by("mac", vm.mac)
805358df Jason Montleon
end

4269abbd Tomas Strachota
def vm_compute_attributes_for(uuid)
vm_attrs = super
if vm_attrs[:memory_size].nil?
vm_attrs[:memory] = nil
logger.debug("Compute attributes for VM '#{uuid}' diddn't contain :memory_size")
else
vm_attrs[:memory] = vm_attrs[:memory_size]*1024 # value is returned in megabytes, we need bytes
end
vm_attrs
end

f37934af Ohad Levy
protected

def client
# WARNING potential connection leak
c5c84034 Ohad Levy
tries ||= 3
b43fa642 Ohad Levy
Thread.current[url] ||= ::Fog::Compute.new(:provider => "Libvirt", :libvirt_uri => url)
c5c84034 Ohad Levy
rescue ::Libvirt::RetrieveError
Thread.current[url] = nil
retry unless (tries -= 1).zero?
f37934af Ohad Levy
end

def disconnect
client.terminate if Thread.current[url]
Thread.current[url] = nil
end

def vm_instance_defaults
557b8543 Joseph Mitchell Magen
super.merge(
02cf7a53 Tomer Brisker
:memory => 768*Foreman::SIZE[:mega],
f37934af Ohad Levy
:nics => [new_nic],
:volumes => [new_volume],
96144a47 Daniel Lobato
:display => { :type => display_type.downcase,
:listen => Setting[:libvirt_default_console_address],
b760d48d Greg Sutcliffe
:password => random_password,
96144a47 Daniel Lobato
:port => '-1' }
557b8543 Joseph Mitchell Magen
)
f37934af Ohad Levy
end

5f029ed6 Daniel Lobato
def create_volumes(args)
dab82c90 Dmitri Dolguikh
args[:volumes].each {|vol| validate_volume_capacity(vol)}
3d03e334 Dominic Cleal
# if using image creation, the first volume needs a backing disk set
if args[:backing_id].present?
raise ::Foreman::Exception.new(N_('At least one volume must be specified for image-based provisioning.')) unless args[:volumes].size >= 1
args[:volumes].first.backing_volume = template(args[:backing_id])
end

dab82c90 Dmitri Dolguikh
begin
vols = []
(volumes = args[:volumes]).each do |vol|
vol.name = "#{args[:prefix]}-disk#{volumes.index(vol)+1}"
vol.capacity = "#{vol.capacity}G" unless vol.capacity.to_s.end_with?('G')
41fb208d Lukas Zapletal
vol.allocation = "#{vol.allocation}G" unless vol.allocation.to_s.end_with?('G')
dab82c90 Dmitri Dolguikh
vol.save
vols << vol
end
vols
rescue => e
3d85bef8 Lukas Zapletal
logger.error "Failure detected #{e}: removing already created volumes" if vols.any?
dab82c90 Dmitri Dolguikh
vols.each { |vol| vol.destroy }
raise e
f37934af Ohad Levy
end
end

dab82c90 Dmitri Dolguikh
def validate_volume_capacity(vol)
a6b0eeb0 Joseph Magen
if vol.capacity.to_s.empty? or /\A\d+G?\Z/.match(vol.capacity.to_s).nil?
dab82c90 Dmitri Dolguikh
raise Foreman::Exception.new(N_("Please specify volume size. You may optionally use suffix 'G' to specify volume size in gigabytes."))
end
end
334d0359 Amos Benari
end
end