foreman_fog_proxmox 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_vm.js +54 -33
  3. data/app/assets/javascripts/foreman_fog_proxmox/proxmox_vm_server.js +25 -5
  4. data/app/assets/stylesheets/foreman_fog_proxmox/accordion.css +21 -0
  5. data/app/helpers/proxmox_compute_resources_helper.rb +1 -1
  6. data/app/helpers/proxmox_form_helper.rb +2 -2
  7. data/app/helpers/proxmox_vm_attrs_helper.rb +131 -0
  8. data/app/helpers/proxmox_vm_cloudinit_helper.rb +1 -1
  9. data/app/helpers/proxmox_vm_interfaces_helper.rb +3 -3
  10. data/app/helpers/proxmox_vm_volumes_helper.rb +3 -3
  11. data/app/models/concerns/fog_extensions/proxmox/node.rb +1 -1
  12. data/app/models/foreman_fog_proxmox/proxmox.rb +1 -1
  13. data/app/models/foreman_fog_proxmox/proxmox_compute_attributes.rb +3 -3
  14. data/app/models/foreman_fog_proxmox/proxmox_console.rb +1 -1
  15. data/app/models/foreman_fog_proxmox/proxmox_images.rb +5 -0
  16. data/app/models/foreman_fog_proxmox/proxmox_version.rb +1 -1
  17. data/app/models/foreman_fog_proxmox/proxmox_vm_commands.rb +5 -2
  18. data/app/models/foreman_fog_proxmox/proxmox_vm_new.rb +1 -1
  19. data/app/overrides/compute_resources_vms/form/add_from_profile_to_compute_attributes_form.rb +8 -0
  20. data/app/overrides/compute_resources_vms/form/add_react_component_to_host.rb +25 -0
  21. data/app/overrides/compute_resources_vms/form/update_react_component_to_host_form.rb +25 -0
  22. data/app/views/compute_resources_vms/form/proxmox/_add_react_component_to_host_form.html.erb +5 -0
  23. data/app/views/compute_resources_vms/form/proxmox/_base.html.erb +21 -21
  24. data/app/views/compute_resources_vms/form/proxmox/_update_react_component_to_host_form.html.erb +26 -0
  25. data/app/views/compute_resources_vms/form/proxmox/container/_config.html.erb +17 -13
  26. data/app/views/compute_resources_vms/form/proxmox/server/_config.html.erb +20 -20
  27. data/app/views/compute_resources_vms/form/proxmox/server/_volume_hard_disk.html.erb +4 -1
  28. data/config/routes.rb +3 -3
  29. data/lib/foreman_fog_proxmox/version.rb +1 -1
  30. data/package.json +42 -0
  31. data/test/factories/proxmox_factory.rb +7 -7
  32. data/test/unit/foreman_fog_proxmox/proxmox_compute_attributes_test.rb +1 -1
  33. data/test/unit/foreman_fog_proxmox/proxmox_interfaces_test.rb +6 -6
  34. data/webpack/components/GeneralTabContent.js +107 -0
  35. data/webpack/components/ProxmoxComputeSelectors.js +141 -0
  36. data/webpack/components/ProxmoxContainer/MountPoint.js +91 -0
  37. data/webpack/components/ProxmoxContainer/ProxmoxContainerHardware.js +85 -0
  38. data/webpack/components/ProxmoxContainer/ProxmoxContainerNetwork.js +179 -0
  39. data/webpack/components/ProxmoxContainer/ProxmoxContainerOptions.js +104 -0
  40. data/webpack/components/ProxmoxContainer/ProxmoxContainerStorage.js +194 -0
  41. data/webpack/components/ProxmoxContainer/components/NetworkInterface.js +193 -0
  42. data/webpack/components/ProxmoxServer/ProxmoxServerHardware.js +204 -0
  43. data/webpack/components/ProxmoxServer/ProxmoxServerNetwork.js +161 -0
  44. data/webpack/components/ProxmoxServer/ProxmoxServerOptions.js +105 -0
  45. data/webpack/components/ProxmoxServer/ProxmoxServerStorage.js +272 -0
  46. data/webpack/components/ProxmoxServer/components/CDRom.js +149 -0
  47. data/webpack/components/ProxmoxServer/components/CPUFlagsModal.js +88 -0
  48. data/webpack/components/ProxmoxServer/components/HardDisk.js +143 -0
  49. data/webpack/components/ProxmoxServer/components/NetworkInterface.js +150 -0
  50. data/webpack/components/ProxmoxStoragesUtils.js +50 -0
  51. data/webpack/components/ProxmoxVmType.js +256 -0
  52. data/webpack/components/ProxmoxVmUtils.js +62 -0
  53. data/webpack/components/common/FormInputs.js +143 -0
  54. data/webpack/global_index.js +15 -0
  55. data/webpack/index.js +7 -0
  56. metadata +50 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b41c2a927bcce7383e1fd55c588247e935e0c6e11ba967fe6628db72a80730df
