foreman_fog_proxmox 0.12.2 → 0.13.3

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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -5
  3. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_compute_resource.js +36 -2
  4. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_vm.js +134 -60
  5. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_vm_server.js +2 -50
  6. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_volume.js +39 -0
  7. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_volume_cdrom.js +63 -0
  8. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_volume_cloudinit.js +25 -0
  9. data/app/controllers/concerns/foreman_fog_proxmox/controller/parameters/compute_resource.rb +1 -1
  10. data/app/controllers/foreman_fog_proxmox/compute_resources_controller.rb +23 -25
  11. data/app/helpers/proxmox_compute_controllers_helper.rb +39 -0
  12. data/app/helpers/proxmox_compute_resources_helper.rb +49 -0
  13. data/app/helpers/proxmox_compute_selectors_helper.rb +6 -44
  14. data/app/helpers/proxmox_form_helper.rb +12 -4
  15. data/app/{models/concerns/fog_extensions/proxmox/volume.rb → helpers/proxmox_storages_helper.rb} +5 -8
  16. data/app/helpers/proxmox_vm_cdrom_helper.rb +35 -0
  17. data/app/helpers/proxmox_vm_cloudinit_helper.rb +43 -0
  18. data/app/helpers/proxmox_vm_config_helper.rb +160 -0
  19. data/app/helpers/proxmox_vm_helper.rb +24 -62
  20. data/app/helpers/proxmox_vm_interfaces_helper.rb +85 -0
  21. data/app/helpers/proxmox_vm_os_template_helper.rb +47 -0
  22. data/app/{models/foreman_fog_proxmox/proxmox_token_expiration.rb → helpers/proxmox_vm_uuid_helper.rb} +14 -10
  23. data/app/helpers/proxmox_vm_volumes_helper.rb +105 -0
  24. data/app/models/concerns/fog_extensions/proxmox/disk.rb +17 -2
  25. data/app/models/concerns/fog_extensions/proxmox/interface.rb +19 -4
  26. data/app/models/concerns/fog_extensions/proxmox/server.rb +12 -3
  27. data/app/models/concerns/fog_extensions/proxmox/server_config.rb +8 -30
  28. data/app/models/concerns/host_ext/proxmox/interfaces.rb +7 -2
  29. data/app/models/concerns/orchestration/proxmox/compute.rb +49 -0
  30. data/app/models/foreman_fog_proxmox/proxmox.rb +58 -15
  31. data/app/models/foreman_fog_proxmox/proxmox_compute_attributes.rb +14 -18
  32. data/app/models/foreman_fog_proxmox/proxmox_connection.rb +14 -9
  33. data/app/models/foreman_fog_proxmox/proxmox_images.rb +2 -1
  34. data/app/models/foreman_fog_proxmox/proxmox_interfaces.rb +53 -28
  35. data/app/models/foreman_fog_proxmox/proxmox_operating_systems.rb +1 -1
  36. data/app/models/foreman_fog_proxmox/proxmox_version.rb +7 -2
  37. data/app/models/foreman_fog_proxmox/proxmox_vm_commands.rb +19 -31
  38. data/app/models/foreman_fog_proxmox/proxmox_vm_new.rb +108 -94
  39. data/app/models/foreman_fog_proxmox/proxmox_vm_queries.rb +9 -6
  40. data/app/models/foreman_fog_proxmox/proxmox_volumes.rb +79 -22
  41. data/app/services/foreman_fog_proxmox/node_dashboard/data.rb +6 -2
  42. data/app/views/api/v2/compute_resources/proxmox.json.rabl +1 -1
  43. data/app/views/compute_resources/form/_proxmox.html.erb +23 -10
  44. data/app/views/compute_resources/show/_proxmox.html.erb +6 -6
  45. data/app/views/compute_resources_vms/form/proxmox/_add_vm_type_to_nic_provider_specific_form.html.erb +3 -1
  46. data/app/views/compute_resources_vms/form/proxmox/_add_vm_type_to_volumes_edit.html.erb +46 -29
  47. data/app/views/compute_resources_vms/form/proxmox/_base.html.erb +3 -3
  48. data/app/views/compute_resources_vms/form/proxmox/_removable_layout.html.erb +2 -1
  49. data/app/views/compute_resources_vms/form/proxmox/container/_network.html.erb +8 -7
  50. data/app/views/compute_resources_vms/form/proxmox/server/_advanced.html.erb +0 -2
  51. data/app/views/compute_resources_vms/form/proxmox/server/_config.html.erb +15 -14
  52. data/app/views/compute_resources_vms/form/proxmox/server/_network.html.erb +2 -2
  53. data/app/views/compute_resources_vms/form/proxmox/server/_volume_cdrom.html.erb +34 -0
  54. data/app/views/compute_resources_vms/form/proxmox/server/_volume_cloud_init.html.erb +29 -0
  55. data/app/views/compute_resources_vms/form/proxmox/server/{_volume.html.erb → _volume_hard_disk.html.erb} +7 -3
  56. data/app/views/compute_resources_vms/show/_proxmox.html.erb +2 -0
  57. data/config/routes.rb +7 -7
  58. data/db/migrate/20210312105013_update_proxmox_uuid_host.rb +29 -0
  59. data/lib/foreman_fog_proxmox/engine.rb +15 -10
  60. data/lib/foreman_fog_proxmox/hash_collection.rb +69 -0
  61. data/lib/foreman_fog_proxmox/version.rb +1 -1
  62. data/lib/tasks/foreman_fog_proxmox_tasks.rake +0 -3
  63. data/test/factories/foreman_fog_proxmox/proxmox_container_mock_factory.rb +20 -8
  64. data/test/factories/foreman_fog_proxmox/proxmox_node_mock_factory.rb +5 -5
  65. data/test/factories/foreman_fog_proxmox/proxmox_server_mock_factory.rb +17 -7
  66. data/test/factories/proxmox_factory.rb +4 -4
  67. data/test/functional/compute_resources_controller_test.rb +4 -4
  68. data/test/unit/foreman_fog_proxmox/helpers/proxmox_container_helper_test.rb +53 -32
  69. data/test/unit/foreman_fog_proxmox/helpers/proxmox_server_helper_test.rb +56 -31
  70. data/test/unit/foreman_fog_proxmox/helpers/proxmox_vm_helper_test.rb +22 -20
  71. data/test/unit/foreman_fog_proxmox/helpers/proxmox_vm_uuid_helper_test.rb +38 -0
  72. data/test/unit/foreman_fog_proxmox/helpers/proxmox_vm_volumes_helper_test.rb +50 -0
  73. data/test/unit/foreman_fog_proxmox/proxmox_compute_attributes_test.rb +10 -11
  74. data/test/unit/foreman_fog_proxmox/proxmox_interfaces_test.rb +38 -10
  75. data/test/unit/foreman_fog_proxmox/proxmox_version_test.rb +10 -10
  76. data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_container_test.rb +34 -24
  77. data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_server_create_test.rb +8 -8
  78. data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_server_update_cdrom_test.rb +181 -0
  79. data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_server_update_cloudinit_test.rb +131 -0
  80. data/test/unit/foreman_fog_proxmox/{proxmox_vm_commands_server_update_volumes_test.rb → proxmox_vm_commands_server_update_hard_disk_test.rb} +45 -19
  81. data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_server_update_test.rb +21 -21
  82. data/test/unit/foreman_fog_proxmox/proxmox_vm_new_test.rb +3 -3
  83. data/test/unit/foreman_fog_proxmox/proxmox_vm_queries_test.rb +3 -3
  84. metadata +44 -23
  85. data/app/helpers/proxmox_container_helper.rb +0 -163
  86. data/app/helpers/proxmox_server_helper.rb +0 -155
