foreman_fog_proxmox 0.21.0 → 0.23.0

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_vm_container.js +36 -1
  3. data/app/controllers/foreman_fog_proxmox/compute_resources_controller.rb +34 -0
  4. data/app/helpers/proxmox_form_helper.rb +69 -0
  5. data/app/helpers/proxmox_vm_attrs_helper.rb +11 -3
  6. data/app/helpers/proxmox_vm_interfaces_helper.rb +21 -4
  7. data/app/helpers/proxmox_vm_volumes_helper.rb +29 -3
  8. data/app/models/concerns/fog_extensions/proxmox/server.rb +7 -1
  9. data/app/models/concerns/host_ext/proxmox/interfaces.rb +1 -2
  10. data/app/models/foreman_fog_proxmox/proxmox.rb +3 -1
  11. data/app/models/foreman_fog_proxmox/proxmox_compute_attributes.rb +26 -5
  12. data/app/models/foreman_fog_proxmox/proxmox_operating_systems.rb +13 -2
  13. data/app/models/foreman_fog_proxmox/proxmox_vm_new.rb +6 -6
  14. data/app/models/foreman_fog_proxmox/proxmox_vm_queries.rb +13 -3
  15. data/app/models/foreman_fog_proxmox/proxmox_volumes.rb +16 -6
  16. data/app/views/compute_resources_vms/form/proxmox/_add_vm_type_to_nic_provider_specific_form.html.erb +5 -6
  17. data/app/views/compute_resources_vms/form/proxmox/container/_network.html.erb +12 -10
  18. data/app/views/compute_resources_vms/form/proxmox/server/_network.html.erb +1 -2
  19. data/app/views/compute_resources_vms/index/_proxmox.html.erb +1 -0
  20. data/lib/foreman_fog_proxmox/version.rb +1 -1
  21. data/test/unit/foreman_fog_proxmox/helpers/proxmox_container_helper_test.rb +2 -2
  22. data/test/unit/foreman_fog_proxmox/helpers/proxmox_server_helper_test.rb +4 -4
  23. data/test/unit/foreman_fog_proxmox/helpers/proxmox_vm_volumes_helper_test.rb +8 -0
  24. data/test/unit/foreman_fog_proxmox/proxmox_compute_attributes_test.rb +71 -4
  25. data/test/unit/foreman_fog_proxmox/proxmox_interfaces_test.rb +1 -1
  26. data/test/unit/foreman_fog_proxmox/proxmox_operating_systems_test.rb +48 -0
  27. data/test/unit/foreman_fog_proxmox/proxmox_test.rb +6 -0
  28. data/test/unit/foreman_fog_proxmox/proxmox_vm_new_test.rb +44 -0
  29. data/test/unit/foreman_fog_proxmox/proxmox_vm_queries_test.rb +67 -0
  30. data/webpack/components/ProxmoxComputeSelectors.js +5 -0
  31. data/webpack/components/ProxmoxContainer/MountPoint.js +10 -0
  32. data/webpack/components/ProxmoxContainer/ProxmoxContainerNetwork.js +5 -1
  33. data/webpack/components/ProxmoxContainer/ProxmoxContainerOptions.js +13 -1
  34. data/webpack/components/ProxmoxContainer/ProxmoxContainerStorage.js +6 -3
  35. data/webpack/components/ProxmoxContainer/components/NetworkInterface.js +9 -2
  36. data/webpack/components/ProxmoxServer/ProxmoxServerStorage.js +181 -32
  37. data/webpack/components/ProxmoxServer/components/HardDisk.js +86 -17
  38. data/webpack/components/ProxmoxVmType.js +84 -1
  39. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b24eea105cd6eae85d7c04d8f3e17e76ea4354722bb8eef4c85242fc62196b6b
4
- data.tar.gz: 7217844296cc0beb945a356b2ad05ae435795e59cee74871be09a8351c91576b
3
+ metadata.gz: a437699869ae38137e94761791e2a01d741e9dd36f924c286bfaa9afc139f9e7
4
+ data.tar.gz: 5698645c6f2a3a3278959989e5214357263f3ba7c0091830d1f41205033582ea
5
5
  SHA512:
