foreman/app/models/compute_resources/foreman/model/ovirt.rb @ 4269abbd
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
|