foreman_fog_proxmox 0.25.1 → 1.0.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 (26) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_network_interface_modal.js +11 -0
  3. data/app/assets/stylesheets/foreman_fog_proxmox/network_interface_modal.css +17 -0
  4. data/app/controllers/foreman_fog_proxmox/compute_resources_controller.rb +31 -23
  5. data/app/helpers/proxmox_vm_attrs_helper.rb +1 -0
  6. data/app/helpers/proxmox_vm_cloudinit_helper.rb +63 -48
  7. data/app/helpers/proxmox_vm_config_helper.rb +1 -1
  8. data/app/models/concerns/fog_extensions/proxmox/server.rb +1 -1
  9. data/app/models/foreman_fog_proxmox/proxmox_images.rb +1 -1
  10. data/app/models/foreman_fog_proxmox/proxmox_vm_commands.rb +2 -2
  11. data/app/models/foreman_fog_proxmox/proxmox_vm_new.rb +14 -0
  12. data/app/views/compute_resources_vms/form/proxmox/_add_react_component_to_host_form.html.erb +5 -0
  13. data/app/views/compute_resources_vms/form/proxmox/_add_vm_type_to_nic_provider_specific_form.html.erb +5 -4
  14. data/lib/foreman_fog_proxmox/engine.rb +8 -2
  15. data/lib/foreman_fog_proxmox/version.rb +1 -1
  16. data/test/functional/compute_resources_controller_test.rb +41 -0
  17. data/test/unit/foreman_fog_proxmox/proxmox_images_test.rb +13 -0
  18. data/test/unit/foreman_fog_proxmox/proxmox_vm_commands_server_create_test.rb +123 -10
  19. data/test/unit/foreman_fog_proxmox/proxmox_vm_new_test.rb +40 -0
  20. data/webpack/components/GeneralTabContent.js +99 -61
  21. data/webpack/components/ProxmoxVmType.js +20 -0
  22. data/webpack/components/__tests__/GeneralTabContent.test.js +207 -0
  23. data/webpack/components/__tests__/ProxmoxVmType.test.js +36 -1
  24. data/webpack/components/hooks/useInheritedImageSelection.js +42 -0
  25. metadata +6 -3
  26. data/app/controllers/concerns/foreman_fog_proxmox/controller/parameters/compute_resource.rb +0 -41
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d59641775e4da477886d59bf332dadd576426ef079d1c6af43ef2e9f70b9c5a5
4
- data.tar.gz: 5e24be7c7881de3c05d5a59a06500bd9d35f7c1f06d4c10390df473ec6ef8b01
3
+ metadata.gz: 9dcd1a0e6c90d8847b0e31986425ab3354e4659676994ea6ab84ec9648b0d17d
4
+ data.tar.gz: 8dc424df461ebfe7f7cf07f11e7fecebf56fa75c3bdb4c52b51ef2fdcda01fa5
5
5
  SHA512:
6
- metadata.gz: e04095637c92c2a341043e96f345d4d9ddcd5a3f86b4285fc1ee5199a0e9c36fd542e40a28e748d66d4da56cec743618d144300b907be7c4d82a0a4daac21d82
7
- data.tar.gz: 18a1bb26c3d35273d36826012d35c7e5f21a9c65193e86b7ab9babbde49ae5d15dcea828fa37a024e91740bdfb5a29788047f8ad8cd5829215324e21267e4079
6
+ metadata.gz: a99138711e3912f07dfe3c875106745d543db41e466fbf50460a3fd9abab1476e1da72620f2233d4455984698c64763b849c87f317131d6021b458f1150ea2f5
7
+ data.tar.gz: 852428fd8ed987daca7f45d0908a8b9d71ecbacb9affe3bd65fdc9ecff12b7be80859ea3071d59bd00d8182d5bd75eac1510f922e9c3977be6fd638a879e8187
@@ -0,0 +1,11 @@
1
+ $(document)
2
+ .on('show.bs.modal', '#interfaceModal', function () {
3
+ var modal = $(this);
4
+ modal.toggleClass(
5
+ 'proxmox-interface-modal',
6
+ modal.find('.proxmox-interface-form').length > 0
7
+ );
8
+ })
9
+ .on('hidden.bs.modal', '#interfaceModal', function () {
10
+ $(this).removeClass('proxmox-interface-modal');
11
+ });
@@ -0,0 +1,17 @@
1
+ #interfaceModal.proxmox-interface-modal {
2
+ overflow-y: hidden;
3
+ }
4
+
5
+ #interfaceModal.proxmox-interface-modal .modal-content {
6
+ display: flex;
7
+ flex-direction: column;
8
+ max-height: 90vh;
9
+ overflow: hidden;
10
+ }
11
+
12
+ #interfaceModal.proxmox-interface-modal .modal-body {
13
+ flex: 1 1 auto;
14
+ min-height: 0;
15
+ overflow-y: auto;
16
+ overflow-x: hidden;
17
+ }
@@ -95,20 +95,21 @@ module ForemanFogProxmox
95
95
  # GET foreman_fog_proxmox/metadata/:compute_resource_id
