foreman_fog_proxmox 0.13.0 → 0.13.4

Sign up to get free protection for your applications and to get access to all the features.
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