@@ -20,18 +20,18 @@
20
20
  module ForemanFogProxmox
21
21
  module ProxmoxComputeAttributes
22
22
  def host_compute_attrs(host)
23
- super.tap do |_attrs|
24
- ostype = host.compute_attributes['config_attributes']['ostype']
25
- type = host.compute_attributes['type']
26
- case type
27
- when 'lxc'
28
- host.compute_attributes['config_attributes'].store('hostname', host.name)
29
- when 'qemu'
30
- unless compute_os_types(host).include?(ostype)
31
- raise ::Foreman::Exception, format(_('Operating system family %<type>s is not consistent with %<ostype>s'), type: host.operatingsystem.type, ostype: ostype)
32
- end
23
+ ostype = host.compute_attributes['config_attributes']['ostype']
24
+ type = host.compute_attributes['type']
25
+ case type
26
+ when 'lxc'
27
+ host.compute_attributes['config_attributes'].store('hostname', host.name)
28
+ when 'qemu'
29
+ host.compute_attributes['config_attributes'].store('name', host.name)
30
+ unless compute_os_types(host).include?(ostype)
31
+ raise ::Foreman::Exception, format(_('Operating system family %<type>s is not consistent with %<ostype>s'), type: host.operatingsystem.type, ostype: ostype)
33
32
  end
34
33
  end
34
+ super
35
35
  end
36
36
 
37
37
  def not_config_key?(vm, key)
@@ -39,20 +39,16 @@ module ForemanFogProxmox
39
39
  end
40
40
 
41
41
  def interface_compute_attributes(interface_attributes)
42
- vm_attrs = {}
43
- vm_attrs.store(:mac, interface_attributes[:macaddr])
44
- vm_attrs.store(:id, interface_attributes[:id])
45
- vm_attrs.store(:identifier, interface_attributes[:id])
46
- vm_attrs.store(:ip, interface_attributes[:ip])
47
- vm_attrs.store(:ip6, interface_attributes[:ip6])
48
- vm_attrs[:compute_attributes] = interface_attributes.reject { |k, _v| [:macaddr, :id].include?(k) }
42
+ vm_attrs = ForemanFogProxmox::HashCollection.new_hash_reject_keys(interface_attributes, [:identifier, :mac])
43
+ vm_attrs[:dhcp] = interface_attributes[:ip] == 'dhcp' ? '1' : '0'
44
+ vm_attrs[:dhcp6] = interface_attributes[:ip6] == 'dhcp' ? '1' : '0'
49
45
  vm_attrs
50
46
  end
51
47
 