6
- metadata.gz: 202536e1272df6cb0440bd8c538f6e15a2753cda3aba3a0e726e49264fdeb82a90491c8b225b9001c795a2a52379d574770a79480b6b79e211f8d36b1d122bc8
7
- data.tar.gz: a43bf12d2ccd3b0b80fcc939487e7ac5ec2309b0b775620583f99da7aba7ccec8291b28258db55d8a639ff233d289e55a9d0b4aa4ac39983359f61a236603063
6
+ metadata.gz: 1d29712272ab785a33e4bdc70f345b0da96ff22d020820bbefc27c14302c24c1207d198d6ec742562a4d30a84083b1e081b43bb6a855c432e5a67ce60ca8a5b9
7
+ data.tar.gz: 9f3c39bbeca9d7364f4ee92cef02cc6fc7c398692cbc12f03a7c581324a9c21ca65ef1af33fcc892b1da02e3378f3f7c214fad2fb79bded7cfb028d55f5e5827
@@ -21,4 +21,39 @@ function storageOstemplateSelected(item) {
21
21
  var node_id = $('#host_compute_attributes_node_id').val();
22
22
  if (node_id == undefined) node_id = $("#compute_attribute_vm_attrs_node_id").val();
23
23
  updateOptions('ostemplates', 'compute_attributes_ostemplate', 'file', undefined, undefined, 'volid', node_id, storage);
24
- }
24
+ }
25
+
26
+ function setDisabled($el, disabled) {
27
+ if (!$el || !$el.length) return;
28
+ $el.prop('disabled', disabled);
29
+ $el.toggleClass('disabled', disabled);
30
+ }
31
+
32
+ function syncDhcpIP(item, suffix) {
33
+ var $dhcp = $(item);
34
+ var checked = $dhcp.is(':checked');
35
+ var $fieldset = $dhcp.closest('fieldset');
36
+ var $cidr = $fieldset.find('input.proxmox-cidr-' + suffix).first();
37
+ var $gw = $fieldset.find('input.proxmox-gw-' + suffix).first();
38
+
39
+ setDisabled($cidr, checked);
40
+ setDisabled($gw, checked);
41
+ }
42
+
43
+ function dhcpIPSelected(item, suffix) {
44
+ syncDhcpIP(item, suffix);
45
+ }
46
+
47
+ function syncAllProxmoxDhcpFields() {
48
+ $('input.proxmox-dhcp-ipv4').each(function() {
49
+ syncDhcpIP(this, 'ipv4');
50
+ });
51
+
52
+ $('input.proxmox-dhcp-ipv6').each(function() {
53
+ syncDhcpIP(this, 'ipv6');
54
+ });
55
+ }
56
+
57
+ $(document).on('turbolinks:load', function() {
58
+ syncAllProxmoxDhcpFields();
59
+ });
@@ -101,6 +101,7 @@ module ForemanFogProxmox
101
101
  pools: extract_pools(cr),
102
102
  storages: extract_storages(cr),
103
103
  bridges: extract_bridges(cr),
104
+ images: extract_images(cr),
104
105
  }
105
106
  end
106
107
 
@@ -141,6 +142,39 @@ module ForemanFogProxmox
141
142
  end
142
143
  end
143
144
 
145
+ def extract_images(compute_resource)
146
+ images = compute_resource.images
147
+ images = images.order(:name) if images.respond_to?(:order)
148
+
149
+ Array(images).map do |image|
150
+ data = image.respond_to?(:as_json) ? image.as_json : image
151
+ uuid = image_value(image, data, :uuid, :id)
152
+
153
+ {
154
+ uuid: uuid,
155
+ name: image_value(image, data, :name),
156
+ disks: image_disks(compute_resource, uuid),
157
+ }
158
+ end
159
+ end
160
+
161
+ def image_disks(compute_resource, uuid)
162
+ return nil unless uuid && compute_resource.respond_to?(:find_vm_by_uuid)
163
+
164
+ compute_resource.find_vm_by_uuid(uuid)&.disks
165
+ rescue ActiveRecord::RecordNotFound, StandardError
166
+ nil
167
+ end
168
+
169
+ def image_value(image, data, *keys)
170
+ keys.each do |key|
171
+ value = [image.try(key), data.try(:[], key), data.try(:[], key.to_s)].find(&:present?)
172
+ return value if value.present?
173
+ end
174
+
175
+ nil
176
+ end
177
+
144
178
  def load_compute_resource(compute_resource_id)
145
179
  ComputeResource.find(compute_resource_id)
146
180
  end
@@ -18,6 +18,47 @@
18
18
  # along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
19
19
 
20
20
  module ProxmoxFormHelper
