foreman_fog_proxmox 0.13.0 → 0.13.4

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +42 -9
  3. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_compute_resource.js +25 -13
  4. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_vm.js +62 -60
  5. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_vm_server.js +2 -2
  6. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_volume.js +10 -10
  7. data/app/controllers/concerns/foreman_fog_proxmox/compute_resources_vms_controller.rb +80 -0
  8. data/app/controllers/foreman_fog_proxmox/compute_resources_controller.rb +23 -25
  9. data/app/helpers/proxmox_compute_resources_vms_helper.rb +99 -0
  10. data/app/helpers/proxmox_vm_config_helper.rb +5 -4
  11. data/app/helpers/proxmox_vm_uuid_helper.rb +34 -0
  12. data/app/models/concerns/fog_extensions/proxmox/server.rb +4 -0
  13. data/app/models/concerns/host_ext/proxmox/associator.rb +46 -0
  14. data/app/models/concerns/host_ext/proxmox/for_vm.rb +33 -0
  15. data/app/models/concerns/orchestration/proxmox/compute.rb +65 -7
  16. data/app/models/foreman_fog_proxmox/proxmox.rb +4 -0
  17. data/app/models/foreman_fog_proxmox/proxmox_compute_attributes.rb +3 -6
  18. data/app/models/foreman_fog_proxmox/proxmox_images.rb +13 -4
  19. data/app/models/foreman_fog_proxmox/proxmox_interfaces.rb +2 -1
  20. data/app/models/foreman_fog_proxmox/proxmox_vm_commands.rb +5 -3
  21. data/app/models/foreman_fog_proxmox/proxmox_vm_new.rb +2 -4
  22. data/app/models/foreman_fog_proxmox/proxmox_vm_queries.rb +14 -7
  23. data/app/models/foreman_fog_proxmox/vms.rb +1 -1
  24. data/app/services/concerns/foreman_fog_proxmox/compute_resource_host_associator.rb +34 -0
  25. data/app/services/foreman_fog_proxmox/node_dashboard/data.rb +6 -2
  26. data/app/views/api/v2/compute_resources/proxmox.json.rabl +1 -1
  27. data/app/views/compute_resources_vms/form/proxmox/server/_volume_cdrom.html.erb +1 -1
  28. data/app/views/compute_resources_vms/index/_proxmox.html.erb +2 -2
  29. data/config/routes.rb +7 -7
  30. data/db/migrate/20210312105013_update_proxmox_uuid_host.rb +29 -0
  31. data/lib/foreman_fog_proxmox/engine.rb +13 -2
  32. data/lib/foreman_fog_proxmox/version.rb +1 -1
  33. data/test/functional/compute_resources_controller_test.rb +4 -4
  34. data/test/unit/foreman_fog_proxmox/helpers/proxmox_container_helper_test.rb +4 -3
  35. data/test/unit/foreman_fog_proxmox/helpers/proxmox_server_helper_test.rb +3 -1
  36. data/test/unit/foreman_fog_proxmox/helpers/proxmox_vm_uuid_helper_test.rb +38 -0
  37. data/test/unit/foreman_fog_proxmox/proxmox_images_test.rb +3 -3
  38. data/test/unit/foreman_fog_proxmox/proxmox_interfaces_test.rb +2 -2
  39. data/test/unit/foreman_fog_proxmox/proxmox_vm_queries_test.rb +3 -3
  40. metadata +12 -3
@@ -19,59 +19,57 @@
19
19
 
20
20
  module ForemanFogProxmox
21
21
  class ComputeResourcesController < ::ApplicationController
22
- before_action :load_compute_resource
23
-
24
- # GET foreman_fog_proxmox/isos/:node_id/:storage
25
- def isos_by_node_and_storage
26
- volumes = @compute_resource.images_by_storage(params[:node_id], params[:storage], 'iso')
22
+ # GET foreman_fog_proxmox/isos/:compute_resource_id/:node_id/:storage
23
+ def isos_by_id_and_node_and_storage
24
+ volumes = load_compute_resource(params[:compute_resource_id]).images_by_storage(params[:node_id], params[:storage], 'iso')
27
25
  respond_to do |format|
28
26
  format.json { render :json => volumes }
29
27
  end
30
28
  end
31
29
 
