Class: ForemanFogProxmox::Proxmox

Inherits:
ComputeResource
  • Object
show all
Includes:
ProxmoxContainerHelper, ProxmoxServerHelper, ProxmoxVmHelper
Defined in:
app/models/foreman_fog_proxmox/proxmox.rb

Constant Summary

Constants included from ProxmoxContainerHelper

ProxmoxContainerHelper::GIGA, ProxmoxContainerHelper::KILO, ProxmoxContainerHelper::MEGA

Constants included from ProxmoxServerHelper

ProxmoxServerHelper::GIGA, ProxmoxServerHelper::KILO, ProxmoxServerHelper::MEGA

Constants included from ProxmoxVmHelper

ProxmoxVmHelper::GIGA, ProxmoxVmHelper::KILO, ProxmoxVmHelper::MEGA

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ProxmoxContainerHelper

#parse_container_cpu, #parse_container_interface, #parse_container_interfaces, #parse_container_memory, #parse_container_ostemplate, #parse_container_vm, #parse_container_volume, #parse_container_volumes

Methods included from ProxmoxServerHelper

#parse_server_cdrom, #parse_server_cpu, #parse_server_interface, #parse_server_interfaces, #parse_server_memory, #parse_server_vm, #parse_server_volume, #parse_server_volumes

Methods included from ProxmoxVmHelper

#add_cdrom_to_config_server, #convert_memory_size, #convert_sizes, #disk_to_cdrom, #mount_point_disk?, #object_to_config_hash, #parse_type_and_vmid, #remove_deletes, #server_disk?

Class Method Details

.model_nameObject



47
48
49
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 47

def self.model_name
  ComputeResource.model_name
end

.provider_friendly_nameObject



39
40
41
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 39

def self.provider_friendly_name
  "Proxmox"
end

Instance Method Details

#associated_host(vm) ⇒ Object



83
84
85
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 83

def associated_host(vm)
  associate_by('mac', vm.mac)
end

#available_imagesObject



93
94
95
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 93

def available_images
  templates.collect { |template| OpenStruct.new(id: template.vmid) }
end

#bridgesObject



87
88
89
90
91
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 87

def bridges
  node = network_client.nodes.find_by_id node_name
  bridges = node.networks.all(type: 'any_bridge')
  bridges.sort_by(&:iface)
end

#capabilitiesObject



43
44
45
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 43

def capabilities
  [:build, :new_volume, :image]
end

#certs_to_storeObject



343
344
345
346
347
348
349
350
351
352
353
354
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 343

def certs_to_store
  return if ssl_certs.blank?
  store = OpenSSL::X509::Store.new
  ssl_certs.split(/(?=-----BEGIN)/).each do |cert|
    x509_cert = OpenSSL::X509::Certificate.new cert
    store.add_cert x509_cert
  end
  store
rescue => e
  logger.error(e)
  raise ::Foreman::Exception.new N_("Unable to store X509 certificates")
end

#connection_optionsObject



364
365
366
367
368
369
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 364

def connection_options
  opts = http_proxy ? {proxy: http_proxy.full_url} : {disable_proxy: 1}
  opts.store(:ssl_verify_peer, ssl_verify_peer)
  opts.store(:ssl_cert_store, certs_to_store) if Foreman::Cast.to_bool(ssl_verify_peer)
  opts
end

#console(uuid) ⇒ Object



371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 371

def console(uuid)
  vm = find_vm_by_uuid(uuid)
  type_console = vm.config.type_console
  options = {}
  options.store(:websocket, 1) if type_console == 'vnc'
  begin
    vnc_console = vm.start_console(options)  
    WsProxy.start(:host => host, :host_port => vnc_console['port'], :password => vnc_console['ticket']).merge(:name => vm.name, :type => type_console)
  rescue => e
    logger.error(e)
    raise ::Foreman::Exception.new(_("%s console is not supported at this time") % type_console)
  end
end

#create_vm(args = {}) ⇒ Object



226
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
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 226