21
+ include ProxmoxVMInterfacesHelper
22
+
23
+ def proxmox_vm_type_and_node_id(host, form_object, params)
24
+ compute_attributes = form_object.compute_attributes || {}
25
+ host_compute_attrs = host.respond_to?(:compute_attributes) ? (host.compute_attributes || {}) : {}
26
+ host_vm_type = extract_attr(host_compute_attrs, :type)
27
+
28
+ profile_attrs = profile_compute_attributes(host)
29
+ profile_vm_type = extract_attr(profile_attrs, :type)
30
+
31
+ vm_type = [
32
+ params.dig(:host, :compute_attributes, :type).presence,
33
+ params.dig(:compute_attribute, :vm_attrs, :type).presence,
34
+ host_vm_type,
35
+ profile_vm_type,
36
+ 'qemu',
37
+ ].find(&:present?)
38
+
39
+ compute_attributes = proxmox_default_interface_compute_attributes(host, vm_type) unless proxmox_valid_interface_compute_attributes?(compute_attributes, vm_type)
40
+
41
+ compute_node_id = extract_attr(compute_attributes, :node_id)
42
+ object_node_id = form_object.respond_to?(:node_id) ? form_object.node_id : nil
43
+ host_node_id = extract_attr(host_compute_attrs, :node_id)
44
+ profile_node_id = extract_attr(profile_attrs, :node_id)
45
+
46
+ node_id = [
47
+ params.dig(:host, :compute_attributes, :node_id).presence,
48
+ params.dig(:compute_attribute, :vm_attrs, :node_id).presence,
49
+ object_node_id,
50
+ compute_node_id,
51
+ host_node_id,
52
+ profile_node_id,
53
+ ].find(&:present?)
54
+
55
+ {
56
+ compute_attributes: compute_attributes,
57
+ vm_type: vm_type,
58
+ node_id: node_id,
59
+ }
60
+ end
61
+
21
62
  def password_proxmox_f(f, attr, options = {})
22
63
  unset_button = options.delete(:unset)
23
64
  value = f.object[attr] if options.delete(:keep_value)
@@ -79,4 +120,32 @@ module ProxmoxFormHelper
79
120
  hide += '$("[data-association=' + type + '_volumes]").show();' unless ['hard_disk', 'mp'].include?(type)
80
121
  f.hidden_field(opts[:method] || :_destroy) + link_to_function(name, 'remove_child_node(this);' + hide, opts)
81
122
  end
123
+
124
+ private
125
+
126
+ def proxmox_default_interface_compute_attributes(host, vm_type)
127
+ host.compute_resource.interface_typed_defaults(vm_type).fetch(:compute_attributes).deep_dup
128
+ end
129
+
130
+ def proxmox_valid_interface_compute_attributes?(compute_attributes, vm_type)
131
+ return false unless compute_attributes.respond_to?(:keys)
132
+
133
+ valid_keys = interface_compute_attributes_typed_keys(vm_type)
134
+ compute_attributes.keys.map(&:to_s).all? { |key| valid_keys.include?(key) }
135
+ end
136
+
137
+ def extract_attr(attrs, key)
138
+ return unless attrs.respond_to?(:[])
139
+
140
+ attrs[key.to_s] || attrs[key.to_sym]
141
+ end
142
+
143
+ def profile_compute_attributes(host)
144
+ inherited_profile_id = host.respond_to?(:hostgroup) ? host.hostgroup&.inherited_compute_profile_id : nil
145
+ profile_id = host.compute_profile_id || inherited_profile_id
146
+
147
+ return {} if profile_id.blank?
148
+
149
+ host.compute_resource.compute_profile_attributes_for(profile_id) || {}
150
+ end
82
151
  end
@@ -49,7 +49,7 @@ module ProxmoxVMAttrsHelper
49
49
 
50
50
  def cpu_flags_attrs(param_scope, config)
51
51
  flag_attrs = ActiveSupport::HashWithIndifferentAccess.new
52
- Fog::Proxmox::CpuHelper.flags.each do |key, _val|
52
+ Fog::Proxmox::CpuHelper.flags.each_key do |key|
53
53
  flag_attrs.merge!({ key => { :name => "#{param_scope}[config_attributes][#{key}]", :value => config.public_send(key) } })
54
54
  end
55
55
  flag_attrs
@@ -73,7 +73,7 @@ module ProxmoxVMAttrsHelper
73
73
  keys = ['id', 'volid', 'storage', 'size', 'storage_type']
74
74
  type = 'rootfs'
75
75
  elsif vol.hard_disk?
76
- keys = ['id', 'volid', 'storage_type', 'storage', 'controller', 'device', 'cache', 'size', '_delete']
76
+ keys = ['id', 'volid', 'storage_type', 'storage', 'controller', 'device', 'cache', 'size', '_delete', 'backup']
77
77
  type = 'hard_disk'
78
78
  elsif vol.cdrom?
79
79
  keys = ['id', 'storage_type', 'cdrom', 'storage', 'volid']
@@ -82,7 +82,7 @@ module ProxmoxVMAttrsHelper
82
82
  keys = ['id', 'volid', 'storage_type', 'storage', 'controller', 'device']
83
83
  type = 'cloud_init'
