the_foreman_proxmox 0.3.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 (40) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +619 -0
  3. data/README.md +102 -0
  4. data/Rakefile +47 -0
  5. data/app/assets/javascripts/the_foreman_proxmox/proxmox.js +110 -0
  6. data/app/controllers/concerns/the_foreman_proxmox/controller/parameters/compute_resource.rb +41 -0
  7. data/app/controllers/the_foreman_proxmox/compute_resources_controller.rb +38 -0
  8. data/app/helpers/proxmox_compute_helper.rb +154 -0
  9. data/app/helpers/proxmox_compute_selectors_helper.rb +148 -0
  10. data/app/models/concerns/fog_extensions/proxmox/disk.rb +29 -0
  11. data/app/models/concerns/fog_extensions/proxmox/server.rb +82 -0
  12. data/app/models/concerns/fog_extensions/proxmox/server_config.rb +49 -0
  13. data/app/models/concerns/fog_extensions/proxmox/volume.rb +29 -0
  14. data/app/models/the_foreman_proxmox/options_select.rb +36 -0
  15. data/app/models/the_foreman_proxmox/proxmox.rb +394 -0
  16. data/app/views/api/v2/compute_resources/proxmox.json.rabl +1 -0
  17. data/app/views/compute_resources/form/_proxmox.html.erb +24 -0
  18. data/app/views/compute_resources/show/_proxmox.html.erb +21 -0
  19. data/app/views/compute_resources_vms/form/proxmox/_base.html.erb +45 -0
  20. data/app/views/compute_resources_vms/form/proxmox/_config.html.erb +55 -0
  21. data/app/views/compute_resources_vms/form/proxmox/_network.html.erb +24 -0
  22. data/app/views/compute_resources_vms/form/proxmox/_volume.html.erb +23 -0
  23. data/app/views/compute_resources_vms/index/_proxmox.html.erb +45 -0
  24. data/app/views/compute_resources_vms/show/_proxmox.html.erb +36 -0
  25. data/app/views/dashboard/_the_foreman_proxmox_widget.erb +19 -0
  26. data/app/views/images/form/_proxmox.html.erb +4 -0
  27. data/config/routes.rb +24 -0
  28. data/lib/tasks/the_foreman_proxmox_tasks.rake +47 -0
  29. data/lib/the_foreman_proxmox.rb +23 -0
  30. data/lib/the_foreman_proxmox/engine.rb +92 -0
  31. data/lib/the_foreman_proxmox/version.rb +22 -0
  32. data/locale/Makefile +60 -0
  33. data/locale/en/foreman_proxmox.po +19 -0
  34. data/locale/gemspec.rb +2 -0
  35. data/locale/the_foreman_proxmox.pot +19 -0
  36. data/test/factories/the_foreman_proxmox_factories.rb +33 -0
  37. data/test/helpers/proxmox_compute_helper_test.rb +130 -0
  38. data/test/test_plugin_helper.rb +15 -0
  39. data/test/unit/the_foreman_proxmox_test.rb +11 -0
  40. metadata +143 -0