32
- # GET foreman_fog_proxmox/ostemplates/:node_id/:storage
33
- def ostemplates_by_node_and_storage
34
- volumes = @compute_resource.images_by_storage(params[:node_id], params[:storage], 'vztmpl')
30
+ # GET foreman_fog_proxmox/ostemplates/:compute_resource_id/:node_id/:storage
31
+ def ostemplates_by_id_and_node_and_storage
32
+ volumes = load_compute_resource(params[:compute_resource_id]).images_by_storage(params[:node_id], params[:storage], 'vztmpl')
35
33
  respond_to do |format|
36
34
  format.json { render :json => volumes }
37
35
  end
38
36
  end
39
37
 
40
- # GET foreman_fog_proxmox/isos/:node_id
41
- def isos_by_node
42
- volumes = @compute_resource.images_by_storage(params[:node_id], params[:storage], 'iso')
38
+ # GET foreman_fog_proxmox/isos/:compute_resource_id/:node_id
39
+ def isos_by_id_and_node
40
+ volumes = load_compute_resource(params[:compute_resource_id]).images_by_storage(params[:node_id], params[:storage], 'iso')
43
41
  respond_to do |format|
44
42
  format.json { render :json => volumes }
45
43
  end
46
44
  end
47
45
 
48
- # GET foreman_fog_proxmox/ostemplates/:node_id
49
- def ostemplates_by_node
50
- storages = @compute_resource.storages(params[:node_id], 'vztmpl')
46
+ # GET foreman_fog_proxmox/ostemplates/:compute_resource_id/:node_id
47
+ def ostemplates_by_id_and_node
48
+ storages = load_compute_resource(params[:compute_resource_id]).storages(params[:node_id], 'vztmpl')
51
49
  respond_to do |format|
52
50
  format.json { render :json => storages }
53
51
  end
54
52
  end
55
53
 
56
- # GET foreman_fog_proxmox/storages/:node_id
57
- def storages_by_node
58
- storages = @compute_resource.storages(params[:node_id])
54
+ # GET foreman_fog_proxmox/storages/:compute_resource_id/:node_id
55
+ def storages_by_id_and_node
56
+ storages = load_compute_resource(params[:compute_resource_id]).storages(params[:node_id])
59
57
  respond_to do |format|
60
58
  format.json { render :json => storages }
61
59
  end
62
60
  end
63
61
 
64
- # GET foreman_fog_proxmox/isostorages/:node_id
65
- def iso_storages_by_node
66
- storages = @compute_resource.storages(params[:node_id], 'iso')
62
+ # GET foreman_fog_proxmox/isostorages/:compute_resource_id/:node_id
63
+ def iso_storages_by_id_and_node
64
+ storages = load_compute_resource(params[:compute_resource_id]).storages(params[:node_id], 'iso')
67
65
  respond_to do |format|
68
66
  format.json { render :json => storages }
69
67
  end
70
68
  end
71
69
 
72
- # GET foreman_fog_proxmox/bridges/:node_id
73
- def bridges_by_node
74
- bridges = @compute_resource.bridges(params[:node_id])
70
+ # GET foreman_fog_proxmox/bridges/:compute_resource_id/:node_id
71
+ def bridges_by_id_and_node
72
+ bridges = load_compute_resource(params[:compute_resource_id]).bridges(params[:node_id])
75
73
  respond_to do |format|
76
74
  format.json { render :json => bridges }
77
75
  end
@@ -79,8 +77,8 @@ module ForemanFogProxmox
79
77
 
80
78
  private
81
79
 
82
- def load_compute_resource
83
- @compute_resource = ComputeResource.find_by(type: 'ForemanFogProxmox::Proxmox')
80
+ def load_compute_resource(compute_resource_id)
81
+ ComputeResource.find(compute_resource_id)
84
82
  end
85
83
  end
86
84
  end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Tristan Robert