84
84
  elsif vol.mount_point?
85
- keys = ['id', 'volid', 'storage_type', 'storage', 'device', 'mp', 'size']
85
+ keys = ['id', 'volid', 'storage_type', 'storage', 'device', 'mp', 'size', 'backup']
86
86
  type = 'mount_point'
87
87
  end
88
88
  vol_attrs << { :name => type, :value => vol_keys(param_scope, keys, vol, id) }
@@ -107,6 +107,14 @@ module ProxmoxVMAttrsHelper
107
107
  camel_key = key.to_s.include?('_') ? snake_to_camel(key.to_s).to_sym : key
108
108
  attrs[camel_key] = { :name => "#{param_scope}[interfaces_attributes][#{id}][#{key}]", :value => value }
109
109
  end
110
+ if interface.respond_to?(:dhcp)
111
+ attrs[:dhcp] = { :name => "#{param_scope}[interfaces_attributes][#{id}][dhcp]",
112
+ :value => interface.dhcp ? '1' : '0' }
113
+ end
114
+ if interface.respond_to?(:dhcp6)
115
+ attrs[:dhcp6] = { :name => "#{param_scope}[interfaces_attributes][#{id}][dhcp6]",
116
+ :value => interface.dhcp6 ? '1' : '0' }
117
+ end
110
118
  networks_attrs << { :name => 'interface', :value => attrs }
111
119
  end
112
120
  networks_attrs
@@ -60,10 +60,27 @@ module ProxmoxVMInterfacesHelper
60
60
  end
61
61
 
62
62
  def compute_dhcps(interface_attributes_h)
63
- interface_attributes_h[:dhcp] = (interface_attributes_h[:ip] == 'dhcp') ? '1' : '0'
64
- interface_attributes_h[:ip] = '' if interface_attributes_h[:dhcp] == '1'
65
- interface_attributes_h[:dhcp6] = (interface_attributes_h[:ip6] == 'dhcp') ? '1' : '0'
66
- interface_attributes_h[:ip6] = '' if interface_attributes_h[:dhcp6] == '1'
63
+ return if interface_attributes_h.nil?
64
+
65
+ sync_dhcp_for_ip(interface_attributes_h, 'ip', 'dhcp')
66
+ sync_dhcp_for_ip(interface_attributes_h, 'ip6', 'dhcp6')
67
+ end
68
+
69
+ def sync_dhcp_for_ip(attrs, ip_key, dhcp_key)
70
+ dhcp_value = attrs[dhcp_key] || attrs[dhcp_key.to_sym]
71
+ if dhcp_value.nil?
72
+ ip_value = attrs[ip_key] || attrs[ip_key.to_sym]
73
+ attrs[dhcp_key.to_sym] = (ip_value == 'dhcp') ? '1' : '0'
74
+ attrs[ip_key.to_sym] = '' if attrs[dhcp_key.to_sym] == '1'
75
+ return
76
+ end
77
+
78
+ ip_value = attrs[ip_key] || attrs[ip_key.to_sym]
79
+ if dhcp_value.to_s == '1'
80
+ attrs[ip_key.to_sym] = 'dhcp' if ip_value.to_s.empty?
81
+ elsif ip_value.to_s == 'dhcp'
82
+ attrs[ip_key.to_sym] = ''
83
+ end
67
84
  end
68
85
 
69
86
  def add_or_delete_typed_interface(interface_attributes, interfaces_to_delete, interfaces_to_add, type)
@@ -51,13 +51,35 @@ module ProxmoxVMVolumesHelper
51
51
  def parse_hard_disk_volume(args)
52
52
  logger.debug(format(_('parse_hard_disk_volume(): args=%<args>s'), args: args))
53
53
  disk = {}
54
+ rootfs_volume = rootfs_volume?(args)
55
+ set_disk_attributes(disk, args)
56
+ if rootfs_volume
57
+ args.delete('backup')
58
+ elsif args['backup'].nil?
59
+ args['backup'] = '1'
60
+ end
61
+ if args.key?('options')
62
+ options = args['options']
63
+ if rootfs_volume && options.respond_to?(:delete)
64
+ options = options.dup
65
+ options.delete('backup')
66
+ end
67
+ disk[:options] = options
68
+ else
69
+ add_disk_options(disk, args)
70
+ end
71
+ disk.key?(:storage) ? disk : {}
72
+ end
73
+
74
+ def rootfs_volume?(args)
75
+ args['storage_type'] == 'rootfs' || args['id'] == 'rootfs'
76
+ end
77
+
78
+ def set_disk_attributes(disk, args)
54
79
  disk[:id] = args['id'] if args.key?('id')