96
96
  def metadata
97
97
  cr = ComputeResource.find(params[:compute_resource_id])
98
+ nodes = Array(cr.nodes)
98
99
 
99
100
  render json: {
100
- nodes: extract_nodes(cr),
101
+ nodes: extract_nodes(nodes),
101
102
  pools: extract_pools(cr),
102
- storages: extract_storages(cr),
103
- bridges: extract_bridges(cr),
103
+ storages: extract_storages(cr, nodes),
104
+ bridges: extract_bridges(cr, nodes),
104
105
  images: extract_images(cr),
105
106
  }
106
107
  end
107
108
 
108
109
  private
109
110
 
110
- def extract_nodes(compute_resource)
111
- Array(compute_resource.nodes).map { |n| { node: n.node } }
111
+ def extract_nodes(nodes)
112
+ nodes.map { |n| { node: n.node } }
112
113
  end
113
114
 
114
115
  def extract_pools(compute_resource)
@@ -118,27 +119,34 @@ module ForemanFogProxmox
118
119
  end
119
120
  end
120
121
 
121
- def extract_storages(compute_resource)
122
- Array(compute_resource.storages).map do |s|
123
- h = s.respond_to?(:as_json) ? s.as_json : s
124
- {
125
- storage: (h[:storage] || h['storage']),
126
- node_id: (h[:node_id] || h['node_id']),
127
- content: (h[:content] || h['content']),
128
- avail: (h[:avail] || h['avail']),
129
- used: (h[:used] || h['used']),
130
- total: (h[:total] || h['total']),
131
- }
122
+ def extract_storages(compute_resource, nodes)
123
+ nodes.flat_map do |node|
124
+ node_id = node.node
125
+ Array(compute_resource.storages(node_id)).map do |storage|
126
+ h = storage.respond_to?(:as_json) ? storage.as_json : storage
127
+ {
128
+ storage: (h[:storage] || h['storage']),
129
+ node_id: node_id,
130
+ content: (h[:content] || h['content']),
131
+ avail: (h[:avail] || h['avail']),
132
+ used: (h[:used] || h['used']),
133
+ total: (h[:total] || h['total']),
134
+ shared: (h[:shared] || h['shared']),
135
+ }
136
+ end
132
137
  end
133
138
  end
134
139
 
135
- def extract_bridges(compute_resource)
136
- Array(compute_resource.bridges).map do |b|
137
- h = b.respond_to?(:as_json) ? b.as_json : b
138
- {
139
- node_id: (h[:node_id] || h['node_id']),
140
- iface: (h[:iface] || h['iface']),
141
- }
140
+ def extract_bridges(compute_resource, nodes)
141
+ nodes.flat_map do |node|
142
+ node_id = node.node
143
+ Array(compute_resource.bridges(node_id)).map do |bridge|
144
+ h = bridge.respond_to?(:as_json) ? bridge.as_json : bridge
145
+ {
146
+ node_id: node_id,
147
+ iface: (h[:iface] || h['iface']),
148
+ }
149
+ end
142
150
  end
143
151
  end
144
152
 
