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.
- checksums.yaml +4 -4
- data/app/controllers/concerns/foreman_fog_proxmox/controller/parameters/compute_resource.rb +1 -2
- data/app/controllers/foreman_fog_proxmox/compute_resources_controller.rb +1 -2
- data/app/helpers/proxmox_compute_controllers_helper.rb +6 -4
- data/app/helpers/proxmox_compute_selectors_helper.rb +2 -1
- data/app/helpers/proxmox_vm_attrs_helper.rb +2 -1
- data/app/helpers/proxmox_vm_cloudinit_helper.rb +0 -8
- data/app/helpers/proxmox_vm_config_helper.rb +3 -1
- data/app/helpers/proxmox_vm_efidisk_helper.rb +64 -0
- data/app/helpers/proxmox_vm_helper.rb +22 -0
- data/app/helpers/proxmox_vm_image_template_helper.rb +55 -0
- data/app/models/concerns/fog_extensions/proxmox/server_config.rb +9 -0
- data/app/models/foreman_fog_proxmox/proxmox.rb +0 -10
- data/app/models/foreman_fog_proxmox/proxmox_efidisks.rb +11 -3
- data/app/models/foreman_fog_proxmox/proxmox_images.rb +22 -36
- data/app/models/foreman_fog_proxmox/proxmox_pools.rb +2 -19
- data/app/models/foreman_fog_proxmox/proxmox_vm_commands.rb +23 -16
- data/app/models/foreman_fog_proxmox/proxmox_vm_new.rb +19 -1
- data/app/models/foreman_fog_proxmox/proxmox_vm_queries.rb +13 -36
- data/app/views/api/v2/compute_resources/proxmox.json.rabl +1 -1
- data/app/views/compute_resources/form/_proxmox.html.erb +0 -2
- data/app/views/compute_resources/show/_proxmox.html.erb +4 -4
- data/lib/foreman_fog_proxmox/engine.rb +13 -10
- data/lib/foreman_fog_proxmox/version.rb +1 -1
- data/test/functional/compute_resources_controller_test.rb +0 -12
- data/test/unit/foreman_fog_proxmox/helpers/proxmox_vm_helper_test.rb +100 -0
- data/test/unit/foreman_fog_proxmox/proxmox_images_test.rb +3 -5
- data/test/unit/foreman_fog_proxmox/proxmox_test.rb +0 -27
- data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_container_test.rb +3 -1
- data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_server_create_test.rb +113 -8
- data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_server_update_test.rb +4 -4
- data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_test.rb +25 -0
- data/test/unit/foreman_fog_proxmox/proxmox_vm_new_test.rb +76 -0
- data/test/unit/foreman_fog_proxmox/proxmox_vm_queries_test.rb +2 -61
- data/webpack/components/ProxmoxBiosContext.js +14 -3
- data/webpack/components/ProxmoxComputeSelectors.js +1 -0
- data/webpack/components/ProxmoxServer/ProxmoxServerOptions.js +17 -4
- data/webpack/components/ProxmoxServer/ProxmoxServerStorage.js +5 -1
- data/webpack/components/ProxmoxServer/components/EFIDisk.js +13 -4
- data/webpack/components/ProxmoxServer/components/SecureBoot.js +69 -0
- data/webpack/components/ProxmoxStoragesUtils.js +6 -2
- data/webpack/components/ProxmoxVmType.js +5 -1
- data/webpack/components/__tests__/EFIDisk.test.js +59 -0
- data/webpack/global_test_setup.js +2 -1
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d59641775e4da477886d59bf332dadd576426ef079d1c6af43ef2e9f70b9c5a5
|
|
4
|
+
data.tar.gz: 5e24be7c7881de3c05d5a59a06500bd9d35f7c1f06d4c10390df473ec6ef8b01
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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:
|
|
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:
|
|
28
|
-
ForemanFogProxmox::OptionsSelect.new(id: 'sata', name: 'SATA', range:
|
|
29
|
-
ForemanFogProxmox::OptionsSelect.new(id: 'scsi', name: 'SCSI', range:
|
|
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(
|
|
25
|
-
logger.info("vm #{
|
|
26
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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(
|
|
87
|
-
logger.debug("create_vm(): clone #{
|
|
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
|
-
|
|
24
|
-
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|