Class: ForemanXen::Xenserver

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

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.model_nameObject



34
35
36
# File 'app/models/foreman_xen/xenserver.rb', line 34

def self.model_name
  ComputeResource.model_name
end

Instance Method Details

#associated_host(vm) ⇒ Object



160
161
162
# File 'app/models/foreman_xen/xenserver.rb', line 160

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

#available_hypervisorsObject



63
64
65
# File 'app/models/foreman_xen/xenserver.rb', line 63

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

#available_hypervisors!Object



67
68
69
70
71
72
# File 'app/models/foreman_xen/xenserver.rb', line 67

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

#builtin_templatesObject



150
151
152
# File 'app/models/foreman_xen/xenserver.rb', line 150

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

#builtin_templates!Object



154
155
156
157
158
# File 'app/models/foreman_xen/xenserver.rb', line 154

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

#capabilitiesObject



12
13
14
# File 'app/models/foreman_xen/xenserver.rb', line 12

def capabilities
  [:build]
end

#console(uuid) ⇒ Object



343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'app/models/foreman_xen/xenserver.rb', line 343

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



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'app/models/foreman_xen/xenserver.rb', line 202

def create_vm(args = {})
  custom_template_name  = args[:custom_template_name].to_s
  builtin_template_name = args[:builtin_template_name].to_s

  if builtin_template_name != '' && custom_template_name != ''
    logger.info "custom_template_name: #{custom_template_name}"
    logger.info "builtin_template_name: #{builtin_template_name}"
    raise 'you can select at most one template type'
  end
  begin
    logger.info "create_vm(): custom_template_name: #{custom_template_name}"
    logger.info "create_vm(): builtin_template_name: #{builtin_template_name}"
    vm = custom_template_name != '' ? create_vm_from_custom(args) : create_vm_from_builtin(args)
    vm.set_attribute('name_description', 'Provisioned by Foreman')
    vm.set_attribute('VCPUs_max', args[:vcpus_max])
    vm.set_attribute('VCPUs_at_startup', args[:vcpus_max])
    vm.reload
    return vm
  rescue => e
    logger.info e
    logger.info e.backtrace.join("\n")
    return false
  end
end

#create_vm_from_builtin(args) ⇒ Object



277
278
279
280
281
282
283
284
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
# File 'app/models/foreman_xen/xenserver.rb', line 277

def create_vm_from_builtin(args)
  mem_max = args[:memory_max]
  mem_min = args[:memory_min]

  host = get_hypervisor_host(args)

  logger.info "create_vm_from_builtin: host : #{host.name}"

  builtin_template_name = args[:builtin_template_name]
  builtin_template_name = builtin_template_name.to_s

  storage_repository = client.storage_repositories.find { |sr| sr.uuid == (args[:VBDs][:sr_uuid]).to_s }

  gb   = 1_073_741_824 # 1gb in bytes
  size = args[:VBDs][:physical_size].to_i * gb
  vdi  = client.vdis.create :name               => "#{args[:name]}-disk1",
                            :storage_repository => storage_repository,
                            :description        => "#{args[:name]}-disk_1",
                            :virtual_size       => size.to_s

  other_config = {}
  if builtin_template_name != ''
    template     = client.builtin_templates.find { |tmp| tmp.name == args[:builtin_template_name] }
    other_config = template.other_config
    other_config.delete 'disks'
    other_config.delete 'default_template'
    other_config['mac_seed'] = SecureRandom.uuid
  end
  vm = client.servers.new :name               => args[:name],
                          :affinity           => host,
                          :pv_bootloader      => '',
                          :hvm_boot_params    => { :order => 'dnc' },
                          :other_config       => other_config,
                          :memory_static_max  => mem_max,
                          :memory_static_min  => mem_min,
                          :memory_dynamic_max => mem_max,
                          :memory_dynamic_min => mem_min

  vm.save :auto_start => false
  client.vbds.create :vm => vm, :vdi => vdi

  create_network(vm, args)

  if args[:xstools] == '1'
    # Add xs-tools ISO to newly created VMs
    dvd_vdi = client.vdis.find { |isovdi| isovdi.name == 'xs-tools.iso' || isovdi.name == 'guest-tools.iso' }
    vbdconnectcd = {
      'vdi'                  => dvd_vdi,
      'vm'                   => vm.reference,
      'userdevice'           => '1',
      'mode'                 => 'RO',
      'type'                 => 'cd',
      'other_config'         => {},
      'qos_algorithm_type'   => '',
      'qos_algorithm_params' => {}
    }
    vm.vbds = client.vbds.create vbdconnectcd
    vm.reload
  end

  vm.provision
  vm.set_attribute('HVM_boot_policy', 'BIOS order')
  vm.reload
  vm
end