55
80
  disk[:volid] = args['volid'] if args.key?('volid') && !args['volid'].empty?
56
81
  disk[:storage] = args['storage'].to_s if args.key?('storage') && !args['storage'].empty?
57
82
  disk[:size] = args['size'].to_i if args.key?('size') && !args['size'].empty?
58
- add_disk_options(disk, args) unless args.key?('options')
59
- disk[:options] = args['options'] if args.key?('options')
60
- disk.key?(:storage) ? disk : {}
61
83
  end
62
84
 
63
85
  def volume_type?(args, type)
@@ -71,6 +93,7 @@ module ProxmoxVMVolumesHelper
71
93
  end
72
94
 
73
95
  def parse_typed_volume(args, type)
96
+ return if Foreman::Cast.to_bool(args['_delete'])
74
97
  logger.debug("parse_typed_volume(#{type}): args=#{args}")
75
98
  disk = parse_hard_disk_volume(args) if volume_type?(args,
76
99
  'hard_disk') || volume_type?(args, 'mp') || volume_type?(args, 'rootfs')
@@ -94,6 +117,9 @@ module ProxmoxVMVolumesHelper
94
117
 
95
118
  def remove_volume_keys(args)
96
119
  if args.key?('volumes_attributes')
120
+ args['volumes_attributes'].delete_if do |_index, volume_attributes|
121
+ Foreman::Cast.to_bool(volume_attributes['_delete'])
122
+ end
97
123
  args['volumes_attributes'].each_value do |volume_attributes|
98
124
  ForemanFogProxmox::HashCollection.remove_keys(volume_attributes, ['_delete'])
99
125
  end
@@ -21,12 +21,18 @@ module FogExtensions
21
21
  module Proxmox
22
22
  module Server
23
23
  extend ActiveSupport::Concern
24
- attr_accessor :image_id, :templated, :ostemplate_storage, :ostemplate_file, :password, :start_after_create
24
+ attr_accessor :image_id, :templated, :ostemplate_storage, :ostemplate_file, :password, :start_after_create, :compute_resource_id
25
25
 
26
26
  def unique_cluster_identity(compute_resource)
27
27
  compute_resource.id.to_s + '_' + identity.to_s
28
28
  end
29
29
 
30
+ def foreman_uuid
31
+ return identity.to_s if compute_resource_id.nil?
32
+
33
+ compute_resource_id.to_s + '_' + identity.to_s
34
+ end
35
+
30
36
  def start
31
37
  action('start')
32
38
  end
@@ -49,12 +49,11 @@ module HostExt
49
49
 
50
50
  def add_interface_to_compute_attributes(index, interface_attributes, compute_attributes)
51
51
  compute_attributes[index] = {}
52
- compute_attributes[index].store('id', interface_attributes['identifier'])
53
52
  compute_attributes[index].store('_delete', interface_attributes['_destroy'])
54
53
  compute_attributes[index].store('macaddr', interface_attributes['mac'])
55
54
  compute_attributes[index].store('ip', cidr_ip(interface_attributes))
56
55
  compute_attributes[index].store('ip6', cidr_ip(interface_attributes, 6))
57
- compute_attributes[index].merge!(interface_attributes['compute_attributes'].reject { |k, _v| k == 'id' })
56
+ compute_attributes[index].merge(interface_attributes['compute_attributes'])
58
57
  end
59
58
  end
60
59
  end
@@ -45,6 +45,7 @@ module ForemanFogProxmox
45
45
 
46
46
  def provided_attributes
47
47
  super.merge(
48
+ :uuid => :foreman_uuid,
48
49
  :mac => :mac
49
50
  )
50
51
  end
@@ -62,10 +63,11 @@ module ForemanFogProxmox
62
63
  end
63
64
 
64
65
  def associated_host(vm)
65
- associate_by('mac', vm.mac)
66
+ associate_by('mac', vm&.mac)
66
67
  end
67
68
 
68
69
  def associate_by(name, attributes)
70
+ return nil if attributes.blank?
69
71
  Host.authorized(:view_hosts,
70
72
  Host).joins(:primary_interface).where(:nics => { :primary => true }).where("nics.#{name}".downcase => attributes.downcase).readonly(false).first
71
73
  end
@@ -19,8 +19,13 @@
19
19
 
20
20
  module ForemanFogProxmox
21
21
  module ProxmoxComputeAttributes
22
+ FOREMAN_INTERFACE_ATTRIBUTES = [:id, :mac, :ip, :ip6].freeze
23
+ PROXMOX_MAC_ATTRIBUTES = [:macaddr, :hwaddr].freeze
24
+ PROXMOX_INTERFACE_METADATA = [:identifier, :compute_attributes].freeze
25
+
22
26
  def host_compute_attrs(host)