52
48
  def vm_compute_attributes(vm)
53
49
  vm_attrs = {}
50
+ vm_attrs = vm_attrs.merge(vmid: vm.identity, node_id: vm.node_id, type: vm.type)
54
51
  if vm.respond_to?(:config)
55
- vm_attrs = vm_attrs.merge(vmid: vm.identity, node_id: vm.node_id, type: vm.type)
56
52
  vm_attrs[:volumes_attributes] = Hash[vm.config.disks.each_with_index.map { |disk, idx| [idx.to_s, disk.attributes] }] if vm.config.respond_to?(:disks)
57
53
  if vm.config.respond_to?(:interfaces)
58
54
  vm_attrs[:interfaces_attributes] = Hash[vm.config.interfaces.each_with_index.map { |interface, idx| [idx.to_s, interface_compute_attributes(interface.attributes)] }]
@@ -28,23 +28,28 @@ module ForemanFogProxmox
28
28
  opts
29
29
  end
30
30
 
31
- def fog_credentials
32
- credentials = { pve_url: url,
33
- pve_username: user,
34
- pve_password: password,
35
- connection_options: connection_options }
36
- ticket = Fog::Proxmox.credentials[:ticket]
37
- credentials.store(:pve_ticket, ticket) if renew
38
- credentials
31
+ def access_ticket?
32
+ auth_method == 'access_ticket'
33
+ end
34
+
35
+ def user_token?
36
+ auth_method == 'user_token'
39
37
  end
40
38
 
41
39
  def credentials_valid?
42
- errors[:url].empty? && errors[:user].empty? && errors[:user].include?('@') && errors[:password].empty? && errors[:node_id].empty?
40
+ errors[:url].empty? && errors[:auth_method].empty?
41
+ errors[:user].empty? && errors[:user].include?('@') && errors[:password].empty? && errors[:node_id].empty? if access_ticket?
42
+ errors[:user].empty? && errors[:user].include?('@') && errors[:token_id].empty? && errors[:token].empty? && errors[:node_id].empty? if user_token?
43
+ end
44
+
45
+ def current_user_token_expire
46
+ identity_client ? identity_client.expires : 0
43
47
  end
44
48
 
45
49
  def test_connection(options = {})
46
50
  super
47
51
  credentials_valid?
52
+ identity_client
48
53
  version_suitable?
49
54
  rescue StandardError => e
50
55
  errors[:base] << e.message
@@ -27,6 +27,7 @@ module ForemanFogProxmox
27
27
  node = client.nodes.get node_id
28
28
  node ||= default_node
29
29
  storage = node.storages.get storage_id if storage_id
30
+ logger.debug(format(_('images_by_storage(): node_id %<node_id>s storage_id %<storage_id>s type %<type>s'), node_id: node_id, storage_id: storage_id, type: type))
30
31
  storage.volumes.list_by_content_type(type).sort_by(&:volid) if storage
31
32
  end
32
33
 
@@ -40,7 +41,7 @@ module ForemanFogProxmox
40
41
  storage = storages(node.node).first
41
42
  volumes += storage.volumes.list_by_content_type('images')
42
43
  end
43
- volumes.select(&:templated?)
44
+ volumes.select(&:template?)
44
45
  end
45
46
 
46
47
  def template(vmid)
@@ -30,8 +30,14 @@ module ForemanFogProxmox
30
30
  raise ::Foreman::Exception, _(format('Invalid identifier interface[%<index>s]. Must be net[n] with n integer >= 0', index: index)) unless Fog::Proxmox::NicHelper.nic?(nic.identifier)
31
31
  end
32
32
 
33
+ def vm_type(host)
34
+ type = host.compute_attributes['type']
35
+ type ||= host.compute_attributes[:config_attributes].key?(:arch) ? 'lxc' : 'qemu'
36
+ type
37
+ end
38
+
33
39
  def container?(host)
34
- host.compute_attributes['type'] == 'lxc'
40
+ vm_type(host) == 'lxc'
35
41
  end
36
42
 
37
43
  def container_nic_name_valid?(nic)
@@ -44,32 +50,46 @@ module ForemanFogProxmox
44
50
  end
45
51
 
46
52
  def cidr_prefix(nic_compute_attributes, v6 = false)
47
- attr_name = 'cidrv'
48
- attr_name += v6 ? '6' : '4'
49
- attr_name += '_prefix'
50
- nic_compute_attributes[attr_name] if nic_compute_attributes[attr_name].presence
53
+ attr_name = "cidr#{v6_s(v6)}"
54
+ nic_compute_attributes[attr_name] if nic_compute_attributes.key?(attr_name)
51
55
  end
52
56
 
53
- def set_ip(host, nic, nic_compute_attributes, v6 = false)
54
- return 'dhcp' if dhcp?(nic_compute_attributes, v6)
55
-
56
- ip = v6 ? nic.ip6 : nic.ip
57
- return ip unless container?(host) || cidr_prefix(nic_compute_attributes, v6)
57
+ def cidr_prefix_method(v6)
58
+ "cidr#{v6_s(v6)}_prefix?".to_sym
59
+ end
58
60
 
