Class: Ovirt::Service

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/ovirt/service.rb

Constant Summary collapse

DEFAULT_OPTIONS =
{}
REQUIRED_OPTIONS =
[:server, :username, :password]
DEFAULT_PORT_3_0 =
8443
DEFAULT_PORT_3_1 =
443
DEFAULT_PORT =
DEFAULT_PORT_3_1
DEFAULT_SCHEME =
'https'.freeze
SESSION_ID_KEY =
'JSESSIONID'.freeze
CANDIDATE_API_PATHS =

The list of absolute URI paths where the API can be available:

[
  '/api',
  '/ovirt-engine/api',
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Service

Returns a new instance of Service.



34
35
36
37
38
39
40
41
# File 'lib/ovirt/service.rb', line 34

def initialize(options = {})
  @options = DEFAULT_OPTIONS.merge(options)
  parse_domain_name
  REQUIRED_OPTIONS.each { |key| raise "No #{key} specified" unless @options.key?(key) }
  @password   = @options.delete(:password)
  @session_id = @options[:session_id]
  @api_path   = @options[:path]
end

Instance Attribute Details

#session_idObject

Returns the value of attribute session_id.



24
25
26
# File 'lib/ovirt/service.rb', line 24

def session_id
  @session_id
end

Class Method Details

.name_to_class(name) ⇒ Object



26
27
28
# File 'lib/ovirt/service.rb', line 26

def self.name_to_class(name)
  Ovirt.const_get(name.camelize)
end

.ovirt?(options) ⇒ Boolean

Returns:

  • (Boolean)


216
217
218
219
220
221
# File 'lib/ovirt/service.rb', line 216

def self.ovirt?(options)
  options[:username] = options[:password] = "_unused"
  !new(options).engine_ssh_public_key.to_s.blank?
rescue RestClient::ResourceNotFound, NoMethodError
  false
end

Instance Method Details

#api(reload = false) ⇒ Object



47
48
49
50
# File 'lib/ovirt/service.rb', line 47

def api(reload = false)
  @api   = nil if reload
  @api ||= xml_to_object(Api, resource_get)
end

#api_pathObject

Returns the path of the API, probing it if needed.



193
194
195
# File 'lib/ovirt/service.rb', line 193

def api_path
  @api_path ||= find_api_path(base_uri)
end

#api_uri(path = nil) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/ovirt/service.rb', line 197

def api_uri(path = nil)
  # Calculate the complete URI:
  uri = URI.join(base_uri, api_path).to_s

  # The path passed to this method will have the "/api" prefix if it comes from the "ems_ref"
  # attribute stored in the database, and will have the "/ovirt-engine/api" if it comes directly
  # from the "href" attribute of the XML documents, for example when using the "relationships"
  # method to fetch secondary objects related to the primary object. This means that to have
  # a clean path we need to remove both "ovirt-engine" and "api".
  unless path.nil?
    parts = path.to_s.split('/')
    parts.shift if parts.first == ''
    parts.shift if parts.first == 'ovirt-engine'
    parts.shift if parts.first == 'api'
    uri += "/#{parts.join('/')}" unless parts.empty?
  end
  uri
end

#blank_templateObject



103
104
105
106
107
108
# File 'lib/ovirt/service.rb', line 103

def blank_template
  @blank_template ||= begin
    href = special_objects[:"templates/blank"]
    href.blank? ? nil : Template.find_by_href(self, href)
  end
end

#ca_certificateObject



81
82
83
# File 'lib/ovirt/service.rb', line 81

def ca_certificate
  @ca_certificate ||= verify_certificate(get_ca_certificate)
end

#create_resource(path = nil) ⇒ Object



264
265
266
# File 'lib/ovirt/service.rb', line 264

def create_resource(path = nil)
  RestClient::Resource.new(api_uri(path), resource_options)
end

#disconnectObject



125
126
# File 'lib/ovirt/service.rb', line 125

def disconnect
end

#engine_ssh_public_keyObject



223
224
225
# File 'lib/ovirt/service.rb', line 223

def engine_ssh_public_key
  RestClient::Resource.new("#{base_uri}/engine.ssh.key.txt", resource_options).get
end

#find_api_path(uri) ⇒ Object

Probes all the candidate paths of the API, and returns the first that success. If all probes fail, then the first candidate will be returned.



188
189
190
# File 'lib/ovirt/service.rb', line 188

def find_api_path(uri)
  CANDIDATE_API_PATHS.detect { |path| probe_api_path(uri, path) } || CANDIDATE_API_PATHS.first
end

#get_ca_certificateObject



93
94
95
96
97
# File 'lib/ovirt/service.rb', line 93

def get_ca_certificate
  require "rest-client"
  RestClient::Resource.new("#{base_uri}/ca.crt", resource_options).get
rescue RestClient::ResourceNotFound
end

#get_resource_by_ems_ref(uri_suffix, element_name = nil) ⇒ Object



128
129
130
131
132
133
134
# File 'lib/ovirt/service.rb', line 128

def get_resource_by_ems_ref(uri_suffix, element_name = nil)
  xml            = resource_get(uri_suffix)
  doc            = Nokogiri::XML(xml)
  element_name ||= doc.root.name
  klass          = self.class.name_to_class(element_name)
  xml_to_object(klass, doc.root)
end

#get_resources_by_uri_path(uri_suffix, element_name = nil) ⇒ Object



136
137
138
139
140
141
142
143
# File 'lib/ovirt/service.rb', line 136

def get_resources_by_uri_path(uri_suffix, element_name = nil)
  xml            = resource_get(uri_suffix)
  doc            = Nokogiri::XML(xml)
  element_name ||= doc.root.name
  klass          = self.class.name_to_class(element_name)
  objects        = doc.xpath("//#{element_name}")
  objects.collect { |obj| xml_to_object(klass, obj) }
end

#inspectObject

just like the default inspect, but WITHOUT @password



43
44
45
# File 'lib/ovirt/service.rb', line 43

def inspect # just like the default inspect, but WITHOUT @password
  "#<#{self.class.name}:0x#{(object_id << 1).to_s(16).rjust(14, '0')} @options=#{@options.inspect}>"
end

#iso_imagesObject



121
122
123
# File 'lib/ovirt/service.rb', line 121

def iso_images
  iso_storage_domain.nil? ? [] : iso_storage_domain.iso_images
end

#iso_storage_domainObject



117
118
119
# File 'lib/ovirt/service.rb', line 117

def iso_storage_domain
  @iso_storage_domain ||= StorageDomain.iso_storage_domain(self)
end

#nameObject



56
57
58
# File 'lib/ovirt/service.rb', line 56

def name
  @name ||= product_info[:name]
end

#paginate_resource_get(path = nil, sort_by = :name, direction = :asc) ⇒ Object



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/ovirt/service.rb', line 227

def paginate_resource_get(path = nil, sort_by = :name, direction = :asc)
  log_header = "#{self.class.name}#paginate_resource_get"
  page       = 1
  full_xml   = nil
  loop do
    uri = "#{path}?search=sortby%20#{sort_by}%20#{direction}%20page%20#{page}"
    partial_xml_str = resource_get(uri)
    if full_xml.nil?
      full_xml = Nokogiri::XML(partial_xml_str)
    else
      partial_xml = Nokogiri::XML(partial_xml_str)
      break if partial_xml.root.children.count == 0
      logger.debug "#{log_header}: Combining resource elements for <#{path}> from page:<#{page}>"
      full_xml.root << partial_xml.root.children
    end
    page += 1
  end
  logger.debug "#{log_header}: Combined elements for <#{path}>.  Total elements:<#{full_xml.root.children.count}>"
  full_xml
end

#probe_api_path(uri, path) ⇒ Object

Checks if the API is available in the given candidate path. It does so sending a request without authentication. If the API is available there it will respond with the “WWW-Autenticate” header and with the “RESTAPI” or “ENGINE” realm.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/ovirt/service.rb', line 169

def probe_api_path(uri, path)
  uri = URI.join(uri, path)
  request = RestClient::Resource.new(uri.to_s, :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
  begin
    request.get
  rescue RestClient::Exception => exception
    response = exception.response
    if response.code == 401
      www_authenticate = response.headers[:www_authenticate]
      if www_authenticate =~ /^Basic realm="?(RESTAPI|ENGINE)"?$/
        return true
      end
    end
  end
  false
end

#product_infoObject



52
53
54
# File 'lib/ovirt/service.rb', line 52

def product_info
  @product_info ||= api[:product_info]
end

#resource_delete(path) ⇒ Object



260
261
262
# File 'lib/ovirt/service.rb', line 260

def resource_delete(path)
  resource_verb(path, :delete)
end

#resource_get(path = nil) ⇒ Object



248
249
250
# File 'lib/ovirt/service.rb', line 248

def resource_get(path = nil)
  resource_verb(path, :get)
end

#resource_post(path, payload, additional_headers = {:content_type => :xml, :accept => :xml}) ⇒ Object



256
257
258
# File 'lib/ovirt/service.rb', line 256

def resource_post(path, payload, additional_headers = {:content_type => :xml, :accept => :xml})
  resource_verb(path, :post, payload, additional_headers)
end

#resource_put(path, payload, additional_headers = {:content_type => :xml, :accept => :xml}) ⇒ Object



252
253
254
# File 'lib/ovirt/service.rb', line 252

def resource_put(path, payload, additional_headers = {:content_type => :xml, :accept => :xml})
  resource_verb(path, :put, payload, additional_headers)
end

#root_tagObject



110
111
112
113
114
115
# File 'lib/ovirt/service.rb', line 110

def root_tag
  @root_tag ||= begin
    href = special_objects[:"tags/root"]
    href.blank? ? nil : Tag.find_by_href(self, href)
  end
end

#special_objectsObject



99
100
101
# File 'lib/ovirt/service.rb', line 99

def special_objects
  @special_objects ||= api[:special_objects]
end

#standard_collection(uri_suffix, element_name = nil, paginate = false, sort_by = :name, direction = :asc) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/ovirt/service.rb', line 145

def standard_collection(uri_suffix, element_name = nil, paginate = false, sort_by = :name, direction = :asc)
  if paginate
    doc = paginate_resource_get(uri_suffix, sort_by, direction)
  else
    xml = resource_get(uri_suffix)
    doc = Nokogiri::XML(xml)
  end
  element_name ||= uri_suffix.singularize
  klass          = self.class.name_to_class(element_name)

  xml_path = uri_suffix == 'api' ? element_name : "#{element_name.pluralize}/#{element_name}"
  objects  = doc.xpath("//#{xml_path}")
  objects.collect { |obj| xml_to_object(klass, obj) }
end

#status(link) ⇒ Object



160
161
162
163
164
# File 'lib/ovirt/service.rb', line 160

def status(link)
  response = resource_get(link)
  node     = Base.xml_to_nokogiri(response)
  node.xpath('status/state').text
end

#summaryObject



77
78
79
# File 'lib/ovirt/service.rb', line 77

def summary
  api(true)[:summary] # This is volatile information
end

#vendorObject



60
61
62
# File 'lib/ovirt/service.rb', line 60

def vendor
  @vendor ||= product_info[:vendor]
end

#verify_certificate(certificate) ⇒ Object



85
86
87
88
89
90
91
# File 'lib/ovirt/service.rb', line 85

def verify_certificate(certificate)
  return if certificate.to_s.strip.empty?

  require 'openssl'
  OpenSSL::X509::Certificate.new(certificate).to_s
rescue OpenSSL::X509::CertificateError
end

#versionObject



64
65
66
67
# File 'lib/ovirt/service.rb', line 64

def version
  # HACK: using full_version if available due to version being wrong, https://bugzilla.redhat.com/show_bug.cgi?id=1284654
  @version ||= full_version || product_info[:version]
end

#version_3_0?Boolean

Returns:

  • (Boolean)


73
74
75
# File 'lib/ovirt/service.rb', line 73

def version_3_0?
  version_string.starts_with?("3.0")
end

#version_stringObject



69
70
71
# File 'lib/ovirt/service.rb', line 69

def version_string
  @version_string ||= "#{version[:major]}.#{version[:minor]}.#{version[:revision]}.#{version[:build]}"
end

#xml_to_object(klass, xml) ⇒ Object



30
31
32
# File 'lib/ovirt/service.rb', line 30

def xml_to_object(klass, xml)
  klass.create_from_xml(self, xml)
end