foreman_fog_proxmox 0.24.0 → 0.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/foreman_fog_proxmox/controller/parameters/compute_resource.rb +1 -2
  3. data/app/controllers/foreman_fog_proxmox/compute_resources_controller.rb +1 -2
  4. data/app/helpers/proxmox_compute_controllers_helper.rb +6 -4
  5. data/app/helpers/proxmox_compute_selectors_helper.rb +2 -1
  6. data/app/helpers/proxmox_vm_attrs_helper.rb +2 -1
  7. data/app/helpers/proxmox_vm_cloudinit_helper.rb +0 -8
  8. data/app/helpers/proxmox_vm_config_helper.rb +3 -1
  9. data/app/helpers/proxmox_vm_efidisk_helper.rb +64 -0
  10. data/app/helpers/proxmox_vm_helper.rb +22 -0
  11. data/app/helpers/proxmox_vm_image_template_helper.rb +55 -0
  12. data/app/models/concerns/fog_extensions/proxmox/server_config.rb +9 -0
  13. data/app/models/foreman_fog_proxmox/proxmox.rb +0 -10
  14. data/app/models/foreman_fog_proxmox/proxmox_efidisks.rb +11 -3
  15. data/app/models/foreman_fog_proxmox/proxmox_images.rb +22 -36
  16. data/app/models/foreman_fog_proxmox/proxmox_pools.rb +2 -19
  17. data/app/models/foreman_fog_proxmox/proxmox_vm_commands.rb +23 -16
  18. data/app/models/foreman_fog_proxmox/proxmox_vm_new.rb +19 -1
  19. data/app/models/foreman_fog_proxmox/proxmox_vm_queries.rb +13 -36
  20. data/app/views/api/v2/compute_resources/proxmox.json.rabl +1 -1
  21. data/app/views/compute_resources/form/_proxmox.html.erb +0 -2
  22. data/app/views/compute_resources/show/_proxmox.html.erb +4 -4
  23. data/lib/foreman_fog_proxmox/engine.rb +13 -10
  24. data/lib/foreman_fog_proxmox/version.rb +1 -1
  25. data/test/functional/compute_resources_controller_test.rb +0 -12
  26. data/test/unit/foreman_fog_proxmox/helpers/proxmox_vm_helper_test.rb +100 -0
  27. data/test/unit/foreman_fog_proxmox/proxmox_images_test.rb +3 -5
  28. data/test/unit/foreman_fog_proxmox/proxmox_test.rb +0 -27
  29. data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_container_test.rb +3 -1
  30. data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_server_create_test.rb +113 -8
  31. data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_server_update_test.rb +4 -4
  32. data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_test.rb +25 -0
  33. data/test/unit/foreman_fog_proxmox/proxmox_vm_new_test.rb +76 -0
  34. data/test/unit/foreman_fog_proxmox/proxmox_vm_queries_test.rb +2 -61
  35. data/webpack/components/ProxmoxBiosContext.js +14 -3
  36. data/webpack/components/ProxmoxComputeSelectors.js +1 -0
  37. data/webpack/components/ProxmoxServer/ProxmoxServerOptions.js +17 -4
  38. data/webpack/components/ProxmoxServer/ProxmoxServerStorage.js +5 -1
  39. data/webpack/components/ProxmoxServer/components/EFIDisk.js +13 -4
  40. data/webpack/components/ProxmoxServer/components/SecureBoot.js +69 -0
  41. data/webpack/components/ProxmoxStoragesUtils.js +6 -2
  42. data/webpack/components/ProxmoxVmType.js +5 -1
  43. data/webpack/components/__tests__/EFIDisk.test.js +59 -0
  44. data/webpack/global_test_setup.js +2 -1
  45. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 494d9e13c53b6e6e1a4821d8c046d145060bac1058f9285763661bd935e5a4ef
4
- data.tar.gz: ba0639aa5659316381788213a3d6049ae98a8238a1612dd1dc8db7eed583d11f
3
+ metadata.gz: d59641775e4da477886d59bf332dadd576426ef079d1c6af43ef2e9f70b9c5a5
4
+ data.tar.gz: 5e24be7c7881de3c05d5a59a06500bd9d35f7c1f06d4c10390df473ec6ef8b01
5
5
  SHA512:
6
- metadata.gz: 4d7e5fc3d3bff0e58256217b5f69910dc03d0f08662dcc2ef1af35e242e5115f4e8023c565afa99cd83821dea73018d66b235354106766a4dbed351266c165ce
7
- data.tar.gz: 6ec6c8942c3e58cc7b3a05be5f2c0131bc47a0fb3cb9a4948d46cea8b51610ee33b7b6f0cbd8a08eac66818e7e83a2f0f51e3dca2ce8d0a9c051092d84c21c81
6
+ metadata.gz: e04095637c92c2a341043e96f345d4d9ddcd5a3f86b4285fc1ee5199a0e9c36fd542e40a28e748d66d4da56cec743618d144300b907be7c4d82a0a4daac21d82
7
+ data.tar.gz: 18a1bb26c3d35273d36826012d35c7e5f21a9c65193e86b7ab9babbde49ae5d15dcea828fa37a024e91740bdfb5a29788047f8ad8cd5829215324e21267e4079
@@ -27,8 +27,7 @@ module ForemanFogProxmox
27
27
  def compute_resource_params_filter
28
28
  super.tap do |filter|
29
29
  filter.permit :ssl_verify_peer,
30
- :ssl_certs, :disable_proxy, :auth_method, :token_id, :token,
31
- :caching_enabled
30
+ :ssl_certs, :disable_proxy, :auth_method, :token_id, :token
32
31
  end
33
32
  end
34
33
 
@@ -84,8 +84,7 @@ module ForemanFogProxmox
84
84
  node_id = params[:node_id]
85
85
  storage = params[:storage]
86
86
 
87
- node = cr.send(:client).nodes.get(node_id)
88
- vols = node&.storages&.get(storage)&.volumes || []
87
+ vols = cr.storages(node_id).find { |s| s.storage == storage }&.volumes || []
89
88
 
90
89
  render json: Array(vols).map { |v|
91
90
  h = v.respond_to?(:as_json) ? v.as_json : v
@@ -18,15 +18,17 @@
18
18
  # along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
19
19
 
20
20
  module ProxmoxComputeControllersHelper
21
+ CONTROLLER_LIMITS = { 'ide' => 3, 'sata' => 5, 'scsi' => 30, 'virtio' => 15 }.freeze
22
+
21
23
  def proxmox_controllers_map
22
24
  proxmox_controllers_cloudinit_map << ForemanFogProxmox::OptionsSelect.new(name: 'VirtIO Block', id: 'virtio',
23
- range: 15)
25
+ range: CONTROLLER_LIMITS['virtio'])
24
26
  end
25
27
 
26
28
  def proxmox_controllers_cloudinit_map
27
- [ForemanFogProxmox::OptionsSelect.new(id: 'ide', name: 'IDE', range: 3),
28
- ForemanFogProxmox::OptionsSelect.new(id: 'sata', name: 'SATA', range: 5),
29
- ForemanFogProxmox::OptionsSelect.new(id: 'scsi', name: 'SCSI', range: 13)]
29
+ [ForemanFogProxmox::OptionsSelect.new(id: 'ide', name: 'IDE', range: CONTROLLER_LIMITS['ide']),
30
+ ForemanFogProxmox::OptionsSelect.new(id: 'sata', name: 'SATA', range: CONTROLLER_LIMITS['sata']),
31
+ ForemanFogProxmox::OptionsSelect.new(id: 'scsi', name: 'SCSI', range: CONTROLLER_LIMITS['scsi'])]
30
32
  end
31
33
 
32
34
  def proxmox_scsi_controllers_map
@@ -141,6 +141,7 @@ module ProxmoxComputeSelectorsHelper
141
141
 
142
142
  def proxmox_bios_map
143
143
  [OpenStruct.new(id: 'seabios', name: '(Default) Seabios'),
144
- OpenStruct.new(id: 'ovmf', name: 'OVMF (UEFI)')]
144
+ OpenStruct.new(id: 'ovmf', name: 'OVMF (UEFI)'),
145
+ OpenStruct.new(id: 'uefi_secure_boot', name: 'OVMF (UEFI Secure Boot)')]
145
146
  end