4
- data.tar.gz: ce7634ce750c8317bc28906e4394888d50b66931b9c77586f3af90c339ca07c2
3
+ metadata.gz: 66a00f7786605946bb2388d497b7a2f512fb29fcf00a12f35bc7059e8b0825f4
4
+ data.tar.gz: 3e2bf00ae5430cad3c5927ba4636582cd78d83cefa7065088fa4458da638b173
5
5
  SHA512:
6
- metadata.gz: 65a3b9ac47bb382cc0c2b30430151a9db92bf64dcc9c64ed6c9fffb1fbb5dc525316d3475a7c5d88c21d30e3ffb8ad8eaf6ef5d6676cff53cfbd43dd5a086378
7
- data.tar.gz: e210dad89239964322d0dc1b5f3a326a11f449ada2ad61d23f87f2047d421454020aa4b14ffed1efc173f31fd7cb0c68d92a76a9df0757133c810993a49a5a85
6
+ metadata.gz: 9534979808ea0e6ad262d696497189b3129e6594478601649d74d475ef1cee060898988e662d50db447f04257b6e6cdcfa6d2d144fea0ef498e66c3a899bd140
7
+ data.tar.gz: 981cfaa3815ce58182a465490541051b4a79612f0153e3419142bbf5e3c7e58c68b7758b2cac8eaa4bae31a428717d239315695b6847f2d1d207b04f26ff8a41
@@ -15,8 +15,6 @@
15
15
  // You should have received a copy of the GNU General Public License
16
16
  // along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
17
17
 
18
- //= require jquery-ui/widgets/accordion
19
-
20
18
  $(document).ready(vmTypeSelected);
21
19
 