def create_vm(args = {})
  vmid = args[:vmid].to_i
  type = args[:type]
  raise ::Foreman::Exception.new N_("invalid vmid=%{vmid}") % { vmid: vmid } unless node.servers.id_valid?(vmid)
  image_id = args[:image_id]
  if image_id
    logger.debug(_("create_vm(): clone %{image_id} in %{vmid}") % { image_id: image_id, vmid: vmid })
    image = node.servers.get image_id
    image.clone(vmid)
    clone = node.servers.get vmid
    clone.update(name: args[:name])        
  else
    logger.debug(_("create_vm(): %{args}") % { args: args })
    convert_sizes(args)
    remove_deletes(args)
    case type
      when 'qemu'
        node.servers.create(parse_server_vm(args))
      when 'lxc'
        hash = parse_container_vm(args)
        hash = hash.merge(vmid: vmid)
        node.containers.create(hash.reject { |key,_value| %w[ostemplate_storage ostemplate_file].include? key })
    end
  end
  vm = find_vm_by_uuid("#{type}_#{vmid}")
  vm
rescue => e
  logger.warn(_("failed to create vm: %{e}") % { e: e })
  destroy_vm vm.id if vm
  raise e
end

#credentials_valid?Boolean

Returns:

  • (Boolean)


51
52
53
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 51

def credentials_valid?
  errors[:url].empty? && errors[:user].empty? && errors[:user].include?('@') && errors[:password].empty? && errors[:node_name].empty?
end

#editable_network_interfaces?Boolean

Returns:

  • (Boolean)


289
290
291
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 289

def editable_network_interfaces?
  true
end

#find_vm_by_uuid(uuid) ⇒ Object



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

def find_vm_by_uuid(uuid)
  type, vmid = parse_type_and_vmid(uuid)
  case type
  when 'qemu'
    node.servers.get(vmid)
  when 'lxc'
    node.containers.get(vmid)
  end
rescue Fog::Errors::Error => e
  Foreman::Logging.exception(_("Failed retrieving proxmox vm by vmid=%{vmid} and type=%{type}") % { vmid: vmid, type: type }, e)
  raise(ActiveRecord::RecordNotFound)
end

#host_compute_attrs(host) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 109

def host_compute_attrs(host)
  super.tap do |attrs|
    ostype = host.compute_attributes['config_attributes']['ostype']
    case host.compute_attributes['type']
    when 'lxc'
      host.compute_attributes['config_attributes'].store('hostname',host.name)
    when 'qemu'
      raise ::Foreman::Exception.new(_("Operating system family %{type} is not consistent with %{ostype}") % { type: host.operatingsystem.type, ostype: ostype }) unless compute_os_types(host).include?(ostype)
    end
  end
end

#host_interfaces_attrs(host) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 121

def host_interfaces_attrs(host)
  host.interfaces.select(&:physical?).each.with_index.reduce({}) do |hash, (nic, index)|
    # Set default interface identifier to net[n]
    nic.identifier = "net%{index}" % {index: index} if nic.identifier.empty?
    raise ::Foreman::Exception.new _("Invalid identifier interface[%{index}]. Must be net[n] with n integer >= 0" % { index: index }) unless Fog::Proxmox::NicHelper.valid?(nic.identifier)
    # Set default container interface name to eth[n]
    container = host.compute_attributes['type'] == 'lxc'
    nic.compute_attributes['name'] = "eth%{index}" % {index: index} if container && nic.compute_attributes['name'].empty?
    raise ::Foreman::Exception.new _("Invalid name interface[%{index}]. Must be eth[n] with n integer >= 0" % { index: index }) if container && !/^(eth)(\d+)$/.match?(nic.compute_attributes['name'])
    nic_compute_attributes = nic.compute_attributes.merge(id: nic.identifier)
    nic_compute_attributes.store(:ip, nic.ip) if (nic.ip && !nic.ip.empty?)
    nic_compute_attributes.store(:ip6, nic.ip6) if (nic.ip6 && !nic.ip6.empty?)
    hash.merge(index.to_s => nic_compute_attributes)
  end
end

#image_exists?(image) ⇒ Boolean

Returns:

  • (Boolean)


297
298
299
300
301
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 297

def image_exists?(image)
  vm = find_vm_by_uuid("qemu_#{image}")
  vm = find_vm_by_uuid("lxc_#{image}") unless vm
  vm!=nil