146
147
  end
@@ -132,8 +132,9 @@ module ProxmoxVMAttrsHelper
132
132
  ostemplate_file: vms.ostemplate_file,
133
133
  start_after_create: vms.start_after_create,
134
134
  templated: vms.templated,
135
+ is_secure_boot: vms.config.secure_boot?,
135
136
  }
136
- vms_keys = [:cpu_type, :nameserver, :searchdomain, :hostname]
137
+ vms_keys = [:cpu_type, :nameserver, :searchdomain, :hostname, :is_secure_boot]
137
138
  extra_attrs = ActiveSupport::HashWithIndifferentAccess.new
138
139
  attributes.each do |key, value|
139
140
  camel_key = key.to_s.include?('_') ? snake_to_camel(key.to_s).to_sym : key
@@ -106,7 +106,6 @@ module ProxmoxVMCloudinitHelper
106
106
  configs = filenames.zip(config_data).to_h
107
107
 
108
108
  iso = create_cloudinit_iso(args[:name], configs, ssh)
109
- args[:config_attributes]&.merge!(update_boot_order(args[:image_id]))
110
109
  args.merge!(attach_cloudinit_iso(args[:node_id], iso))
111
110
  end
112
111
 
@@ -126,13 +125,6 @@ module ProxmoxVMCloudinitHelper
126
125
  "/var/lib/vz/template/iso"
127
126
  end
128
127
 
129
- def update_boot_order(image_id)
130
- vm = find_vm_by_uuid(image_id)
131
- return if vm.disks.nil?
132
- disks = vm.disks.map { |disk| disk.split(":")[0] }.join(";")
133
- { boot: "order=" + disks }
134
- end
135
-
136
128
  def vm_ssh
137
129
  ssh = Fog::SSH.new(URI.parse(fog_credentials[:proxmox_url]).host, fog_credentials[:proxmox_username].split('@')[0], { password: fog_credentials[:proxmox_password] })
138
130
  ssh.run('ls') # test if ssh is successful
@@ -60,7 +60,9 @@ module ProxmoxVMConfigHelper
60
60
  cpu_a += ForemanFogProxmox::HashCollection.stringify_keys(Fog::Proxmox::CpuHelper.flags).keys
61
61
  memory_a = ['memory', 'balloon', 'shares']
62
62
  cloud_init_a = ['ciuser', 'cipassword', 'searchdomain', 'nameserver']
63
+ firmware_a = ['is_secure_boot']
63
64
  keys.store(:cloud_init, cloud_init_a)
65
+ keys.store(:firmware, firmware_a)
64
66
  end
65
67
  keys.store(:main, main_a)
66
68
  keys.store(:cpu, cpu_a)
@@ -76,7 +78,7 @@ module ProxmoxVMConfigHelper
76
78
  config_a = []
77
79
  case type
78
80
  when 'qemu'
79
- [:cpu, :memory, :general, :cloud_init].each { |key| config_a += config_typed_keys(type)[key] }
81
+ [:cpu, :memory, :general, :cloud_init, :firmware].each { |key| config_a += config_typed_keys(type)[key] }
80
82
  when 'lxc'
81
83
  [:main].each { |key| config_a += config_typed_keys(type)[key] }
82
84
  end
@@ -23,6 +23,70 @@ require 'foreman_fog_proxmox/hash_collection'
23
23
 
24
24
  # Convert a foreman form server hash into a fog-proxmox server attributes hash
25
25
  module ProxmoxVMEfidiskHelper