22
20
  function vmTypeSelected() {
@@ -25,13 +23,21 @@ function vmTypeSelected() {
25
23
  var host_uuid = $("input[id='host_uuid']").val();
26
24
  var new_vm = host_uuid == undefined;
27
25
  var fieldsets = [];
26
+ var fieldconfig = [];
28
27
  fieldsets.push({id: 'config_advanced_options', toggle: true, new_vm: new_vm, selected: selected});
29
28
  fieldsets.push({id: 'config_ext', toggle: true, new_vm: new_vm, selected: selected});
30
29
  fieldsets.push({id: 'volume', toggle: true, new_vm: new_vm, selected: selected});
31
30
  fieldsets.push({id: 'network', toggle: true, new_vm: true, selected: selected});
31
+ fieldconfig.push({id: 'config_options', new_vm: new_vm, selected: selected});
32
+ fieldconfig.push({id: 'config_cpu', new_vm: new_vm, selected: selected});
33
+ fieldconfig.push({id: 'config_memory', new_vm: new_vm, selected: selected});
34
+ fieldconfig.push({id: 'config_cdrom', new_vm: new_vm, selected: selected});
35
+ fieldconfig.push({id: 'config_os', new_vm: new_vm, selected: selected});
36
+ fieldconfig.push({id: 'config_dns', new_vm: new_vm, selected: selected});
32
37
  fieldsets.forEach(toggleFieldsets);
33
- toggleAccordion(selected);
34
38
  toggleVolumes(selected);
39
+ fieldconfig.forEach(toggleConfigs);
40
+ toggleAccordions();
35
41
  return false;
36
42
  }
37
43
 
@@ -131,7 +137,7 @@ function enableFieldset(fieldsetId, fieldset) {
131
137
  input_hidden_id(fieldsetId).removeAttr('disabled');
132
138
  }
133
139
 
134
- function disableFieldset(fieldsetId, fieldset) {
140
+ function disableFieldset(fieldsetId, fieldset) {
135
141
  if (fieldset.toggle && fieldset.new_vm){
136
142
  fieldset_id(fieldsetId, fieldset).hide();
137
143
  }
@@ -139,57 +145,72 @@ function disableFieldset(fieldsetId, fieldset) {
139
145
  input_hidden_id(fieldsetId).attr('disabled','disabled');
140
146
  }
141
147
 
142
- function enableConfigOptions(fieldsetId) {
143
- var field = $("#" + fieldsetId + "_advanced_options");
144
- field.accordion({collapsible : true, heightStyle: "content"});
145
- field.removeClass('disabled').find("*").prop("disabled", false);
146
- field.removeClass('hide');
148
+ function toggleFieldset(fieldsetId, fieldset, type1, type2) {
149
+ type1 === type2 ? enableFieldset(fieldsetId, fieldset) : disableFieldset(fieldsetId, fieldset);
147
150
  }
148
151
 
149
- function disableConfigOptions(fieldsetId) {
150
- var field = $("#" + fieldsetId + "_advanced_options");
151
- field.addClass('disabled').find("*").prop("disabled", true);
152
- field.addClass('hide');
152
+ function input_hidden_id(volume_id){
153
+ return $("div[id^='"+ volume_id +"_volumes']" + " + input:hidden");
153
154
  }
154
155
 
155
- function toggleConfigOptions(fieldsetId, type1, type2) {
156
- if (type1 === type2) {
157
- enableConfigOptions(fieldsetId);
158
- } else {
159
- disableConfigOptions(fieldsetId);
160
- }
156
+ function fieldset_id(fieldsetId, fieldset){
157
+ return $("fieldset[id^='" + fieldsetId + "_"+fieldset.id+"']");
161
158
  }
162
159
 
163
- function toggleAccordion(selected){
160
+ function fieldsets(type){
161
+ return type === 'qemu' ? ['server'] : ['container'];
162
+ }
163
+
164
+ function toggleFieldsets(fieldset){
165
+ var removable_input_hidden = $("div.removable-item[style='display: none;']" + " + input:hidden");
166
+ removable_input_hidden.attr('disabled','disabled');
164
167
  ['qemu', 'lxc'].forEach(function(type){
165
168
  fieldsets(type).forEach(function(fieldsetId){
166
- toggleConfigOptions(fieldsetId, selected, type);
169
+ toggleFieldset(fieldsetId, fieldset, fieldset.selected, type);
167
170
  });
168
171
  });
169
172
  }
170
173
 
171
- function toggleFieldset(fieldsetId, fieldset, type1, type2) {
172
- type1 === type2 ? enableFieldset(fieldsetId, fieldset) : disableFieldset(fieldsetId, fieldset);
174
+ function toggleAccordions() {
175
+ $('.accordion-content').hide();
176
+ $('.accordion-section').off('click').on('click', function(event) {
177
+ var $content = $(this).find('.accordion-content');
178
+ $content.slideToggle();
179
+ $(this).toggleClass('active');
180
+ $('.accordion-content').not($content).slideUp();
181
+ $('.accordion-section').not($(this)).removeClass('active');
182
+ event.stopPropagation();
183
+ });
184
+ $('.accordion-content').on('click', function(event) {
185
+ event.stopPropagation();
186
+ });
173
187
  }
174
188
 
175
- function input_hidden_id(volume_id){
176
- return $("div[id^='"+ volume_id +"_volumes']" + " + input:hidden");
189
+ function enableConfig(fieldsetId, fieldset) {
190
+ fieldset_id(fieldsetId, fieldset).removeClass('hide');
191
+ fieldset_id(fieldsetId, fieldset).removeAttr('disabled');
192
+ input_hidden_id(fieldsetId).removeAttr('disabled');
193
+
177
194
  }
178
195
 
179
- function fieldset_id(fieldsetId, fieldset){
180
- return $("fieldset[id^='" + fieldsetId + "_"+fieldset.id+"']");
196
+ function disableConfig(fieldsetId, fieldset) {
197
+ fieldset_id(fieldsetId, fieldset).addClass('hide');
198
+ fieldset_id(fieldsetId, fieldset).attr('disabled','disabled');
199
+ input_hidden_id(fieldsetId).attr('disabled','disabled');
181
200
  }
182
201
 
183
- function fieldsets(type){
184
- return type === 'qemu' ? ['server'] : ['container'];
202
+ function toggleConfig(fieldsetId, fieldset, type1, type2) {
203
+ if (type1 === type2) {
204
+ enableConfig(fieldsetId, fieldset);
205
+ } else {
206
+ disableConfig(fieldsetId, fieldset);
207
+ }
185
208
  }
186
209
 
187
- function toggleFieldsets(fieldset){
188
- var removable_input_hidden = $("div.removable-item[style='display: none;']" + " + input:hidden");
189
- removable_input_hidden.attr('disabled','disabled');
210
+ function toggleConfigs(fieldset){
190
211
  ['qemu', 'lxc'].forEach(function(type){
191
212
  fieldsets(type).forEach(function(fieldsetId){
192
- toggleFieldset(fieldsetId, fieldset, fieldset.selected, type);
213
+ toggleConfig(fieldsetId, fieldset, fieldset.selected, type);
193
214
  });
194
215
  });
195
216
  }
@@ -32,7 +32,7 @@ function getIndex(item) {
32
32
  }
33
33
 
34
34
  function isProfile() {
35
- return $(volumesAttributesSelector(true,0,'id')) !== undefined;
35
+ return $(volumesAttributesSelector(true,0,'id')).val() !== undefined;
36
36
  }
37
37
 
38
38
  function controllerSelected(item) {
@@ -42,23 +42,43 @@ function controllerSelected(item) {
42
42
  var profile = isProfile();
43
43
  var device_selector = volumesAttributesSelector(profile,index,'device');
44
44
  var id_selector = volumesAttributesSelector(profile,index,'id');
45
- $(device_selector).attr('data-soft-max', max);
46
- var device = $(device_selector).limitedSpinner('value');
45
+ var device = $(device_selector).val();
47
46
  var id = controller + device;
47
+ validateDeviceLimit(device_selector, max);
48
48
  $(id_selector).val(id);
49
49
  }
50
50
 
51
51
  function deviceSelected(item) {
52
- var device = $(item).limitedSpinner('value');
52
+ var device = $(item).val();
53
53
  var index = getIndex(item);
54
54
  var profile = isProfile();
55
55
  var controller_selector = volumesAttributesSelector(profile,index,'controller');
56
56
  var id_selector = volumesAttributesSelector(profile,index,'id');
57
57
  var controller = $(controller_selector).val();
58
58
  var id = controller + device;
59
+ var max = computeControllerMaxDevice(controller);
60
+ validateDeviceLimit(item, max);
59
61
  $(id_selector).val(id);
60
62
  }
61
63
 
64
+ function validateDeviceLimit(item, maxLimit) {
65
+ var warningContainer = $(item).closest('form').find('.warning-container');
66
+ var deviceNumber = $(item).val();
67
+ var submitButton = $(item).closest('form').find('[data-disable-with]');
68
+ if (!isNaN(maxLimit) && !isNaN(deviceNumber) && deviceNumber > maxLimit) {
69
+ if (warningContainer.length === 0) {
70
+ warningContainer = $('<div class="warning-container" style="color: red;"><span style="color: red;">&#9888;</span> Warning: Value exceeds the maximum limit of ' + maxLimit + '.</div>');
71
+ $(item).closest('.col-md-4').append(warningContainer);
72
+ }
73
+ warningContainer.show();
74
+ submitButton.prop('disabled', true);
75
+ } else {
76
+ warningContainer.hide();
77
+ warningContainer.remove();
78
+ submitButton.prop('disabled', false);
79
+ }
80
+ }
81
+
62
82
  function computeControllerMaxDevice(controller) {
63
83
  switch (controller) {
64
84
  case 'ide':
@@ -72,4 +92,4 @@ function computeControllerMaxDevice(controller) {
72
92
  default:
73
93
  return 1;
74
94
  }
75
- }
95
+ }
@@ -0,0 +1,21 @@
1
+ .accordion-section {
2
+ cursor: pointer;
3
+ }
4
+
5
+ .accordion-section > legend::before {
6
+ content: "\f054";
7
+ font-family: "Font Awesome 5 Free";
8
+ position: relative;
9
+ left: -1em;
10
+ top: 50%;
11
+ transform: translateY(-50%);
12
+ }
13
+
14
+ .accordion-section.active > legend::before {
15
+ content: "\f078";
16
+ font-family: "Font Awesome 5 Free";
17
+ position: relative;
18
+ left: -1em;
19
+ top: 50%;
20
+ transform: translateY(-50%);
21
+ }
@@ -34,7 +34,7 @@ module ProxmoxComputeResourcesHelper
34
34
  def cluster_nodes(compute_resource)
35
35
  nodes = compute_resource.nodes ? compute_resource.nodes.collect(&:node) : []
36
36
  rescue ::Foreman::Exception => e
37
- return [] if e.message == 'User token expired'
37
+ [] if e.message == 'User token expired'
38
38
  rescue StandardError => e
39
39
  logger.warn("failed to get cluster nodes: #{e}")
40
40
  raise e
@@ -30,7 +30,7 @@ module ProxmoxFormHelper
30
30
  addClass options, 'form-control'
31
31
  pass = f.password_field(attr, options) +
32
32
  tag(:span, '', class: 'glyphicon glyphicon-warning-sign input-addon', title: 'Caps lock ON',
33
- style: 'display:none')
33
+ style: 'display:none')
34
34
  if unset_button
35
35
  button = link_to_function(icon_text('edit', '', :kind => 'pficon'), 'toggle_input_group(this)',
36
36
  :id => 'disable-pass-btn', :class => 'btn btn-default', :title => _('Change the password'))
@@ -56,7 +56,7 @@ style: 'display:none')
56
56
  options[:form_builder_attrs] ||= {}
57
57
 
58
58
  content_tag(:div, :class => "#{options[:type]}_#{association}_fields_template form_template",
59
- :style => 'display: none;') do
59
+ :style => 'display: none;') do
60
60
  form_builder.fields_for(association, options[:object],
61
61
  :child_index => "new_#{options[:type]}_#{association}") do |f|
62
62
  render(:partial => options[:partial], :layout => options[:layout],
@@ -0,0 +1,131 @@
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
+ require 'fog/proxmox/helpers/disk_helper'
21
+ require 'fog/proxmox/helpers/nic_helper'
22
+ require 'fog/proxmox/helpers/cpu_helper'
23
+ require 'foreman_fog_proxmox/value'
24
+ require 'foreman_fog_proxmox/hash_collection'
25
+
26
+ # Convert a foreman form server hash into a fog-proxmox server attributes hash
27
+ module ProxmoxVmAttrsHelper
28
+ def object_to_attributes_hash(vms, from_profile, start_checked)
29
+ param_scope = from_profile ? "compute_attribute[vm_attrs]" : "host[compute_attributes]"
30
+ vm_h = ActiveSupport::HashWithIndifferentAccess.new
31
+ keys = [:vmid, :node_id, :type, :pool]
32
+ main = vms.attributes.select { |key, _value| keys.include? key }
33
+ vms.config.all_attributes.each do |key, value|
34
+ camel_key = key.to_s.include?('_') ? snake_to_camel(key.to_s).to_sym : key
35
+ vm_h[camel_key] = { :name => "#{param_scope}[config_attributes][#{key}]", :value => value } unless keys.include? key
36
+ end
37
+ main.each do |key, value|
38
+ camel_key = key.to_s.include?('_') ? snake_to_camel(key.to_s).to_sym : key
39
+ vm_h[camel_key] = { :name => "#{param_scope}[#{key}]", :value => value } if keys.include? key
40
+ end
41
+
42
+ vm_h.merge!(additional_attrs(vms, param_scope, start_checked))
43
+ vm_h[:interfaces] = network_attrs(param_scope, vms.interfaces)
44
+ vm_h[:disks] = volumes_attrs(param_scope, vms.volumes)
45
+ vm_h.merge(cpu_flags_attrs(param_scope, vms.config))
46
+ end
47
+
48
+ def cpu_flags_attrs(param_scope, config)
49
+ flag_attrs = ActiveSupport::HashWithIndifferentAccess.new
50
+ Fog::Proxmox::CpuHelper.flags.each do |key, _val|
51
+ flag_attrs.merge!({ key => { :name => "#{param_scope}[config_attributes][#{key}]", :value => config.public_send(key) } })
52
+ end
53
+ flag_attrs
54
+ end
55
+
56
+ def volumes_attrs(param_scope, volumes)
57
+ vol_attrs = []
58
+ volumes.each_with_index do |vol, id|
59
+ keys = []
60
+ type = ""
61
+ if vol.rootfs?
62
+ keys = ['id', 'volid', 'storage', 'size', 'storage_type']
63
+ type = 'rootfs'
64
+ elsif vol.hard_disk?
65
+ keys = ['id', 'volid', 'storage_type', 'storage', 'controller', 'device', 'cache', 'size']
66
+ type = 'hard_disk'
67
+ elsif vol.cdrom?
68
+ keys = ['id', 'storage_type', 'cdrom', 'storage', 'volid']
69
+ type = 'cdrom'
70
+ elsif vol.cloud_init?
71
+ keys = ['id', 'volid', 'storage_type', 'storage', 'controller', 'device']
72
+ type = 'cloud_init'
73
+ elsif vol.mount_point?
74
+ keys = ['id', 'volid', 'storage_type', 'storage', 'device', 'mp', 'size']
75
+ type = 'mount_point'
76
+ end
77
+ vol_attrs << { :name => type, :value => vol_keys(param_scope, keys, vol, id) }
78
+ end
79
+ vol_attrs
80
+ end
81
+
82
+ def vol_keys(param_scope, keys, vol, id)
83
+ attrs = ActiveSupport::HashWithIndifferentAccess.new
84
+ keys.each do |key|
85
+ camel_key = key.to_s.include?('_') ? snake_to_camel(key.to_s).to_sym : key
86
+ attrs[camel_key] = { :name => "#{param_scope}[volumes_attributes][#{id}][#{key}]", :value => vol.public_send(key) }
87
+ end
88
+ attrs
89
+ end
90
+
91
+ def network_attrs(param_scope, interfaces)
92
+ networks_attrs = []
93
+ interfaces.each_with_index do |interface, id|
94
+ attrs = ActiveSupport::HashWithIndifferentAccess.new
95
+ interface.all_attributes.each do |key, value|
96
+ camel_key = key.to_s.include?('_') ? snake_to_camel(key.to_s).to_sym : key
97
+ attrs[camel_key] = { :name => "#{param_scope}[interfaces_attributes][#{id}][#{key}]", :value => value }
98
+ end
99
+ networks_attrs << { :name => 'interface', :value => attrs }
100
+ end
101
+ networks_attrs
102
+ end
103
+
104
+ def additional_attrs(vms, param_scope, start_checked)
105
+ attributes = {
106
+ pool: vms.pool,
107
+ image_id: vms.image_id,
108
+ cpu_type: vms.config.cpu_type,
109
+ nameserver: vms.config.nameserver,
110
+ searchdomain: vms.config.searchdomain,
111
+ hostname: vms.config.hostname,
112
+ ostemplate_storage: vms.ostemplate_storage,
113
+ ostemplate_file: vms.ostemplate_file,
114
+ start_after_create: vms.start_after_create,
115
+ templated: vms.templated,
116
+ }
117
+ vms_keys = [:pool, :start_after_create]
118
+ extra_attrs = ActiveSupport::HashWithIndifferentAccess.new
119
+ attributes.each do |key, value|
120
+ camel_key = key.to_s.include?('_') ? snake_to_camel(key.to_s).to_sym : key
121
+ nested_key = vms_keys.include?(key) ? key : "config_attributes[#{key}]"
122
+ value = start_checked if key == :start_after_create
123
+ extra_attrs[camel_key] = { name: "#{param_scope}[#{nested_key}]", value: value }
124
+ end
125
+ extra_attrs
126
+ end
127
+
128
+ def snake_to_camel(str)
129
+ str.split('_').inject([]) { |buffer, e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
130
+ end
131
+ end
@@ -48,7 +48,7 @@ module ProxmoxVmCloudinitHelper
48
48
  wd = create_temp_directory(ssh)
49
49
 
50
50
  configs.each do |config|
51
- config_file = ssh.run(%(echo "#{config[1]}" >> "#{wd}/#{config[0]}"))
51
+ config_file = ssh.run(%(echo '#{config[1]}' >> "#{wd}/#{config[0]}"))
52
52
  unless config_file.first.status.zero?
53
53
  delete_temp_dir(ssh, wd)
54
54
  raise ::Foreman::Exception, "Failed to create file #{config[0]}: #{config_file.first.stdout}"
@@ -56,13 +56,13 @@ module ProxmoxVmInterfacesHelper
56
56
  end
57
57
 
58
58
  def interface_common_typed_keys(type)
59
- ['id', type == 'qemu' ? 'macaddr' : 'hwaddr']
59
+ ['id', (type == 'qemu') ? 'macaddr' : 'hwaddr']
60
60
  end
61
61
 
62
62
  def compute_dhcps(interface_attributes_h)
63
- interface_attributes_h[:dhcp] = interface_attributes_h[:ip] == 'dhcp' ? '1' : '0'
63
+ interface_attributes_h[:dhcp] = (interface_attributes_h[:ip] == 'dhcp') ? '1' : '0'
64
64
  interface_attributes_h[:ip] = '' if interface_attributes_h[:dhcp] == '1'
65
- interface_attributes_h[:dhcp6] = interface_attributes_h[:ip6] == 'dhcp' ? '1' : '0'
65
+ interface_attributes_h[:dhcp6] = (interface_attributes_h[:ip6] == 'dhcp') ? '1' : '0'
66
66
  interface_attributes_h[:ip6] = '' if interface_attributes_h[:dhcp6] == '1'
67
67
  end
68
68
 
@@ -52,9 +52,9 @@ module ProxmoxVmVolumesHelper
52
52
  logger.debug(format(_('parse_hard_disk_volume(): args=%<args>s'), args: args))
53
53
  disk = {}
54
54
  disk[:id] = args['id'] if args.key?('id')
55
- disk[:volid] = args['volid'] if args.key?('volid')
56
- disk[:storage] = args['storage'].to_s if args.key?('storage')
57
- disk[:size] = args['size'].to_i if args.key?('size')
55
+ disk[:volid] = args['volid'] if args.key?('volid') && !args['volid'].empty?
56
+ disk[:storage] = args['storage'].to_s if args.key?('storage') && !args['storage'].empty?
57
+ disk[:size] = args['size'].to_i if args.key?('size') && !args['size'].empty?
58
58
  add_disk_options(disk, args) unless args.key?('options')
59
59
  disk[:options] = args['options'] if args.key?('options')
60
60
  disk.key?(:storage) ? disk : {}
@@ -31,7 +31,7 @@ module FogExtensions
31
31
  # We need to explicitly use the base 'each' method here on the page,
32
32
  # otherwise we get infinite recursion
33
33
  base_each = Fog::Collection.instance_method(:each)
34
- base_each.bind(page).call(&block)
34
+ base_each.bind_call(page, &block)
35
35
  end
36
36
  end
37
37
  self
@@ -181,7 +181,7 @@ module ForemanFogProxmox
181
181
  your server cannot connect to Proxmox due to network issues."
182
182
  end
183
183
 
184
- def host
184
+ def proxmox_host
185
185
  URI.parse(url).host
186
186
  end
187
187
  end
@@ -30,7 +30,7 @@ module ForemanFogProxmox
30
30
  unless compute_os_types(host).include?(ostype)
31
31
  raise ::Foreman::Exception,
32
32
  format(_('Operating system family %<type>s is not consistent with %<ostype>s'), type: host.operatingsystem.type,
33
- ostype: ostype)
33
+ ostype: ostype)
34
34
  end
35
35
  end
36
36
  super
@@ -42,8 +42,8 @@ ostype: ostype)
42
42
 
43
43
  def interface_compute_attributes(interface_attributes)
44
44
  vm_attrs = ForemanFogProxmox::HashCollection.new_hash_reject_keys(interface_attributes, [:identifier, :macaddr, :hwaddr])
45
- vm_attrs[:dhcp] = interface_attributes[:ip] == 'dhcp' ? '1' : '0'
46
- vm_attrs[:dhcp6] = interface_attributes[:ip6] == 'dhcp' ? '1' : '0'
45
+ vm_attrs[:dhcp] = (interface_attributes[:ip] == 'dhcp') ? '1' : '0'
46
+ vm_attrs[:dhcp6] = (interface_attributes[:ip6] == 'dhcp') ? '1' : '0'
47
47
  vm_attrs
48
48
  end
49
49
 
@@ -33,7 +33,7 @@ module ForemanFogProxmox
33
33
  vnc_console = vm.start_console(options)
34
34
  vmid = extract_vmid(uuid).to_i
35
35
  vnc_host_port = vnc_console['port'].to_i + vmid
36
- WsProxy.start(:host => host, :host_port => vnc_host_port, :password => vnc_console['ticket']).merge(
36
+ WsProxy.start(:host => proxmox_host, :host_port => vnc_host_port, :password => vnc_console['ticket']).merge(
37
37
  :name => vm.name, :type => type_console
38
38
  )
39
39
  rescue StandardError => e
@@ -48,9 +48,14 @@ module ForemanFogProxmox
48
48
  volumes = []
49
49
  nodes.each do |node|
50
50
  storages(node.node).each do |storage|
51
+ # fetches volumes of QEMU servers for images
51
52
  volumes += storage.volumes.list_by_content_type('images')
53
+
54
+ # fetches volumes of KVM containers for images
55
+ volumes += storage.volumes.list_by_content_type('rootdir')
52
56
  end
53
57
  end
58
+ # for creating image, only list volumes which are templated
54
59
  volumes.select(&:template?)
55
60
  end
56
61
 
@@ -36,7 +36,7 @@ module ForemanFogProxmox
36
36
  v = identity_client.read_version if identity_client
37
37
  v ? v['version'] : 'Unknown'
38
38
  rescue ::Foreman::Exception => e
39
- return 'Unkown' if e.message == 'User token expired'
39
+ 'Unkown' if e.message == 'User token expired'
40
40
  rescue StandardError => e
41
41
  logger.warn("failed to get identity client version: #{e}")
42
42
  raise e
@@ -32,10 +32,9 @@ module ForemanFogProxmox
32
32
  end
33
33
 
34
34
  def create_vm(args = {})
35
- vmid = args[:vmid].to_i
36
35
  type = args[:type]
37
36
  node = client.nodes.get(args[:node_id])
38
- vmid = node.servers.next_id.to_i if vmid < 1
37
+ vmid = args[:vmid] = assign_vmid(args[:vmid].to_i, node)
39
38
  raise ::Foreman::Exception, format(N_('invalid vmid=%<vmid>s'), vmid: vmid) unless node.servers.id_valid?(vmid)
40
39
 
41
40
  image_id = args[:image_id]
@@ -55,6 +54,10 @@ module ForemanFogProxmox
55
54
  raise e
56
55
  end
57
56
 
57
+ def assign_vmid(vmid, node)
58
+ (vmid < 1) ? node.servers.next_id : vmid
59
+ end
60
+
58
61
  def compute_clone_attributes(args, container, type)
59
62
  args = parse_cloudinit_config(args) if args[:user_data]
60
63
  parsed_args = parse_typed_vm(args, type)
@@ -191,7 +191,7 @@ module ForemanFogProxmox
191
191
  new_attr_type ||= type
192
192
  logger.debug("new_typed_vm(#{type}): new_attr_type=#{new_attr_type}")
193
193
  logger.debug("new_typed_vm(#{type}): new_attr=#{new_attr}'")
194
- options = !new_attr.key?('vmid') || ForemanFogProxmox::Value.empty?(new_attr['vmid']) ? vm_typed_instance_defaults(type).merge(new_attr).merge(type: type) : new_attr
194
+ options = (!new_attr.key?('vmid') || ForemanFogProxmox::Value.empty?(new_attr['vmid'])) ? vm_typed_instance_defaults(type).merge(new_attr).merge(type: type) : new_attr
195
195
  logger.debug("new_typed_vm(#{type}): options=#{options}")
196
196
  vm_h = parse_typed_vm(options, type).deep_symbolize_keys
197
197
  logger.debug("new_typed_vm(#{type}): vm_h=#{vm_h}")
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright 2018 Tristan Robert
2
4
 
3
5
  # This file is part of ForemanFogProxmox.
@@ -15,6 +17,12 @@
15
17
  # You should have received a copy of the GNU General Public License
16
18
  # along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
17
19
 
20
+ Deface::Override.new(
21
+ virtual_path: 'compute_attributes/_compute_form',
22
+ name: 'remove_networks_and_volumes_partial',
23
+ remove: "erb[loud]:contains('compute_resources_vms/form/networks'), erb[loud]:contains('compute_resources_vms/form/volumes')"
24
+ )
25
+
18
26
  Deface::Override.new(
19
27
  :virtual_path => 'compute_attributes/_form',
20
28
  :name => 'add_from_profile_to_compute_attributes_form',
@@ -0,0 +1,25 @@
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
+ Deface::Override.new(
21
+ virtual_path: 'hosts/_form',
22
+ name: 'add_react_component_to_virtual_machine_tab',
23
+ insert_after: "li.active",
24
+ partial: 'compute_resources_vms/form/proxmox/add_react_component_to_host_form'
25
+ )
@@ -0,0 +1,25 @@
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
+ Deface::Override.new(
21
+ :virtual_path => 'hosts/_compute',
22
+ :name => 'update_react_component_to_virtual_machine_tab',
23
+ :replace => "erb[loud]:contains('hosts/compute_detail')",
24
+ :partial => 'compute_resources_vms/form/proxmox/update_react_component_to_host_form'
25
+ )
@@ -0,0 +1,5 @@
1
+ <% content_for(:javascripts) do %>
2
+ <%= webpacked_plugins_js_for :foreman_fog_proxmox %>
3
+ <%= javascript_include_tag 'foreman_fog_proxmox/proxmox_vm', "data-turbolinks-track" => true %>
4
+ <% end %>
5
+ <%= react_component('ProxmoxVmType', { registerComp: true }) unless @host.managed %>