@@ -130,6 +130,7 @@ module ProxmoxVMAttrsHelper
130
130
  hostname: vms.config.hostname,
131
131
  ostemplate_storage: vms.ostemplate_storage,
132
132
  ostemplate_file: vms.ostemplate_file,
133
+ iso_upload_storage: vms.iso_upload_storage,
133
134
  start_after_create: vms.start_after_create,
134
135
  templated: vms.templated,
135
136
  is_secure_boot: vms.config.secure_boot?,
@@ -21,6 +21,9 @@ require 'fog/proxmox/helpers/disk_helper'
21
21
  require 'fog/proxmox/helpers/nic_helper'
22
22
  require 'foreman_fog_proxmox/value'
23
23
  require 'foreman_fog_proxmox/hash_collection'
24
+ require 'fileutils'
25
+ require 'open3'
26
+ require 'tmpdir'
24
27
 
25
28
  # Convert a foreman form server hash into a fog-proxmox server attributes hash
26
29
  module ProxmoxVMCloudinitHelper
@@ -42,54 +45,59 @@ module ProxmoxVMCloudinitHelper
42
45
  cloudinit_h
43
46
  end
44
47
 
45
- def create_cloudinit_iso(vm_name, configs, ssh)
46
- iso = File.join(default_iso_path, "#{vm_name.tr('.', '_')}_cloudinit.iso")
48
+ def create_cloudinit_iso(vm_name, configs)
49
+ wd = create_temp_directory
50
+ iso = File.join(wd, "#{vm_name.tr('.', '_')}_cloudinit.iso")
47
51
  files = []
48
- wd = create_temp_directory(ssh)
49
52
 
50
53
  configs.each do |config|
51
- config_file = ssh.run("cat <<'EOF' > '#{wd}/#{config[0]}'\n#{config[1]}\nEOF")
52
- unless config_file.first.status.zero?
53
- delete_temp_dir(ssh, wd)
54
- raise ::Foreman::Exception, "Failed to create file #{config[0]}: #{config_file.first.stdout}"
55
- end
56
- files.append(File.join(wd, config[0]))
54
+ config_file = File.join(wd, config[0])
55
+ File.write(config_file, config[1])
56
+ files.append(config_file)
57
57
  end
58
- generated_iso = ssh.run(generate_iso_command(iso, files))
59
- unless generated_iso.first.status.zero?
60
- delete_temp_dir(ssh, wd)
61
- raise Foreman::Exception, N_("ISO build failed: #{generated_iso.first.stdout}")
62
- end
63
- delete_temp_dir(ssh, wd)
64
- iso
58
+ logger.debug("Generating cloud-init ISO at #{iso}")
59
+ stdout, stderr, status = Open3.capture3(*generate_iso_command(iso, files))
60
+ raise Foreman::Exception, N_("ISO build failed: #{stderr.presence || stdout}") unless status.success?
61
+
62
+ yield iso
63
+ ensure
64
+ delete_temp_dir(wd) if wd && Dir.exist?(wd)
65
65
  end
66
66
 
67
67
  def generate_iso_command(iso_file, config_files)
68
- arguments = ["genisoimage", "-output #{iso_file}", '-volid', 'cidata', '-joliet', '-rock']
69
- iso_command = arguments.concat(config_files).join(' ')
70
- logger.debug("iso image generation args: #{iso_command}")
71
- iso_command
68
+ arguments = ["genisoimage", '-output', iso_file, '-volid', 'cidata', '-joliet', '-rock']
69
+ arguments.concat(config_files)
70
+ logger.debug("iso image generation args: #{arguments.join(' ')}")
71
+ arguments
72
72
  end
73
73
 
74
- def create_temp_directory(ssh)
75
- res = ssh.run("mktemp -d")
76
- raise ::Foreman::Exception, "Could not create working directory to store cloudinit config data: #{res.first.stdout}." unless res.first.status.zero?
77
- res.first.stdout.chomp
74
+ def create_temp_directory
75
+ Dir.mktmpdir
76
+ rescue StandardError => e
77
+ raise ::Foreman::Exception, "Could not create working directory to store cloudinit config data: #{e}."
78
78
  end
