Class: ForemanXen::Xenserver

Inherits:
ComputeResource
  • Object
show all
Defined in:
app/models/foreman_xen/xenserver.rb

Constant Summary collapse

GB_BYTES =

1gb in bytes

1_073_741_824

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.model_nameObject



80
81
82
# File 'app/models/foreman_xen/xenserver.rb', line 80

def self.model_name
  ComputeResource.model_name
end

Instance Method Details

#all_isosObject



169
170
171
# File 'app/models/foreman_xen/xenserver.rb', line 169

def all_isos
  read_from_cache('isos', 'isos!')
end

#all_isos!Object



173
174
175
176
177
178
# File 'app/models/foreman_xen/xenserver.rb', line 173

def all_isos!
  store_in_cache('isos') do
    isos = iso_libraries.map(&:vdis).flatten
    isos.sort_by(&:name)
  end
end

#associated_host(vm) ⇒ Object



230
231
232
# File 'app/models/foreman_xen/xenserver.rb', line 230

def associated_host(vm)
  associate_by('mac', vm.interfaces.map(&:mac).map { |mac| Net::Validations.normalize_mac(mac) })
end

#available_hypervisorsObject



113
114
115
# File 'app/models/foreman_xen/xenserver.rb', line 113

def available_hypervisors
  hypervisors.select(&:enabled)
end

#available_hypervisors!Object



117
118
119
# File 'app/models/foreman_xen/xenserver.rb', line 117

def available_hypervisors!
  hypervisors!.select(&:enabled)
end

#available_imagesObject



109
110
111
# File 'app/models/foreman_xen/xenserver.rb', line 109

def available_images
  custom_templates!
end

#builtin_templatesObject



220
221
222
# File 'app/models/foreman_xen/xenserver.rb', line 220

def builtin_templates
  read_from_cache('builtin_templates', 'builtin_templates!')
end

#builtin_templates!Object



224
225
226
227
228
# File 'app/models/foreman_xen/xenserver.rb', line 224

def builtin_templates!
  store_in_cache('builtin_templates') do
    get_templates(client.builtin_templates)
  end
end

#capabilitiesObject



14
15
16
# File 'app/models/foreman_xen/xenserver.rb', line 14

def capabilities
  %i[build image new_volume]
end

#cleanup_configdrive(uuid) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
# File 'app/models/foreman_xen/xenserver.rb', line 42

def cleanup_configdrive(uuid)
  iso_file_name = "foreman-configdrive-#{uuid}.iso"
  begin
    path = File.join(iso_library_mountpoint, iso_file_name)
    exist = File.exist? path
    FileUtils.rm(path) if exist
  rescue
    return true unless exist

    return false
  end
end

#console(uuid) ⇒ Object



392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'app/models/foreman_xen/xenserver.rb', line 392

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 && c.vm.reference == vm.reference && c.protocol == 'rfb' }
  raise "No console for vm #{vm.name}" if console.nil?

  session_ref = (vm.service.instance_variable_get :@connection).instance_variable_get :@credentials
  full_url    = "#{console.location}&session_id=#{session_ref}"
  tunnel      = VNCTunnel.new full_url
  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

#create_vm(args = {}) ⇒ Object



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'app/models/foreman_xen/xenserver.rb', line 285