59
- valid = v6 ? Fog::Proxmox::IpHelper.cidr6_prefix?(cidr_prefix(nic_compute_attributes, v6)) : Fog::Proxmox::IpHelper.cidr_prefix?(cidr_prefix(nic_compute_attributes, v6))
60
- ipv = v6 ? 'IPv6' : 'IPv4'
61
+ def check_cidr(nic_compute_attributes, v6, ip)
62
+ valid = Fog::Proxmox::IpHelper.send(cidr_prefix_method(v6), cidr_prefix(nic_compute_attributes, v6))
63
+ ipv = "IPv#{v6 ? '6' : '4'}"
61
64
  max = v6 ? 128 : 32
62
- unless valid
63
- raise ::Foreman::Exception, _(format('Invalid Interface Proxmox CIDR %<ip>s. If %<ip>s is not empty, Proxmox CIDR prefix must be an integer between 0 and %<max>i.', ip: ipv, max: max))
64
- end
65
+ checked = valid || ForemanFogProxmox::Value.empty?(ip)
66
+ message = format('Invalid Interface Proxmox CIDR %<ip>s. If %<ip>s is not empty, Proxmox CIDR prefix must be an integer between 0 and %<max>i.', ip: ipv, max: max)
67
+ raise ::Foreman::Exception, _(message) unless checked
68
+ end
65
69
 
66
- v6 ? Fog::Proxmox::IpHelper.to_cidr6(nic.ip6, cidr_prefix(nic_compute_attributes, v6)) : Fog::Proxmox::IpHelper.to_cidr(nic.ip, cidr_prefix(nic_compute_attributes, v6))
70
+ def v6_s(v6)
71
+ v6 ? '6' : ''
67
72
  end
68
73
 
69
- def set_gw(nic_compute_attributes, v6 = false)
70
- attr_name = 'gwv'
71
- attr_name += v6 ? '6' : '4'
72
- nic_compute_attributes[attr_name] if nic_compute_attributes[attr_name].presence
74
+ def ip_s(v6)
75
+ "ip#{v6_s(v6)}"
76
+ end
77
+
78
+ def to_cidr_method(v6)
79
+ "to_cidr#{v6_s(v6)}".to_sym
80
+ end
81
+
82
+ def set_ip(host, nic, nic_compute_attributes, v6 = false)
83
+ ip = nic.send(ip_s(v6).to_sym)
84
+ if container?(host)
85
+ if dhcp?(nic_compute_attributes, v6)
86
+ ip = 'dhcp'
87
+ elsif !ForemanFogProxmox::Value.empty?(cidr_prefix(nic_compute_attributes, v6))
88
+ check_cidr(nic_compute_attributes, v6, ip)
89
+ ip = Fog::Proxmox::IpHelper.send(to_cidr_method(v6), nic.send(ip_s(v6).to_sym), cidr_prefix(nic_compute_attributes, v6)) if ip
90
+ end
91
+ end
92
+ nic_compute_attributes[ip_s(v6).to_sym] = ip
73
93
  end
74
94
 
75
95
  def to_boolean(value)
@@ -77,9 +97,14 @@ module ForemanFogProxmox
77
97
  end
78
98
 
79
99
  def dhcp?(nic_compute_attributes, v6 = false)
80
- attr_name = 'dhcpv'
81
- attr_name += v6 ? '6' : '4'
82
- to_boolean(nic_compute_attributes[attr_name]) if nic_compute_attributes[attr_name].present?
100
+ attr_name = "dhcp#{v6_s(v6)}"
101
+ nic_compute_attributes.key?(attr_name) ? to_boolean(nic_compute_attributes[attr_name]) : false
102
+ end
103
+
104
+ def set_mac(nic_compute_attributes, mac, type)
105
+ mac_attr_name = { 'qemu' => :macaddr, 'lxc' => :hwaddr }
106
+ mac_key = mac_attr_name[type] || 'mac'
107
+ nic_compute_attributes[mac_key] = mac
83
108
  end
84
109
 
85
110
  def host_interfaces_attrs(host)
@@ -87,15 +112,15 @@ module ForemanFogProxmox
87
112
  set_nic_identifier(nic, index)
88
113
  set_container_interface_name(host, nic, index) if container?(host)
89
114
  nic_compute_attributes = nic.compute_attributes.merge(id: nic.identifier)
115
+ ForemanFogProxmox::HashCollection.remove_empty_values(nic_compute_attributes)
90
116
  mac = nic.mac
91
117
  mac ||= nic.attributes['mac']
92
- nic_compute_attributes.store(:macaddr, mac) if mac.present?
118
+ set_mac(nic_compute_attributes, mac, vm_type(host)) if mac.present?
93
119
  interface_compute_attributes = host.compute_attributes['interfaces_attributes'] ? host.compute_attributes['interfaces_attributes'].select { |_k, v| v['id'] == nic.identifier } : {}
94
120
  nic_compute_attributes.store(:_delete, interface_compute_attributes[interface_compute_attributes.keys[0]]['_delete']) unless interface_compute_attributes.empty?