79
79
 
80
- def delete_temp_dir(ssh, working_dir)
81
- ssh.run("rm -rf #{working_dir}")
82
- rescue Foreman::Exception => e
80
+ def delete_temp_dir(working_dir)
81
+ FileUtils.remove_entry(working_dir)
82
+ rescue StandardError => e
83
83
  logger.warn("Could not delete directory for config files: #{e}. Please delete it manually at #{working_dir}")
84
84
  end
85
85
 
86
- def parse_cloudinit_config(args)
86
+ def cloudinit_clone_args(args, vm_instance)
87
+ return args unless args[:user_data]
88
+
89
+ actual_node = vm_instance.node_id
90
+ logger.info("Cloud-init ISO storage will be validated for cloned VM node #{actual_node} instead of selected node #{args[:node_id]}") if args[:node_id] != actual_node
91
+
92
+ parse_cloudinit_config(args, vm_node: actual_node)
93
+ end
94
+
95
+ def parse_cloudinit_config(args, vm_node: args[:node_id])
87
96
  filenames = ["meta-data"]
88
97
  config_data = ["instance-id: #{args[:name]}"]
89
98
  user_data = args.delete(:user_data)
90
99
  return args if user_data == ''
91
100
  check_template_format(user_data)
92
- ssh = vm_ssh
93
101
 
94
102
  if user_data.include?('#network-config') && user_data.include?('#cloud-config')
95
103
  config_data.concat(user_data.split('#network-config'))
@@ -105,32 +113,39 @@ module ProxmoxVMCloudinitHelper
105
113
  return args if config_data.length == 1
106
114
  configs = filenames.zip(config_data).to_h
107
115
 
108
- iso = create_cloudinit_iso(args[:name], configs, ssh)
109
- args.merge!(attach_cloudinit_iso(args[:node_id], iso))
116
+ create_cloudinit_iso(args[:name], configs) do |iso|
117
+ storage = upload_iso(args[:node_id], args[:iso_upload_storage], iso, vm_node: vm_node)
118
+ args.merge!(attach_cloudinit_iso(vm_node, storage, iso))
119
+ end
110
120
  end
111
121
 
112
- def attach_cloudinit_iso(node, iso)
113
- volume = nil
114
- storages(node, 'iso').each do |storage|
115
- volume = storage.volumes.detect { |v| v.volid.include? File.basename(iso) }
116
- break if volume
117
- end
122
+ def upload_iso(node, storage_id, iso, vm_node: node)
123
+ raise ::Foreman::Exception, _('ISO upload storage must be selected') if storage_id.blank?
118
124
 
119
- raise ::Foreman::Exception, "Could not find generated cloud-init ISO #{File.basename(iso)} on any ISO storage for node #{node}" unless volume
125
+ iso_storages = storages(node, 'iso')
126
+ storage = iso_storages.find { |s| s.identity == storage_id }
127
+ raise ::Foreman::Exception, "Could not find selected ISO upload storage #{storage_id} for node #{node}" unless storage
120
128
 
121
- { ide2: "#{volume.volid},media=cdrom" }
122
- end
129
+ if node != vm_node
130
+ vm_storage = storages(vm_node, 'iso').find { |s| s.identity == storage_id }
131
+ raise ::Foreman::Exception, "Selected ISO upload storage #{storage_id} is not accessible from cloned VM node #{vm_node}" unless vm_storage
123
132
 
124
- def default_iso_path
125
- "/var/lib/vz/template/iso"
133
+ storage = vm_storage
134
+ end
135
+
136
+ storage.upload_iso(iso)
137
+ storage.identity
126
138
  end
127
139
 
