Project

General

Profile

Download (9.79 KB) Statistics
| Branch: | Tag: | Revision:
5f45920e Amos Benari
require 'foreman/exception'
85ed3c3f Dominic Cleal
require 'uri'

334d0359 Amos Benari
module Foreman::Model
class Ovirt < ComputeResource
f2c78d4a Joseph Magen
validates :url, :format => { :with => URI.regexp }
validates :user, :password, :presence => true
5f45920e Amos Benari
before_create :update_public_key
7e031001 Ohad Levy
8a0ffcfa Joseph Magen
alias_attribute :datacenter, :uuid

334d0359 Amos Benari
def self.model_name
ComputeResource.model_name
end

dd42df0a Ohad Levy
def capabilities
99527500 Jimmi Dyson
[:build, :image]
dd42df0a Ohad Levy
end

4269abbd Tomas Strachota
def find_vm_by_uuid(uuid)
super
rescue OVIRT::OvirtException
raise(ActiveRecord::RecordNotFound)
end

a6a6b703 David Swift
def supports_update?
true
end

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

334d0359 Amos Benari
#FIXME
def max_cpu_count
8
end

def max_memory
02cf7a53 Tomer Brisker
16*Foreman::SIZE[:giga]
334d0359 Amos Benari
end

180d7f43 Jason Montleon
def quotas
client.quotas
end

def ovirt_quota=(ovirt_quota_id)
self.attrs[:ovirt_quota_id] = ovirt_quota_id
end

def ovirt_quota
0809c9ae Ivan Nečas
if self.attrs[:ovirt_quota_id].blank?
nil
else
self.attrs[:ovirt_quota_id]
end
180d7f43 Jason Montleon
end

4f7a4d0b David Davis
def templates(opts = {})
334d0359 Amos Benari
client.templates
end

d6026572 Amos Benari
def available_images
templates
end

c6e02bd3 Joseph Magen
def template(id)
99527500 Jimmi Dyson
compute = client.templates.get(id) || raise(ActiveRecord::RecordNotFound)
compute.interfaces
compute.volumes
compute
334d0359 Amos Benari
end

def clusters
client.clusters
end

85ed3c3f Dominic Cleal
# Check if HTTPS is mandatory, since rest_client will fail with a POST
def test_https_required
RestClient.post url, {} if URI(url).scheme == 'http'
true
rescue => e
case e.message
when /406/
true
else
raise e
end
end
private :test_https_required

5f029ed6 Daniel Lobato
def test_connection(options = {})
334d0359 Amos Benari
super
5f45920e Amos Benari
if errors[:url].empty? and errors[:username].empty? and errors[:password].empty?
update_public_key options
datacenters && test_https_required
end
334d0359 Amos Benari
rescue => e
case e.message
when /404/
errors[:url] << e.message
85ed3c3f Dominic Cleal
when /302/
errors[:url] << 'HTTPS URL is required for API access'
334d0359 Amos Benari
when /401/
errors[:user] << e.message
else
errors[:base] << e.message
end
end

4f7a4d0b David Davis
def datacenters(options = {})
334d0359 Amos Benari
client.datacenters(options).map { |dc| [dc[:name], dc[:id]] }
end

4f7a4d0b David Davis
def networks(opts = {})
334d0359 Amos Benari
if opts[:cluster_id]
client.clusters.get(opts[:cluster_id]).networks
else
[]
end
end

ea49a05e James Netherton
def available_clusters
clusters
end

4f7a4d0b David Davis
def available_networks(cluster_id = nil)
e4d88172 Greg Petras
raise ::Foreman::Exception.new(N_('Cluster ID is required to list available networks')) if cluster_id.nil?
2312cccf Daniel Lobato
networks({:cluster_id => cluster_id})
ea49a05e James Netherton
end

4f7a4d0b David Davis
def available_storage_domains(storage_domain = nil)
ea49a05e James Netherton
storage_domains
end

4f7a4d0b David Davis
def storage_domains(opts = {})
96ede451 Amos Benari
client.storage_domains({:role => 'data'}.merge(opts))
end

def start_vm(uuid)
334d0359 Amos Benari
find_vm_by_uuid(uuid).start(:blocking => true)
end

