fog-opennebula 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/.gitignore +32 -0
- data/CONTRIBUTORS.md +4 -0
- data/Gemfile +9 -0
- data/LICENSE.md +20 -0
- data/README.md +95 -0
- data/Rakefile +118 -0
- data/fog-opennebula.gemspec +35 -0
- data/lib/fog/bin/opennebula.rb +32 -0
- data/lib/fog/opennebula.rb +30 -0
- data/lib/fog/opennebula/compute.rb +136 -0
- data/lib/fog/opennebula/models/compute/flavor.rb +190 -0
- data/lib/fog/opennebula/models/compute/flavors.rb +46 -0
- data/lib/fog/opennebula/models/compute/group.rb +28 -0
- data/lib/fog/opennebula/models/compute/groups.rb +38 -0
- data/lib/fog/opennebula/models/compute/interface.rb +39 -0
- data/lib/fog/opennebula/models/compute/interfaces.rb +20 -0
- data/lib/fog/opennebula/models/compute/network.rb +48 -0
- data/lib/fog/opennebula/models/compute/networks.rb +42 -0
- data/lib/fog/opennebula/models/compute/server.rb +85 -0
- data/lib/fog/opennebula/models/compute/servers.rb +33 -0
- data/lib/fog/opennebula/requests/compute/OpenNebulaVNC.rb +314 -0
- data/lib/fog/opennebula/requests/compute/get_vnc_console.rb +58 -0
- data/lib/fog/opennebula/requests/compute/image_pool.rb +33 -0
- data/lib/fog/opennebula/requests/compute/list_groups.rb +87 -0
- data/lib/fog/opennebula/requests/compute/list_networks.rb +79 -0
- data/lib/fog/opennebula/requests/compute/list_vms.rb +79 -0
- data/lib/fog/opennebula/requests/compute/template_pool.rb +120 -0
- data/lib/fog/opennebula/requests/compute/vm_allocate.rb +97 -0
- data/lib/fog/opennebula/requests/compute/vm_destroy.rb +39 -0
- data/lib/fog/opennebula/requests/compute/vm_disk_snapshot.rb +33 -0
- data/lib/fog/opennebula/requests/compute/vm_resume.rb +35 -0
- data/lib/fog/opennebula/requests/compute/vm_shutdown.rb +22 -0
- data/lib/fog/opennebula/requests/compute/vm_stop.rb +21 -0
- data/lib/fog/opennebula/requests/compute/vm_suspend.rb +38 -0
- data/lib/fog/opennebula/version.rb +9 -0
- data/tests/opennebula/compute_tests.rb +15 -0
- data/tests/opennebula/models/compute/flavor_tests.rb +34 -0
- data/tests/opennebula/models/compute/flavors_tests.rb +15 -0
- data/tests/opennebula/models/compute/group_tests.rb +25 -0
- data/tests/opennebula/models/compute/groups_tests.rb +14 -0
- data/tests/opennebula/models/compute/network_tests.rb +24 -0
- data/tests/opennebula/models/compute/networks_tests.rb +14 -0
- data/tests/opennebula/requests/compute/vm_allocate_tests.rb +70 -0
- data/tests/opennebula/requests/compute/vm_disk_snapshot_test.rb +44 -0
- data/tests/opennebula/requests/compute/vm_suspend_resume_tests.rb +45 -0
- metadata +243 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'fog/compute/models/server'
|
2
|
+
|
3
|
+
module Fog
|
4
|
+
|
5
|
+
module Compute
|
6
|
+
|
7
|
+
class OpenNebula
|
8
|
+
|
9
|
+
class Server < Fog::Compute::Server
|
10
|
+
|
11
|
+
identity :id
|
12
|
+
attribute :template_str
|
13
|
+
attribute :name
|
14
|
+
attribute :uuid
|
15
|
+
attribute :state
|
16
|
+
attribute :status
|
17
|
+
attribute :ip
|
18
|
+
attribute :mac
|
19
|
+
attribute :vcpu
|
20
|
+
attribute :cpu
|
21
|
+
attribute :memory
|
22
|
+
attribute :user
|
23
|
+
attribute :gid
|
24
|
+
attribute :group
|
25
|
+
attribute :onevm_object
|
26
|
+
attribute :flavor
|
27
|
+
|
28
|
+
def save
|
29
|
+
merge_attributes(service.vm_allocate(attributes))
|
30
|
+
end
|
31
|
+
|
32
|
+
def vm_ip_address
|
33
|
+
ip
|
34
|
+
end
|
35
|
+
|
36
|
+
def private_ip_address
|
37
|
+
ip
|
38
|
+
end
|
39
|
+
|
40
|
+
def public_ip_address
|
41
|
+
ip
|
42
|
+
end
|
43
|
+
|
44
|
+
def vm_mac_address
|
45
|
+
mac
|
46
|
+
end
|
47
|
+
|
48
|
+
def start
|
49
|
+
service.vm_resume(id) if status == 4
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
def stop
|
54
|
+
Fog::Logger.warning("stop VM: ID:#{id}")
|
55
|
+
service.vm_stop(id)
|
56
|
+
end
|
57
|
+
|
58
|
+
def suspend
|
59
|
+
service.vm_suspend(id)
|
60
|
+
end
|
61
|
+
|
62
|
+
def resume
|
63
|
+
service.vm_resume(id)
|
64
|
+
end
|
65
|
+
|
66
|
+
def destroy
|
67
|
+
service.vm_destroy(id)
|
68
|
+
end
|
69
|
+
|
70
|
+
def ready?
|
71
|
+
(status == 3)
|
72
|
+
end
|
73
|
+
|
74
|
+
def console_output
|
75
|
+
requires :id
|
76
|
+
service.get_vnc_console(id, 'vnc', onevm_object)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'fog/core/collection'
|
2
|
+
require 'fog/opennebula/models/compute/server'
|
3
|
+
|
4
|
+
module Fog
|
5
|
+
|
6
|
+
module Compute
|
7
|
+
|
8
|
+
class OpenNebula
|
9
|
+
|
10
|
+
class Servers < Fog::Collection
|
11
|
+
|
12
|
+
model Fog::Compute::OpenNebula::Server
|
13
|
+
|
14
|
+
def all(filter = {})
|
15
|
+
load(service.list_vms(filter))
|
16
|
+
end
|
17
|
+
|
18
|
+
def get(id)
|
19
|
+
data = service.list_vms(:id => id)
|
20
|
+
new data.first unless data.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
def shutdown(id)
|
24
|
+
service.vm_shutdown(id)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,314 @@
|
|
1
|
+
#
|
2
|
+
# This class provides support for launching and stopping a websockify proxy
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'json'
|
7
|
+
require 'opennebula'
|
8
|
+
|
9
|
+
# if !ONE_LOCATION
|
10
|
+
NOVNC_LOCK_FILE = '/var/lock/.novnc.lock'.freeze
|
11
|
+
# else
|
12
|
+
# NOVNC_LOCK_FILE= ONE_LOCATION + "/var/.novnc.lock"
|
13
|
+
# end
|
14
|
+
|
15
|
+
TOKEN_EXPIRE_SECONDS = 4
|
16
|
+
|
17
|
+
VNC_STATES = [
|
18
|
+
# 0, #LCM_INIT
|
19
|
+
# 1, #PROLOG
|
20
|
+
# 2, #BOOT
|
21
|
+
'3', # RUNNING
|
22
|
+
'4', # MIGRATE
|
23
|
+
# 5, #SAVE_STOP
|
24
|
+
# 6, #SAVE_SUSPEND
|
25
|
+
# 7, #SAVE_MIGRATE
|
26
|
+
# 8, #PROLOG_MIGRATE
|
27
|
+
# 9, #PROLOG_RESUME
|
28
|
+
# 10, #EPILOG_STOP
|
29
|
+
# 11, #EPILOG
|
30
|
+
'12', # SHUTDOWN
|
31
|
+
'13', # CANCEL
|
32
|
+
# 14, #FAILURE
|
33
|
+
# 15, #CLEANUP_RESUBMIT
|
34
|
+
'16', # UNKNOWN
|
35
|
+
'17', # HOTPLUG
|
36
|
+
'18', # SHUTDOWN_POWEROFF
|
37
|
+
# 19, #BOOT_UNKNOWN
|
38
|
+
# 20, #BOOT_POWEROFF
|
39
|
+
# 21, #BOOT_SUSPENDED
|
40
|
+
# 22, #BOOT_STOPPED
|
41
|
+
# 23, #CLEANUP_DELETE
|
42
|
+
'24', # HOTPLUG_SNAPSHOT
|
43
|
+
'25', # HOTPLUG_NIC
|
44
|
+
'26', # HOTPLUG_SAVEAS
|
45
|
+
'27', # HOTPLUG_SAVEAS_POWEROFF
|
46
|
+
'28', # HOTPLUG_SAVEAS_SUSPENDED
|
47
|
+
'29' # SHUTDOWN_UNDEPLOY
|
48
|
+
# 30, #EPILOG_UNDEPLOY
|
49
|
+
# 31, #PROLOG_UNDEPLOY
|
50
|
+
# 32 #BOOT_UNDEPLOY
|
51
|
+
].freeze
|
52
|
+
|
53
|
+
VAR_LOCATION = Dir.pwd + '/extras/noVNC'
|
54
|
+
SHARE_LOCATION = Dir.pwd + '/extras/noVNC'
|
55
|
+
class OpenNebulaVNC
|
56
|
+
|
57
|
+
attr_reader :proxy_port
|
58
|
+
|
59
|
+
def initialize(config, logger, options = {})
|
60
|
+
opts = { :json_errors => true,
|
61
|
+
:token_folder_name => 'sunstone_vnc_tokens' }.merge(options)
|
62
|
+
|
63
|
+
@pipe = nil
|
64
|
+
@token_folder = File.join(VAR_LOCATION, opts[:token_folder_name])
|
65
|
+
@proxy_path = File.join(SHARE_LOCATION, 'websockify/websocketproxy.py')
|
66
|
+
@proxy_port = config[:vnc_proxy_port]
|
67
|
+
|
68
|
+
@proxy_ipv6 = config[:vnc_proxy_ipv6]
|
69
|
+
|
70
|
+
@wss = config[:vnc_proxy_support_wss]
|
71
|
+
|
72
|
+
@lock_file = NOVNC_LOCK_FILE
|
73
|
+
|
74
|
+
if (@wss == 'yes') || (@wss == 'only') || (@wss == true)
|
75
|
+
@enable_wss = true
|
76
|
+
@cert = config[:vnc_proxy_cert]
|
77
|
+
@key = config[:vnc_proxy_key]
|
78
|
+
else
|
79
|
+
@enable_wss = false
|
80
|
+
end
|
81
|
+
@options = opts
|
82
|
+
@logger = logger
|
83
|
+
end
|
84
|
+
|
85
|
+
def start
|
86
|
+
if is_running?
|
87
|
+
message = 'VNC server already running'
|
88
|
+
STDERR.puts message
|
89
|
+
@logger.info message
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
|
93
|
+
create_token_dir
|
94
|
+
|
95
|
+
proxy_options = "--target-config=#{@token_folder} "
|
96
|
+
|
97
|
+
if @enable_wss
|
98
|
+
proxy_options << " --cert #{@cert}"
|
99
|
+
proxy_options << " --key #{@key}" if @key && !@key.empty?
|
100
|
+
proxy_options << ' --ssl-only' if @wss == 'only'
|
101
|
+
end
|
102
|
+
|
103
|
+
proxy_options << ' -6' if @proxy_ipv6
|
104
|
+
|
105
|
+
cmd = "python #{@proxy_path} #{proxy_options} #{@proxy_port}"
|
106
|
+
|
107
|
+
begin
|
108
|
+
@logger.info { "Starting VNC proxy: #{cmd}" }
|
109
|
+
pid = start_daemon(cmd, VNC_LOG)
|
110
|
+
rescue Exception => e
|
111
|
+
@logger.error e.message
|
112
|
+
return false
|
113
|
+
end
|
114
|
+
|
115
|
+
File.open(@lock_file, 'w') do |f|
|
116
|
+
f.write(pid.to_s)
|
117
|
+
end
|
118
|
+
|
119
|
+
sleep 1
|
120
|
+
|
121
|
+
unless is_running?
|
122
|
+
message = 'Error starting VNC proxy'
|
123
|
+
STDERR.puts message
|
124
|
+
@logger.error message
|
125
|
+
File.delete(@lock_file) if File.exist?(@lock_file)
|
126
|
+
|
127
|
+
return false
|
128
|
+
end
|
129
|
+
|
130
|
+
STDOUT.puts 'VNC proxy started'
|
131
|
+
|
132
|
+
true
|
133
|
+
end
|
134
|
+
|
135
|
+
def proxy(vm_resource)
|
136
|
+
# Check configurations and VM attributes
|
137
|
+
# if !is_running?
|
138
|
+
# return error(400, "VNC Proxy is not running")
|
139
|
+
# end
|
140
|
+
|
141
|
+
unless VNC_STATES.include?(vm_resource['LCM_STATE'])
|
142
|
+
return error(400, "Wrong state (#{vm_resource['LCM_STATE']}) to open a VNC session")
|
143
|
+
end
|
144
|
+
|
145
|
+
if vm_resource['TEMPLATE/GRAPHICS/TYPE'].nil? ||
|
146
|
+
!vm_resource['TEMPLATE/GRAPHICS/TYPE'].casecmp('vnc').zero?
|
147
|
+
return error(400, 'VM has no VNC configured')
|
148
|
+
end
|
149
|
+
|
150
|
+
# Proxy data
|
151
|
+
host = vm_resource['HISTORY_RECORDS/HISTORY[last()]/HOSTNAME']
|
152
|
+
vnc_port = vm_resource['TEMPLATE/GRAPHICS/PORT']
|
153
|
+
vnc_pw = vm_resource['TEMPLATE/GRAPHICS/PASSWD']
|
154
|
+
|
155
|
+
# Generate token random_str: host:port
|
156
|
+
random_str = rand(36**20).to_s(36) # random string a-z0-9 length 20
|
157
|
+
token = "#{random_str}: #{host}:#{vnc_port}"
|
158
|
+
token_file = 'one-' + vm_resource['ID']
|
159
|
+
|
160
|
+
# Create token file
|
161
|
+
|
162
|
+
begin
|
163
|
+
f = File.open(File.join(@token_folder, token_file), 'w')
|
164
|
+
f.write(token)
|
165
|
+
f.close
|
166
|
+
rescue Exception => e
|
167
|
+
# @logger.error e.message
|
168
|
+
return error(500, 'Cannot create VNC proxy token')
|
169
|
+
end
|
170
|
+
|
171
|
+
info = {
|
172
|
+
:proxy_port => '29876',
|
173
|
+
:password => vnc_pw,
|
174
|
+
:token => random_str,
|
175
|
+
:vm_name => vm_resource['NAME']
|
176
|
+
}
|
177
|
+
|
178
|
+
[200, info]
|
179
|
+
end
|
180
|
+
|
181
|
+
# Delete proxy token file
|
182
|
+
def delete_token(filename)
|
183
|
+
File.delete(File.join(@token_folder, filename))
|
184
|
+
rescue StandardError => e
|
185
|
+
@logger.error "Error deleting token file for VM #{vm_id}"
|
186
|
+
@logger.error e.message
|
187
|
+
end
|
188
|
+
|
189
|
+
def stop(force = false)
|
190
|
+
pid = get_pid
|
191
|
+
|
192
|
+
if pid
|
193
|
+
@logger.info 'Killing VNC proxy'
|
194
|
+
|
195
|
+
force ? signal = 'KILL' : signal = 'TERM'
|
196
|
+
Process.kill(signal, pid)
|
197
|
+
|
198
|
+
sleep 1
|
199
|
+
|
200
|
+
begin
|
201
|
+
Process.getpgid(pid)
|
202
|
+
|
203
|
+
Process.kill('KILL', pid)
|
204
|
+
rescue StandardError
|
205
|
+
end
|
206
|
+
|
207
|
+
if is_running?
|
208
|
+
message = 'VNC server is still running'
|
209
|
+
STDERR.puts message
|
210
|
+
logger.error message
|
211
|
+
return false
|
212
|
+
end
|
213
|
+
|
214
|
+
delete_token_dir
|
215
|
+
else
|
216
|
+
message = 'VNC server is not running'
|
217
|
+
@logger.info message
|
218
|
+
STDERR.puts message
|
219
|
+
end
|
220
|
+
true
|
221
|
+
end
|
222
|
+
|
223
|
+
def status
|
224
|
+
if is_running?
|
225
|
+
STDOUT.puts 'VNC is running'
|
226
|
+
true
|
227
|
+
else
|
228
|
+
STDOUT.puts 'VNC is NOT running'
|
229
|
+
false
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
private
|
234
|
+
|
235
|
+
def error(code, msg)
|
236
|
+
if @options[:json_errors]
|
237
|
+
[code, OpenNebula::Error.new(msg).to_json]
|
238
|
+
else
|
239
|
+
[code, msg]
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def create_token_dir
|
244
|
+
delete_token_dir
|
245
|
+
begin
|
246
|
+
Dir.mkdir(@token_folder) unless File.exist?(@token_folder)
|
247
|
+
rescue Exception => e
|
248
|
+
@logger.error 'Cannot create token folder'
|
249
|
+
@logger.error e.message
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def delete_token_dir
|
254
|
+
if File.exist?(@token_folder)
|
255
|
+
begin
|
256
|
+
Dir.glob("#{@token_folder}/*").each do |file|
|
257
|
+
File.delete(file)
|
258
|
+
end
|
259
|
+
rescue StandardError => e
|
260
|
+
@logger.error 'Error deleting token folder'
|
261
|
+
@logger.error e.message
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def is_running?
|
267
|
+
if File.exist?(@lock_file)
|
268
|
+
pid = File.read(@lock_file).strip
|
269
|
+
|
270
|
+
return pid.to_i if system("ps #{pid} 1> /dev/null")
|
271
|
+
|
272
|
+
@logger.info 'Deleting stale lock file'
|
273
|
+
File.delete(@lock_file)
|
274
|
+
end
|
275
|
+
|
276
|
+
false
|
277
|
+
end
|
278
|
+
alias get_pid is_running?
|
279
|
+
|
280
|
+
if RUBY_VERSION < '1.9'
|
281
|
+
def spawn(*args)
|
282
|
+
fork do
|
283
|
+
command = args[0..-2]
|
284
|
+
|
285
|
+
# Close stdin and point out and err to log file
|
286
|
+
$stdin.close
|
287
|
+
$stdout.reopen(VNC_LOG, 'a')
|
288
|
+
$stderr.reopen(VNC_LOG, 'a')
|
289
|
+
|
290
|
+
# Detach process from the parent
|
291
|
+
Process.setsid
|
292
|
+
|
293
|
+
exec(*command)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def start_daemon(cmd, log)
|
299
|
+
options = {
|
300
|
+
:pgroup => true,
|
301
|
+
:in => :close,
|
302
|
+
[:out, :err] => [log, 'a'],
|
303
|
+
:close_others => true
|
304
|
+
}
|
305
|
+
|
306
|
+
params = cmd.split(' ') + [options]
|
307
|
+
pid = spawn(*params)
|
308
|
+
|
309
|
+
Process.detach(pid)
|
310
|
+
|
311
|
+
pid
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.expand_path('OpenNebulaVNC', __dir__)
|
2
|
+
module Fog
|
3
|
+
|
4
|
+
module Compute
|
5
|
+
|
6
|
+
class OpenNebula
|
7
|
+
|
8
|
+
class Mock
|
9
|
+
|
10
|
+
# Get a vnc console for an instance.
|
11
|
+
#
|
12
|
+
# === Parameters
|
13
|
+
# * server_id <~String> - The ID of the server.
|
14
|
+
# * console_type <~String> - Type of vnc console to get ('novnc' or 'xvpvnc').
|
15
|
+
# === Returns
|
16
|
+
# * response <~Excon::Response>:
|
17
|
+
# * body <~Hash>:
|
18
|
+
# * url <~String>
|
19
|
+
# * type <~String>
|
20
|
+
def get_vnc_console(_server_id, _console_type)
|
21
|
+
body = {
|
22
|
+
:type => 'novnc',
|
23
|
+
:proxy_port => '29876',
|
24
|
+
:password => 'null',
|
25
|
+
:token => '3n32dtwpsdj5jkug3b4w',
|
26
|
+
:proxy_host => 'example.com'
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class Real
|
33
|
+
|
34
|
+
def get_vnc_console(_server_id, _console_type, onevm_object)
|
35
|
+
logger = Fog::Logger.new
|
36
|
+
$conf = { 'vnc_proxy_port' => '29876', 'vnc_proxy_ipv6' => '',
|
37
|
+
'vnc_proxy_support_wss' => '', 'vnc_proxy_cert' => '',
|
38
|
+
'vnc_proxy_key' => '' }
|
39
|
+
$vnc = OpenNebulaVNC.new($conf, logger)
|
40
|
+
ret = startvnc(onevm_object, $vnc)
|
41
|
+
|
42
|
+
response = Excon::Response.new
|
43
|
+
response.status = ret[0]
|
44
|
+
response.body = ret[1]
|
45
|
+
response
|
46
|
+
end
|
47
|
+
|
48
|
+
def startvnc(onevm_object, vnc)
|
49
|
+
vnc.proxy(onevm_object)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|