95
- nic_compute_attributes.store(:ip, set_ip(host, nic, nic_compute_attributes))
96
- nic_compute_attributes.store(:gw, set_gw(nic_compute_attributes))
97
- nic_compute_attributes.store(:ip6, set_ip(host, nic, nic_compute_attributes, true))
98
- nic_compute_attributes.store(:gw6, set_gw(nic_compute_attributes, true))
121
+ set_ip(host, nic, nic_compute_attributes)
122
+ set_ip(host, nic, nic_compute_attributes, true)
123
+ ForemanFogProxmox::HashCollection.remove_keys(nic_compute_attributes, ['dhcp', 'dhcp6', 'cidr', 'cidr6'])
99
124
  hash.merge(index.to_s => nic_compute_attributes)
100
125
  end
101
126
  end
@@ -39,7 +39,7 @@ module ForemanFogProxmox
39
39
  end
40
40
 
41
41
  def os_linux_types_mapping(host)
42
- ['Debian', 'Redhat', 'Suse', 'Altlinux', 'Archlinux', 'Coreos', 'Gentoo'].include?(host.operatingsystem.type) ? available_linux_operating_systems : []
42
+ ['Debian', 'Redhat', 'Suse', 'Altlinux', 'Archlinux', 'Coreos', 'Rancheros', 'Gentoo'].include?(host.operatingsystem.type) ? available_linux_operating_systems : []
43
43
  end
44
44
 
45
45
  def os_windows_types_mapping(host)
@@ -29,8 +29,13 @@ module ForemanFogProxmox
29
29
  end
30
30
 
31
31
  def version
32
- v = identity_client.read_version
33
- "#{v['version']}.#{v['release']}"
32
+ v = identity_client.read_version if identity_client
33
+ v ? v['version'] : 'Unknown'
34
+ rescue ::Foreman::Exception => e
35
+ return 'Unkown' if e.message == 'User token expired'
36
+ rescue StandardError => e
37
+ logger.warn(format(_('failed to get identity client version: %<e>s'), e: e))
38
+ raise e
34
39
  end
35
40
  end
36
41
  end
@@ -17,10 +17,13 @@
17
17
  # You should have received a copy of the GNU General Public License
18
18
  # along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
19
19
 
20
+ require 'foreman_fog_proxmox/hash_collection'
21
+
20
22
  module ForemanFogProxmox
21
23
  module ProxmoxVmCommands
22
24
  include ProxmoxVolumes
23
25
  include ProxmoxPools
26
+ include ProxmoxVmHelper
24
27
 
25
28
  def start_on_boot(vm, args)
26
29
  startonboot = args[:start_after_create].blank? ? false : Foreman::Cast.to_bool(args[:start_after_create])
@@ -40,20 +43,14 @@ module ForemanFogProxmox
40
43
  clone_from_image(image_id, args, vmid)
41
44
  else
42
45
  convert_sizes(args)
43
- remove_deletes(args)
44
- case type
45
- when 'qemu'
46
- vm = node.servers.create(parse_server_vm(args))
47
- when 'lxc'
48
- hash = parse_container_vm(args)
49
- hash = hash.merge(vmid: vmid)
50
- vm = node.containers.create(hash.reject { |key, _value| ['ostemplate_storage', 'ostemplate_file'].include? key })
51
- end
46
+ remove_volume_keys(args)
47
+ logger.warn(format(_('create vm: args=%<args>s'), args: args))
48
+ vm = node.send(vm_collection(type)).create(parse_typed_vm(args, type))
52
49
  start_on_boot(vm, args)
53
50
  end
54
51
  rescue StandardError => e
55
52
  logger.warn(format(_('failed to create vm: %<e>s'), e: e))
56
- destroy_vm vm.id if vm
53
+ destroy_vm client.identity + '_' + vm.id if vm
57
54
  raise e
58
55
  end
59
56
 
@@ -70,24 +67,18 @@ module ForemanFogProxmox
70
67
  true
71
68
  end
72
69
 
73
- def update_required?(old_attrs, new_attrs)
74
- return true if super(old_attrs, new_attrs)
75
-
76
- new_attrs[:interfaces_attributes]&.each do |key, interface|
77
- return true if (interface[:id].blank? || interface[:_delete] == '1') && key != 'new_interfaces' # ignore the template
78
- end
79
-
80
- new_attrs[:volumes_attributes]&.each do |key, volume|
81
- return true if (volume[:id].blank? || volume[:_delete] == '1') && key != 'new_volumes' # ignore the template
82
- end
83
-
84
- false
85
- end
86
-
87
70
  def user_data_supported?
88
71
  true
89
72
  end
90
73
 
74
+ def compute_config_attributes(parsed_attr)
75
+ excluded_keys = [:vmid, :templated, :ostemplate, :ostemplate_file, :ostemplate_storage, :volumes_attributes, :pool]
76
+ config_attributes = parsed_attr.reject { |key, _value| excluded_keys.include? key.to_sym }
77
+ ForemanFogProxmox::HashCollection.remove_empty_values(config_attributes)
78
+ config_attributes = config_attributes.reject { |key, _value| Fog::Proxmox::DiskHelper.disk?(key) }
79
+ { config_attributes: config_attributes }
80
+ end
81
+
91
82
  def save_vm(uuid, new_attributes)
92
83
  vm = find_vm_by_uuid(uuid)