26
+ def process_firmware_attributes(args, type)
27
+ return args unless type == 'qemu'
28
+ return args unless args
29
+
30
+ normalized_args = ActiveSupport::HashWithIndifferentAccess.new(args)
31
+ config = normalized_args[:config_attributes] || normalized_args
32
+ return args unless config.key?(:bios) || config.key?(:is_secure_boot)
33
+
34
+ args = normalized_args
35
+
36
+ bios = config[:bios]
37
+ secure_boot_value = config[:is_secure_boot]
38
+ secure_boot = bios == 'uefi_secure_boot' ||
39
+ (bios == 'ovmf' && Foreman::Cast.to_bool(secure_boot_value))
40
+ config[:is_secure_boot] = secure_boot ? '1' : '0'
41
+
42
+ unless secure_boot
43
+ normalize_pre_enrolled_keys(args)
44
+ return args
45
+ end
46
+
47
+ config[:bios] = 'ovmf'
48
+ process_secure_boot_efidisk(args, config)
49
+ end
50
+
51
+ def normalize_pre_enrolled_keys(args)
52
+ efidisk = args[:efidisk_attributes]
53
+ return args if ForemanFogProxmox::Value.empty?(efidisk)
54
+
55
+ efidisk[:pre_enrolled_keys] = '0'
56
+ args
57
+ end
58
+
59
+ def process_secure_boot_efidisk(args, config)
60
+ efidisk = args[:efidisk_attributes]
61
+ unless ForemanFogProxmox::Value.empty?(efidisk)
62
+ args[:efidisk_attributes] = secure_boot_efidisk_attributes(
63
+ efidisk.merge('efitype' => '4m', 'pre_enrolled_keys' => '1')
64
+ )
65
+ return args
66
+ end
67
+
68
+ volumes = args[:volumes_attributes] || config[:volumes_attributes]
69
+ storage = volumes&.each_value&.filter_map { |volume| volume[:storage] }&.find(&:present?)
70
+ return args if ForemanFogProxmox::Value.empty?(storage)
71
+
72
+ args[:efidisk_attributes] = secure_boot_efidisk_attributes(storage: storage)
73
+ args
74
+ end
75
+
76
+ def efidisk_defaults(storage)
77
+ {
78
+ 'id' => '0',
79
+ 'volid' => "#{storage}:0",
80
+ 'efitype' => '4m',
81
+ 'pre_enrolled_keys' => '1',
82
+ }
83
+ end
84
+
85
+ def secure_boot_efidisk_attributes(attr = {})
86
+ attr = attr.to_h.stringify_keys
87
+ efidisk_defaults(attr['storage']).merge(attr)
88
+ end
89
+
26
90
  def parsed_typed_efidisk(args, type, parsed_vm)
27
91
  attrs = args['efidisk_attributes']
28
92
 
@@ -19,11 +19,13 @@
19
19
 
20
20
  require 'fog/proxmox/helpers/disk_helper'
21
21
  require 'fog/proxmox/helpers/nic_helper'
22
+ require 'foreman_fog_proxmox/hash_collection'
22
23
  require 'foreman_fog_proxmox/value'
23
24
 
24
25
  module ProxmoxVMHelper
25
26
  include ProxmoxVMInterfacesHelper
26
27
  include ProxmoxVMVolumesHelper
28
+ include ProxmoxVMImageTemplateHelper
27
29
  include ProxmoxVMConfigHelper
28
30
  include ProxmoxVMOsTemplateHelper
29
31
  include ProxmoxVMEfidiskHelper
@@ -34,8 +36,28 @@ module ProxmoxVMHelper
34
36
  collection
35
37
  end
36
38
 
39
+ def parse_vm_update_attributes(attributes, type)
40
+ ForemanFogProxmox::HashCollection.new_hash_reject_keys(
41
+ attributes, ['volumes_attributes']
42
+ ).merge(type: type)
43
+ end
44
+
45
+ def update_boot_order(instance, exclude_cdrom: false, include_network: false)
46
+ return {} unless instance
47
+
48
+ disks = Array(instance.disks).map { |disk| disk.split(":")[0] }
49
+ disks.delete("ide2") if exclude_cdrom
50
+ network_interfaces = include_network ? Array(instance.interfaces).map(&:id) : []
51
+ boot_devices = network_interfaces + disks
52
+
53
+ return {} if boot_devices.empty?
54
+
55
+ { boot: "order=" + boot_devices.join(";") }
56
+ end
57
+
37
58
  # Convert a foreman form server/container vm hash into a fog-proxmox server/container attributes hash
38
59
  def parse_typed_vm(args, type)
60
+ args = process_firmware_attributes(args, type)
39
61
  args = ActiveSupport::HashWithIndifferentAccess.new(args)
40
62
  return {} unless args
