foreman_xen 0.0.1
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.
- checksums.yaml +7 -0
- data/LICENSE +619 -0
- data/README.md +40 -0
- data/Rakefile +7 -0
- data/app/models/concerns/fog_extensions/xenserver/server.rb +51 -0
- data/app/models/foreman_xen/xenserver.rb +288 -0
- data/app/views/compute_resources/form/_xenserver.html.erb +11 -0
- data/app/views/compute_resources/show/_xenserver.html.erb +4 -0
- data/app/views/compute_resources_vms/form/_network.html.erb +16 -0
- data/app/views/compute_resources_vms/form/_templates.html.erb +30 -0
- data/app/views/compute_resources_vms/form/_volume.html.erb +12 -0
- data/app/views/compute_resources_vms/form/_xenserver.html.erb +74 -0
- data/app/views/compute_resources_vms/form/_xenstore.html.erb +128 -0
- data/app/views/compute_resources_vms/index/_xenserver.html.erb +23 -0
- data/app/views/compute_resources_vms/show/_xenserver.html.erb +17 -0
- data/lib/foreman_xen.rb +3 -0
- data/lib/foreman_xen/engine.rb +32 -0
- data/lib/foreman_xen/version.rb +3 -0
- data/lib/foreman_xen/vnc_tunnel.rb +90 -0
- data/lib/tasks/foreman_xen_tasks.rake +4 -0
- data/locale/Makefile +6 -0
- data/test/foreman_xen_test.rb +7 -0
- data/test/test_helper.rb +15 -0
- metadata +96 -0
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Foreman XEN Plugin
|
2
|
+
|
3
|
+
This plugin enables provisioning and managing XEN Server in Foreman.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Please see the Foreman manual for appropriate instructions:
|
8
|
+
|
9
|
+
* [Foreman: How to Install a Plugin](http://theforeman.org/manuals/latest/index.html#6.1InstallaPlugin)
|
10
|
+
|
11
|
+
The gem name is "foreman_xen".
|
12
|
+
|
13
|
+
## Compatibility
|
14
|
+
|
15
|
+
| Foreman Version | Plugin Version |
|
16
|
+
| ---------------:| --------------:|
|
17
|
+
| >= 1.5 | 0.0.1 |
|
18
|
+
|
19
|
+
## Latest code
|
20
|
+
|
21
|
+
You can get the develop branch of the plugin by specifying your Gemfile in this way:
|
22
|
+
|
23
|
+
gem 'foreman_xen', :git => "https://github.com/ohadlevy/foreman_xen.git"
|
24
|
+
|
25
|
+
# Copyright
|
26
|
+
|
27
|
+
Copyright (c) 2014 ooVoo LLC
|
28
|
+
|
29
|
+
This program is free software: you can redistribute it and/or modify
|
30
|
+
it under the terms of the GNU General Public License as published by
|
31
|
+
the Free Software Foundation, either version 3 of the License, or
|
32
|
+
(at your option) any later version.
|
33
|
+
|
34
|
+
This program is distributed in the hope that it will be useful,
|
35
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
36
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
37
|
+
GNU General Public License for more details.
|
38
|
+
|
39
|
+
You should have received a copy of the GNU General Public License
|
40
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
module FogExtensions
|
2
|
+
module Xenserver
|
3
|
+
module Server
|
4
|
+
|
5
|
+
def to_s
|
6
|
+
name
|
7
|
+
end
|
8
|
+
|
9
|
+
def nics_attributes=(attrs); end
|
10
|
+
|
11
|
+
def volumes_attributes=(attrs); end
|
12
|
+
|
13
|
+
def memory_min
|
14
|
+
memory_static_min.to_i
|
15
|
+
end
|
16
|
+
|
17
|
+
def memory_max
|
18
|
+
memory_static_max.to_i
|
19
|
+
end
|
20
|
+
|
21
|
+
def memory
|
22
|
+
memory_static_max.to_i
|
23
|
+
end
|
24
|
+
|
25
|
+
def reset
|
26
|
+
reboot
|
27
|
+
end
|
28
|
+
|
29
|
+
def ready?
|
30
|
+
running?
|
31
|
+
end
|
32
|
+
|
33
|
+
def mac
|
34
|
+
vifs.first.mac
|
35
|
+
end
|
36
|
+
|
37
|
+
def state
|
38
|
+
power_state
|
39
|
+
end
|
40
|
+
|
41
|
+
def custom_template_name
|
42
|
+
template_name
|
43
|
+
end
|
44
|
+
|
45
|
+
def builtin_template_name
|
46
|
+
template_name
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,288 @@
|
|
1
|
+
module ForemanXen
|
2
|
+
class Xenserver < ComputeResource
|
3
|
+
validates_presence_of :url, :user, :password
|
4
|
+
|
5
|
+
def provided_attributes
|
6
|
+
super.merge(
|
7
|
+
{ :uuid => :reference,
|
8
|
+
:mac => :mac
|
9
|
+
})
|
10
|
+
end
|
11
|
+
|
12
|
+
def capabilities
|
13
|
+
[:build]
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_vm_by_uuid(ref)
|
17
|
+
client.servers.get(ref)
|
18
|
+
rescue Fog::XenServer::RequestFailed => e
|
19
|
+
raise(ActiveRecord::RecordNotFound) if e.message.include?('HANDLE_INVALID')
|
20
|
+
raise(ActiveRecord::RecordNotFound) if e.message.include?('VM.get_record: ["SESSION_INVALID"')
|
21
|
+
raise e
|
22
|
+
end
|
23
|
+
|
24
|
+
# we default to destroy the VM's storage as well.
|
25
|
+
def destroy_vm(ref, args = {})
|
26
|
+
find_vm_by_uuid(ref).destroy
|
27
|
+
rescue ActiveRecord::RecordNotFound
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.model_name
|
32
|
+
ComputeResource.model_name
|
33
|
+
end
|
34
|
+
|
35
|
+
def max_cpu_count
|
36
|
+
## 16 is a max number of cpus per vm according to XenServer doc
|
37
|
+
[hypervisor.host_cpus.size, 16].min
|
38
|
+
end
|
39
|
+
|
40
|
+
def max_memory
|
41
|
+
xenServerMaxDoc = 128*1024*1024*1024
|
42
|
+
[hypervisor.metrics.memory_total.to_i, xenServerMaxDoc].min
|
43
|
+
rescue => e
|
44
|
+
logger.debug "unable to figure out free memory, guessing instead due to:#{e}"
|
45
|
+
16*1024*1024*1024
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_connection(options = {})
|
49
|
+
super
|
50
|
+
errors[:url].empty? and hypervisor
|
51
|
+
rescue => e
|
52
|
+
disconnect rescue nil
|
53
|
+
errors[:base] << e.message
|
54
|
+
end
|
55
|
+
|
56
|
+
def new_nic(attr={})
|
57
|
+
client.networks.new attr
|
58
|
+
end
|
59
|
+
|
60
|
+
def new_volume(attr={})
|
61
|
+
client.storage_repositories.new attr
|
62
|
+
end
|
63
|
+
|
64
|
+
def storage_pools
|
65
|
+
client.storage_repositories rescue []
|
66
|
+
end
|
67
|
+
|
68
|
+
def interfaces
|
69
|
+
client.interfaces rescue []
|
70
|
+
end
|
71
|
+
|
72
|
+
def networks
|
73
|
+
client.networks rescue []
|
74
|
+
end
|
75
|
+
|
76
|
+
def templates
|
77
|
+
client.servers.templates rescue []
|
78
|
+
end
|
79
|
+
|
80
|
+
def custom_templates
|
81
|
+
tmps = client.servers.custom_templates.select { |t| !t.is_a_snapshot } rescue []
|
82
|
+
tmps.sort { |a, b| a.name <=> b.name }
|
83
|
+
end
|
84
|
+
|
85
|
+
def builtin_templates
|
86
|
+
tmps = client.servers.builtin_templates.select { |t| !t.is_a_snapshot } rescue []
|
87
|
+
tmps.sort { |a, b| a.name <=> b.name }
|
88
|
+
end
|
89
|
+
|
90
|
+
def new_vm(attr={})
|
91
|
+
|
92
|
+
test_connection
|
93
|
+
return unless errors.empty?
|
94
|
+
opts = vm_instance_defaults.merge(attr.to_hash).symbolize_keys
|
95
|
+
|
96
|
+
[:networks, :volumes].each do |collection|
|
97
|
+
nested_attrs = opts.delete("#{collection}_attributes".to_sym)
|
98
|
+
opts[collection] = nested_attributes_for(collection, nested_attrs) if nested_attrs
|
99
|
+
end
|
100
|
+
opts.reject! { |_, v| v.nil? }
|
101
|
+
client.servers.new opts
|
102
|
+
end
|
103
|
+
|
104
|
+
def create_vm(args = {})
|
105
|
+
custom_template_name = args[:custom_template_name]
|
106
|
+
builtin_template_name = args[:builtin_template_name]
|
107
|
+
raise 'you can select at most one template type' if builtin_template_name != '' and custom_template_name != ''
|
108
|
+
begin
|
109
|
+
vm = nil
|
110
|
+
if custom_template_name != ''
|
111
|
+
vm = create_vm_from_custom args
|
112
|
+
else
|
113
|
+
vm = create_vm_from_builtin args
|
114
|
+
end
|
115
|
+
vm.set_attribute('name_description', 'Provisioned by Foreman')
|
116
|
+
cpus = args[:vcpus_max]
|
117
|
+
if vm.vcpus_max.to_i < cpus.to_i
|
118
|
+
vm.set_attribute('VCPUs_max', cpus)
|
119
|
+
vm.set_attribute('VCPUs_at_startup', cpus)
|
120
|
+
else
|
121
|
+
vm.set_attribute('VCPUs_at_startup', cpus)
|
122
|
+
vm.set_attribute('VCPUs_max', cpus)
|
123
|
+
end
|
124
|
+
vm.refresh
|
125
|
+
return vm
|
126
|
+
rescue => e
|
127
|
+
logger.info e
|
128
|
+
logger.info e.backtrace.join("\n")
|
129
|
+
return false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def create_vm_from_custom(args)
|
134
|
+
mem_max = args[:memory_max]
|
135
|
+
mem_min = args[:memory_min]
|
136
|
+
|
137
|
+
raise 'Memory max cannot be lower than Memory min' if mem_min.to_i > mem_max.to_i
|
138
|
+
vm = client.servers.new :name => args[:name],
|
139
|
+
:template_name => args[:custom_template_name]
|
140
|
+
|
141
|
+
vm.save :auto_start => false
|
142
|
+
|
143
|
+
vm.provision
|
144
|
+
|
145
|
+
vm.vifs.first.destroy rescue nil
|
146
|
+
|
147
|
+
create_network(vm, args)
|
148
|
+
|
149
|
+
args['xenstore']['vm-data']['ifs']['0']['mac'] = vm.vifs.first.mac
|
150
|
+
xenstore_data = xenstore_hash_flatten(args['xenstore'])
|
151
|
+
|
152
|
+
vm.set_attribute('xenstore_data', xenstore_data)
|
153
|
+
if vm.memory_static_max.to_i < mem_max.to_i
|
154
|
+
vm.set_attribute('memory_static_max', mem_max)
|
155
|
+
vm.set_attribute('memory_dynamic_max', mem_max)
|
156
|
+
vm.set_attribute('memory_dynamic_min', mem_min)
|
157
|
+
vm.set_attribute('memory_static_min', mem_min)
|
158
|
+
else
|
159
|
+
vm.set_attribute('memory_static_min', mem_min)
|
160
|
+
vm.set_attribute('memory_dynamic_min', mem_min)
|
161
|
+
vm.set_attribute('memory_dynamic_max', mem_max)
|
162
|
+
vm.set_attribute('memory_static_max', mem_max)
|
163
|
+
end
|
164
|
+
|
165
|
+
disks = vm.vbds.select { |vbd| vbd.type == 'Disk' }
|
166
|
+
disks.sort! { |a, b| a.userdevice <=> b.userdevice }
|
167
|
+
i = 0
|
168
|
+
disks.each do |vbd|
|
169
|
+
vbd.vdi.set_attribute('name-label', "#{args[:name]}_#{i}")
|
170
|
+
i+=1
|
171
|
+
end
|
172
|
+
vm
|
173
|
+
end
|
174
|
+
|
175
|
+
def create_vm_from_builtin(args)
|
176
|
+
|
177
|
+
host = client.hosts.first
|
178
|
+
storage_repository = client.storage_repositories.find { |sr| sr.name == "#{args[:VBDs][:print]}" }
|
179
|
+
|
180
|
+
gb = 1073741824 #1gb in bytes
|
181
|
+
size = args[:VBDs][:physical_size].to_i * gb
|
182
|
+
vdi = client.vdis.create :name => "#{args[:name]}-disk1",
|
183
|
+
:storage_repository => storage_repository,
|
184
|
+
:description => "#{args[:name]}-disk_1",
|
185
|
+
:virtual_size => size.to_s
|
186
|
+
|
187
|
+
mem_max = args[:memory_max]
|
188
|
+
mem_min = args[:memory_min]
|
189
|
+
other_config = {}
|
190
|
+
if args[:builtin_template_name] != ''
|
191
|
+
template = client.servers.builtin_templates.find { |tmp| tmp.name == args[:builtin_template_name] }
|
192
|
+
other_config = template.other_config
|
193
|
+
other_config.delete 'disks'
|
194
|
+
other_config.delete 'default_template'
|
195
|
+
end
|
196
|
+
vm = client.servers.new :name => args[:name],
|
197
|
+
:affinity => host,
|
198
|
+
:pv_bootloader => '',
|
199
|
+
:hvm_boot_params => { :order => 'dn' },
|
200
|
+
:other_config => other_config,
|
201
|
+
:memory_static_max => mem_max,
|
202
|
+
:memory_static_min => mem_min,
|
203
|
+
:memory_dynamic_max => mem_max,
|
204
|
+
:memory_dynamic_min => mem_min
|
205
|
+
|
206
|
+
vm.save :auto_start => false
|
207
|
+
client.vbds.create :server => vm, :vdi => vdi
|
208
|
+
|
209
|
+
create_network(vm, args)
|
210
|
+
|
211
|
+
vm.provision
|
212
|
+
vm.set_attribute('HVM_boot_policy', 'BIOS order')
|
213
|
+
vm.refresh
|
214
|
+
vm
|
215
|
+
end
|
216
|
+
|
217
|
+
def console(uuid)
|
218
|
+
vm = find_vm_by_uuid(uuid)
|
219
|
+
raise 'VM is not running!' unless vm.ready?
|
220
|
+
|
221
|
+
|
222
|
+
console = vm.service.consoles.find { |c| c.__vm == vm.reference && c.protocol == 'rfb' }
|
223
|
+
raise "No console fore vm #{vm.name}" if console == nil
|
224
|
+
|
225
|
+
session_ref = (vm.service.instance_variable_get :@connection).instance_variable_get :@credentials
|
226
|
+
fullURL = "#{console.location}&session_id=#{session_ref}"
|
227
|
+
tunnel = VNCTunnel.new fullURL
|
228
|
+
tunnel.start
|
229
|
+
logger.info 'VNCTunnel started'
|
230
|
+
WsProxy.start(:host => tunnel.host, :host_port => tunnel.port, :password => '').merge(:type => 'vnc', :name => vm.name)
|
231
|
+
|
232
|
+
rescue Error => e
|
233
|
+
logger.warn e
|
234
|
+
raise e
|
235
|
+
end
|
236
|
+
|
237
|
+
def hypervisor
|
238
|
+
client.hosts.first
|
239
|
+
end
|
240
|
+
|
241
|
+
protected
|
242
|
+
|
243
|
+
def client
|
244
|
+
@client ||= ::Fog::Compute.new({ :provider => 'XenServer', :xenserver_url => url, :xenserver_username => user, :xenserver_password => password })
|
245
|
+
end
|
246
|
+
|
247
|
+
def disconnect
|
248
|
+
client.terminate if @client
|
249
|
+
@client = nil
|
250
|
+
end
|
251
|
+
|
252
|
+
def vm_instance_defaults
|
253
|
+
super.merge({})
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
private
|
258
|
+
|
259
|
+
def create_network(vm, args)
|
260
|
+
net = client.networks.find { |n| n.name == args[:VIFs][:print] }
|
261
|
+
net_config = {
|
262
|
+
'MAC_autogenerated' => 'True',
|
263
|
+
'VM' => vm.reference,
|
264
|
+
'network' => net.reference,
|
265
|
+
'MAC' => '',
|
266
|
+
'device' => '0',
|
267
|
+
'MTU' => '0',
|
268
|
+
'other_config' => {},
|
269
|
+
'qos_algorithm_type' => 'ratelimit',
|
270
|
+
'qos_algorithm_params' => {}
|
271
|
+
}
|
272
|
+
client.create_vif_custom net_config
|
273
|
+
vm.reload
|
274
|
+
end
|
275
|
+
|
276
|
+
def xenstore_hash_flatten(nested_hash, key=nil, keychain=nil, out_hash={})
|
277
|
+
nested_hash.each do |k, v|
|
278
|
+
if v.is_a? Hash
|
279
|
+
xenstore_hash_flatten(v, k, "#{keychain}#{k}/", out_hash)
|
280
|
+
else
|
281
|
+
out_hash["#{keychain}#{k}"] = v
|
282
|
+
end
|
283
|
+
end
|
284
|
+
out_hash
|
285
|
+
@key = key
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<%= text_f f, :url, :class => "input-xlarge", :help_block => _("e.g. x.x.x.x") %>
|
2
|
+
<%= text_f f, :user %>
|
3
|
+
<%= password_f f, :password %>
|
4
|
+
|
5
|
+
<% hypervisor = f.object.hypervisor.uuid rescue nil%>
|
6
|
+
<% if hypervisor -%>
|
7
|
+
<%= f.hidden_field :uuid, :value => hypervisor %>
|
8
|
+
<% end -%>
|
9
|
+
<%= link_to_function _("Test Connection"), "testConnection(this)", :class => "btn + #{hypervisor.nil? ? "" : "btn-success"}", :'data-url' => test_connection_compute_resources_path %>
|
10
|
+
|
11
|
+
<%= image_tag('/assets/spinner.gif', :id => 'test_connection_indicator', :class => 'hide') %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<div class="fields">
|
2
|
+
<%
|
3
|
+
nat = compute_resource.networks
|
4
|
+
selected = ""
|
5
|
+
if params && params['host'] && params['host']['compute_attributes']
|
6
|
+
selected = params['host']['compute_attributes']['VIFs']['print']
|
7
|
+
end
|
8
|
+
-%>
|
9
|
+
|
10
|
+
<div id='nat' class=''>
|
11
|
+
<%= selectable_f f, :print, nat.map(&:name),
|
12
|
+
{ :include_blank => nat.any? ? false : _("No networks"),
|
13
|
+
:selected => selected },
|
14
|
+
{ :class => "span2", :label => _("Network") } %>
|
15
|
+
</div>
|
16
|
+
</div>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<div class="fields">
|
2
|
+
<%
|
3
|
+
selected_item_c = ''
|
4
|
+
selected_item_b = ''
|
5
|
+
if params && params['host'] && params['host']['compute_attributes']
|
6
|
+
selected_item_c = params['host']['compute_attributes']['custom_template_name']
|
7
|
+
selected_item_b = params['host']['compute_attributes']['builtin_template_name']
|
8
|
+
end
|
9
|
+
%>
|
10
|
+
|
11
|
+
<div id='templates' class=''>
|
12
|
+
<label class="control-label" for="host_compute_attributes_custom_template_name">Custom template</label>
|
13
|
+
|
14
|
+
<div class="controls">
|
15
|
+
<select class="input-xlarge" id="host_compute_attributes_custom_template_name" name="host[compute_attributes][custom_template_name]">
|
16
|
+
<%= options_for_select([[_("No template"), ""]] + compute_resource.custom_templates.map { |t| [t.name, t.name] }, selected_item_c) %>
|
17
|
+
</select>
|
18
|
+
</div>
|
19
|
+
|
20
|
+
<label class="control-label" for="host_compute_attributes_builtin_template_name">Built-in template</label>
|
21
|
+
|
22
|
+
<div class="controls">
|
23
|
+
<select class="input-xlarge" id="host_compute_attributes_builtin_template_name" name="host[compute_attributes][builtin_template_name]">
|
24
|
+
<%= options_for_select([[_("No template"), ""]] + compute_resource.builtin_templates.map { |t| [t.name, t.name] }, selected_item_b) %>
|
25
|
+
</select>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
30
|
+
</div>
|