128
- def vm_ssh
129
- ssh = Fog::SSH.new(URI.parse(fog_credentials[:proxmox_url]).host, fog_credentials[:proxmox_username].split('@')[0], { password: fog_credentials[:proxmox_password] })
130
- ssh.run('ls') # test if ssh is successful
131
- ssh
132
- rescue StandardError => e
133
- raise ::Foreman::Exception, "Unable to ssh into proxmox server: #{e}"
140
+ def attach_cloudinit_iso(node, storage_id, iso)
141
+ storage = client.nodes.get(node).storages.get(storage_id)
142
+ volume = storage&.volumes&.all&.detect do |v|
143
+ File.basename(v.volid) == File.basename(iso)
144
+ end
145
+
146
+ raise ::Foreman::Exception, "Could not find generated cloud-init ISO #{File.basename(iso)} on storage #{storage_id} for node #{node}" unless volume
147
+
148
+ { ide2: "#{volume.volid},media=cdrom" }
134
149
  end
135
150
 
136
151
  def check_template_format(user_data)
@@ -41,7 +41,7 @@ module ProxmoxVMConfigHelper
41
41
 
42
42
  def general_a(type)
43
43
  general_a = ['node_id', 'type', 'config_attributes', 'efidisk_attributes', 'volumes_attributes', 'interfaces_attributes', 'image_id']
44
- general_a += ['firmware_type', 'provision_method', 'container_volumes', 'server_volumes', 'start_after_create']
44
+ general_a += ['firmware_type', 'provision_method', 'container_volumes', 'server_volumes', 'start_after_create', 'iso_upload_storage']
45
45
  general_a += ['name'] if type == 'lxc'
46
46
  general_a
47
47
  end
@@ -21,7 +21,7 @@ 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, :compute_resource_id
24
+ attr_accessor :image_id, :templated, :ostemplate_storage, :ostemplate_file, :iso_upload_storage, :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
@@ -41,7 +41,7 @@ module ForemanFogProxmox
41
41
  end
42
42
 
43
43
  def available_images
44
- templates.collect { |template| OpenStruct.new(id: template_uuid(template), name: template_name(template)) }
44
+ templates.collect { |template| OpenStruct.new(id: template_uuid(template), name: template_name(template)) }.uniq
45
45
  end
46
46
 
47
47
  def templates
@@ -42,7 +42,8 @@ module ForemanFogProxmox
42
42
  image = find_vm_by_uuid(image_id)
43
43
  validate_image_template_disk_slots!(image, args) if type == 'qemu'
44
44
  vm = clone_from_image(image, vmid)
45
- vm.update(compute_clone_attributes(args, vm.container?, type, image: image))
45
+ cloudinit_args = cloudinit_clone_args(args, vm)
46
+ vm.update(compute_clone_attributes(cloudinit_args, vm.container?, type, image: image))
46
47
  update_pool(vm, args[:pool]) if args[:pool]
47
48
  else
48
49
  logger.warn("create vm: args=#{args}")
@@ -71,7 +72,6 @@ module ForemanFogProxmox
71
72
  end
72
73
 
73
74
  def compute_clone_attributes(args, container, type, image: nil)
74
- args = parse_cloudinit_config(args) if args[:user_data]
75
75
  args[:config_attributes].merge!(update_boot_order(image)) if image && args[:config_attributes]
76
76
  parsed_args = parse_typed_vm(args, type)
77
77
  if container
@@ -195,6 +195,17 @@ module ForemanFogProxmox
195
195
  vm_h[:is_secure_boot] = secure_boot if type == 'qemu' && secure_boot
196
196
  end
197
197
 
198
+ def add_image_attribute(vm_h, options)
199
+ image_id = options['image_id']
200
+ vm_h[:image_id] = image_id if image_id.present?
201
+ end
202
+
203
+ def add_iso_upload_storage_attribute(vm_h, options, type)
204
+ iso_upload_storage = options['iso_upload_storage']
205
+ vm_h[:iso_upload_storage] = iso_upload_storage if type == 'qemu' && iso_upload_storage.present?
206
+ end
207
+
208
+ # rubocop:disable Metrics/AbcSize
198
209
  def new_typed_vm(new_attr, type)
199
210
  convert_config_attributes(new_attr) if new_attr.key?(:config_attributes)
200
211
  node_id = new_attr['node_id']
@@ -210,11 +221,14 @@ module ForemanFogProxmox
210
221
  logger.debug("new_typed_vm(#{type}): options=#{options}")