41
63
  return {} if args.empty?
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of ForemanFogProxmox.
4
+
5
+ # ForemanFogProxmox is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+
10
+ # ForemanFogProxmox is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'fog/proxmox/helpers/disk_helper'
19
+
20
+ module ProxmoxVMImageTemplateHelper
21
+ def validate_image_template_disk_slots!(image, args)
22
+ reserved_slots = image.config.disks.select(&:hard_disk?).map(&:id)
23
+ limits = ProxmoxComputeControllersHelper::CONTROLLER_LIMITS
24
+ volumes = args['volumes_attributes']&.values || []
25
+ used_slots = reserved_slots + volumes.filter_map { |volume| hard_disk_slot(volume) }
26
+
27
+ volumes.each do |volume_attributes|
28
+ volume = volume_attributes.with_indifferent_access
29
+ slot = hard_disk_slot(volume_attributes)
30
+ next unless slot
31
+
32
+ controller = volume[:controller]
33
+ if reserved_slots.include?(slot)
34
+ device = (0..limits.fetch(controller, -1)).find do |number|
35
+ !used_slots.include?("#{controller}#{number}")
36
+ end
37
+ unless device
38
+ raise ::Foreman::Exception,
39
+ format(_('No free disk device is available for the %<controller>s controller.'), controller: controller)
40
+ end
41
+
42
+ volume_attributes['device'] = device.to_s
43
+ volume_attributes['id'] = slot = "#{controller}#{device}"
44
+ end
45
+ used_slots << slot
46
+ end
47
+ end
48
+
49
+ def hard_disk_slot(attributes)
50
+ volume = attributes.with_indifferent_access
51
+ id = volume[:id]
52
+ id if volume_type?(volume, 'hard_disk') &&
53
+ Fog::Proxmox::DiskHelper.server_disk?(id)
54
+ end
55
+ end
@@ -44,6 +44,15 @@ module FogExtensions
44
44
  def cloud_init?
45
45
  disks.any?(&:cloud_init?)
46
46
  end
47
+
48
+ def secure_boot?
49
+ attrs = attributes.with_indifferent_access
50
+ secure_boot = attrs[:is_secure_boot]
51
+ return Foreman::Cast.to_bool(secure_boot) ? '1' : '0' if attrs.key?(:is_secure_boot)
52
+
53
+ secure_boot = bios == 'ovmf' && Foreman::Cast.to_bool(efidisk&.pre_enrolled_keys)
54
+ Foreman::Cast.to_bool(secure_boot) ? '1' : '0'
55
+ end
47
56
  end
48
57
  end
49
58
  end
@@ -36,8 +36,6 @@ module ForemanFogProxmox
36
36
  include ProxmoxVersion
37
37
  include ProxmoxConsole
38
38
  include ComputeAttributesUpdateDetector
39
- include ComputeResourceCaching
40
-
41
39
  validates :url, :format => { :with => URI::DEFAULT_PARSER.make_regexp }, :presence => true
42
40
  validates :auth_method, :presence => true, :inclusion => { in: ['access_ticket', 'user_token'],
43
41
  message: ->(value) do format('%<value>s is not a valid authentication method', { value: value }) end }
@@ -131,14 +129,6 @@ module ForemanFogProxmox
131
129
 
132
130
  private
133
131
 
134
- def structs_from_cache(items)
135
- Array(items).map { |item| OpenStruct.new(item) }
136
- end
137
-
138
- def extract_attributes(resource, fields)
139
- slice_vm_attributes(fields.index_with { |field| resource.try(field) }, fields)
140
- end
141
-
142
132
  def fog_credentials
