foreman_gridscale 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # ForemanGridscale
2
+
3
+ *Plugin to enable management of gridscale instances from within foreman*
4
+
5
+ ## Requirements
6
+ Foreman gridscale has the following requirements:
7
+
8
+ * Foreman 1.15+
9
+ * A working internet connection
10
+
11
+ ## Installation
12
+
13
+ 1. Extract the supplied archive on the server which has Foreman installed on it. Make sure to do this in a directory which can be accessed by the user running Foreman.
14
+
15
+ 2. In the foreman/bundler.d directory (usually found in /usr/share/), create or edit the file Gemfile.local.rb and add the following lines to it:
16
+
17
+ ```ruby
18
+ gem 'fog-gridscale', :path => 'path to fog-gridscale directory'
19
+ gem 'foreman_gridscale', :path => 'path to foreman-gridscale directory'
20
+ ```
21
+
22
+ 3. Next run the following command:
23
+
24
+ ```
25
+ $ cd /usr/share/foreman && sudo -u foreman /usr/bin/foreman-ruby /usr/bin/bundle install
26
+ ```
27
+
28
+ 4. Then restart Foreman with:
29
+ ```
30
+ $ touch /usr/share/foreman/tmp/restart.txt
31
+ ```
32
+
33
+ See [how to install Foreman plugins](http://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Plugin) to learn more about plugins.
34
+
35
+
36
+ ## How to Use
37
+ ### Configuration
38
+
39
+ Go to **Infrastructure > Compute Resources** and click on "New Compute Resource".
40
+
41
+ Choose **gridscale** as the provider, and fill in all the fields. You need your API token and user uuid with read and write access, which can be created in the [gridscale API section](https://my.gridscale.io/APIs/).
42
+
43
+ That's it. You're now ready to create and manage servers in your new gridscale compute resource.
44
+
45
+ You should see something like this on the [Compute Resource](https://theforeman.org/manuals/1.19/index.html#5.2ComputeResources) page:
46
+
47
+ ### Host Creation
48
+ 1. Go to host > create host and choose gridscale as a deployment target. The virtual machine tab with all of the parameter fields to create a server will appear
49
+ 2. If you already set up your compute profile, you can choose it and it will automatically set up the virtual machine parameters. you can also overwrite them
50
+ 3. On the virtual machine tab, fill in the fields with the desired number cores, memory, and capacity of the storage. You can also attach a gridscale or private template to your storage
51
+ 4. Fill the operating system tab
52
+ 5. In the interface tab, click the edit button in actions column, and pick the gridscale network interface you wish to connect your machine to. You can also add multiple network interfaces by clicking the add interface button below the interface table. click OK once you have finished
53
+ 6. Click submit button to finish the Host creation
54
+
55
+ ### Compute Profile
56
+ 1. Click infrastructure > compute profile
57
+ 2. Click create compute profile button on the top right side of the screen
58
+ 3. Fill in the name field, and click the submit button
59
+ 4. Click on the compute resource link that you want to associate with your compute profile
60
+ 5. Fill out the compute profile attributes and click the submit button
61
+ 6. You are now ready to use your compute profile
62
+
63
+ ### Host Management
64
+ Click Host > All Host. You will be able to see all of the hosts that were created through foreman in the tables. The Power column shows the power status of the server. The host’s details can be seen by clicking the link in its name.
65
+
66
+ **Powering off/on**
67
+ 1. Choose the host by checking the box in the first column of the table (you can select all of the hosts by checking the box in the header)
68
+ 2. Click the action button, and choose change power state
69
+ 3. Select the desired power state from the dropdown power list
70
+ 4. Click submit
71
+
72
+ **Delete Host**
73
+ 1. Choose the host by checking the box in the first column of the table (you can select all of the hosts by checking the box on the header)
74
+ 2. Click the action button, and choose delete host (you can also delete them individually by clicking the dropdown list in the actions column)
75
+ 3. Click submit and confirm the action
76
+
77
+
78
+ ###Host management through the compute resource page
79
+ Go to Infrastructure > Compute resource and select the compute resource for gridscale. go to the virtual machine tab. The virtual machine tab displays all of the servers in the gridscale panel.
80
+
81
+ ##Known Issues
82
+ * When creating a host, multiple network interfaces can be set to bootable. The gridscale platform does not support this, which is why only one of the interfaces will actually be set to bootable if this is the case
83
+ * When creating a compute profile, the chosen network interface configuration is not saved
84
+ * The Virtual Machines overview of a compute resource can take a long time to load
85
+ * The data shown in the VM tab of a host is not complete. More input about which information is useful is needed
86
+ * Opening the console of a host in Foreman has not been implemented, but a link to gridscale is supplied
87
+ * Acpi power off fails to shut down a system which did not boot
88
+ * MAC, IP4 and IP6 information is in the VM tab, not Interface
89
+
data/Rakefile ADDED
@@ -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 = 'ForemanGridscale'
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', __FILE__)
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 => _
42
+ puts 'Rubocop not loaded.'
43
+ end
44
+
45
+ task :default do
46
+ Rake::Task['rubocop'].execute
47
+ end
@@ -0,0 +1,128 @@
1
+ module GridscaleImagesHelper
2
+ def gridscale_server_field(f)
3
+ images = @compute_resource.available_servers
4
+ images.each { |image| image.name = image.id if image.name.nil? }
5
+ select_f f, :uuid, images.to_a.sort_by(&:full_name),
6
+ :id, :full_name, {}, :label => _('Image')
7
+ end
8
+
9
+ def select_ipv4(f, compute_resource)
10
+ addresses = Array.new
11
+ compute_resource.ips.each do |ip|
12
+ if ip.relations['servers'].empty? and ip.relations['loadbalancers'].empty? and ip.family ==4
13
+ addresses << ip
14
+ end
15
+ end
16
+ if addresses.empty?
17
+ "No IPs available"
18
+ else
19
+ select_f(f,
20
+ :ipv4_uuid,
21
+ addresses,
22
+ :object_uuid,
23
+ :ip,
24
+ { :include_blank => true },
25
+ { :label => 'IP Address 4'})
26
+ end
27
+ end
28
+
29
+ def select_ipv6(f, compute_resource)
30
+ addresses = Array.new
31
+ compute_resource.ips.each do |ip|
32
+ if ip.relations['servers'].empty? and ip.relations['loadbalancers'].empty? and ip.family ==6
33
+ addresses << ip
34
+ end
35
+ end
36
+ if addresses.empty?
37
+ "No IPs available"
38
+ else
39
+ select_f(f,
40
+ :ipv6_uuid,
41
+ addresses,
42
+ :object_uuid,
43
+ :ip,
44
+ { :include_blank => true },
45
+ { :label => 'IP Address 6'})
46
+ end
47
+ end
48
+
49
+ def select_network(f, compute_resource)
50
+ networks_list = Array.new
51
+ compute_resource.networks.each do |network|
52
+ networks_list << network
53
+ end
54
+
55
+ select_f(f,
56
+ :network_uuid,
57
+ networks_list,
58
+ :object_uuid,
59
+ :name,
60
+ { :include_blank => true },
61
+ { :label => 'Network'})
62
+
63
+ end
64
+
65
+
66
+
67
+ def select_storage(f, compute_resource)
68
+
69
+ storages_list = Array.new
70
+
71
+ compute_resource.storages.each do |storage|
72
+ if storage.relations['servers'].empty?
73
+ storages_list << storage
74
+ end
75
+ end
76
+
77
+ if storages_list.empty?
78
+ "No storage available"
79
+ else
80
+ select_f(f,
81
+ :storage_uuid,
82
+ storages_list,
83
+ :object_uuid,
84
+ :name,
85
+ { :include_blank => true },
86
+ { :label => 'available storage'})
87
+ end
88
+ end
89
+
90
+ def select_template(f, compute_resource)
91
+ template_list = Array.new
92
+ compute_resource.templates.each do |template|
93
+ template_list << template
94
+ end
95
+
96
+ select_f(f,
97
+ :template_uuid,
98
+ template_list,
99
+ :object_uuid,
100
+ :name,
101
+ { :include_blank => true },
102
+ { :label => 'Template'})
103
+
104
+ end
105
+
106
+ def select_sshkey(f, compute_resource)
107
+ template_list = Array.new
108
+ compute_resource.sshkeys.each do |template|
109
+ template_list << template
110
+ end
111
+
112
+ select_f(f,
113
+ :sshkey_uuid,
114
+ template_list,
115
+ :object_uuid,
116
+ :name,
117
+ { :include_blank => true },
118
+ { :label => 'SSH Key'})
119
+
120
+ end
121
+
122
+ def gridscale_image_field(f)
123
+ images = @compute_resource.available_templates
124
+ images.each { |image| image.name = image.object_uuid if image.name.nil? }
125
+ select_f f, :object_uuid, images.to_a,
126
+ :object_uuid, :name, {}, :label => _('Image')
127
+ end
128
+ end
@@ -0,0 +1,63 @@
1
+ module FogExtensions
2
+ module Gridscale
3
+ module Server
4
+ extend ActiveSupport::Concern
5
+
6
+ attr_accessor :object_uuid, :mac, :server_uuid, :interfaces_attributes, :ipv4_address, :ipv6_address
7
+
8
+ def state
9
+ requires :status
10
+ @state ||= status
11
+ end
12
+
13
+ def to_s
14
+ name
15
+ end
16
+
17
+ def reset
18
+ reboot
19
+ end
20
+
21
+ def vm_description
22
+ format(_('%{cpus} CPUs and %{ram} memory'), :cpus => cores, :ram => memory)
23
+ end
24
+
25
+ def ip_addresses
26
+ [ipv4_address, ipv6_address].flatten.select(&:present?)
27
+
28
+ end
29
+
30
+ def ip4_add_in
31
+ x = nil
32
+ if interfaces_attributes != nil
33
+ interfaces_attributes.each do |key, value|
34
+
35
+ if value["ipv4_uuid"] !=nil && value["ipv4_uuid"] != ""
36
+ x = service.ips.get(value["ipv4_uuid"])
37
+ end
38
+ end
39
+ end
40
+ x.ip
41
+ end
42
+
43
+ def ip6_add_in
44
+ x = nil
45
+ if interfaces_attributes != nil
46
+ interfaces_attributes.each do |key, value|
47
+
48
+ if value["ipv6_uuid"] !=nil && value["ipv6_uuid"] != ""
49
+ x = service.ips.get(value["ipv6_uuid"])
50
+ end
51
+ end
52
+ end
53
+ x.ip
54
+ end
55
+
56
+ def mac_addr
57
+ :mac
58
+ end
59
+
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,16 @@
1
+ module ForemanGridscale
2
+ module Concerns
3
+ module HostManagedExtensions
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ # Rails 4 does not provide dynamic finders for delegated methods and
8
+ # the SSH orchestrate compute method uses them.
9
+ def self.find_by_ip(ip)
10
+ nic = Nic::Base.find_by_ip(ip)
11
+ nic.host if nic.present?
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,196 @@
1
+ module ForemanGridscale
2
+ class Gridscale < ComputeResource
3
+ alias_attribute :api_token, :password
4
+ alias_attribute :user_uuid, :user
5
+ alias_attribute :object_uuid, :uuid
6
+
7
+ has_one :key_pair, :foreign_key => :compute_resource_id, :dependent => :destroy
8
+ delegate :to => :client
9
+
10
+ validates :api_token, :user_uuid, :presence => true
11
+ before_create :test_connection
12
+
13
+
14
+ def api_token
15
+ attrs[:api_token]
16
+ end
17
+
18
+ def user_uuid
19
+ attrs[:user_uuid]
20
+ end
21
+
22
+ def api_token=(api_token)
23
+ attrs[:api_token] = api_token
24
+ end
25
+
26
+ def user_uuid=(user_uuid)
27
+ attrs[:user_uuid] = user_uuid
28
+ end
29
+
30
+ def to_label
31
+ "#{name} (#{provider_friendly_name})"
32
+ end
33
+
34
+ def provided_attributes
35
+ super.merge({})
36
+ end
37
+
38
+ def get_ip(ipaddr_uuid)
39
+ client.ips.get(ipaddr_uuid).ip
40
+ end
41
+
42
+ def capabilities
43
+ [:build, :images]
44
+ end
45
+
46
+ def associated_host(vm)
47
+ associate_by('ip', [vm.ipv4_address])
48
+ end
49
+
50
+ def create_vm(args = {})
51
+ args['cores'] = args['cores'].to_i
52
+ args['memory'] = args['memory'].to_i
53
+ args['storage'] = args['storage'].to_i
54
+
55
+ super(args)
56
+ rescue Fog::Errors::Error => e
57
+ logger.error "Unhandled gridscale error: #{e.status}:#{e.message}\n " + e.backtrace.join("\n ")
58
+ raise e
59
+ end
60
+
61
+ def new_interface(attr = {})
62
+ client.interfaces.new attr
63
+ end
64
+
65
+ def power_check(uuid)
66
+ client.server_power_get(uuid).body['power']
67
+ end
68
+
69
+ def power_off(uuid)
70
+ client.server_power_off(uuid) if power_check(uuid)
71
+ end
72
+
73
+ def destroy_vm(uuid)
74
+ attached_storage = []
75
+
76
+ client.servers.get(uuid).relations['storages'].each do |storage|
77
+ attached_storage << storage['object_uuid']
78
+ end
79
+
80
+ if power_check(uuid)
81
+ client.server_power_off(uuid)
82
+ end
83
+ sleep(1) until client.servers.get(uuid).status != "in-provisioning"
84
+ find_vm_by_uuid(uuid).destroy
85
+
86
+ attached_storage.each do |storage_uuid|
87
+ client.storages.destroy(storage_uuid)
88
+ end
89
+ rescue ActiveRecord::RecordNotFound
90
+ # if the VM does not exists, we don't really care.
91
+ true
92
+ end
93
+
94
+ def save_vm(uuid, attr)
95
+ vm = find_vm_by_uuid(uuid)
96
+ vm.attributes.merge!(attr.symbolize_keys).deep_symbolize_keys
97
+ update_interfaces(vm, attr[:interfaces_attributes])
98
+ vm.save
99
+ end
100
+
101
+ def ips
102
+ client.ips
103
+ end
104
+
105
+ def interfaces
106
+ client.interfaces rescue []
107
+ end
108
+
109
+ def networks
110
+ client.networks rescue []
111
+ end
112
+
113
+ def network
114
+ client.networks.get(network_uuid)
115
+ end
116
+
117
+ def storages
118
+ client.storages
119
+ end
120
+
121
+ def templates
122
+ client.templates
123
+ end
124
+
125
+ def sshkeys
126
+ client.sshkeys
127
+ end
128
+
129
+ def available_templates
130
+ images = []
131
+ collection = client.templates
132
+ begin
133
+ images += collection.to_a
134
+ end until !collection.next_page
135
+ images
136
+ end
137
+
138
+ def self.model_name
139
+ ComputeResource.model_name
140
+ end
141
+
142
+ def find_vm_by_uuid(uuid)
143
+ client.servers.get(uuid)
144
+ rescue Fog::Compute::Gridscale::Error
145
+ raise(ActiveRecord::RecordNotFound)
146
+ end
147
+
148
+ def test_connection(options = {})
149
+ super
150
+ errors[:token].empty? && errors[:uuid].empty? && networks.count
151
+ rescue Excon::Errors::Unauthorized => e
152
+ errors[:base] << e.response.body
153
+ rescue Fog::Errors::Error => e
154
+ errors[:base] << e.message
155
+ end
156
+
157
+ def default_region_name
158
+ @default_region_name ||= 'de/fra'
159
+ rescue Excon::Errors::Unauthorized => e
160
+ errors[:base] << e.response.body
161
+ end
162
+
163
+ def self.provider_friendly_name
164
+ 'gridscale'
165
+ end
166
+
167
+ def user_data_supported?
168
+ true
169
+ end
170
+
171
+ private
172
+
173
+ def client
174
+ @client ||= Fog::Compute.new(
175
+ :provider => 'gridscale',
176
+ :api_token => api_token,
177
+ :user_uuid => user_uuid
178
+ )
179
+ end
180
+
181
+ def vm_instance_defaults
182
+ super.merge(
183
+ :location_uuid => '45ed677b-3702-4b36-be2a-a2eab9827950'
184
+ )
185
+ end
186
+
187
+ def default_iface_name(interfaces)
188
+ nic_name_num = 1
189
+ name_blacklist = interfaces.map{ |i| i[:name]}.reject{|n| n.blank?}
190
+ nic_name_num += 1 while name_blacklist.include?("nic#{nic_name_num}")
191
+ "nic#{nic_name_num}"
192
+ end
193
+
194
+
195
+ end
196
+ end