93
84
  templated = new_attributes['templated']
@@ -98,15 +89,12 @@ module ForemanFogProxmox
98
89
  vm.migrate(node_id)
99
90
  else
100
91
  convert_memory_sizes(new_attributes)
92
+ parsed_attr = parse_typed_vm(ForemanFogProxmox::HashCollection.new_hash_reject_keys(new_attributes, ['volumes_attributes']).merge(type: vm.type), vm.type)
93
+ config_attributes = compute_config_attributes(parsed_attr)
101
94
  volumes_attributes = new_attributes['volumes_attributes']
95
+ logger.debug(format(_('save_vm(%<vmid>s) volumes_attributes=%<volumes_attributes>s'), vmid: uuid, volumes_attributes: volumes_attributes))
102
96
  volumes_attributes&.each_value { |volume_attributes| save_volume(vm, volume_attributes) }
103
- parsed_attr = vm.container? ? parse_container_vm(new_attributes.merge(type: vm.type)) : parse_server_vm(new_attributes.merge(type: vm.type))
104
- logger.debug("parsed_attr=#{parsed_attr}")
105
- config_attributes = parsed_attr.reject { |key, _value| [:vmid, :templated, :ostemplate, :ostemplate_file, :ostemplate_storage, :volumes_attributes, :pool].include? key.to_sym }
106
- config_attributes = config_attributes.reject { |_key, value| ForemanFogProxmox::Value.empty?(value) }
107
- cdrom_attributes = parsed_attr.select { |_key, value| Fog::Proxmox::DiskHelper.cdrom?(value.to_s) }
108
- config_attributes = config_attributes.reject { |key, _value| Fog::Proxmox::DiskHelper.disk?(key) }
109
- vm.update(config_attributes.merge(cdrom_attributes))
97
+ vm.update(config_attributes[:config_attributes])
110
98
  poolid = new_attributes['pool'] if new_attributes.key?('pool')
111
99
  update_pool(vm, poolid) if poolid
112
100
  end
@@ -20,76 +20,74 @@
20
20
  require 'fog/proxmox'
21
21
  require 'fog/proxmox/helpers/nic_helper'
22
22
  require 'fog/proxmox/helpers/disk_helper'
23
+ require 'foreman_fog_proxmox/hash_collection'
23
24
 
24
25
  module ForemanFogProxmox
25
26
  module ProxmoxVmNew
26
27
  include ProxmoxVmHelper
27
28
 
28
- def volume_server_defaults(controller = 'scsi', device = 0)
29
- id = "#{controller}#{device}"
30
- { id: id, storage: storages.first.identity.to_s, size: (8 * GIGA), options: { cache: 'none' } }
29
+ def cdrom_defaults
30
+ { storage_type: 'cdrom', id: 'ide2', volid: 'none', media: 'cdrom' }
31
31
  end
32
32
 
33
- def volume_container_defaults(id = 'rootfs')
34
- { id: id, storage: storages.first.identity.to_s, size: (8 * GIGA), options: {} }
33
+ def cloudinit_defaults
34
+ { storage_type: 'cloud_init', id: 'ide0', storage: storages.first.identity.to_s, media: 'cdrom' }
35
35
  end
36
36
 
37
- def new_volume(attr = {})
38
- type = attr['type']
39
- type ||= 'qemu'
40
- case type
41
- when 'lxc'
42
- new_volume_server(attr)
37
+ def hard_disk_typed_defaults(vm_type)
38
+ options = {}
39
+ volume_attributes_h = { storage: storages.first.identity.to_s, size: (8 * GIGA) }
40
+ case vm_type
43
41
  when 'qemu'
44
- new_volume_container(attr)
42
+ controller = 'virtio'
43
+ device = 0
44
+ id = "#{controller}#{device}"
45
+ options = { cache: 'none' }
46
+ volume_attributes_h = volume_attributes_h.merge(controller: controller, device: device)
47
+ when 'lxc'
48
+ id = 'rootfs'
49
+ volume_attributes_h = volume_attributes_h.merge(storage_type: 'rootfs')
45
50
  end
51
+ volume_attributes_h[:id] = id
52
+ volume_attributes_h[:options] = options
53
+ volume_attributes_h
46
54
  end
47
55
 
48
- def new_volume_server(attr = {})
49
- opts = volume_server_defaults.merge(attr.to_h).deep_symbolize_keys
50
- opts[:size] = opts[:size].to_s
56
+ def new_typed_volume(attr, vm_type, volume_type)
57
+ volume_defaults = hard_disk_typed_defaults(vm_type) if ['hard_disk', 'rootfs', 'mp'].include?(volume_type)
58
+ volume_defaults = cdrom_defaults if volume_type == 'cdrom'
59
+ volume_defaults = cloudinit_defaults if volume_type == 'cloud_init'
60
+ opts = volume_defaults.merge(attr.to_h).deep_symbolize_keys
61
+ opts = ForemanFogProxmox::HashCollection.new_hash_transform_values(opts, :to_s)
51
62
  Fog::Proxmox::Compute::Disk.new(opts)
52
63
  end
53
64
 