96ede451 Amos Benari
def create_vm(args = {})
334d0359 Amos Benari
#ovirt doesn't accept '.' in vm name.
args[:name] = args[:name].parameterize
99527500 Jimmi Dyson
if (image_id = args[:image_id])
args.merge!({:template => image_id})
end
180d7f43 Jason Montleon
vm = super({ :first_boot_dev => 'network', :quota => ovirt_quota }.merge(args))
334d0359 Amos Benari
begin
96ede451 Amos Benari
create_interfaces(vm, args[:interfaces_attributes])
create_volumes(vm, args[:volumes_attributes])
334d0359 Amos Benari
rescue => e
destroy_vm vm.id
raise e
end
vm
end

4f7a4d0b David Davis
def new_vm(attr = {})
95be0963 Amos Benari
vm = super
96ede451 Amos Benari
interfaces = nested_attributes_for :interfaces, attr[:interfaces_attributes]
interfaces.map{ |i| vm.interfaces << new_interface(i)}
volumes = nested_attributes_for :volumes, attr[:volumes_attributes]
volumes.map{ |v| vm.volumes << new_volume(v)}
334d0359 Amos Benari
vm
end

4f7a4d0b David Davis
def new_interface(attr = {})
334d0359 Amos Benari
Fog::Compute::Ovirt::Interface.new(attr)
end

4f7a4d0b David Davis
def new_volume(attr = {})
96ede451 Amos Benari
Fog::Compute::Ovirt::Volume.new(attr)
end

def save_vm(uuid, attr)
334d0359 Amos Benari
vm = find_vm_by_uuid(uuid)
3059cea1 Tom Caspy
vm.attributes.merge!(attr.symbolize_keys).deep_symbolize_keys
334d0359 Amos Benari
update_interfaces(vm, attr[:interfaces_attributes])
96ede451 Amos Benari
update_volumes(vm, attr[:volumes_attributes])
334d0359 Amos Benari
vm.interfaces
96ede451 Amos Benari
vm.volumes
334d0359 Amos Benari
vm.save
end

96ede451 Amos Benari
def destroy_vm(uuid)
334d0359 Amos Benari
begin
find_vm_by_uuid(uuid).destroy
rescue OVIRT::OvirtException => e
#404 error are ignored on delete.
raise e unless e.message =~ /404/
end
true
end

b43fa642 Ohad Levy
def console(uuid)
vm = find_vm_by_uuid(uuid)
raise "VM is not running!" if vm.status == "down"
057d4974 Amos Benari
if vm.display[:type] =~ /spice/i
5f45920e Amos Benari
xpi_opts = {:name => vm.name, :address => vm.display[:address], :secure_port => vm.display[:secure_port], :ca_cert => public_key, :subject => vm.display[:subject] }
8ffd9aee Ohad Levy
opts = if vm.display[:secure_port]
{ :host_port => vm.display[:secure_port], :ssl_target => true }
else
{ :host_port => vm.display[:port] }
end
WsProxy.start(opts.merge(:host => vm.display[:address], :password => vm.ticket)).merge(xpi_opts).merge(:type => 'spice')
057d4974 Amos Benari
else
8ffd9aee Ohad Levy
WsProxy.start(:host => vm.display[:address], :host_port => vm.display[:port], :password => vm.ticket).merge(:name => vm.name, :type => 'vnc')
057d4974 Amos Benari
end
b43fa642 Ohad Levy
end

0e5696d3 Amos Benari
def update_required?(old_attrs, new_attrs)
return true if super(old_attrs, new_attrs)

new_attrs[:interfaces_attributes].each do |key, interface|
return true if (interface[:id].blank? || interface[:_delete] == '1') && key != 'new_interfaces' #ignore the template
end if new_attrs[:interfaces_attributes]

new_attrs[:volumes_attributes].each do |key, volume|
return true if (volume[:id].blank? || volume[:_delete] == '1') && key != 'new_volumes' #ignore the template
end if new_attrs[:volumes_attributes]

false
end

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

ddce3dc1 Amos Benari
def self.provider_friendly_name
2ebd2f22 Joseph Magen
"oVirt"
end

5f45920e Amos Benari
def public_key
attrs[:public_key]
end

5f029ed6 Daniel Lobato
def public_key=(key)
5f45920e Amos Benari
attrs[:public_key] = key
end

334d0359 Amos Benari
protected

96ede451 Amos Benari
def bootstrap(args)
334d0359 Amos Benari
client.servers.bootstrap vm_instance_defaults.merge(args.to_hash)
rescue Fog::Errors::Error => e
01e78260 Ivan Nečas
Foreman::Logging.exception("Failed to bootstrap vm", e)
334d0359 Amos Benari
errors.add(:base, e.to_s)
false
end