def create_vm(args = {})
  args = args.deep_symbolize_keys
  logger.debug('create_vm args:')
  logger.debug(args)
  begin
    # Create VM Object
    attr = vm_attr_from_args(args)
    if args[:provision_method] == 'image'
      image = available_images.find { |i| i.uuid == args[:image_id].to_s }
      sr = storage_pools.find { |s| s.uuid == args[:target_sr].to_s }
      vm = create_vm_from_image(image, attr, sr)
    else
      template = builtin_templates.find { |t| t.uuid == args[:builtin_template].to_s }
      raise 'Template not found' unless template

      vm = create_vm_from_template(attr, template)
    end

    raise 'Error creating VM' unless vm

    # Set correct affinity
    set_vm_affinity(vm, args[:hypervisor_host].to_s)

    # Add NICs
    vm.interfaces = args[:interfaces_attributes].map do |_, v|
      create_interface(vm, v[:network])
    end

    # Attach ConfigDrive
    create_and_attach_configdrive(vm, args) if args[:configdrive] == '1' && args[:provision_method] == 'image'

    # Attach ISO
    unless args[:iso].empty?
      iso_vdi = isos.find { |i| i.uuid == args[:iso] }
      attach_iso(vm, iso_vdi)
    end

    # Add new Volumes
    unless args[:volumes_attributes].nil?
      vm.volumes = args[:volumes_attributes].map do |_, v|
        create_volume(vm, v) unless v[:_delete] == '1'
      end
    end

    # Write XenStore data
    xenstore_data = xenstore_set_mac(vm, args[:xenstore])
    set_xenstore_data(vm, xenstore_data)

    # Fix Description
    vm.set_attribute('name-description', args[:name_description])

    return vm
  rescue => e
    cleanup_configdrive(vm.uuid) if vm&.uuid
    vm&.destroy
    vm.volumes.each(&:destroy) if vm&.volumes
    logger.info e
    logger.info e.backtrace.join("\n")
    raise e
  end
end

#create_vm_from_image(image, attr, sr) ⇒ Object



362
363
364
365
366
367
368
369
# File 'app/models/foreman_xen/xenserver.rb', line 362

def create_vm_from_image(image, attr, sr)
  vm_ref = client.copy_server image.reference, attr[:name], sr.reference
  client.provision_server vm_ref
  vm = client.servers.find { |s| s.reference == vm_ref }
  set_vm_profile_attributes(vm, attr)
  rename_cloned_volumes(vm)
  vm
end

#create_vm_from_template(attr, template) ⇒ Object



347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'app/models/foreman_xen/xenserver.rb', line 347

def create_vm_from_template(attr, template)
  vm_attr = template.attributes.dup.merge(attr)
  %i[uuid domid reference allowed_operations].each do |a|
    vm_attr.delete(a)
  end
  vm_attr[:is_a_template] = false
  vm_attr[:other_config].delete('default_template')
  vm_attr[:other_config]['mac_seed'] = SecureRandom.uuid
  vm = new_vm(vm_attr)
  # Set any host affinity (required for saving) - correct later
  vm.affinity = client.hosts.first
  vm.save
  vm
end

#custom_templatesObject



210
211
212
# File 'app/models/foreman_xen/xenserver.rb', line 210

def custom_templates
  read_from_cache('custom_templates', 'custom_templates!')
end

#custom_templates!Object



214
215
216
217
218
# File 'app/models/foreman_xen/xenserver.rb', line 214

def custom_templates!
  store_in_cache('custom_templates') do
    get_templates(client.custom_templates)
  end
end

#destroy_vm(ref, args = {}) ⇒ Object

we default to destroy the VM’s storage as well.



72
73
74
75
76
77
78
# File 'app/models/foreman_xen/xenserver.rb', line 72

def destroy_vm(ref, args = {})
  logger.info "destroy_vm: #{ref} #{args}"
  cleanup_configdrive(ref) if iso_library_mountpoint
  find_vm_by_uuid(ref).destroy
rescue ActiveRecord::RecordNotFound
  true
end

#find_snapshotsObject



249
250
251
252
253
254
255
256
# File 'app/models/foreman_xen/xenserver.rb', line 249

def find_snapshots
  tmps = begin
    client.templates.select(&:is_a_snapshot)
         rescue
           []
  end
  tmps.sort_by(&:name)
end

#find_snapshots_for_vm(vm) ⇒ Object



234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'app/models/foreman_xen/xenserver.rb', line 234

def find_snapshots_for_vm(vm)
  return [] if vm.snapshots.empty?

  tmps = begin
    client.templates.select(&:is_a_snapshot)
         rescue
           []
  end
  retval = []
  tmps.each do |snapshot|
    retval << snapshot if snapshot..include?(vm.uuid)
  end
  retval
end

#find_vm_by_uuid(uuid) ⇒ Object