23
- ostype = host.compute_attributes['config_attributes']['ostype']
27
+ config = host.compute_attributes['config_attributes'] || {}
28
+ ostype = config['ostype']
24
29
  type = host.compute_attributes['type']
25
30
  case type
26
31
  when 'lxc'
@@ -41,19 +46,35 @@ module ForemanFogProxmox
41
46
  end
42
47
 
43
48
  def interface_compute_attributes(interface_attributes)
44
- vm_attrs = ForemanFogProxmox::HashCollection.new_hash_reject_keys(interface_attributes, [:identifier, :macaddr, :hwaddr])
45
- vm_attrs[:dhcp] = (interface_attributes[:ip] == 'dhcp') ? '1' : '0'
46
- vm_attrs[:dhcp6] = (interface_attributes[:ip6] == 'dhcp') ? '1' : '0'
49
+ attrs = interface_attributes.with_indifferent_access
50
+ provider_attrs = attrs[:compute_attributes].present? ? attrs[:compute_attributes].with_indifferent_access : ActiveSupport::HashWithIndifferentAccess.new
51
+
52
+ vm_attrs = FOREMAN_INTERFACE_ATTRIBUTES.index_with do |key|
53
+ attrs[key] || provider_attrs.delete(key)
54
+ end.compact
55
+ vm_attrs[:mac] ||= attrs[:macaddr] || attrs[:hwaddr] || provider_attrs.delete(:macaddr) || provider_attrs.delete(:hwaddr)
56
+
57
+ attrs.except(*FOREMAN_INTERFACE_ATTRIBUTES, *PROXMOX_MAC_ATTRIBUTES, *PROXMOX_INTERFACE_METADATA).each do |key, value|
58
+ provider_attrs[key] = value
59
+ end
60
+
61
+ provider_attrs[:dhcp] = (vm_attrs[:ip] == 'dhcp') ? '1' : '0'
62
+ provider_attrs[:dhcp6] = (vm_attrs[:ip6] == 'dhcp') ? '1' : '0'
63
+ vm_attrs[:compute_attributes] = provider_attrs
47
64
  vm_attrs
48
65
  end
49
66
 
67
+ def volume_compute_attributes(volume_attributes)
68
+ volume_attributes.merge(_delete: '0')
69
+ end
70
+
50
71
  def vm_compute_attributes(vm)
51
72
  vm_attrs = {}
52
73
  vm_attrs = vm_attrs.merge(vmid: vm.identity, node_id: vm.node_id, type: vm.type)
53
74
  if vm.respond_to?(:config)
54
75
  if vm.config.respond_to?(:disks)
55
76
  vm_attrs[:volumes_attributes] = Hash[vm.config.disks.each_with_index.map do |disk, idx|
56
- [idx.to_s, disk.attributes]
77
+ [idx.to_s, volume_compute_attributes(disk.attributes)]
57
78
  end ]
58
79
  end
59
80
  if vm.config.respond_to?(:interfaces)
@@ -20,16 +20,20 @@
20
20
  module ForemanFogProxmox
21
21
  module ProxmoxOperatingSystems
22
22
  def compute_os_types(host)
23
- os_linux_types_mapping(host).empty? ? os_windows_types_mapping(host) : os_linux_types_mapping(host)
23
+ [os_linux_types_mapping(host), os_windows_types_mapping(host), os_other_types_mapping(host)].find(&:any?) || []
24
24
  end
25
25
 
26
26
  def available_operating_systems
27
- operating_systems = ['other', 'solaris']
27
+ operating_systems = available_other_operating_systems
28
28
  operating_systems += available_linux_operating_systems
29
29
  operating_systems += available_windows_operating_systems
30
30
  operating_systems
31
31
  end
32
32
 
33
+ def available_other_operating_systems
34
+ ['other', 'solaris']
35
+ end
36
+
33
37
  def available_linux_operating_systems
34
38
  ['l24', 'l26', 'debian', 'ubuntu', 'centos', 'fedora', 'opensuse', 'archlinux', 'gentoo', 'alpine']
35
39
  end
@@ -50,5 +54,12 @@ module ForemanFogProxmox
50
54
  def os_windows_types_mapping(host)
51
55
  ['Windows'].include?(host.operatingsystem.type) ? available_windows_operating_systems : []
52
56
  end
57
+
58
+ def os_other_types_mapping(host)
59
+ {
60
+ 'Freebsd' => ['other'],
61
+ 'Solaris' => ['solaris'],
62
+ }.fetch(host.operatingsystem.type, [])
63
+ end
53
64
  end
54
65
  end
@@ -31,18 +31,18 @@ module ForemanFogProxmox
31
31
  end