54
- def new_volume_container(attr = {})
55
- opts = volume_container_defaults.merge(attr.to_h).deep_symbolize_keys
56
- opts[:size] = opts[:size].to_s
57
- Fog::Proxmox::Compute::Disk.new(opts)
65
+ def new_volume(attr = {})
66
+ type = attr['type']
67
+ type ||= 'qemu'
68
+ new_typed_volume(attr, type, 'hard_disk')
58
69
  end
59
70
 
60
71
  def interface_defaults(id = 'net0')
61
- { id: id, 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 } }
62
73
  end
63
74
 
64
- def interface_server_defaults(id = 'net0')
65
- { id: id, model: 'virtio', bridge: bridges.first.identity.to_s }
75
+ def interface_typed_defaults(type)
76
+ interface_attributes_h = { id: 'net0', compute_attributes: {} }
77
+ interface_attributes_h[:compute_attributes] = { model: 'virtio', bridge: bridges.first.identity.to_s } if type == 'qemu'
78
+ interface_attributes_h[:compute_attributes] = { name: 'eth0', bridge: bridges.first.identity.to_s, dhcp: 1, dhcp6: 1 } if type == 'lxc'
79
+ interface_attributes_h
66
80
  end
67
81
 
68
- def interface_container_defaults(id = 'net0')
69
- { id: id, name: 'eth0', bridge: bridges.first.identity.to_s, dhcpv4: 1, dhcpv6: 1 }
82
+ def new_typed_interface(attr, type)
83
+ opts = interface_typed_defaults(type).merge(attr.to_h).deep_symbolize_keys
84
+ Fog::Proxmox::Compute::Interface.new(opts)
70
85
  end
71
86
 
72
87
  def new_interface(attr = {})
73
88
  type = attr['type']
74
89
  type ||= 'qemu'
75
- case type
76
- when 'lxc'
77
- new_container_interface(attr)
78
- when 'qemu'
79
- new_server_interface(attr)
80
- end
81
- end
82
-
83
- def new_server_interface(attr = {})
84
- logger.debug('new_server_interface')
85
- opts = interface_server_defaults.merge(attr.to_h).deep_symbolize_keys
86
- Fog::Proxmox::Compute::Interface.new(opts)
87
- end
88
-
89
- def new_container_interface(attr = {})
90
- logger.debug('new_container_interface')
91
- opts = interface_container_defaults.merge(attr.to_h).deep_symbolize_keys
92
- Fog::Proxmox::Compute::Interface.new(opts)
90
+ new_typed_interface(attr, type)
93
91
  end
94
92
 
95
93
  def default_node
@@ -104,74 +102,90 @@ module ForemanFogProxmox
104
102
  default_node.servers.next_id
105
103
  end
106
104
 
107
- def vm_server_instance_defaults
108
- ActiveSupport::HashWithIndifferentAccess.new(
109
- name: "foreman_#{Time.now.to_i}",
110
- vmid: next_vmid,
111
- type: 'qemu',
112
- node_id: default_node_id,
113
- cores: 1,
114
- sockets: 1,
115
- kvm: 1,
116
- vga: 'std',
117
- memory: 512 * MEGA,
118
- ostype: 'l26',
119
- keyboard: 'en-us',
120
- cpu: 'kvm64',
121
- scsihw: 'virtio-scsi-pci',
122
- ide2: 'none,media=cdrom',
123
- templated: 0
124
- ).merge(Fog::Proxmox::DiskHelper.flatten(volume_server_defaults)).merge(Fog::Proxmox::DiskHelper.flatten(volume_container_defaults)).merge(Fog::Proxmox::NicHelper.flatten(interface_defaults))
125
- end
126
-
127
- def vm_container_instance_defaults
128
- ActiveSupport::HashWithIndifferentAccess.new(
129
- name: "foreman_#{Time.now.to_i}",
130
- vmid: next_vmid,
131
- type: 'lxc',
132
- node_id: default_node_id,
133
- memory: 512 * MEGA,
134
- templated: 0
135
- ).merge(Fog::Proxmox::DiskHelper.flatten(volume_container_defaults)).merge(Fog::Proxmox::DiskHelper.flatten(volume_server_defaults)).merge(Fog::Proxmox::NicHelper.flatten(interface_defaults))
105
+ def add_default_typed_interface(type, new_attr)
106
+ interfaces_attributes = []
107
+ interfaces_attributes.push(interface_typed_defaults(type))
108
+ new_attr = new_attr.merge(interfaces_attributes: interfaces_attributes.map.with_index.to_h.invert)
109
+ logger.debug(format(_('add_default_typed_interface(%<type>s) to new_attr=%<new_attr>s'), type: type, new_attr: new_attr))
110
+ new_attr
111
+ end
112
+
113
+ def add_default_typed_volume(new_attr)
114
+ volumes_attributes = []
115
+ volumes_attributes.push(hard_disk_typed_defaults('qemu'))
116
+ volumes_attributes.push(hard_disk_typed_defaults('lxc'))
117
+ new_attr = new_attr.merge(volumes_attributes: volumes_attributes.map.with_index.to_h.invert)
118
+ logger.debug(format(_('add_default_typed_volume(%<type>s) to new_attr=%<new_attr>s'), type: type, new_attr: new_attr))
119
+ new_attr
136
120
  end