end

#images_by_storage(type = 'iso', storage_id) ⇒ Object



78
79
80
81
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 78

def images_by_storage(type = 'iso', storage_id)
  storage = node.storages.find_by_id storage_id if storage_id
  storage.volumes.list_by_content_type(type).sort_by(&:volid) if storage
end

#new_container_vm(attr = {}) ⇒ Object



214
215
216
217
218
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 214

def new_container_vm(attr = {})
  vm = node.containers.new(vm_container_instance_defaults.merge(parse_container_vm(attr.merge(type: 'lxc'))))
  logger.debug(_("new_container_vm() vm.config=%{config}") % { config: vm.config.inspect })
  vm
end

#new_interface(attr = {}) ⇒ Object



161
162
163
164
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 161

def new_interface(attr = {})
  opts = interface_server_defaults.merge(attr.to_h).deep_symbolize_keys
  Fog::Compute::Proxmox::Interface.new(opts)
end

#new_server_vm(attr = {}) ⇒ Object



220
221
222
223
224
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 220

def new_server_vm(attr = {})
  vm = node.servers.new(vm_server_instance_defaults.merge(parse_server_vm(attr.merge(type: 'qemu'))))
  logger.debug(_("new_server_vm() vm.config=%{config}") % { config: vm.config.inspect })
  vm
end

#new_vm(attr = {}) ⇒ Object



200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 200

def new_vm(attr = {})
  attr = ActiveSupport::HashWithIndifferentAccess.new(attr)
  type = attr['type']
  type = 'qemu' unless type
  case type
  when 'lxc'
    vm = new_container_vm(attr)
  when 'qemu'
    vm = new_server_vm(attr)
  end
  logger.debug(_("new_vm() vm.config=%{config}") % { config: vm.config.inspect })
  vm
end

#new_volume(attr = {}) ⇒ Object



137
138
139
140
141
142
143
144
145
146
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 137

def new_volume(attr = {})     
  type = attr['type']
  type = 'qemu' unless type
  case type
  when 'lxc'
    return new_volume_server(attr)
  when 'qemu'
    return new_volume_container(attr)
  end
end

#new_volume_container(attr = {}) ⇒ Object



154
155
156
157
158
159
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 154

def new_volume_container(attr = {})
  id = attr[:id]
  opts = volume_container_defaults(id).merge(attr.to_h).deep_symbolize_keys
  opts[:size] = opts[:size].to_s
  Fog::Compute::Proxmox::Disk.new(opts)
end

#new_volume_server(attr = {}) ⇒ Object



148
149
150
151
152
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 148

def new_volume_server(attr = {})
  opts = volume_server_defaults.merge(attr.to_h).deep_symbolize_keys
  opts[:size] = opts[:size].to_s
  Fog::Compute::Proxmox::Disk.new(opts)
end

#next_vmidObject



319
320
321
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 319

def next_vmid
  node.servers.next_id
end

#nodeObject



331
332
333
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 331

def node
  client.nodes.find_by_id node_name
end

#node_nameObject



323
324
325
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 323

def node_name  
  self.attrs[:node_name]
end

#node_name=(value) ⇒ Object



327
328
329
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 327

def node_name=(value)
  self.attrs[:node_name] = value
end

#nodesObject



63
64
65
66
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 63

def nodes
  nodes = client.nodes.all if client
  nodes.sort_by(&:node) if nodes
end

#poolsObject



68
69
70
71
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 68

def pools
  pools = identity_client.pools.all
  pools.sort_by(&:poolid)
end

#provided_attributesObject



33
34
35
36
37
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 33

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

#save_vm(uuid, attr) ⇒ Object



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 303

def save_vm(uuid, attr)
  type, vmid = parse_type_and_vmid(uuid)
  attr = attr.merge(type: type, vmid: vmid)
  vm = find_vm_by_uuid(uuid)
  logger.debug(N_("save_vm(): %{attr}") % { attr: attr })
  templated = attr[:templated]
  if (templated == '1' && !vm.templated?)
    vm.template
  else
    parsed_attr = vm.container? ? parse_container_vm(attr) : parse_server_vm(attr)
    merged = vm.config.attributes.merge(parsed_attr.symbolize_keys).deep_symbolize_keys
    filtered = merged.reject { |key,value| [:node,:vmid,:type,:templated,:image_id].include?(key) || value.to_s.empty? }
    vm.update(filtered)
  end
