the_foreman_proxmox 0.3.0

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 +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