wakame-vdc-agents 10.11.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.
- data/LICENSE +202 -0
- data/NOTICE +1 -0
- data/Rakefile +142 -0
- data/bin/hva +972 -0
- data/bin/nsa +147 -0
- data/bin/sta +182 -0
- data/config/hva.conf.example +10 -0
- data/config/initializers/isono.rb +43 -0
- data/config/initializers/passenger.rb +6 -0
- data/config/initializers/sequel.rb +21 -0
- data/config/nsa.conf.example +9 -0
- data/config/path_resolver.rb +12 -0
- data/lib/dcmgr.rb +115 -0
- data/lib/dcmgr/endpoints/core_api.rb +1004 -0
- data/lib/dcmgr/endpoints/core_api_mock.rb +816 -0
- data/lib/dcmgr/endpoints/errors.rb +55 -0
- data/lib/dcmgr/endpoints/metadata.rb +129 -0
- data/lib/dcmgr/logger.rb +44 -0
- data/lib/dcmgr/models/account.rb +104 -0
- data/lib/dcmgr/models/account_resource.rb +16 -0
- data/lib/dcmgr/models/base.rb +69 -0
- data/lib/dcmgr/models/base_new.rb +371 -0
- data/lib/dcmgr/models/frontend_system.rb +38 -0
- data/lib/dcmgr/models/host_pool.rb +102 -0
- data/lib/dcmgr/models/image.rb +46 -0
- data/lib/dcmgr/models/instance.rb +255 -0
- data/lib/dcmgr/models/instance_netfilter_group.rb +16 -0
- data/lib/dcmgr/models/instance_nic.rb +68 -0
- data/lib/dcmgr/models/instance_spec.rb +21 -0
- data/lib/dcmgr/models/ip_lease.rb +42 -0
- data/lib/dcmgr/models/netfilter_group.rb +88 -0
- data/lib/dcmgr/models/netfilter_rule.rb +21 -0
- data/lib/dcmgr/models/network.rb +32 -0
- data/lib/dcmgr/models/physical_host.rb +67 -0
- data/lib/dcmgr/models/request_log.rb +25 -0
- data/lib/dcmgr/models/ssh_key_pair.rb +55 -0
- data/lib/dcmgr/models/storage_pool.rb +134 -0
- data/lib/dcmgr/models/tag.rb +126 -0
- data/lib/dcmgr/models/tag_mapping.rb +28 -0
- data/lib/dcmgr/models/volume.rb +130 -0
- data/lib/dcmgr/models/volume_snapshot.rb +47 -0
- data/lib/dcmgr/node_modules/hva_collector.rb +134 -0
- data/lib/dcmgr/node_modules/sta_collector.rb +72 -0
- data/lib/dcmgr/scheduler.rb +12 -0
- data/lib/dcmgr/scheduler/find_last.rb +16 -0
- data/lib/dcmgr/scheduler/find_random.rb +16 -0
- data/lib/dcmgr/stm/instance.rb +25 -0
- data/lib/dcmgr/stm/snapshot_context.rb +33 -0
- data/lib/dcmgr/stm/volume_context.rb +65 -0
- data/lib/dcmgr/web/base.rb +21 -0
- data/lib/sinatra/accept_media_types.rb +128 -0
- data/lib/sinatra/lazy_auth.rb +56 -0
- data/lib/sinatra/rabbit.rb +278 -0
- data/lib/sinatra/respond_to.rb +272 -0
- data/lib/sinatra/sequel_transaction.rb +27 -0
- data/lib/sinatra/static_assets.rb +83 -0
- data/lib/sinatra/url_for.rb +44 -0
- metadata +270 -0
@@ -0,0 +1,1004 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'sinatra/base'
|
4
|
+
require 'sinatra/rabbit'
|
5
|
+
require 'sinatra/sequel_transaction'
|
6
|
+
|
7
|
+
require 'json'
|
8
|
+
require 'extlib/hash'
|
9
|
+
|
10
|
+
require 'dcmgr/endpoints/errors'
|
11
|
+
|
12
|
+
module Dcmgr
|
13
|
+
module Endpoints
|
14
|
+
class CoreAPI < Sinatra::Base
|
15
|
+
include Dcmgr::Logger
|
16
|
+
register Sinatra::Rabbit
|
17
|
+
register Sinatra::SequelTransaction
|
18
|
+
|
19
|
+
disable :sessions
|
20
|
+
disable :show_exceptions
|
21
|
+
|
22
|
+
before do
|
23
|
+
request.env['dcmgr.frotend_system.id'] = 1
|
24
|
+
request.env['HTTP_X_VDC_REQUESTER_TOKEN']='u-xxxxxx'
|
25
|
+
request.env['HTTP_X_VDC_ACCOUNT_UUID']='a-00000000'
|
26
|
+
end
|
27
|
+
|
28
|
+
before do
|
29
|
+
@params = parsed_request_body if request.post?
|
30
|
+
@account = Models::Account[request.env['HTTP_X_VDC_ACCOUNT_UUID']]
|
31
|
+
@requester_token = request.env['HTTP_X_VDC_REQUESTER_TOKEN']
|
32
|
+
#@frontend = Models::FrontendSystem[request.env['dcmgr.frotend_system.id']]
|
33
|
+
|
34
|
+
#raise InvalidRequestCredentials if !(@account && @frontend)
|
35
|
+
raise DisabledAccount if @account.disable?
|
36
|
+
end
|
37
|
+
|
38
|
+
before do
|
39
|
+
Thread.current[Dcmgr::Models::BaseNew::LOCK_TABLES_KEY] = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_by_uuid(model_class, uuid)
|
43
|
+
if model_class.is_a?(Symbol)
|
44
|
+
model_class = Models.const_get(model_class)
|
45
|
+
end
|
46
|
+
model_class[uuid] || raise(UnknownUUIDResource, uuid.to_s)
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_account(account_uuid)
|
50
|
+
find_by_uuid(:Account, account_uuid)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns deserialized hash from HTTP body. Serialization fromat
|
54
|
+
# is guessed from content type header. The query string params
|
55
|
+
# is returned if none of content type header is in HTTP headers.
|
56
|
+
# This method is called only when the request method is POST.
|
57
|
+
def parsed_request_body
|
58
|
+
# @mime_types should be defined by sinatra/respond_to.rb plugin.
|
59
|
+
if @mime_types.nil?
|
60
|
+
# use query string as requested params if Content-Type
|
61
|
+
# header was not sent.
|
62
|
+
# ActiveResource library tells the one level nested hash which has
|
63
|
+
# {'something key'=>real_params} so that dummy key is assinged here.
|
64
|
+
hash = {:dummy=>@params}
|
65
|
+
else
|
66
|
+
mime = @mime_types.first
|
67
|
+
case mime.to_s
|
68
|
+
when 'application/json', 'text/json'
|
69
|
+
require 'json'
|
70
|
+
hash = JSON.load(request.body)
|
71
|
+
hash = hash.to_mash
|
72
|
+
when 'application/yaml', 'text/yaml'
|
73
|
+
require 'yaml'
|
74
|
+
hash = YAML.load(request.body)
|
75
|
+
hash = hash.to_mash
|
76
|
+
else
|
77
|
+
raise "Unsupported body document type: #{mime.to_s}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
return hash.values.first
|
81
|
+
end
|
82
|
+
|
83
|
+
def response_to(res)
|
84
|
+
mime = @mime_types.first unless @mime_types.nil?
|
85
|
+
case mime.to_s
|
86
|
+
when 'application/yaml', 'text/yaml'
|
87
|
+
content_type 'yaml'
|
88
|
+
res.to_yaml
|
89
|
+
when 'application/xml', 'text/xml'
|
90
|
+
raise NotImplementedError
|
91
|
+
else
|
92
|
+
content_type 'json'
|
93
|
+
res.to_json
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# I am not going to use error(ex, &blk) hook since it works only
|
98
|
+
# when matches the Exception class exactly. I expect to match
|
99
|
+
# whole subclasses of APIError so that override handle_exception!().
|
100
|
+
def handle_exception!(boom)
|
101
|
+
logger.error(boom)
|
102
|
+
if boom.kind_of?(APIError)
|
103
|
+
@env['sinatra.error'] = boom
|
104
|
+
error(boom.status_code, boom.class.to_s)
|
105
|
+
else
|
106
|
+
super
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def create_volume_from_snapshot(account_id, snapshot_id)
|
111
|
+
vs = find_by_uuid(:VolumeSnapshot, snapshot_id)
|
112
|
+
raise UnknownVolumeSnapshot if vs.nil?
|
113
|
+
vs.create_volume(account_id)
|
114
|
+
end
|
115
|
+
|
116
|
+
def examine_owner(account_resource)
|
117
|
+
if @account.canonical_uuid == account_resource.account_id ||
|
118
|
+
@account.canonical_uuid == 'a-00000000'
|
119
|
+
return true
|
120
|
+
else
|
121
|
+
return false
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
collection :accounts do
|
126
|
+
operation :index do
|
127
|
+
control do
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
operation :show do
|
132
|
+
control do
|
133
|
+
a = find_account(params[:id])
|
134
|
+
respond_to { |f|
|
135
|
+
f.json { a.to_hash_document.to_json }
|
136
|
+
}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
operation :create do
|
141
|
+
description 'Register a new account'
|
142
|
+
control do
|
143
|
+
a = Models::Account.create()
|
144
|
+
respond_to { |f|
|
145
|
+
f.json { a.to_hash_document.to_json }
|
146
|
+
}
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
operation :destroy do
|
151
|
+
description 'Unregister the account.'
|
152
|
+
# Associated resources all have to be destroied prior to
|
153
|
+
# removing the account.
|
154
|
+
#param :id, :string, :required
|
155
|
+
control do
|
156
|
+
a = find_account(params[:id])
|
157
|
+
a.destroy
|
158
|
+
|
159
|
+
respond_to { |f|
|
160
|
+
f.json { {} }
|
161
|
+
}
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
operation :enable, :method=>:get, :member=>true do
|
166
|
+
description 'Enable the account for all operations'
|
167
|
+
control do
|
168
|
+
a = find_account(params[:id])
|
169
|
+
a.enabled = Models::Account::ENABLED
|
170
|
+
a.save
|
171
|
+
|
172
|
+
respond_to { |f|
|
173
|
+
f.json { {} }
|
174
|
+
}
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
operation :disable, :method=>:get, :member=>true do
|
179
|
+
description 'Disable the account for all operations'
|
180
|
+
control do
|
181
|
+
a = find_account(params[:id])
|
182
|
+
a.enabled = Models::Account::DISABLED
|
183
|
+
a.save
|
184
|
+
|
185
|
+
respond_to { |f|
|
186
|
+
f.json { {} }
|
187
|
+
}
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
operation :add_tag, :method=>:get, :member=>true do
|
192
|
+
description 'Add a tag belongs to the account'
|
193
|
+
#param :tag_name, :string, :required
|
194
|
+
control do
|
195
|
+
a = find_account(params[:id])
|
196
|
+
|
197
|
+
tag_class = Models::Tags.find_tag_class(params[:tag_name])
|
198
|
+
raise "UnknownTagClass: #{params[:tag_name]}" if tag_class.nil?
|
199
|
+
|
200
|
+
a.add_tag(tag_class.new(:name=>params[:name]))
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
operation :remove_tag, :method=>:get, :member=>true do
|
205
|
+
description 'Unlink the associated tag of the account'
|
206
|
+
#param :tag_id, :string, :required
|
207
|
+
control do
|
208
|
+
a = find_account(params[:id])
|
209
|
+
t = a.tags_dataset.filter(:uuid=>params[:tag_id]).first
|
210
|
+
if t
|
211
|
+
a.remove_tag(t)
|
212
|
+
else
|
213
|
+
raise "Unknown or disassociated tag for #{a.cuuid}: #{params[:tag_id]}"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
collection :tags do
|
220
|
+
operation :create do
|
221
|
+
description 'Register new tag to the account'
|
222
|
+
#param :tag_name, :string, :required
|
223
|
+
#param :type_id, :fixnum, :optional
|
224
|
+
#param :account_id, :string, :optional
|
225
|
+
control do
|
226
|
+
tag_class = Models::Tag.find_tag_class(params[:tag_name])
|
227
|
+
|
228
|
+
tag_class.create
|
229
|
+
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
operation :show do
|
234
|
+
#param :account_id, :string, :optional
|
235
|
+
control do
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
operation :destroy do
|
240
|
+
description 'Create a new user'
|
241
|
+
control do
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
operation :update do
|
246
|
+
control do
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# Endpoint to handle VM instance.
|
252
|
+
collection :instances do
|
253
|
+
operation :index do
|
254
|
+
description 'Show list of instances'
|
255
|
+
# params start, fixnum, optional
|
256
|
+
# params limit, fixnum, optional
|
257
|
+
control do
|
258
|
+
start = params[:start].to_i
|
259
|
+
start = start < 1 ? 0 : start
|
260
|
+
limit = params[:limit].to_i
|
261
|
+
limit = limit < 1 ? nil : limit
|
262
|
+
|
263
|
+
total_ds = Models::Instance.where(:account_id=>@account.canonical_uuid)
|
264
|
+
partial_ds = total_ds.dup.order(:id)
|
265
|
+
partial_ds = partial_ds.limit(limit, start) if limit.is_a?(Integer)
|
266
|
+
|
267
|
+
res = [{
|
268
|
+
:owner_total => total_ds.count,
|
269
|
+
:start => start,
|
270
|
+
:limit => limit,
|
271
|
+
:results=> partial_ds.all.map {|i| i.to_api_document }
|
272
|
+
}]
|
273
|
+
|
274
|
+
respond_to { |f|
|
275
|
+
f.json {res.to_json}
|
276
|
+
}
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
operation :create do
|
281
|
+
description 'Runs a new VM instance'
|
282
|
+
# param :image_id, string, :required
|
283
|
+
# param :instance_spec_id, string, :required
|
284
|
+
# param :host_pool_id, string, :optional
|
285
|
+
# param :host_name, string, :optional
|
286
|
+
# param :user_data, string, :optional
|
287
|
+
# param :nf_group, array, :optional
|
288
|
+
# param :ssh_key, string, :optional
|
289
|
+
control do
|
290
|
+
Models::Instance.lock!
|
291
|
+
|
292
|
+
wmi = find_by_uuid(:Image, params[:image_id])
|
293
|
+
spec = find_by_uuid(:InstanceSpec, (params[:instance_spec_id] || 'is-kpf0pasc'))
|
294
|
+
|
295
|
+
if params[:host_pool_id]
|
296
|
+
hp = Models::HostPool[params[:host_pool_id]]
|
297
|
+
raise OutOfHostCapacity unless hp.check_capacity(spec)
|
298
|
+
else
|
299
|
+
# TODO: schedule a host pool owned by SharedPool account.
|
300
|
+
end
|
301
|
+
|
302
|
+
raise UnknownHostPool, "Could not find host pool: #{params[:host_pool_id]}" if hp.nil?
|
303
|
+
|
304
|
+
inst = hp.create_instance(@account, wmi, spec) do |i|
|
305
|
+
# TODO: do not use rand() to decide vnc port.
|
306
|
+
i.runtime_config = {:vnc_port=>rand(2000), :telnet_port=> (rand(2000) + 2000)}
|
307
|
+
i.user_data = params[:user_data] || ''
|
308
|
+
|
309
|
+
if params[:ssh_key]
|
310
|
+
ssh_key_pair = Models::SshKeyPair.find(:account_id=>@account.canonical_uuid,
|
311
|
+
:name=>params[:ssh_key])
|
312
|
+
if ssh_key_pair.nil?
|
313
|
+
raise UnknownSshKeyPair, "#{params[:ssh_key]}"
|
314
|
+
else
|
315
|
+
i.ssh_key_pair_id = ssh_key_pair.canonical_uuid
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
unless params[:nf_group].is_a?(Array)
|
321
|
+
params[:nf_group] = ['default']
|
322
|
+
end
|
323
|
+
inst.join_nfgroup_by_name(@account.canonical_uuid, params[:nf_group])
|
324
|
+
|
325
|
+
case wmi.boot_dev_type
|
326
|
+
when Models::Image::BOOT_DEV_SAN
|
327
|
+
# create new volume from snapshot.
|
328
|
+
snapshot_id = wmi.source[:snapshot_id]
|
329
|
+
vol = create_volume_from_snapshot(@account.canonical_uuid, snapshot_id)
|
330
|
+
|
331
|
+
vol.instance = inst
|
332
|
+
vol.save
|
333
|
+
res = Dcmgr.messaging.submit("kvm-handle.#{hp.node_id}", 'run_vol_store', inst.canonical_uuid, vol.canonical_uuid)
|
334
|
+
when Models::Image::BOOT_DEV_LOCAL
|
335
|
+
res = Dcmgr.messaging.submit("kvm-handle.#{hp.node_id}", 'run_local_store', inst.canonical_uuid)
|
336
|
+
else
|
337
|
+
raise "Unknown boot type"
|
338
|
+
end
|
339
|
+
respond_to { |f|
|
340
|
+
f.json { inst.to_api_document.to_json }
|
341
|
+
}
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
operation :show do
|
346
|
+
#param :account_id, :string, :optional
|
347
|
+
control do
|
348
|
+
i = find_by_uuid(:Instance, params[:id])
|
349
|
+
raise UnknownInstance if i.nil?
|
350
|
+
|
351
|
+
respond_to { |f|
|
352
|
+
f.json { i.to_api_document.to_json }
|
353
|
+
}
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
operation :destroy do
|
358
|
+
description 'Shutdown the instance'
|
359
|
+
control do
|
360
|
+
Models::Instance.lock!
|
361
|
+
i = find_by_uuid(:Instance, params[:id])
|
362
|
+
if examine_owner(i)
|
363
|
+
else
|
364
|
+
raise OperationNotPermitted
|
365
|
+
end
|
366
|
+
res = Dcmgr.messaging.submit("kvm-handle.#{i.host_pool.node_id}", 'terminate', i.canonical_uuid)
|
367
|
+
respond_to { |f|
|
368
|
+
f.json { i.canonical_uuid }
|
369
|
+
}
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
operation :reboot, :method=>:put, :member=>true do
|
374
|
+
description 'Reboots the instance'
|
375
|
+
control do
|
376
|
+
Models::Instance.lock!
|
377
|
+
i = find_by_uuid(:Instance, params[:id])
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
collection :images do
|
383
|
+
operation :create do
|
384
|
+
description 'Register new machine image'
|
385
|
+
control do
|
386
|
+
Models::Image.lock!
|
387
|
+
raise NotImplementedError
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
operation :index do
|
392
|
+
description 'Show list of machine images'
|
393
|
+
control do
|
394
|
+
start = params[:start].to_i
|
395
|
+
start = start < 1 ? 0 : start
|
396
|
+
limit = params[:limit].to_i
|
397
|
+
limit = limit < 1 ? nil : limit
|
398
|
+
|
399
|
+
total_ds = Models::Image.where(:account_id=>@account.canonical_uuid)
|
400
|
+
partial_ds = total_ds.dup.order(:id)
|
401
|
+
partial_ds = partial_ds.limit(limit, start) if limit.is_a?(Integer)
|
402
|
+
|
403
|
+
res = [{
|
404
|
+
:owner_total => total_ds.count,
|
405
|
+
:start => start,
|
406
|
+
:limit => limit,
|
407
|
+
:results=> partial_ds.all.map {|i| i.to_hash }
|
408
|
+
}]
|
409
|
+
|
410
|
+
respond_to { |f|
|
411
|
+
f.json {res.to_json}
|
412
|
+
}
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
operation :show do
|
417
|
+
description "Show a machine image details."
|
418
|
+
control do
|
419
|
+
i = find_by_uuid(:Image, params[:id])
|
420
|
+
# TODO: add visibility by account check
|
421
|
+
unless examine_owner(i)
|
422
|
+
raise OperationNotPermitted
|
423
|
+
end
|
424
|
+
respond_to { |f|
|
425
|
+
f.json { i.to_hash.to_json }
|
426
|
+
}
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
operation :destroy do
|
431
|
+
description 'Delete a machine image'
|
432
|
+
control do
|
433
|
+
Models::Image.lock!
|
434
|
+
i = find_by_uuid(:Image, params[:id])
|
435
|
+
if examine_owner(i)
|
436
|
+
i.delete
|
437
|
+
else
|
438
|
+
raise OperationNotPermitted
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
collection :host_pools do
|
445
|
+
operation :index do
|
446
|
+
description 'Show list of host pools'
|
447
|
+
control do
|
448
|
+
start = params[:start].to_i
|
449
|
+
start = start < 1 ? 0 : start
|
450
|
+
limit = params[:limit].to_i
|
451
|
+
limit = limit < 1 ? nil : limit
|
452
|
+
|
453
|
+
total_ds = Models::HostPool.where(:account_id=>@account.canonical_uuid)
|
454
|
+
partial_ds = total_ds.dup.order(:id)
|
455
|
+
partial_ds = partial_ds.limit(limit, start) if limit.is_a?(Integer)
|
456
|
+
|
457
|
+
res = [{
|
458
|
+
:owner_total => total_ds.count,
|
459
|
+
:start => start,
|
460
|
+
:limit => limit,
|
461
|
+
:results=> partial_ds.all.map {|i| i.to_hash }
|
462
|
+
}]
|
463
|
+
|
464
|
+
respond_to { |f|
|
465
|
+
f.json {res.to_json}
|
466
|
+
}
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
operation :show do
|
471
|
+
description 'Show status of the host'
|
472
|
+
#param :account_id, :string, :optional
|
473
|
+
control do
|
474
|
+
hp = find_by_uuid(:HostPool, params[:id])
|
475
|
+
raise OperationNotPermitted unless examine_owner(hp)
|
476
|
+
|
477
|
+
respond_to { |f|
|
478
|
+
f.json { hp.to_hash.to_json }
|
479
|
+
}
|
480
|
+
end
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
collection :volumes do
|
485
|
+
operation :index do
|
486
|
+
description 'Show lists of the volume'
|
487
|
+
# params start, fixnum, optional
|
488
|
+
# params limit, fixnum, optional
|
489
|
+
control do
|
490
|
+
start = params[:start].to_i
|
491
|
+
start = start < 1 ? 0 : start
|
492
|
+
limit = params[:limit].to_i
|
493
|
+
limit = limit < 1 ? nil : limit
|
494
|
+
|
495
|
+
total_v = Models::Volume.where(:account_id => @account.canonical_uuid)
|
496
|
+
partial_v = total_v.dup.order(:id)
|
497
|
+
partial_v = partial_v.limit(limit, start) if limit.is_a?(Integer)
|
498
|
+
res = [{
|
499
|
+
:owner_total => total_v.count,
|
500
|
+
:start => start,
|
501
|
+
:limit => limit,
|
502
|
+
:results => partial_v.all.map { |v| v.to_hash_document}
|
503
|
+
}]
|
504
|
+
respond_to { |f|
|
505
|
+
f.json { res.to_json}
|
506
|
+
}
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
operation :show do
|
511
|
+
description 'Show the volume status'
|
512
|
+
# params id, string, required
|
513
|
+
control do
|
514
|
+
volume_id = params[:id]
|
515
|
+
raise UndefinedVolumeID if volume_id.nil?
|
516
|
+
v = find_by_uuid(:Volume, volume_id)
|
517
|
+
respond_to { |f|
|
518
|
+
f.json { v.to_hash_document.to_json}
|
519
|
+
}
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
operation :create do
|
524
|
+
description 'Create the new volume'
|
525
|
+
# params volume_size, string, required
|
526
|
+
# params snapshot_id, string, optional
|
527
|
+
# params storage_pool_id, string, optional
|
528
|
+
control do
|
529
|
+
Models::Volume.lock!
|
530
|
+
if params[:snapshot_id]
|
531
|
+
v = create_volume_from_snapshot(@account.canonical_uuid, params[:snapshot_id])
|
532
|
+
sp = v.storage_pool
|
533
|
+
elsif params[:volume_size]
|
534
|
+
raise InvalidVolumeSize if !(Dcmgr.conf.create_volume_max_size.to_i >= params[:volume_size].to_i) || !(params[:volume_size\
|
535
|
+
].to_i >= Dcmgr.conf.create_volume_min_size.to_i)
|
536
|
+
if params[:storage_pool_id]
|
537
|
+
sp = find_by_uuid(:StoragePool, params[:storage_pool_id])
|
538
|
+
raise StoragePoolNotPermitted if sp.account_id != @account.canonical_uuid
|
539
|
+
end
|
540
|
+
raise UnknownStoragePool if sp.nil?
|
541
|
+
begin
|
542
|
+
v = sp.create_volume(@account.canonical_uuid, params[:volume_size])
|
543
|
+
rescue Models::Volume::DiskError => e
|
544
|
+
logger.error(e)
|
545
|
+
raise OutOfDiskSpace
|
546
|
+
rescue Sequel::DatabaseError => e
|
547
|
+
logger.error(e)
|
548
|
+
raise DatabaseError
|
549
|
+
end
|
550
|
+
else
|
551
|
+
raise UndefinedRequiredParameter
|
552
|
+
end
|
553
|
+
|
554
|
+
res = Dcmgr.messaging.submit("zfs-handle.#{sp.values[:node_id]}", 'create_volume', v.canonical_uuid)
|
555
|
+
respond_to { |f|
|
556
|
+
f.json { v.to_hash_document.to_json}
|
557
|
+
}
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
operation :destroy do
|
562
|
+
description 'Delete the volume'
|
563
|
+
# params id, string, required
|
564
|
+
control do
|
565
|
+
Models::Volume.lock!
|
566
|
+
volume_id = params[:id]
|
567
|
+
raise UndefinedVolumeID if volume_id.nil?
|
568
|
+
|
569
|
+
begin
|
570
|
+
v = Models::Volume.delete_volume(@account.canonical_uuid, volume_id)
|
571
|
+
rescue Models::Volume::RequestError => e
|
572
|
+
logger.error(e)
|
573
|
+
raise InvalidDeleteRequest
|
574
|
+
end
|
575
|
+
raise UnknownVolume if v.nil?
|
576
|
+
sp = v.storage_pool
|
577
|
+
|
578
|
+
res = Dcmgr.messaging.submit("zfs-handle.#{sp.values[:node_id]}", 'delete_volume', v.canonical_uuid)
|
579
|
+
respond_to { |f|
|
580
|
+
f.json { v.to_hash_document.to_json}
|
581
|
+
}
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
operation :attach, :method =>:put, :member =>true do
|
586
|
+
description 'Attachd the volume'
|
587
|
+
# params id, string, required
|
588
|
+
# params instance_id, string, required
|
589
|
+
control do
|
590
|
+
raise UndefinedInstanceID if params[:instance_id].nil?
|
591
|
+
raise UndefinedVolumeID if params[:id].nil?
|
592
|
+
|
593
|
+
i = find_by_uuid(:Instance, params[:instance_id])
|
594
|
+
raise UnknownInstance if i.nil?
|
595
|
+
|
596
|
+
v = find_by_uuid(:Volume, params[:id])
|
597
|
+
raise UnknownVolume if v.nil?
|
598
|
+
|
599
|
+
v.instance = i
|
600
|
+
v.save
|
601
|
+
res = Dcmgr.messaging.submit("kvm-handle.#{i.host_pool.node_id}", 'attach', i.canonical_uuid, v.canonical_uuid)
|
602
|
+
|
603
|
+
respond_to { |f|
|
604
|
+
f.json { v.to_hash_document.to_json}
|
605
|
+
}
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
operation :detach, :method =>:put, :member =>true do
|
610
|
+
description 'Detachd the volume'
|
611
|
+
# params id, string, required
|
612
|
+
control do
|
613
|
+
raise UndefinedVolumeID if params[:id].nil?
|
614
|
+
|
615
|
+
v = find_by_uuid(:Volume, params[:id])
|
616
|
+
raise UnknownVolume if v.nil?
|
617
|
+
i = v.instance
|
618
|
+
res = Dcmgr.messaging.submit("kvm-handle.#{i.host_pool.node_id}", 'detach', i.canonical_uuid, v.canonical_uuid)
|
619
|
+
respond_to { |f|
|
620
|
+
f.json {v.to_hash_document.to_json}
|
621
|
+
}
|
622
|
+
end
|
623
|
+
end
|
624
|
+
|
625
|
+
operation :status, :method =>:get, :member =>true do
|
626
|
+
description 'Show the status'
|
627
|
+
control do
|
628
|
+
vl = [{ :id => 1, :uuid => 'vol-xxxxxxx', :status => 1 },
|
629
|
+
{ :id => 2, :uuid => 'vol-xxxxxxx', :status => 0 },
|
630
|
+
{ :id => 3, :uuid => 'vol-xxxxxxx', :status => 3 },
|
631
|
+
{ :id => 4, :uuid => 'vol-xxxxxxx', :status => 2 },
|
632
|
+
{ :id => 5, :uuid => 'vol-xxxxxxx', :status => 4 }]
|
633
|
+
respond_to {|f|
|
634
|
+
f.json { vl.to_json}
|
635
|
+
}
|
636
|
+
end
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
collection :volume_snapshots do
|
641
|
+
operation :index do
|
642
|
+
description 'Show lists of the volume_snapshots'
|
643
|
+
# params start, fixnum, optional
|
644
|
+
# params limit, fixnum, optional
|
645
|
+
control do
|
646
|
+
start = params[:start].to_i
|
647
|
+
start = start < 1 ? 0 : start
|
648
|
+
limit = params[:limit].to_i
|
649
|
+
limit = limit < 1 ? nil : limit
|
650
|
+
|
651
|
+
total_vs = Models::VolumeSnapshot.where(:account_id => @account.canonical_uuid)
|
652
|
+
partial_vs = total_vs.dup.order(:id)
|
653
|
+
partial_vs = partial_vs.limit(limit, start) if limit.is_a?(Integer)
|
654
|
+
res = [{
|
655
|
+
:owner_total => total_vs.count,
|
656
|
+
:start => start,
|
657
|
+
:limit => limit,
|
658
|
+
:results => partial_vs.all.map { |vs| vs.to_hash_document}
|
659
|
+
}]
|
660
|
+
respond_to { |f|
|
661
|
+
f.json { res.to_json}
|
662
|
+
}
|
663
|
+
end
|
664
|
+
end
|
665
|
+
|
666
|
+
operation :show do
|
667
|
+
description 'Show the volume status'
|
668
|
+
# params id, string, required
|
669
|
+
control do
|
670
|
+
snapshot_id = params[:id]
|
671
|
+
raise UndefinedVolumeSnapshotID if snapshot_id.nil?
|
672
|
+
vs = find_by_uuid(:VolumeSnapshot, snapshot_id)
|
673
|
+
respond_to { |f|
|
674
|
+
f.json { vs.to_hash_document.to_json}
|
675
|
+
}
|
676
|
+
end
|
677
|
+
end
|
678
|
+
|
679
|
+
operation :create do
|
680
|
+
description 'Create a new volume snapshot'
|
681
|
+
# params volume_id, string, required
|
682
|
+
# params storage_pool_id, string, optional
|
683
|
+
control do
|
684
|
+
Models::Volume.lock!
|
685
|
+
raise UndefinedVolumeID if params[:volume_id].nil?
|
686
|
+
|
687
|
+
v = find_by_uuid(:Volume, params[:volume_id])
|
688
|
+
raise UnknownVolume if v.nil?
|
689
|
+
|
690
|
+
vs = v.create_snapshot(@account.canonical_uuid)
|
691
|
+
sp = vs.storage_pool
|
692
|
+
|
693
|
+
res = Dcmgr.messaging.submit("zfs-handle.#{sp.node_id}", 'create_snapshot', vs.canonical_uuid)
|
694
|
+
respond_to { |f|
|
695
|
+
f.json { vs.to_hash_document.to_json}
|
696
|
+
}
|
697
|
+
end
|
698
|
+
end
|
699
|
+
|
700
|
+
operation :destroy do
|
701
|
+
description 'Delete the volume snapshot'
|
702
|
+
# params id, string, required
|
703
|
+
control do
|
704
|
+
Models::VolumeSnapshot.lock!
|
705
|
+
snapshot_id = params[:id]
|
706
|
+
raise UndefindVolumeSnapshotID if snapshot_id.nil?
|
707
|
+
|
708
|
+
vs = find_by_uuid(:VolumeSnapshot, snapshot_id)
|
709
|
+
raise UnknownVolumeSnapshot if vs.nil?
|
710
|
+
vs = vs.delete_snapshot
|
711
|
+
sp = vs.storage_pool
|
712
|
+
|
713
|
+
res = Dcmgr.messaging.submit("zfs-handle.#{sp.node_id}", 'delete_snapshot', vs.canonical_uuid)
|
714
|
+
respond_to { |f|
|
715
|
+
f.json { vs.to_hash_document.to_json }
|
716
|
+
}
|
717
|
+
end
|
718
|
+
end
|
719
|
+
|
720
|
+
operation :status, :method =>:get, :member =>true do
|
721
|
+
description 'Show the status'
|
722
|
+
control do
|
723
|
+
vs = [{ :id => 1, :uuid => 'snap-xxxxxxx', :status => 1 },
|
724
|
+
{ :id => 2, :uuid => 'snap-xxxxxxx', :status => 0 },
|
725
|
+
{ :id => 3, :uuid => 'snap-xxxxxxx', :status => 3 },
|
726
|
+
{ :id => 4, :uuid => 'snap-xxxxxxx', :status => 2 },
|
727
|
+
{ :id => 5, :uuid => 'snap-xxxxxxx', :status => 4 }]
|
728
|
+
respond_to {|f|
|
729
|
+
f.json { vs.to_json}
|
730
|
+
}
|
731
|
+
end
|
732
|
+
end
|
733
|
+
end
|
734
|
+
|
735
|
+
collection :netfilter_groups do
|
736
|
+
description 'Show lists of the netfilter_groups'
|
737
|
+
operation :index do
|
738
|
+
control do
|
739
|
+
start = params[:start].to_i
|
740
|
+
start = start < 1 ? 0 : start
|
741
|
+
limit = params[:limit].to_i
|
742
|
+
limit = limit < 1 ? nil : limit
|
743
|
+
|
744
|
+
total_ds = Models::NetfilterGroup.where(:account_id=>@account.canonical_uuid)
|
745
|
+
partial_ds = total_ds.dup.order(:id)
|
746
|
+
partial_ds = partial_ds.limit(limit, start) if limit.is_a?(Integer)
|
747
|
+
|
748
|
+
res = [{
|
749
|
+
:owner_total => total_ds.count,
|
750
|
+
:start => start,
|
751
|
+
:limit => limit,
|
752
|
+
:results=> partial_ds.all.map {|i| i.to_hash }
|
753
|
+
}]
|
754
|
+
|
755
|
+
respond_to { |f|
|
756
|
+
f.json {res.to_json}
|
757
|
+
}
|
758
|
+
end
|
759
|
+
end
|
760
|
+
|
761
|
+
operation :show do
|
762
|
+
description 'Show the netfilter_groups'
|
763
|
+
control do
|
764
|
+
g = find_by_uuid(:NetfilterGroup, params[:id])
|
765
|
+
p params[:id]
|
766
|
+
raise OperationNotPermitted unless examine_owner(g)
|
767
|
+
|
768
|
+
respond_to { |f|
|
769
|
+
f.json { g.to_hash.to_json }
|
770
|
+
}
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
operation :create do
|
775
|
+
description 'Register a new netfilter_group'
|
776
|
+
# params name, string
|
777
|
+
# params description, string
|
778
|
+
# params rule, string
|
779
|
+
control do
|
780
|
+
Models::NetfilterGroup.lock!
|
781
|
+
raise UndefinedNetfilterGroup if params[:name].nil?
|
782
|
+
|
783
|
+
@name = params[:name]
|
784
|
+
# TODO: validate @name. @name can use [a-z] [A-Z] '_' '-'
|
785
|
+
# - invalidate? -> raise InvalidCharacterOfNetfilterGroupName
|
786
|
+
|
787
|
+
g = Models::NetfilterGroup.filter(:name => @name, :account_id => @account.canonical_uuid).first
|
788
|
+
raise DuplicatedNetfilterGroup unless g.nil?
|
789
|
+
|
790
|
+
g = Models::NetfilterGroup.create_group(@account.canonical_uuid, params)
|
791
|
+
respond_to { |f|
|
792
|
+
f.json { g.to_hash.to_json }
|
793
|
+
}
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
797
|
+
operation :update do
|
798
|
+
description "Update parameters for the netfilter group"
|
799
|
+
# params description, string
|
800
|
+
# params rule, string
|
801
|
+
control do
|
802
|
+
g = find_by_uuid(:NetfilterGroup, params[:id])
|
803
|
+
|
804
|
+
raise UnknownNetfilterGroup if g.nil?
|
805
|
+
|
806
|
+
if params[:description]
|
807
|
+
g.description = params[:description]
|
808
|
+
end
|
809
|
+
if params[:rule]
|
810
|
+
g.rule = params[:rule]
|
811
|
+
end
|
812
|
+
|
813
|
+
g.save
|
814
|
+
g.rebuild_rule
|
815
|
+
|
816
|
+
# refresh netfilter_rules
|
817
|
+
Dcmgr.messaging.event_publish('hva/netfilter_updated', :args=>[g.canonical_uuid])
|
818
|
+
|
819
|
+
respond_to { |f|
|
820
|
+
f.json { g.to_hash.to_json }
|
821
|
+
}
|
822
|
+
end
|
823
|
+
end
|
824
|
+
|
825
|
+
operation :destroy do
|
826
|
+
# params name, string
|
827
|
+
description "Delete the netfilter group"
|
828
|
+
|
829
|
+
control do
|
830
|
+
Models::NetfilterGroup.lock!
|
831
|
+
g = find_by_uuid(:NetfilterGroup, params[:id])
|
832
|
+
|
833
|
+
raise UnknownNetfilterGroup if g.nil?
|
834
|
+
raise NetfilterGroupNotPermitted if g.account_id != @account.canonical_uuid
|
835
|
+
|
836
|
+
respond_to { |f|
|
837
|
+
f.json { g.destroy_group.values.to_json }
|
838
|
+
}
|
839
|
+
end
|
840
|
+
end
|
841
|
+
|
842
|
+
end
|
843
|
+
|
844
|
+
collection :netfilter_rules do
|
845
|
+
operation :index do
|
846
|
+
control do
|
847
|
+
end
|
848
|
+
end
|
849
|
+
|
850
|
+
operation :show do
|
851
|
+
description 'Show lists of the netfilter_rules'
|
852
|
+
control do
|
853
|
+
rules = []
|
854
|
+
begin
|
855
|
+
@name = params[:id]
|
856
|
+
g = Models::NetfilterGroup.filter(:name => @name, :account_id => @account.canonical_uuid).first
|
857
|
+
raise UnknownNetfilterGroup if g.nil?
|
858
|
+
|
859
|
+
g.netfilter_rules.each { |rule|
|
860
|
+
rules << rule.values
|
861
|
+
}
|
862
|
+
end
|
863
|
+
|
864
|
+
respond_to { |f|
|
865
|
+
f.json { rules.to_json }
|
866
|
+
}
|
867
|
+
end
|
868
|
+
end
|
869
|
+
end
|
870
|
+
|
871
|
+
collection :storage_pools do
|
872
|
+
operation :index do
|
873
|
+
description 'Show lists of the storage_pools'
|
874
|
+
# params start, fixnum, optional
|
875
|
+
# params limit, fixnum, optional
|
876
|
+
control do
|
877
|
+
start = params[:start].to_i
|
878
|
+
start = start < 1 ? 0 : start
|
879
|
+
limit = params[:limit].to_i
|
880
|
+
limit = limit < 1 ? nil : limit
|
881
|
+
|
882
|
+
total_ds = Models::StoragePool.where(:account_id=>@account.canonical_uuid)
|
883
|
+
partial_ds = total_ds.dup.order(:id)
|
884
|
+
partial_ds = partial_ds.limit(limit, start) if limit.is_a?(Integer)
|
885
|
+
|
886
|
+
res = [{
|
887
|
+
:owner_total => total_ds.count,
|
888
|
+
:start => start,
|
889
|
+
:limit => limit,
|
890
|
+
:results=> partial_ds.all.map {|sp| sp.to_hash_document }
|
891
|
+
}]
|
892
|
+
|
893
|
+
respond_to { |f|
|
894
|
+
f.json { res.to_json}
|
895
|
+
}
|
896
|
+
end
|
897
|
+
end
|
898
|
+
|
899
|
+
operation :show do
|
900
|
+
description 'Show the storage_pool status'
|
901
|
+
# params id, string, required
|
902
|
+
control do
|
903
|
+
pool_id = params[:id]
|
904
|
+
raise UndefinedStoragePoolID if pool_id.nil?
|
905
|
+
vs = find_by_uuid(:StoragePool, pool_id)
|
906
|
+
raise UnknownStoragePool if vs.nil?
|
907
|
+
respond_to { |f|
|
908
|
+
f.json { vs.to_hash_document.to_json}
|
909
|
+
}
|
910
|
+
end
|
911
|
+
end
|
912
|
+
end
|
913
|
+
|
914
|
+
collection :ssh_key_pairs do
|
915
|
+
description "List ssh key pairs in account"
|
916
|
+
operation :index do
|
917
|
+
# params start, fixnum, optional
|
918
|
+
# params limit, fixnum, optional
|
919
|
+
control do
|
920
|
+
start = params[:start].to_i
|
921
|
+
start = start < 1 ? 0 : start
|
922
|
+
limit = params[:limit].to_i
|
923
|
+
limit = limit < 1 ? nil : limit
|
924
|
+
|
925
|
+
total_ds = Models::SshKeyPair.where(:account_id=>@account.canonical_uuid)
|
926
|
+
partial_ds = total_ds.dup.order(:id)
|
927
|
+
partial_ds = partial_ds.limit(limit, start) if limit.is_a?(Integer)
|
928
|
+
|
929
|
+
res = [{
|
930
|
+
:owner_total => total_ds.count,
|
931
|
+
:filter_total => total_ds.count,
|
932
|
+
:start => start,
|
933
|
+
:limit => limit,
|
934
|
+
:results=> partial_ds.all.map {|i| i.to_hash }
|
935
|
+
}]
|
936
|
+
|
937
|
+
respond_to { |f|
|
938
|
+
f.json {res.to_json}
|
939
|
+
}
|
940
|
+
end
|
941
|
+
end
|
942
|
+
|
943
|
+
operation :show do
|
944
|
+
description "Retrieve details about ssh key pair"
|
945
|
+
# params :id required
|
946
|
+
# params :format optional [openssh,putty]
|
947
|
+
control do
|
948
|
+
ssh = find_by_uuid(:SshKeyPair, params[:id])
|
949
|
+
|
950
|
+
respond_to { |f|
|
951
|
+
f.json {ssh.to_hash.to_json}
|
952
|
+
}
|
953
|
+
end
|
954
|
+
end
|
955
|
+
|
956
|
+
operation :create do
|
957
|
+
description "Create ssh key pair information"
|
958
|
+
# params :name required key name (<100 chars)
|
959
|
+
# params :download_once optional set true if you do not want
|
960
|
+
# to save private key info on database.
|
961
|
+
control do
|
962
|
+
Models::SshKeyPair.lock!
|
963
|
+
keydata = Models::SshKeyPair.generate_key_pair
|
964
|
+
savedata = {
|
965
|
+
:name=>params[:name],
|
966
|
+
:account_id=>@account.canonical_uuid,
|
967
|
+
:public_key=>keydata[:public_key]
|
968
|
+
}
|
969
|
+
if params[:download_once] != 'true'
|
970
|
+
savedata[:private_key]=keydata[:private_key]
|
971
|
+
end
|
972
|
+
ssh = Models::SshKeyPair.create(savedata)
|
973
|
+
|
974
|
+
respond_to { |f|
|
975
|
+
# include private_key data in response even if
|
976
|
+
# it's not going to be stored on DB.
|
977
|
+
f.json {ssh.to_hash.merge(:private_key=>keydata[:private_key]).to_json}
|
978
|
+
}
|
979
|
+
end
|
980
|
+
end
|
981
|
+
|
982
|
+
operation :destroy do
|
983
|
+
description "Remove ssh key pair information"
|
984
|
+
# params :id required
|
985
|
+
control do
|
986
|
+
Models::SshKeyPair.lock!
|
987
|
+
ssh = find_by_uuid(:SshKeyPair, params[:id])
|
988
|
+
if examine_owner(ssh)
|
989
|
+
ssh.destroy
|
990
|
+
else
|
991
|
+
raise OperationNotPermitted
|
992
|
+
end
|
993
|
+
|
994
|
+
respond_to { |f|
|
995
|
+
f.json {ssh.to_hash.to_json}
|
996
|
+
}
|
997
|
+
end
|
998
|
+
end
|
999
|
+
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
end
|
1003
|
+
end
|
1004
|
+
end
|