32
32
 
33
33
  def cloudinit_defaults
34
- { storage_type: 'cloud_init', id: 'ide0', storage: storages.first.identity.to_s, media: 'cdrom' }
34
+ { storage_type: 'cloud_init', id: 'ide0', storage: storages.first&.identity.to_s, media: 'cdrom' }
35
35
  end
36
36
 
37
37
  def hard_disk_typed_defaults(vm_type)
38
38
  options = {}
39
- volume_attributes_h = { storage: storages.first.identity.to_s, size: '8' }
39
+ volume_attributes_h = { storage: storages.first&.identity.to_s, size: '8' }
40
40
  case vm_type
41
41
  when 'qemu'
42
42
  controller = 'virtio'
43
43
  device = 0
44
44
  id = "#{controller}#{device}"
45
- options = { cache: 'none' }
45
+ options = { cache: 'none', backup: '1' }
46
46
  volume_attributes_h = volume_attributes_h.merge(controller: controller, device: device)
47
47
  when 'lxc'
48
48
  id = 'rootfs'
@@ -69,18 +69,18 @@ module ForemanFogProxmox
69
69
  end
70
70
 
71
71
  def interface_defaults(id = 'net0')
72
- { id: id, compute_attributes: { model: 'virtio', name: 'eth0', bridge: bridges.first.identity.to_s } }
72
+ { id: id, compute_attributes: { model: 'virtio', name: 'eth0', bridge: bridges.first&.identity.to_s } }
73
73
  end
74
74
 
75
75
  def interface_typed_defaults(type)
76
76
  interface_attributes_h = { id: 'net0', compute_attributes: {} }
77
77
  if type == 'qemu'
78
78
  interface_attributes_h[:compute_attributes] =
79
- { model: 'virtio', bridge: bridges.first.identity.to_s }
79
+ { model: 'virtio', bridge: bridges.first&.identity.to_s }
80
80
  end
81
81
  if type == 'lxc'
82
82
  interface_attributes_h[:compute_attributes] =
83
- { name: 'eth0', bridge: bridges.first.identity.to_s, dhcp: 1, dhcp6: 1 }
83
+ { name: 'eth0', bridge: bridges.first&.identity.to_s, dhcp: 1, dhcp6: 1 }
84
84
  end
85
85
  interface_attributes_h
86
86
  end
@@ -32,7 +32,7 @@ module ForemanFogProxmox
32
32
  node ||= default_node
33
33
  storages = node.storages.list_by_content_type type
34
34
  logger.debug("storages(): node_id #{node_id} type #{type}")
35
- storages.sort_by(&:storage)
35
+ storages.reject { |s| s.enabled.to_i.zero? || s.active.to_i.zero? }.sort_by(&:storage)
36
36
  end
37
37
 
38
38
  def bridges(node_id = default_node_id)
@@ -46,9 +46,10 @@ module ForemanFogProxmox
46
46
  def vms(opts = {})
47
47
  vms = []
48
48
  nodes.each { |node| vms += node.servers.all + node.containers.all }
49
+ vms.each { |vm| attach_compute_resource_id(vm) }
49
50
  if opts.key?(:eager_loading) && opts[:eager_loading]
50
51
  vms_eager = []
51
- vms.each { |vm| vms_eager << vm.collection.get(vm.identity) }
52
+ vms.each { |vm| vms_eager << attach_compute_resource_id(vm.collection.get(vm.identity)) }
52
53
  vms = vms_eager
53
54
  end
54
55
  ForemanFogProxmox::Vms.new(vms)
@@ -71,12 +72,21 @@ module ForemanFogProxmox
71
72
  def find_vm_in_servers_by_vmid(servers, vmid)
72
73
  vm = servers.get(vmid) unless ForemanFogProxmox::Value.empty?(vmid)
73
74
  pool_owner(vm) if vm
74
- vm
75
+ attach_compute_resource_id(vm)
75
76
  rescue Fog::Errors::NotFound
76
77
  nil
77
78
  rescue StandardError => e
78
79
  Foreman::Logging.exception(format(_('Failed retrieving proxmox server vm by vmid=%<vmid>s'), vmid: vmid), e)
79
80
  raise(ActiveRecord::RecordNotFound, e)
80
81
  end
82
+
83
+ private
84
+
85
+ def attach_compute_resource_id(virtual_machine)
86
+ return virtual_machine if virtual_machine.nil?
87
+
88
+ virtual_machine.compute_resource_id = id if virtual_machine.respond_to?(:compute_resource_id=)
89
+ virtual_machine
90
+ end
81
91
  end
82
92
  end
@@ -35,8 +35,12 @@ module ForemanFogProxmox
35
35
 