#create_vm_from_custom(args) ⇒ Object



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'app/models/foreman_xen/xenserver.rb', line 227

def create_vm_from_custom(args)
  mem_max = args[:memory_max]
  mem_min = args[:memory_min]

  host = get_hypervisor_host(args)

  logger.info "create_vm_from_builtin: host : #{host.name}"

  raise 'Memory max cannot be lower than Memory min' if mem_min.to_i > mem_max.to_i

  template    = client.custom_templates.select { |t| t.name == args[:custom_template_name] }.first
  vm          = template.clone args[:name]
  vm.affinity = host

  vm.provision

  begin
    vm.vifs.first.destroy
  rescue
    nil
  end

  create_network(vm, args)

  args['xenstore']['vm-data']['ifs']['0']['mac'] = vm.vifs.first.mac
  xenstore_data                                  = xenstore_hash_flatten(args['xenstore'])

  vm.set_attribute('xenstore_data', xenstore_data)
  if vm.memory_static_max.to_i < mem_max.to_i
    vm.set_attribute('memory_static_max', mem_max)
    vm.set_attribute('memory_dynamic_max', mem_max)
    vm.set_attribute('memory_dynamic_min', mem_min)
    vm.set_attribute('memory_static_min', mem_min)
  else
    vm.set_attribute('memory_static_min', mem_min)
    vm.set_attribute('memory_dynamic_min', mem_min)
    vm.set_attribute('memory_dynamic_max', mem_max)
    vm.set_attribute('memory_static_max', mem_max)
  end

  disks = vm.vbds.select { |vbd| vbd.type == 'Disk' }
  disks.sort! { |a, b| a.userdevice <=> b.userdevice }
  i = 0
  disks.each do |vbd|
    vbd.vdi.set_attribute('name-label', "#{args[:name]}_#{i}")
    i += 1
  end
  vm
end

#custom_templatesObject



140
141
142
# File 'app/models/foreman_xen/xenserver.rb', line 140

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

#custom_templates!Object



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

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.



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

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

#find_snapshotsObject



179
180
181
182
183
184
185
186
# File 'app/models/foreman_xen/xenserver.rb', line 179

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

#find_snapshots_for_vm(vm) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'app/models/foreman_xen/xenserver.rb', line 164

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



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

def find_vm_by_uuid(uuid)
  client.servers.get(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

#hypervisorObject



368
369
370
# File 'app/models/foreman_xen/xenserver.rb', line 368

def hypervisor
  client.hosts.first
end

#interfacesObject



114
115
116
117
118
# File 'app/models/foreman_xen/xenserver.rb', line 114

def interfaces
  client.interfaces
rescue
  []
end

#max_cpu_countObject



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

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



43
44
45
46
47
48
49
# File 'app/models/foreman_xen/xenserver.rb', line 43

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



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

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

#networks!Object



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

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

#new_nic(attr = {}) ⇒ Object



74
75
76
# File 'app/models/foreman_xen/xenserver.rb', line 74

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

#new_vm(attr = {}) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'app/models/foreman_xen/xenserver.rb', line 188

def new_vm(attr = {})
  test_connection
  return unless errors.empty?

  opts = vm_instance_defaults.merge(attr.to_hash).symbolize_keys

  %i[networks 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! { |_, v| v.nil? }
  client.servers.new opts
end

#new_volume(attr = {}) ⇒ Object



78
79
80
# File 'app/models/foreman_xen/xenserver.rb', line 78

def new_volume(attr = {})
  client.storage_repositories.new attr
end

#provided_attributesObject



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

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

#storage_poolsObject



82
83
84
# File 'app/models/foreman_xen/xenserver.rb', line 82

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

#storage_pools!Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'app/models/foreman_xen/xenserver.rb', line 86

def storage_pools!
  store_in_cache('storage_pools') do
    results = []
    storages = client.storage_repositories.select { |sr| sr.type != 'udev' && sr.type != 'iso' }
    storages.each do |sr|
      subresults = {}
      found      = false

      available_hypervisors.each do |host|
        next unless sr.reference == host.suspend_image_sr

        found                     = true
        subresults[:name]         = sr.name
        subresults[:display_name] = sr.name + '(' + host.hostname + ')'
        subresults[:uuid]         = sr.uuid
        break
      end
      unless found
        subresults[:name]         = sr.name
        subresults[:display_name] = sr.name
        subresults[:uuid]         = sr.uuid
      end
      results.push(subresults)
    end
    results.sort_by! { |item| item[:display_name] }
  end
end

#templatesObject



130
131
132
# File 'app/models/foreman_xen/xenserver.rb', line 130

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

#templates!Object



134
135
136
137
138
# File 'app/models/foreman_xen/xenserver.rb', line 134

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

#test_connection(options = {}) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
# File 'app/models/foreman_xen/xenserver.rb', line 51

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