@@ -0,0 +1,102 @@
1
+ ![ForemanProxmox](.github/images/foremanproxmox.png)
2
+
3
+ [![Build Status](https://travis-ci.com/tristanrobert/foreman_proxmox.svg?branch=master)](https://travis-ci.com/tristanrobert/foreman_proxmox)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/922162c278e0fa9207ba/maintainability)](https://codeclimate.com/github/tristanrobert/foreman_proxmox/maintainability)
5
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/922162c278e0fa9207ba/test_coverage)](https://codeclimate.com/github/tristanrobert/foreman_proxmox/test_coverage)
6
+
7
+ # ForemanProxmox
8
+
9
+ [Foreman](http://theforeman.org/) plugin that adds [Proxmox](https://www.proxmox.com/en/proxmox-ve) compute resource: managing virtual machines and containers using the [fog-proxmox](https://github.com/fog/fog-proxmox) module.
10
+
11
+ It is intended to satisfy this [feature](http://projects.theforeman.org/issues/2186).
12
+
13
+ ## Compatibility
14
+
15
+ Tested with:
16
+
17
+ * Foreman = 1.17.1
18
+ * Fog-proxmox >= 0.4.0
19
+
20
+ ## Installation
21
+
22
+ ### Gem
23
+
24
+ ```shell
25
+ gem install the_foreman_proxmox
26
+ ```
27
+
28
+ ### Packages
29
+
30
+ Deb, yum, rpm: work in progress...
31
+
32
+ Please see the Foreman manual for appropriate instructions:
33
+
34
+ * [Foreman: How to Install a Plugin](http://theforeman.org/manuals/latest/index.html#6.1InstallaPlugin)
35
+
36
+ ## Usage
37
+
38
+ * [Compute resource](.github/compute_resource.md)
39
+
40
+ ![Create host](.github/images/create_host.png)
41
+ ![List hosts](.github/images/hosts.png)
42
+ ![Show host](.github/images/show_host.png)
43
+ ![VNC Console](.github/images/vnc_console.png)
44
+ ![VNC Console 2](.github/images/vnc_console2.png)
45
+
46
+ ## Development
47
+
48
+ You need a Proxmox VE >= 5.1 server running.
49
+
50
+ * Fork this github repo.
51
+ * Clone it on your local machine
52
+
53
+ To install the plugin with foreman in a Docker container, move to the source code:
54
+
55
+ ```shell
56
+ cd foreman_proxmox
57
+ ```
58
+
59
+ Build a docker container:
60
+
61
+ ```shell
62
+ sudo docker build -t foreman .
63
+ ```
64
+
65
+ If you are behind a proxy http server, add:
66
+
67
+ ```shell
68
+ --build-arg http_proxy=http://<user>:<password>@<ip>:<port>
69
+ ...
70
+ ```
71
+
72
+ Run it:
73
+
74
+ ```shell
75
+ sudo docker run -it -p 3808:5000 --name foreman foreman
76
+ ```
77
+
78
+ Access container's bash console:
79
+
80
+ ```shell
81
+ sudo docker exec -it foreman bash
82
+ ```
83
+
84
+ The docker container use ruby 2.3.7, latest nodejs 8.x (8.11.2) and foreman 1.17.1.
85
+
86
+ If you want to debug live. You have to clone foreman 1.17.1 repo and install foreman_proxmox plugin as gem.
87
+ See [how to create a foreman plugin](https://projects.theforeman.org/projects/foreman/wiki/How_to_Create_a_Plugin)
88
+
89
+ ## Contributing
90
+
91
+ You can reach the [contributors](CONTRIBUTORS.md).
92
+ Bug reports and pull requests are welcome on GitHub at [ForemanProxmox](https://github.com/tristanrobert/foreman_proxmox). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
93
+
94
+ Please read [how to contribute](CONTRIBUTING.md).
95
+
96
+ ## License
97
+
98
+ The code is available as open source under the terms of the [GNU Public License v3](LICENSE).
99
+
100
+ ## Code of Conduct
101
+
102
+ Everyone interacting in the ForemanProxmox project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ForemanProxmox'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+ task default: :test
37
+
38
+ begin
39
+ require 'rubocop/rake_task'
40
+ RuboCop::RakeTask.new
41
+ rescue StandardError => _
42
+ puts 'Rubocop not loaded.'
43
+ end
44
+
45
+ task :default do
46
+ Rake::Task['rubocop'].execute
47
+ end
@@ -0,0 +1,110 @@
1
+ // Copyright 2018 Tristan Robert
2
+
3
+ // This file is part of TheForemanProxmox.
4
+
5
+ // TheForemanProxmox is free software: you can redistribute it and/or modify
6
+ // it under the terms of the GNU General Public License as published by
7
+ // the Free Software Foundation, either version 3 of the License, or
8
+ // (at your option) any later version.
9
+
10
+ // TheForemanProxmox is distributed in the hope that it will be useful,
11
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ // GNU General Public License for more details.
14
+
15
+ // You should have received a copy of the GNU General Public License
16
+ // along with TheForemanProxmox. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ function cdromSelected(item) {
19
+ var selected = $(item).val();
20
+ var cdrom_image_form = $('#cdrom_image_form');
21
+
22
+ switch (selected) {
23
+ case 'none':
24
+ initStorage();
25
+ initOptions('iso');
26
+ cdrom_image_form.hide();
27
+ break;
28
+ case 'cdrom':
29
+ initStorage();
30
+ initOptions('iso');
31
+ cdrom_image_form.hide();
32
+ break;
33
+ case 'image':
34
+ initStorage();
35
+ initOptions('iso');
36
+ cdrom_image_form.show();
37
+ break;
38
+ default:
39
+ break;
40
+ }
41
+ return false;
42
+ }
43
+
44
+ function initStorage(){
45
+ var select = '#host_compute_attributes_config_attributes_cdrom_storage';
46
+ $(select + ' option:selected').prop('selected',false);
47
+ $(select).val('');
48
+ }
49
+
50
+ function initOptions(name){
51
+ var select = '#host_compute_attributes_config_attributes_cdrom_'+name;
52
+ $(select).empty();
53
+ $(select).append($("<option></option>").val('').text(''));
54
+ $(select).val('');
55
+ }
56
+
57
+ function storageIsoSelected(item) {
58
+ var storage = $(item).val();
59
+ if (storage != '') {
60
+ tfm.tools.showSpinner();
61
+ $.getJSON({
62
+ type: 'get',
63
+ url: '/foreman_proxmox/isos/'+storage,
64
+ complete: function(){
65
+ tfm.tools.hideSpinner();
66
+ },
67
+ error: function(j,status,error){
68
+ console.log("Error=" + error +", status=" + status + " loading isos for storage=" + storage);
69
+ },
70
+ success: function(isos) {
71
+ initOptions('iso');
72
+ $.each(isos, function(i,iso){
73
+ $('#host_compute_attributes_config_attributes_cdrom_iso').append($("<option></option>").val(iso.volid).text(iso.volid));
74
+ });
75
+ }
76
+ });
77
+ } else {
78
+ initOptions('iso');
79
+ }
80
+ }
81
+
82
+ function controllerSelected(item){
83
+ var controller = $(item).val();
84
+ var id = $(item).attr('id');
85
+ var pattern = /(\w+)(\d+)(\w+)/i;
86
+ var index = pattern.exec(id)[2];
87
+ var max = computeControllerMaxDevice(controller);
88
+ $('#host_compute_attributes_volumes_attributes_' + index + '_device').attr('data-soft-max',max);
89
+ tfm.numFields.initAll();
90
+ }
91
+
92
+ function computeControllerMaxDevice(controller){
93
+ switch (controller) {
94
+ case 'ide':
95
+ return 3;
96
+ break;
97
+ case 'sata':
98
+ return 5;
99
+ break;
100
+ case 'scsi':
101
+ return 13;
102
+ break;
103
+ case 'virtio':
104
+ return 15;
105
+ break;
106
+ default:
107
+ return 1;
108
+ break;
109
+ }
110
+ }
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2018 Tristan Robert
4
+
5
+ # This file is part of TheForemanProxmox.
6
+
7
+ # TheForemanProxmox 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
+ # TheForemanProxmox 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 TheForemanProxmox. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ module TheForemanProxmox
21
+ module Controller
22
+ module Parameters
23
+ module ComputeResource
24
+ extend ActiveSupport::Concern
25
+
26
+ class_methods do
27
+ def compute_resource_params_filter
28
+ super.tap do |filter|
29
+ filter.permit :ssl_verify_peer,
30
+ :ssl_certs, :disable_proxy
31
+ end
32
+ end
33
+
34
+ def compute_resource_params
35
+ self.class.compute_resource_params_filter.filter_params(params, parameter_filter_context)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2018 Tristan Robert
4
+
5
+ # This file is part of TheForemanProxmox.
6
+
7
+ # TheForemanProxmox 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
+ # TheForemanProxmox 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 TheForemanProxmox. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ module TheForemanProxmox
21
+ class ComputeResourcesController < ::ApplicationController
22
+ before_action :load_compute_resource
23
+
24
+ # GET the_foreman_proxmox/isos/:storage
25
+ def isos
26
+ volumes = @compute_resource.isos(params[:storage])
27
+ respond_to do |format|
28
+ format.json { render :json => volumes }
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def load_compute_resource
35
+ @compute_resource = ComputeResource.find_by(type: 'TheForemanProxmox::Proxmox')
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2018 Tristan Robert
4
+
5
+ # This file is part of TheForemanProxmox.
6
+
7
+ # TheForemanProxmox 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
+ # TheForemanProxmox 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 TheForemanProxmox. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ require 'fog/proxmox/helpers/disk_helper'
21
+ require 'fog/proxmox/helpers/nic_helper'
22
+
23
+ module ProxmoxComputeHelper
24
+
25
+ KILO = 1024
26
+ MEGA = KILO * KILO
27
+ GIGA = KILO * MEGA
28
+
29
+ def parse_vm(args)
30
+ return {} unless args
31
+ return {} if args.empty?
32
+ config = args['config_attributes']
33
+ cdrom_a = %w[cdrom cdrom_storage cdrom_iso]
34
+ cdrom = parse_cdrom(config.select { |key,_value| cdrom_a.include? key })
35
+ volumes = parse_volumes(args['volumes_attributes'])
36
+ cpu_a = %w[cpu_type spectre pcid vcpus cpulimit cpuunits cores sockets numa]
37
+ cpu = parse_cpu(config.select { |key,_value| cpu_a.include? key })
38
+ memory_a = %w[memory min_memory balloon shares]
39
+ memory = parse_memory(config.select { |key,_value| memory_a.include? key })
40
+ interfaces_attributes = args['interfaces_attributes']
41
+ networks = parse_interfaces(interfaces_attributes)
42
+ general_a = %w[node config_attributes volumes_attributes interfaces_attributes firmware_type provision_method]
43
+ logger.debug("general_a: #{general_a}")
44
+ parsed_vm = args.reject { |key,value| general_a.include?(key) || value.empty? }
45
+ config_a = []
46
+ config_a += cpu_a
47
+ config_a += cdrom_a
48
+ config_a += memory_a
49
+ parsed_config = config.reject { |key,value| config_a.include?(key) || value.empty? }
50
+ logger.debug("parse_config(): #{parsed_config}")
51
+ parsed_vm = parsed_vm.merge(parsed_config).merge(cpu).merge(memory).merge(cdrom)
52
+ networks.each { |network| parsed_vm = parsed_vm.merge(network) }
53
+ volumes.each { |volume| parsed_vm = parsed_vm.merge(volume) }
54
+ logger.debug("parse_vm(): #{parsed_vm}")
55
+ parsed_vm
56
+ end
57
+
58
+ def parse_memory(args)
59
+ memory = { memory: args['memory'].to_i }
60
+ ballooned = args['balloon'].to_i == 1
61
+ if ballooned
62
+ memory.store(:shares,args['shares'].to_i)
63
+ memory.store(:balloon,args['min_memory'].to_i)
64
+ else
65
+ memory.store(:balloon,args['balloon'].to_i)
66
+ end
67
+ logger.debug("parse_memory(): #{memory}")
68
+ memory
69
+ end
70
+
71
+ def parse_cpu(args)
72
+ cpu = "cputype=#{args['cpu_type']}"
73
+ spectre = args['spectre'].to_i == 1
74
+ pcid = args['pcid'].to_i == 1
75
+ cpu += ",flags=" if spectre || pcid
76
+ cpu += "+spec-ctrl" if spectre
77
+ cpu += ";" if spectre && pcid
78
+ cpu += "+pcid" if pcid
79
+ args.delete_if { |key,value| %w[cpu_type spectre pcid].include?(key) || value.empty? }
80
+ args.each_value { |value| value.to_i }
81
+ parsed_cpu = { cpu: cpu }.merge(args)
82
+ logger.debug("parse_cpu(): #{parsed_cpu}")
83
+ parsed_cpu
84
+ end
85
+
86
+ def parse_cdrom(args)
87
+ cdrom = args['cdrom']
88
+ cdrom_image = args['cdrom_iso']
89
+ volid = cdrom_image.empty? ? cdrom : cdrom_image
90
+ cdrom = "#{volid},media=cdrom"
91
+ {ide2: cdrom}
92
+ end
93
+
94
+ def parse_volume(args)
95
+ disk = {}
96
+ id = args['id']
97
+ id = "#{args['controller']}#{args['device']}" unless id
98
+ delete = args['_delete'].to_i == 1
99
+ args.delete_if { |_key,value| value.empty? }
100
+ if delete
101
+ logger.debug("parse_volume(): delete id=#{id}")
102
+ disk.store(:delete, id)
103
+ disk
104
+ else
105
+ disk.store(:id, id)
106
+ disk.store(:volid, args['volid'])
107
+ disk.store(:storage, args['storage'].to_s)
108
+ disk.store(:size, args['size'])
109
+ options = args.reject { |key,_value| %w[id volid controller device storage size _delete].include? key}
110
+ disk.store(:options, options)
111
+ logger.debug("parse_volume(): add disk=#{disk}")
112
+ Fog::Proxmox::DiskHelper.flatten(disk)
113
+ end
114
+ end
115
+
116
+ def parse_volumes(args)
117
+ volumes = []
118
+ args.each_value { |value| volumes.push(parse_volume(value))} if args
119
+ logger.debug("parse_volumes(): volumes=#{volumes}")
120
+ volumes
121
+ end
122
+
123
+ def parse_interfaces(args)
124
+ nics = []
125
+ args.each_value { |value| nics.push(parse_interface(value))} if args
126
+ logger.debug("parse_interfaces(): nics=#{nics}")
127
+ nics
128
+ end
129
+
130
+ def parse_interface(args)
131
+ args.delete_if { |_key,value| value.empty? }
132
+ nic = {}
133
+ id = args['id']
134
+ logger.debug("parse_interface(): id=#{id}")
135
+ delete = args['_delete'].to_i == 1
136
+ if delete
137
+ logger.debug("parse_interface(): delete id=#{id}")
138
+ nic.store(:delete, id)
139
+ nic
140
+ else
141
+ nic.store(:id, id)
142
+ nic.store(:tag, args['vlan'].to_i) if args['vlan']
143
+ nic.store(:model, args['model'].to_s)
144
+ nic.store(:bridge, args['bridge'].to_s) if args['bridge']
145
+ nic.store(:firewall, args['firewall'].to_i) if args['firewall']
146
+ nic.store(:rate, args['rate'].to_i) if args['rate']
147
+ nic.store(:link_down, args['disconnect'].to_i) if args['disconnect']
148
+ nic.store(:queues, args['queues'].to_i) if args['queues']
149
+ logger.debug("parse_interface(): add nic=#{nic}")
150
+ Fog::Proxmox::NicHelper.flatten(nic)
151
+ end
152
+ end
153
+
154
+ end