rubocop:disable Rails/DynamicFindBy Fog::XenServer::Compute (client) isn’t an ActiveRecord model which supports find_by()



58
59
60
61
62
63
64
65
66
67
68
# File 'app/models/foreman_xen/xenserver.rb', line 58

def find_vm_by_uuid(uuid)
  return client.servers.find { |s| s.reference == uuid } if uuid.start_with? 'OpaqueRef:'

  client.servers.find_by_uuid(uuid)
rescue Fog::XenServer::RequestFailed => e
  Foreman::Logging.exception("Failed retrieving xenserver vm by uuid #{uuid}", 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

#host_compute_attrs(host) ⇒ Object



18
19
20
21
22
23
24
25
26
# File 'app/models/foreman_xen/xenserver.rb', line 18

def host_compute_attrs(host)
  super(host).merge(
    name_description: host.comment,
    is_a_template:    false,
    is_a_shapshot:    false,
    xenstore:         host_xenstore_data(host),
    network_data:     host_network_data(host)
  )
end

#hypervisorObject



417
418
419
# File 'app/models/foreman_xen/xenserver.rb', line 417

def hypervisor
  client.hosts.first
end

#hypervisorsObject



121
122
123
# File 'app/models/foreman_xen/xenserver.rb', line 121

def hypervisors
  read_from_cache('hypervisors', 'hypervisors!')
end

#hypervisors!Object



125
126
127
128
129
130
# File 'app/models/foreman_xen/xenserver.rb', line 125

def hypervisors!
  store_in_cache('hypervisors') do
    hosts = client.hosts
    hosts.sort_by(&:name)
  end
end

#interfacesObject



184
185
186
187
188
# File 'app/models/foreman_xen/xenserver.rb', line 184

def interfaces
  client.vifs
rescue
  []
end

#iso_library_mountpointObject



32
33
34
# File 'app/models/foreman_xen/xenserver.rb', line 32

def iso_library_mountpoint
  attrs[:iso_library_mountpoint]
end

#iso_library_mountpoint=(path) ⇒ Object



36
37
38
39
40
# File 'app/models/foreman_xen/xenserver.rb', line 36

def iso_library_mountpoint=(path)
  mountpoint = path.to_s.end_with?('/') ? path.to_s : "#{path}/"
  mountpoint = nil if path.to_s.strip.empty?
  attrs[:iso_library_mountpoint] = mountpoint
end

#isosObject



157
158
159
160
161
# File 'app/models/foreman_xen/xenserver.rb', line 157

def isos
  all_isos.reject do |iso|
    iso.name =~ /foreman-configdrive/
  end
end

#isos!Object



163
164
165
166
167
# File 'app/models/foreman_xen/xenserver.rb', line 163

def isos!
  all_isos!.reject do |iso|
    iso.name =~ /foreman-configdrive/
  end
end

#max_cpu_countObject



84
85
86
87
# File 'app/models/foreman_xen/xenserver.rb', line 84

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

#max_memoryObject



89
90
91
92
93
94
95
# File 'app/models/foreman_xen/xenserver.rb', line 89

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

#networksObject



190
191
192
# File 'app/models/foreman_xen/xenserver.rb', line 190

def networks
  read_from_cache('networks', 'networks!')
end

#networks!Object



194
195
196
197
198
# File 'app/models/foreman_xen/xenserver.rb', line 194

def networks!
  store_in_cache('networks') do
    client.networks.sort_by(&:name)
  end
end

#new_interface(attr = {}) ⇒ Object



180
181
182
# File 'app/models/foreman_xen/xenserver.rb', line 180

def new_interface(attr = {})
  client.vifs.new attr
end

#new_nic(attr = {}) ⇒ Object



132
133
134
# File 'app/models/foreman_xen/xenserver.rb', line 132

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

#new_vm(attr = {}) ⇒ Object



258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'app/models/foreman_xen/xenserver.rb', line 258

def new_vm(attr = {})
  attr = attr.to_hash.deep_symbolize_keys
  %i[networks interfaces].each do |collection|
    nested_attr = attr.delete("#{collection}_attributes".to_sym)
    attr[collection] = nested_attributes_for(collection, nested_attr) if nested_attr
  end
  if attr[:volumes_attributes]
    vol_attr = nested_attributes_for('volumes', attr[:volumes_attributes])
    attr[:volumes] = vol_attr.map { |v| new_volume(v) }
  end
  attr.reject! { |_, v| v.nil? }
  super(attr)
end

#new_volume(attr = {}) ⇒ Object



136
137
138
139
140
141
142
# File 'app/models/foreman_xen/xenserver.rb', line 136

def new_volume(attr = {})
  size = attr[:virtual_size_gb].to_i * GB_BYTES
  vdi = client.vdis.new virtual_size: size.to_s
  vdi.type = 'user'
  vdi.sr = storage_pools.find { |s| s.uuid == attr[:sr].to_s } if attr[:sr]
  vdi
end

#provided_attributesObject



7
8
9
10
11
12
# File 'app/models/foreman_xen/xenserver.rb', line 7

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

#rename_cloned_volumes(vm) ⇒ Object



383
384
385
386
387
388
389
390
# File 'app/models/foreman_xen/xenserver.rb', line 383

def rename_cloned_volumes(vm)
  vm.volumes.each do |vol|
    udev = vol.vbds.find { |v| v.vm.uuid == vm.uuid }.userdevice
    name = "#{vm.name}-#{udev}"
    vol.set_attribute 'name-label', name
    vol.set_attribute 'name-description', name
  end
end

#set_vm_profile_attributes(vm, attr) ⇒ Object



371
372
373
374
375
376
377
378
379
380
381
# File 'app/models/foreman_xen/xenserver.rb', line 371

def set_vm_profile_attributes(vm, attr)
  # Memory limits must satisfy:
  # static_min <= dynamic_min <= dynamic_max <= static_max
  mem = %w[memory_static_max memory_dynamic_max
           memory_dynamic_min memory_static_min]
  mem.reverse! if vm.memory_static_max.to_i > attr[:memory_static_max].to_i
  # VCPU values must satisfy: 0 < vcpus_at_startup <= vcpus_max
  cpu = %w[vcpus_max vcpus_at_startup]
  cpu.reverse! if vm.vcpus_at_startup > attr[:vcpus_at_startup]
  (mem + cpu).each { |e| vm.set_attribute e, attr[e.to_sym] }
end

#storage_poolsObject



144
145
146
# File 'app/models/foreman_xen/xenserver.rb', line 144

def storage_pools
  read_from_cache('storage_pools', 'storage_pools!')
end

#storage_pools!Object



148
149
150
151
152
153
154
155
# File 'app/models/foreman_xen/xenserver.rb', line 148

def storage_pools!
  store_in_cache('storage_pools') do
    pools = client.storage_repositories.select do |sr|
      sr.type != 'udev' && sr.type != 'iso'
    end
    pools.sort_by(&:display_name)
  end
end

#templatesObject



200
201
202
# File 'app/models/foreman_xen/xenserver.rb', line 200

def templates
  read_from_cache('templates', 'templates!')
end

#templates!Object



204
205
206
207
208
# File 'app/models/foreman_xen/xenserver.rb', line 204

def templates!
  store_in_cache('templates') do
    client.templates.sort_by(&:name)
  end
end

#test_connection(options = {}) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
# File 'app/models/foreman_xen/xenserver.rb', line 97

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

#user_data_supportedObject



28
29
30
# File 'app/models/foreman_xen/xenserver.rb', line 28

def user_data_supported
  true
end

#vm_attr_from_args(args) ⇒ Object



272
273
274
275
276
277
278
279
280
281
282
283
# File 'app/models/foreman_xen/xenserver.rb', line 272

def vm_attr_from_args(args)
  {
    name:               args[:name],
    name_description:   args[:comment],
    vcpus_max:          args[:vcpus_max],
    vcpus_at_startup:   args[:vcpus_max],
    memory_static_max:  args[:memory_max],
    memory_dynamic_max: args[:memory_max],
    memory_dynamic_min: args[:memory_min],
    memory_static_min:  args[:memory_min]
  }
end