4
+
5
+ # This file is part of ForemanFogProxmox.
6
+
7
+ # ForemanFogProxmox is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+
12
+ # ForemanFogProxmox is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ module ProxmoxComputeResourcesVmsHelper
21
+ def proxmox_vm_id(compute_resource, vm)
22
+ id = vm.identity
23
+ id = vm.unique_cluster_identity(compute_resource) if compute_resource.class == ForemanFogProxmox::Proxmox
24
+ id
25
+ end
26
+
27
+ def vm_host_action(vm)
28
+ host = Host.for_vm_uuid(@compute_resource, vm).first
29
+ return unless host
30
+
31
+ display_link_if_authorized(_("Host"), hash_for_host_path(:id => host), :class => 'btn btn-default')
32
+ end
33
+
34
+ def vm_power_action(vm, authorizer = nil)
35
+ opts = hash_for_power_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => proxmox_vm_id(@compute_resource, vm)).merge(:auth_object => @compute_resource, :permission => 'power_compute_resources_vms', :authorizer => authorizer)
36
+ html = power_action_html(vm)
37
+
38
+ display_link_if_authorized "Power #{action_string(vm)}", opts, html.merge(:method => :put)
39
+ end
40
+
41
+ def vm_associate_action(vm)
42
+ display_link_if_authorized(
43
+ _('Associate VM'),
44
+ hash_for_associate_compute_resource_vm_path(
45
+ :compute_resource_id => @compute_resource,
46
+ :id => proxmox_vm_id(@compute_resource, vm)
47
+ ).merge(
48
+ :auth_object => @compute_resource,
49
+ :permission => 'edit_compute_resources'),
50
+ :title => _('Associate VM to a Foreman host'),
51
+ :method => :put,
52
+ :class => 'btn btn-default'
53
+ )
54
+ end
55
+
56
+ def vm_import_action(vm, html_options = {})
57
+ @_linked_hosts_cache ||= Host.where(:compute_resource_id => @compute_resource.id).pluck(:uuid)
58
+ return if @_linked_hosts_cache.include?(proxmox_vm_id(@compute_resource, vm).to_s)
59
+
60
+ import_managed_link = display_link_if_authorized(
61
+ _('Import as managed Host'),
62
+ hash_for_import_compute_resource_vm_path(
63
+ :compute_resource_id => @compute_resource,
64
+ :id => proxmox_vm_id(@compute_resource, vm),
65
+ :type => 'managed'),
66
+ html_options
67
+ )
68
+ import_unmanaged_link = display_link_if_authorized(
69
+ _('Import as unmanaged Host'),
70
+ hash_for_import_compute_resource_vm_path(
71
+ :compute_resource_id => @compute_resource,
72
+ :id => proxmox_vm_id(@compute_resource, vm),
73
+ :type => 'unmanaged'),
74
+ html_options
75
+ )
76
+
77
+ import_managed_link + import_unmanaged_link
78
+ end
79
+
80
+ def vm_console_action(vm)
81
+ return unless vm.ready?
82
+
83
+ link_to_if_authorized(
84
+ _('Console'),
85
+ hash_for_console_compute_resource_vm_path.merge(
86
+ :auth_object => @compute_resource,
87
+ :id => proxmox_vm_id(@compute_resource, vm)
88
+ ),
89
+ {
90
+ :id => 'console-button',
91
+ :class => 'btn btn-info'
92
+ }
93
+ )
94
+ end
95
+
96
+ def vm_delete_action(vm, authorizer = nil)
97
+ display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => proxmox_vm_id(@compute_resource, vm)).merge(:auth_object => @compute_resource, :authorizer => authorizer), :class => 'btn btn-danger')
98
+ end
99
+ end
@@ -31,7 +31,7 @@ module ProxmoxVmConfigHelper
31
31
 
32
32
  def object_to_config_hash(vm, type)
33
33
  vm_h = ActiveSupport::HashWithIndifferentAccess.new
34
- main_a = ['hostname', 'name', 'vmid']
34
+ main_a = ['vmid']
35
35
  main = vm.attributes.select { |key, _value| main_a.include? key }
36
36
  main_a += ['templated']
37
37
  config = vm.config.attributes.reject { |key, _value| main_a.include?(key) || Fog::Proxmox::DiskHelper.disk?(key) || Fog::Proxmox::NicHelper.nic?(key) }
@@ -47,14 +47,15 @@ module ProxmoxVmConfigHelper
47
47
  config_hash.store(key, memory)
48
48
  end
49
49
 