end

#set_vm_config_attributes(vm, vm_attrs) ⇒ Object



174
175
176
177
178
179
180
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 174

def set_vm_config_attributes(vm, vm_attrs)
  if vm.respond_to?(:config)
    config = vm.config.attributes.reject { |key,value| [:disks,:mount_points,:interfaces].include?(key) || value.to_s.empty?}
    vm_attrs[:config_attributes] = config
  end
  vm_attrs
end

#set_vm_interfaces_attributes(vm, vm_attrs) ⇒ Object



188
189
190
191
192
193
194
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 188

def set_vm_interfaces_attributes(vm, vm_attrs)
  if vm.config.respond_to?(:interfaces)
    interfaces = vm.config.interfaces || []
    vm_attrs[:interfaces_attributes] = Hash[interfaces.each_with_index.map { |interface, idx| [idx.to_s, interface.attributes] }]
  end
  vm_attrs
end

#set_vm_volumes_attributes(vm, vm_attrs) ⇒ Object



182
183
184
185
186
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 182

def set_vm_volumes_attributes(vm, vm_attrs)
  volumes = vm.container? ? vm.config.mount_points : vm.config.disks || []
  vm_attrs[:volumes_attributes] = Hash[volumes.each_with_index.map { |volume, idx| [idx.to_s, volume.attributes] }]
  vm_attrs
end

#ssl_certsObject



335
336
337
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 335

def ssl_certs  
  self.attrs[:ssl_certs]
end

#ssl_certs=(value) ⇒ Object



339
340
341
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 339

def ssl_certs=(value)
  self.attrs[:ssl_certs] = value
end

#ssl_verify_peerObject



356
357
358
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 356

def ssl_verify_peer
  self.attrs[:ssl_verify_peer].blank? ? false : Foreman::Cast.to_bool(self.attrs[:ssl_verify_peer])
end

#ssl_verify_peer=(value) ⇒ Object



360
361
362
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 360

def ssl_verify_peer=(value)
  self.attrs[:ssl_verify_peer] = value
end

#storages(type = 'images') ⇒ Object



73
74
75
76
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 73

def storages(type = 'images')
  storages = node.storages.list_by_content_type type
  storages.sort_by(&:storage)
end

#supports_update?Boolean

Returns:

  • (Boolean)


271
272
273
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 271

def supports_update?
  true
end

#template(vmid) ⇒ Object



103
104
105
106
107
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 103

def template(vmid)
  vm = find_vm_by_uuid("qemu_#{vmid}")
  vm = find_vm_by_uuid("lxc_#{vmid}") unless vm
  vm
end

#templatesObject



97
98
99
100
101
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 97

def templates
  storage = storages.first
  images = storage.volumes.list_by_content_type('images')
  images.select { |image| image.templated? }
end

#test_connection(options = {}) ⇒ Object



55
56
57
58
59
60
61
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 55

def test_connection(options = {})
  super
  credentials_valid?
rescue => e
  errors[:base] << e.message
  errors[:url] << e.message
end

#update_required?(old_attrs, new_attrs) ⇒ Boolean

Returns:

  • (Boolean)


275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 275

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

#user_data_supported?Boolean

Returns:

  • (Boolean)


293
294
295
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 293

def user_data_supported?
  true
end

#vm_compute_attributes(vm) ⇒ Object



166
167
168
169
170
171
172
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 166

def vm_compute_attributes(vm)
  vm_attrs = vm.attributes.reject { |key,value| [:config].include?(key) || value.to_s.empty? }
  vm_attrs = set_vm_config_attributes(vm, vm_attrs)
  vm_attrs = set_vm_volumes_attributes(vm, vm_attrs)
  vm_attrs = set_vm_interfaces_attributes(vm, vm_attrs)
  vm_attrs
end

#vms(opts = {}) ⇒ Object



196
197
198
# File 'app/models/foreman_fog_proxmox/proxmox.rb', line 196

def vms(opts = {})
  node.servers
end