def client
5f45920e Amos Benari
return @client if @client
client = ::Fog::Compute.new(
334d0359 Amos Benari
:provider => "ovirt",
:ovirt_username => user,
:ovirt_password => password,
:ovirt_url => url,
5f45920e Amos Benari
:ovirt_datacenter => uuid,
:ovirt_ca_cert_store => ca_cert_store(public_key)
334d0359 Amos Benari
)
5f45920e Amos Benari
client.datacenters
@client = client
rescue => e
if e.message =~ /SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed/
raise Foreman::FingerprintException.new(
N_("The remote system presented a public key signed by an unidentified certificate authority. If you are sure the remote system is authentic, go to the compute resource edit page, press the 'Test Connection' or 'Load Datacenters' button and submit"),
ca_cert)
else
raise e
end
end

5f029ed6 Daniel Lobato
def update_public_key(options = {})
5f45920e Amos Benari
return unless public_key.blank? || options[:force]
client
rescue Foreman::FingerprintException => e
e944a1b2 Lukas Zapletal
self.public_key = e.fingerprint if self.public_key.blank?
334d0359 Amos Benari
end

55f8636a Amos Benari
def api_version
5f45920e Amos Benari
@api_version ||= client.api_version
end

5f029ed6 Daniel Lobato
def ca_cert_store(cert)
5f45920e Amos Benari
return if cert.blank?
OpenSSL::X509::Store.new.add_cert(OpenSSL::X509::Certificate.new(cert))
rescue => e
raise _("Failed to create X509 certificate, error: %s" % e.message)
55f8636a Amos Benari
end

5f45920e Amos Benari
def ca_cert
057d4974 Amos Benari
ca_url = URI.parse(url)
ca_url.path = "/ca.crt"
6a85948c Jimmi Dyson
http = Net::HTTP.new(ca_url.host, ca_url.port)
http.use_ssl = (ca_url.scheme == 'https')
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(ca_url.path)
2e417b58 Jason Montleon
http.request(request).body
057d4974 Amos Benari
end

334d0359 Amos Benari
private
abd8f1d1 Daniel Lobato
96ede451 Amos Benari
def create_interfaces(vm, attrs)
334d0359 Amos Benari
#first remove all existing interfaces
vm.interfaces.each do |interface|
#The blocking true is a work-around for ovirt bug, it should be removed.
vm.destroy_interface(:id => interface.id, :blocking => true)
end if vm.interfaces
#add interfaces
96ede451 Amos Benari
interfaces = nested_attributes_for :interfaces, attrs
interfaces.map{ |i| vm.add_interface(i)}
334d0359 Amos Benari
vm.interfaces.reload
end

96ede451 Amos Benari
def create_volumes(vm, attrs)
#add volumes
volumes = nested_attributes_for :volumes, attrs
2bf991a8 Leon Strong
volumes.map do |vol|
vol[:sparse] = "true"
vol[:format] = "raw" if vol[:preallocate] == "1"
vol[:sparse] = "false" if vol[:preallocate] == "1"
#The blocking true is a work-around for ovirt bug fixed in ovirt version 3.1.
vm.add_volume({:bootable => 'false', :quota => ovirt_quota, :blocking => api_version.to_f < 3.1}.merge(vol)) if vol[:id].blank?
end
96ede451 Amos Benari
vm.volumes.reload
end

334d0359 Amos Benari
def update_interfaces(vm, attrs)
2a08c26b Amos Benari
interfaces = nested_attributes_for :interfaces, attrs
interfaces.each do |interface|
afe02d30 Daniel Lobato
vm.destroy_interface(:id => interface[:id]) if interface[:_delete] == '1' && interface[:id]
vm.add_interface(interface) if interface[:id].blank?
2a08c26b Amos Benari
end
334d0359 Amos Benari
end

96ede451 Amos Benari
def update_volumes(vm, attrs)
2a08c26b Amos Benari
volumes = nested_attributes_for :volumes, attrs
volumes.each do |volume|
55f8636a Amos Benari
vm.destroy_volume(:id => volume[:id], :blocking => api_version.to_f < 3.1) if volume[:_delete] == '1' && volume[:id].present?
180d7f43 Jason Montleon
vm.add_volume({:bootable => 'false', :quota => ovirt_quota, :blocking => api_version.to_f < 3.1}.merge(volume)) if volume[:id].blank?
2a08c26b Amos Benari
end
96ede451 Amos Benari
end
334d0359 Amos Benari
end
end