50
- def general_a
51
- general_a = ['name', 'node_id', 'type', 'config_attributes', 'volumes_attributes', 'interfaces_attributes']
50
+ def general_a(type)
51
+ general_a = ['node_id', 'type', 'config_attributes', 'volumes_attributes', 'interfaces_attributes']
52
52
  general_a += ['firmware_type', 'provision_method', 'container_volumes', 'server_volumes', 'start_after_create']
53
+ general_a += ['name'] if type == 'lxc'
53
54
  general_a
54
55
  end
55
56
 
56
57
  def config_typed_keys(type)
57
- keys = { general: general_a }
58
+ keys = { general: general_a(type) }
58
59
  main_a = ['name', 'type', 'node_id', 'vmid', 'interfaces', 'mount_points', 'disks']
59
60
  case type
60
61
  when 'lxc'
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2018 Tristan Robert
4
+
5
+ # This file is part of ForemanFogProxmox.
6
+
7
+ # ForemanFogProxmox is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+
12
+ # ForemanFogProxmox is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ module ProxmoxVmUuidHelper
21
+ UUID_REGEXP = /(?<cluster_id>\d+)[_](?<vmid>\d+)/.freeze
22
+ def extract(uuid, name)
23
+ captures_h = uuid ? UUID_REGEXP.match(uuid.to_s) : { cluster_id: '', vmid: '' }
24
+ captures_h ? captures_h[name] : ''
25
+ end
26
+
27
+ def match_uuid?(uuid)
28
+ extract(uuid, :cluster_id) != ''
29
+ end
30
+
31
+ def extract_vmid(uuid)
32
+ extract(uuid, :vmid)
33
+ end
34
+ end
@@ -23,6 +23,10 @@ module FogExtensions
23
23
  extend ActiveSupport::Concern
24
24
  attr_accessor :image_id, :templated, :ostemplate_storage, :ostemplate_file, :password, :start_after_create
25
25
 
26
+ def unique_cluster_identity(compute_resource)
27
+ compute_resource.id.to_s + '_' + identity
28
+ end
29
+
26
30
  def start
27
31
  action('start')
28
32
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Tristan Robert
4
+
5
+ # This file is part of ForemanFogProxmox.
6
+
7
+ # ForemanFogProxmox is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+
12
+ # ForemanFogProxmox is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ module HostExt
21
+ module Proxmox
22
+ module Associator
23
+ extend ActiveSupport::Concern
24
+ included do
25
+ prepend Overrides
26
+ end
27
+ module Overrides
28
+ def associate!(cr, vm)
29
+ self.uuid = proxmox_vm_id(cr, vm)
30
+ self.compute_resource_id = cr.id
31
+ save!(:validate => false) # don't want to trigger callbacks
32
+ end
33
+
34
+ def proxmox_vm_id(compute_resource, vm)
35
+ id = vm.identity
36
+ id = vm.unique_cluster_identity(compute_resource) if compute_resource.class == ForemanFogProxmox::Proxmox
37
+ id
38
+ end
39
+ end
40
+
41
+ def for_vm_uuid(cr, vm)
42
+ where(:compute_resource_id => cr.id, :uuid => Array.wrap(vm).compact.map(cr.id.to_s + '_' + vm&.identity).map(&:to_s))
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2021 Tristan Robert
4
+
5
+ # This file is part of ForemanFogProxmox.
6
+
7
+ # ForemanFogProxmox is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, either version 3 of the License, or
10
+ # (at your option) any later version.
11
+
12
+ # ForemanFogProxmox is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # GNU General Public License for more details.
16
+
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ module HostExt
21
+ module Proxmox
22
+ module ForVm
23
+ extend ActiveSupport::Concern
24
+ module ClassMethods
25
+ def for_vm_uuid(cr, vm)
26
+ uuid = vm&.identity
27
+ uuid = cr.id.to_s + '_' + vm&.identity if cr.class == ForemanFogProxmox::Proxmox
28
+ where(:compute_resource_id => cr.id, :uuid => uuid)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -23,21 +23,79 @@ module Orchestration
23
23
  extend ActiveSupport::Concern
24
24
 
25
25
  def setComputeUpdate