143
133
  hash = {
144
134
  proxmox_url: url,
@@ -21,9 +21,17 @@ module ForemanFogProxmox
21
21
  module ProxmoxEfidisks
22
22
  include ProxmoxVMHelper
23
23
 
24
- def delete_efidisk(vmobj)
25
- logger.info("vm #{vmobj.identity} delete efidisk0")
26
- vmobj.detach('efidisk0')
24
+ def delete_efidisk(vm_obj)
25
+ logger.info("vm #{vm_obj.identity} delete efidisk0")
26
+ vm_obj.detach('efidisk0')
27
+ end
28
+
29
+ def process_efidisk_removal(vm_obj, attributes)
30
+ return if vm_obj.config.efidisk.blank?
31
+ return if attributes['efidisk_attributes'].present?
32
+
33
+ logger.debug("Removing efidisk from VM #{vm_obj}")
34
+ delete_efidisk(vm_obj)
27
35
  end
28
36
  end
29
37
  end
@@ -24,18 +24,11 @@ module ForemanFogProxmox
24
24
  end
25
25
 
26
26
  def images_by_storage(node_id, storage_id, type = 'iso')
27
- cached_images = cache.cache(:"images_by_storage-#{node_id}-#{storage_id}-#{type}") do
28
- node = client.nodes.get(node_id) || default_node
29
- storage = node.storages.get storage_id if storage_id
30
- logger.debug("images_by_storage(): node_id #{node_id} storage_id #{storage_id} type #{type}")
31
- next [] unless storage
32
-
33
- storage.volumes.list_by_content_type(type).sort_by(&:volid).map do |volume|
34
- extract_attributes(volume, [:volid, :content, :format, :size, :used])
35
- end
36
- end
37
-
38
- structs_from_cache(cached_images)
27
+ node = client.nodes.get node_id
28
+ node ||= default_node
29
+ storage = node.storages.get storage_id if storage_id
30
+ logger.debug("images_by_storage(): node_id #{node_id} storage_id #{storage_id} type #{type}")
31
+ storage.volumes.list_by_content_type(type).sort_by(&:volid) if storage
39
32
  end
40
33
 
41
34
  def template_name(template)
@@ -52,40 +45,33 @@ module ForemanFogProxmox
52
45
  end
53
46
 
54
47
  def templates
55
- cached_templates = cache.cache(:templates) do
56
- volumes = fog_nodes.flat_map do |node|
57
- storages = node.storages.list_by_content_type 'images'
58
- logger.debug("storages(): node_id #{node.node} type images")
59
- storages.reject { |storage| storage.active.to_i.zero? }.sort_by(&:storage).flat_map do |storage|
60
- # Skip disabled storages (enabled == 0 or nil)
61
- unless storage.enabled.to_i == 1
62
- logger.warn("Skipping disabled storage #{storage.identity} on #{node.node}")
63
- next []
64
- end
65
-
66
- # Fetch QEMU and LXC template images
67
- storage.volumes.list_by_content_type('images') + storage.volumes.list_by_content_type('rootdir')
68
- rescue StandardError => e
69
- logger.error("Failed to fetch volumes for storage #{storage.identity} on #{node.node}: #{e.message}")
70
- []
48
+ volumes = []
49
+ nodes.each do |node|
50
+ storages(node.node).each do |storage|
51
+ # Skip disabled storages (enabled == 0 or nil)
52
+ unless storage.enabled.to_i == 1
53
+ logger.warn("Skipping disabled storage #{storage.identity} on #{node.node}")
54
+ next
71
55
  end
72
- end
73
56
 
74
- volumes.select(&:template?).map do |volume|
75
- extract_attributes(volume, [:vmid, :name, :volid, :node_id]).merge(template: true)
57
+ # Fetch QEMU and LXC template images
58
+ volumes += storage.volumes.list_by_content_type('images')
59
+ volumes += storage.volumes.list_by_content_type('rootdir')
60
+ rescue StandardError => e
61
+ logger.error("Failed to fetch volumes for storage #{storage.identity} on #{node.node}: #{e.message}")
62
+ next
76
63
  end
77
64
  end
78
-
79
- structs_from_cache(cached_templates)
65
+ # for creating image, only list volumes which are templated
66
+ volumes.select(&:template?)
80
67
  end
81
68
 
82
69
  def template(uuid)
83
70
  find_vm_by_uuid(uuid)
84
71
  end
85
72
 
86
- def clone_from_image(image_id, vmid)
87
- logger.debug("create_vm(): clone #{image_id} in #{vmid}")
88
- image = find_vm_by_uuid(image_id)
73
+ def clone_from_image(image, vmid)
74
+ logger.debug("create_vm(): clone #{image.identity} in #{vmid}")
89
75
  image.clone(vmid)
90
76
  find_vm_by_uuid(id.to_s + '_' + vmid.to_s)
91
77
  end
@@ -20,25 +20,8 @@
20
20
  module ForemanFogProxmox
21
21
  module ProxmoxPools
22
22
  def pools
23
- cached_pools = cache.cache(:pools) do
24
- pools = identity_client.pools.all
25
- pools.sort_by(&:poolid).map do |pool|
26
- extract_attributes(pool, [:poolid, :comment]).merge(
27
- members: Array(pool.try(:members)).map do |member|
28
- { vmid: member['vmid'] }
29
- end
30
- )
31
- end
32
- end
33
-
34
- # Store only serializable hashes in the cache. After retrieval we recreate the pool objects and reattach the `has_server?` helper method.
35
- Array(cached_pools).map do |pool|
36
- OpenStruct.new(pool).tap do |pool_object|
37
- pool_object.define_singleton_method(:has_server?) do |vmid|
38
- Array(members).any? { |member| member[:vmid].to_s == vmid.to_s || member['vmid'].to_s == vmid.to_s }
39
- end
40
- end
41
- end
23
+ pools = identity_client.pools.all
24
+ pools.sort_by(&:poolid)
42
25
  end
43
26
 
44
27
  def pool_owner(vm)
@@ -36,17 +36,21 @@ module ForemanFogProxmox
36
36
  type = args[:type]
37
37
  node = client.nodes.get(args[:node_id])
38
38
  vmid = args[:vmid] = assign_vmid(args[:vmid].to_i, node)
39
- raise ::Foreman::Exception, format(N_('invalid vmid=%<vmid>s'), vmid: vmid) unless node.servers.id_valid?(vmid)
40
-
41
39
  image_id = args[:image_id]
42
40
  remove_volume_keys(args)
43
41
  if image_id
44
- vm = clone_from_image(image_id, vmid)
45
- vm.update(compute_clone_attributes(args, vm.container?, type))
42
+ image = find_vm_by_uuid(image_id)
43
+ validate_image_template_disk_slots!(image, args) if type == 'qemu'
44
+ vm = clone_from_image(image, vmid)
45
+ vm.update(compute_clone_attributes(args, vm.container?, type, image: image))
46
46
  update_pool(vm, args[:pool]) if args[:pool]
47
47
  else
48
48
  logger.warn("create vm: args=#{args}")
49
49
  vm = node.send(vm_collection(type)).create(parse_typed_vm(args, type))
50
+ if type == 'qemu' && vm
51
+ boot_order = update_boot_order(vm, exclude_cdrom: true, include_network: true)
52
+ vm.update(boot_order) if boot_order
53
+ end
50
54
  end
51
55
  start_on_boot(vm, args)
52
56
  rescue StandardError => e
@@ -55,12 +59,20 @@ module ForemanFogProxmox
55
59
  raise e
56
60
  end
57
61
 
58
- def assign_vmid(vmid, node)
59
- (vmid < 1) ? node.servers.next_id : vmid
62
+ def assign_vmid(vmid, node, log: true)
63
+ vmid = node.servers.next_id if vmid < 1
64
+
65
+ unless node.servers.id_valid?(vmid)
66
+ logger.warn("Requested VMID #{vmid} is occupied or out of range, requesting a new VMID") if log
67
+ vmid = node.servers.next_id
68
+ end
69
+
70
+ vmid
60
71
  end
61
72
 
62
- def compute_clone_attributes(args, container, type)
73
+ def compute_clone_attributes(args, container, type, image: nil)
63
74
  args = parse_cloudinit_config(args) if args[:user_data]
75
+ args[:config_attributes].merge!(update_boot_order(image)) if image && args[:config_attributes]
64
76
  parsed_args = parse_typed_vm(args, type)
65
77
  if container
66
78
  options = { :hostname => args[:name] }
@@ -105,10 +117,9 @@ module ForemanFogProxmox
105
117
  elsif vm.node_id != node_id
106
118
  vm.migrate(node_id)
107
119
  else
108
- parsed_attr = parse_typed_vm(
109
- ForemanFogProxmox::HashCollection.new_hash_reject_keys(new_attributes,
110
- ['volumes_attributes']).merge(type: vm.type), vm.type
111
- )
120
+ processed_attributes = process_firmware_attributes(new_attributes, vm.type)
121
+ update_attributes = parse_vm_update_attributes(processed_attributes, vm.type)
122
+ parsed_attr = parse_typed_vm(update_attributes, vm.type)
112
123
  config_attributes = compute_config_attributes(parsed_attr)
113
124
 
114
125
  volumes_attributes = new_attributes['volumes_attributes']
@@ -118,11 +129,7 @@ module ForemanFogProxmox
118
129
  save_volume(vm, volume_attributes)
119
130
  end
120
131
 
121
- efidisk_attributes = new_attributes['efidisk_attributes']
122
- if vm.config.efidisk.present? && efidisk_attributes.empty?
123
- logger.debug("Removing efidisk from VM #{vm}")
124
- delete_efidisk(vm)
125
- end
132
+ process_efidisk_removal(vm, processed_attributes)
126
133
 
127
134
  vm.update(config_attributes[:config_attributes])
128
135
  poolid = new_attributes['pool'] if new_attributes.key?('pool')
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics/ModuleLength
4
+
3
5
  # Copyright 2019 Tristan Robert
4
6
 
5
7
  # This file is part of ForemanFogProxmox.
@@ -97,7 +99,7 @@ module ForemanFogProxmox
97
99
  end
98
100
 
99
101
  def default_node
100
- fog_nodes.first
102
+ nodes.first
101
103
  end
102
104
 
103
105
  def default_node_id
@@ -182,18 +184,32 @@ module ForemanFogProxmox
182
184
  config_attributes.delete_if { |key, _value| ['disks', 'interfaces'].include?(key) }
183
185
  end
184
186
 
187
+ def assign_available_vmid(new_attr, node)
188
+ return if new_attr['vmid'].blank? || new_attr['node_id'].blank?
189
+
190
+ new_attr['vmid'] = assign_vmid(new_attr['vmid'].to_i, node, log: false)
191
+ end
192
+
193
+ def add_secure_boot_attribute(vm_h, options, type)
194
+ secure_boot = options.dig('config_attributes', 'is_secure_boot')
195
+ vm_h[:is_secure_boot] = secure_boot if type == 'qemu' && secure_boot
196
+ end
197
+
185
198
  def new_typed_vm(new_attr, type)
186
199
  convert_config_attributes(new_attr) if new_attr.key?(:config_attributes)
187
200
  node_id = new_attr['node_id']
188
201
  node = node_id ? client.nodes.get(node_id) : default_node
202
+ assign_available_vmid(new_attr, node)
189
203
  new_attr_type = new_attr['type']
190
204
  new_attr_type ||= new_attr['config_attributes']['type'] if new_attr.key?('config_attributes')
191
205
  new_attr_type ||= type
192
206
  logger.debug("new_typed_vm(#{type}): new_attr_type=#{new_attr_type}")
193
207
  logger.debug("new_typed_vm(#{type}): new_attr=#{new_attr}'")
194
208
  options = (!new_attr.key?('vmid') || ForemanFogProxmox::Value.empty?(new_attr['vmid'])) ? vm_typed_instance_defaults(type).merge(new_attr).merge(type: type) : new_attr
209
+ options = process_firmware_attributes(options, type)
195
210
  logger.debug("new_typed_vm(#{type}): options=#{options}")
196
211
  vm_h = parse_typed_vm(options, type).deep_symbolize_keys
212
+ add_secure_boot_attribute(vm_h, options, type)
197
213
  logger.debug("new_typed_vm(#{type}): vm_h=#{vm_h}")
198
214
  vm_h = vm_h.merge(vm_typed_instance_defaults(type)) if vm_h.empty?
199
215
  logger.debug(format(_('new_typed_vm(%<type>s) with vm_typed_instance_defaults: vm_h=%<vm_h>s'), type: type, vm_h: vm_h))
@@ -201,3 +217,5 @@ module ForemanFogProxmox
201
217
  end
202
218
  end
203
219
  end
220
+
221
+ # rubocop:enable Metrics/ModuleLength