137
121
 
138
122
  def vm_instance_defaults
139
- super.merge(vmid: next_vmid, node_id: default_node_id)
123
+ super.merge(vmid: next_vmid, node_id: default_node_id, type: 'qemu')
124
+ end
125
+
126
+ def vm_typed_instance_defaults(type)
127
+ defaults = vm_instance_defaults
128
+ defaults = defaults.merge(config_attributes: config_attributes(type))
129
+ defaults = add_default_typed_volume(defaults)
130
+ defaults = add_default_typed_interface(type, defaults)
131
+ defaults
132
+ end
133
+
134
+ def config_attributes(type = 'qemu')
135
+ case type
136
+ when 'qemu'
137
+ config_attributes = {
138
+ cores: 1,
139
+ sockets: 1,
140
+ kvm: 0,
141
+ vga: 'std',
142
+ memory: 512 * MEGA,
143
+ ostype: 'l26',
144
+ cpu: 'cputype=kvm64',
145
+ scsihw: 'virtio-scsi-pci',
146
+ templated: 0
147
+ }
148
+ config_attributes = config_attributes
149
+ when 'lxc'
150
+ config_attributes = {
151
+ memory: 512 * MEGA,
152
+ templated: 0
153
+ }
154
+ end
155
+ config_attributes
140
156
  end
141
157
 
142
158
  def new_vm(new_attr = {})
143
159
  new_attr = ActiveSupport::HashWithIndifferentAccess.new(new_attr)
144
160
  type = new_attr['type']
145
161
  type ||= 'qemu'
146
- case type
147
- when 'lxc'
148
- vm = new_container_vm(new_attr)
149
- when 'qemu'
150
- vm = new_server_vm(new_attr)
151
- end
152
- logger.debug(format(_('new_vm() vm.config=%<config>s'), config: vm.config.inspect))
162
+ vm = new_typed_vm(new_attr, type)
153
163
  vm
154
164
  end
155
165
 
156
- def new_container_vm(new_attr = {})
157
- options = new_attr
158
- node_id = new_attr['node_id']
159
- node = node_id ? client.nodes.get(node_id) : default_node
160
- options = options.merge(type: 'lxc').merge(vmid: next_vmid)
161
- options = vm_container_instance_defaults.merge(options) if new_attr.empty?
162
- vm = node.containers.new(parse_container_vm(options).deep_symbolize_keys)
163
- logger.debug(format(_('new_container_vm() vm.config=%<config>s'), config: vm.config.inspect))
164
- vm
166
+ def convert_config_attributes(new_attr)
167
+ config_attributes = new_attr[:config_attributes]
168
+ config_attributes[:volumes_attributes] = Hash[config_attributes[:disks].each_with_index.map { |disk, idx| [idx.to_s, disk.attributes] }] if config_attributes.key?(:disks)
169
+ if config_attributes.key?(:interfaces)
170
+ config_attributes[:interfaces_attributes] = Hash[config_attributes[:interfaces].each_with_index.map { |interface, idx| [idx.to_s, interface_compute_attributes(interface.attributes)] }]
171
+ end
172
+ config_attributes.delete_if { |key, _value| ['disks', 'interfaces'].include?(key) }
165
173
  end
166
174
 
167
- def new_server_vm(new_attr = {})
168
- options = new_attr
175
+ def new_typed_vm(new_attr, type)
176
+ convert_config_attributes(new_attr) if new_attr.key?(:config_attributes)
169
177
  node_id = new_attr['node_id']
170
178
  node = node_id ? client.nodes.get(node_id) : default_node
171
- options = options.merge(type: 'qemu').merge(vmid: next_vmid)
172
- options = vm_server_instance_defaults.merge(options) if new_attr.empty?
173
- vm = node.servers.new(parse_server_vm(options).deep_symbolize_keys)
174
- logger.debug(format(_('new_server_vm() vm.config=%<config>s'), config: vm.config.inspect))
179
+ new_attr_type = new_attr['type']
180
+ new_attr_type ||= new_attr['config_attributes']['type'] if new_attr.key?('config_attributes')
181
+ new_attr_type ||= type
182
+ logger.debug(format(_('new_typed_vm(%<type>s): new_attr_type=%<new_attr_type>s'), type: type, new_attr_type: new_attr_type))
183
+ logger.debug(format(_('new_typed_vm(%<type>s): new_attr=%<new_attr>s'), type: type, new_attr: new_attr))
184
+ options = !new_attr.key?('vmid') || ForemanFogProxmox::Value.empty?(new_attr['vmid']) ? new_attr.merge(vm_typed_instance_defaults(type)).merge(type: type) : new_attr
185
+ logger.debug(format(_('new_typed_vm(%<type>s): options=%<options>s'), type: type, options: options))
186
+ vm_h = parse_typed_vm(options, type).deep_symbolize_keys
187
+ logger.debug(format(_('new_typed_vm(%<type>s): vm_h=%<vm_h>s'), type: type, vm_h: vm_h))
188
+ vm = node.send(vm_collection(type)).new(vm_h)
175
189
  vm
176
190
  end
177
191
  end