26
- logger.info "Update Proxmox Compute instance for #{name}"
27
- final_compute_attributes = compute_attributes.merge(compute_resource.host_compute_attrs(self))
28
- logger.debug("setComputeUpdate: final_compute_attributes=#{final_compute_attributes}")
29
- compute_resource.save_vm uuid, final_compute_attributes
26
+ if compute_resource.class != ForemanFogProxmox::Proxmox
27
+ super
28
+ else
29
+ logger.info "Update Proxmox Compute instance for #{name}"
30
+ final_compute_attributes = compute_attributes.merge(compute_resource.host_compute_attrs(self))
31
+ logger.debug("setComputeUpdate: final_compute_attributes=#{final_compute_attributes}")
32
+ compute_resource.save_vm uuid, final_compute_attributes
33
+ end
30
34
  rescue StandardError => e
31
35
  failure format(_('Failed to update a compute %<compute_resource>s instance %<name>s: %<e>s'), :compute_resource => compute_resource, :name => name, :e => e), e
32
36
  end
33
37
 
34
38
  def delComputeUpdate
35
- logger.info "Undo Update Proxmox Compute instance for #{name}"
36
- final_compute_attributes = old.compute_attributes.merge(compute_resource.host_compute_attrs(old))
37
- compute_resource.save_vm uuid, final_compute_attributes
39
+ if compute_resource.class != ForemanFogProxmox::Proxmox
40
+ super
41
+ else
42
+ logger.info "Undo Update Proxmox Compute instance for #{name}"
43
+ final_compute_attributes = old.compute_attributes.merge(compute_resource.host_compute_attrs(old))
44
+ compute_resource.save_vm uuid, final_compute_attributes
45
+ end
38
46
  rescue StandardError => e
39
47
  failure format(_('Failed to undo update compute %<compute_resource>s instance %<name>s: %<e>s'), :compute_resource => compute_resource, :name => name, :e => e), e
40
48
  end
49
+
50
+ def empty_provided_ips?(ip, ip6)
51
+ ip.blank? && ip6.blank? && (compute_provides?(:ip) || compute_provides?(:ip6))
52
+ end
53
+
54
+ def ips_keys
55
+ [:ip, :ip6]
56
+ end
57
+
58
+ def computeIp(foreman_attr, fog_attr)
59
+ vm.send(fog_attr) || find_address(foreman_attr)
60
+ end
61
+
62
+ def computeValue(foreman_attr, fog_attr)
63
+ value = ''
64
+ value += compute_resource.id.to_s + '_' if foreman_attr == :uuid
65
+ value += vm.send(fog_attr)
66
+ value
67
+ end
68
+
69
+ def setVmDetails
70
+ attrs = compute_resource.provided_attributes
71
+ result = true
72
+ attrs.each do |foreman_attr, fog_attr|
73
+ if foreman_attr == :mac
74
+ result = false unless match_macs_to_nics(fog_attr)
75
+ elsif ips_keys.include?(foreman_attr)
76
+ value = computeIp(foreman_attr, fog_attr)
77
+ send("#{foreman_attr}=", value)
78
+ result = false if send(foreman_attr).present? && !validate_foreman_attr(value, ::Nic::Base, foreman_attr)
79
+ else
80
+ value = computeValue(foreman_attr, fog_attr)
81
+ send("#{foreman_attr}=", value)
82
+ result = false unless validate_required_foreman_attr(value, Host, foreman_attr)
83
+ end
84
+ end
85
+ return failure(format(_('Failed to acquire IP addresses from compute resource for %<name>s'), name: name)) if empty_provided_ips?(ip, ip6)
86
+
87
+ result
88
+ end
89
+
90
+ def setComputeDetails
91
+ if compute_resource.class != ForemanFogProxmox::Proxmox
92
+ super
93
+ elsif vm
94
+ setVmDetails
95
+ else
96
+ failure format(_('failed to save %<name>s'), name: name)
97
+ end
98
+ end
41
99
  end
42
100
  end
43
101
  end
@@ -64,6 +64,10 @@ module ForemanFogProxmox
64
64
  associate_by('mac', vm.mac)
65
65
  end
66
66
 
67
+ def associate_by(name, attributes)
68
+ Host.authorized(:view_hosts, Host).joins(:primary_interface).where(:nics => { :primary => true }).where("nics.#{name}" => attributes).readonly(false).first
69
+ end
70
+
67
71
  def ssl_certs
68
72
  attrs[:ssl_certs]
69
73
  end