211
222
  vm_h = parse_typed_vm(options, type).deep_symbolize_keys
212
223
  add_secure_boot_attribute(vm_h, options, type)
224
+ add_image_attribute(vm_h, options)
225
+ add_iso_upload_storage_attribute(vm_h, options, type)
213
226
  logger.debug("new_typed_vm(#{type}): vm_h=#{vm_h}")
214
227
  vm_h = vm_h.merge(vm_typed_instance_defaults(type)) if vm_h.empty?
215
228
  logger.debug(format(_('new_typed_vm(%<type>s) with vm_typed_instance_defaults: vm_h=%<vm_h>s'), type: type, vm_h: vm_h))
216
229
  node.send(vm_collection(type)).new(vm_h)
217
230
  end
231
+ # rubocop:enable Metrics/AbcSize
218
232
  end
219
233
  end
220
234
 
@@ -1,4 +1,9 @@
1
+ <% content_for(:stylesheets) do %>
2
+ <%= stylesheet_link_tag 'foreman_fog_proxmox/network_interface_modal' %>
3
+ <% end %>
4
+
1
5
  <% content_for(:javascripts) do %>
6
+ <%= javascript_include_tag 'foreman_fog_proxmox/proxmox_network_interface_modal' %>
2
7
  <%= webpacked_plugins_js_for :foreman_fog_proxmox %>
3
8
  <% end %>
4
9
  <%= react_component('ProxmoxVmType', { registerComp: true }) unless @host.managed %>
@@ -20,12 +20,13 @@ along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>. %>
20
20
  <% vm_type = vm_type_and_node_id[:vm_type] %>
21
21
  <% node_id = vm_type_and_node_id[:node_id] %>
22
22
 
23
- <%= f.fields_for 'compute_attributes', OpenStruct.new(compute_attributes) do |f| %>
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 %>
25
- <% end %>
23
+ <div class="proxmox-interface-form">
24
+ <%= f.fields_for 'compute_attributes', OpenStruct.new(compute_attributes) do |f| %>
25
+ <%= 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
+ <% end %>
27
+ </div>
26
28
  <% else %>
27
29
  <%= f.fields_for 'compute_attributes', OpenStruct.new(f.object.compute_attributes) do |f| %>
28
30
  <%= render provider_partial(@host.compute_resource, 'network'), :f => f, :disabled => f.object.persisted?, :compute_resource => @host.compute_resource, :new_host => new_vm, :new_vm => new_vm %>
29
31
  <% end %>
30
32
  <% end %>
31
-
@@ -40,7 +40,14 @@ module ForemanFogProxmox
40
40
  register_global_js_file 'global'
41
41
  # Register Proxmox VE compute resource in foreman
42
42
  compute_resource ForemanFogProxmox::Proxmox
43
- parameter_filter(ComputeResource, :uuid)
43
+ parameter_filter ComputeResource,
44
+ :uuid,
45
+ :ssl_verify_peer,
46
+ :ssl_certs,
47
+ :disable_proxy,
48
+ :auth_method,
49
+ :token_id,
50
+ :token
44
51
  # add dashboard widget
45
52
  widget 'foreman_fog_proxmox_widget', name: N_('Foreman Fog Proxmox widget'), sizex: 8, sizey: 1
46
53
 
@@ -111,7 +118,6 @@ module ForemanFogProxmox
111
118
  Fog::Proxmox::Compute::Interface.include FogExtensions::Proxmox::Interface
112
119
  Fog::Proxmox::Compute::Disk.include FogExtensions::Proxmox::Disk
113
120
  Fog::Proxmox::Compute::Node.include FogExtensions::Proxmox::Node
114
- ::ComputeResourcesController.include ForemanFogProxmox::Controller::Parameters::ComputeResource
115
121
  ::ComputeResourcesVmsController.include ForemanFogProxmox::ComputeResourcesVmsController
116
122
  ::HostsController.include ForemanFogProxmox::HostsController
117
123
  ::Host::Managed.include Orchestration::Proxmox::Compute
@@ -18,5 +18,5 @@
18
18
  # along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
19
19
 
20
20
  module ForemanFogProxmox