36
36
  def volume_options(vm, id, volume_attributes)
37
37
  options = {}
38
+ backup = volume_attributes['backup'].to_s
39
+ cache = volume_attributes['cache'].to_s
40
+ backup = '1' if backup.empty? && (!vm.container? || id != 'rootfs')
38
41
  options.store(:mp, volume_attributes['mp']) if vm.container? && id != 'rootfs'
39
- options.store(:cache, volume_attributes['cache']) unless vm.container? || volume_attributes['cache'].empty?
42
+ options.store(:cache, cache) unless vm.container? || cache.empty?
43
+ options.store(:backup, backup) unless backup.empty? || (vm.container? && id == 'rootfs')
40
44
  options
41
45
  end
42
46
 
@@ -87,14 +91,12 @@ module ForemanFogProxmox
87
91
  raise ::Foreman::Exception,
88
92
  format(_('Unable to shrink %<id>s size. Proxmox allows only increasing size.'), id: id)
89
93
  end
90
- diff_size = volume_attributes['size'].to_i - disk.size.to_i if volume_attributes['size'] && disk.size
91
- raise ::Foreman::Exception, format(_('Unable to shrink %<id>s size. Proxmox allows only increasing size.'), id: id) unless diff_size >= 0
92
94
 
93
- new_storage = volume_attributes['storage']
95
+ new_storage = volume_attributes['storage'].presence || disk.storage
94
96
 
95
97
  if diff_size > 0
96
98
  extend_volume(vm, id, diff_size)
97
- elsif disk.storage != new_storage
99
+ elsif new_storage.present? && disk.storage != new_storage
98
100
  move_volume(id, vm, new_storage)
99
101
  else
100
102
  update_options(disk, vm, volume_attributes)
@@ -102,6 +104,13 @@ module ForemanFogProxmox
102
104
  end
103
105
  end
104
106
 
107
+ def normalize_existing_volume_attributes(disk, volume_attributes)
108
+ return volume_attributes unless disk.hard_disk?
109
+ return volume_attributes if volume_attributes['storage'].present?
110
+
111
+ volume_attributes.merge('storage' => disk.storage)
112
+ end
113
+
105
114
  def volume_exists?(vm, volume_attributes)
106
115
  disk = vm.config.disks.get(volume_attributes['id'])
107
116
  return false unless disk
@@ -154,7 +163,8 @@ module ForemanFogProxmox
154
163
  delete_volume(vm, id, volume_attributes)
155
164
  else
156
165
  disk = vm.config.disks.get(id)
157
- update_volume(vm, disk, volume_attributes) if update_volume_required?(disk.attributes, volume_attributes)
166
+ normalized_volume_attributes = normalize_existing_volume_attributes(disk, volume_attributes)
167
+ update_volume(vm, disk, normalized_volume_attributes) if update_volume_required?(disk.attributes, normalized_volume_attributes)
158
168
  end
159
169
  else
160
170
  add_volume(vm, id, volume_attributes)
@@ -15,13 +15,12 @@ GNU General Public License for more details.
15
15
  You should have received a copy of the GNU General Public License
16
16
  along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>. %>
17
17
  <% if @host.compute_resource.class == ForemanFogProxmox::Proxmox %>
18
- <% compute_attributes = f.object.compute_attributes %>
19
- <% logger.debug("_add_vm_type_to_nic_provider_specific_form.html compute_attributes=#{compute_attributes}") %>
20
- <% node_id = f.object.respond_to?('node_id') ? f.object.node_id : nil %>
18
+ <% vm_type_and_node_id = proxmox_vm_type_and_node_id(@host, f.object, params) %>
19
+ <% compute_attributes = vm_type_and_node_id[:compute_attributes] %>
20
+ <% vm_type = vm_type_and_node_id[:vm_type] %>
21
+ <% node_id = vm_type_and_node_id[:node_id] %>
21
22
 
22
- <%= f.fields_for 'compute_attributes', OpenStruct.new(f.object.compute_attributes) do |f| %>
23
- <% vm_type = compute_attributes.respond_to?('type') ? compute_attributes.type : 'qemu' %>
24
- <% logger.debug("_add_vm_type_to_nic_provider_specific_form.html vm_type=#{vm_type}") %>
23
+ <%= f.fields_for 'compute_attributes', OpenStruct.new(compute_attributes) do |f| %>
25
24
  <%= render provider_partial(@host.compute_resource, 'network'), :f => f, :vm_type => vm_type, :node_id => node_id, :disabled => f.object.persisted?, :compute_resource => @host.compute_resource, :new_host => new_vm, :new_vm => new_vm %>
26
25
  <% end %>
27
26
  <% else %>