21
- VERSION = '0.25.1'
21
+ VERSION = '1.0.0'
22
22
  end
@@ -79,6 +79,18 @@ module ForemanFogProxmox
79
79
  assert_instance_of Array, json_response
80
80
  end
81
81
  test 'should get metadata' do
82
+ node_a = stub(node: 'node-a')
83
+ node_b = stub(node: 'node-b')
84
+ node_a_storages = [{ storage: 'shared-iso', content: 'iso', avail: 100, used: 20, total: 120, shared: 1 }]
85
+ node_b_storages = [{ storage: 'local', content: 'iso', avail: 200, used: 30, total: 230, shared: 0 }]
86
+ node_a_bridges = [{ iface: 'vmbr0' }]
87
+ node_b_bridges = [{ iface: 'vmbr1' }]
88
+ @compute_resource.stubs(:nodes).returns([node_a, node_b])
89
+ @compute_resource.expects(:storages).with('node-a').returns(node_a_storages)
90
+ @compute_resource.expects(:storages).with('node-b').returns(node_b_storages)
91
+ @compute_resource.expects(:bridges).with('node-a').returns(node_a_bridges)
92
+ @compute_resource.expects(:bridges).with('node-b').returns(node_b_bridges)
93
+
82
94
  get :metadata, params: { :compute_resource_id => @compute_resource.id }, session: set_session_user
83
95
  assert_response :success
84
96
  show_response = @response.body
@@ -93,6 +105,35 @@ module ForemanFogProxmox
93
105
  assert_instance_of Array, json_response['pools']
94
106
  assert_instance_of Array, json_response['storages']
95
107
  assert_instance_of Array, json_response['bridges']
108
+ assert_equal ['node-a', 'node-b'], (json_response['storages'].map { |storage| storage['node_id'] })
109
+ assert_equal [1, 0], (json_response['storages'].map { |storage| storage['shared'] })
110
+ assert_equal ['node-a', 'node-b'], (json_response['bridges'].map { |bridge| bridge['node_id'] })
111
+ assert_equal ['vmbr0', 'vmbr1'], (json_response['bridges'].map { |bridge| bridge['iface'] })
112
+ end
113
+
114
+ test 'metadata includes shared storage for every node where it is available' do
115
+ node_a = stub(node: 'node-a')
116
+ node_b = stub(node: 'node-b')
117
+ shared_storage = {
118
+ storage: 'shared-iso',
119
+ content: 'iso',
120
+ avail: 100,
121
+ used: 20,
122
+ total: 120,
123
+ shared: 1,
124
+ }
125
+ @compute_resource.stubs(:nodes).returns([node_a, node_b])
126
+ @compute_resource.expects(:storages).with('node-a').returns([shared_storage])
127
+ @compute_resource.expects(:storages).with('node-b').returns([shared_storage])
128
+
129
+ get :metadata, params: { :compute_resource_id => @compute_resource.id }, session: set_session_user
130
+ assert_response :success
131
+ json_response = JSON.parse(@response.body)
132
+
133
+ assert_equal [
134
+ ['shared-iso', 'node-a'],
135
+ ['shared-iso', 'node-b'],
136
+ ], (json_response['storages'].map { |storage| [storage['storage'], storage['node_id']] })
96
137
  end
97
138
  end
98
139
  end
@@ -43,5 +43,18 @@ module ForemanFogProxmox
43
43
  @cr.clone_from_image(@image, @vmid)
44
44
  end
45
45
  end
46
+
47
+ describe 'available_images' do
48
+ it 'removes duplicates' do
49
+ @cr = FactoryBot.build_stubbed(:proxmox_cr)
50
+ clone = mock('vm')
51
+ clone.stubs(:name).returns('vm-1')
52
+ @cr.stubs(:find_vm_by_uuid).returns(clone)
53
+ template = mock('template')
54
+ template.stubs(:vmid).returns(100).at_least(2)
55
+ @cr.stubs(:templates).returns([template, template])
56
+ assert_equal 1, @cr.available_images.length
57
+ end
58
+ end
46
59
  end
47
60
  end