vagrant-subutai 1.0.3 → 1.1.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.
@@ -0,0 +1,547 @@
1
+ require_relative '../../vagrant-subutai'
2
+ require 'json'
3
+
4
+ module VagrantSubutai
5
+ module Blueprint
6
+ class VariablesController
7
+ attr_accessor :json,
8
+ :variables,
9
+ :required_ram, # sum of all containers ram size required (in unit GB)
10
+ :required_disk, # sum of all containers disk size required (in unit GB)
11
+ :available_ram, # sum of all containers ram size free (in unit GB)
12
+ :available_disk, # sum of all containers disk size free (in unit GB)
13
+ :mode, # Environment build mode (peer, bazaar)
14
+ :cookies # Bazaar cookies(for reserving domain)
15
+
16
+ KEYS = {
17
+ name: 'name',
18
+ description: 'description',
19
+ containers: 'containers',
20
+ hostname: 'hostname',
21
+ template: 'template',
22
+ size: 'size',
23
+ peer_criteria: 'peer-criteria',
24
+ port_mapping: 'port-mapping',
25
+ protocol: 'protocol',
26
+ domain: 'domain',
27
+ internal_port: 'internal-port',
28
+ external_port: 'external-port',
29
+ max_price: 'max-price',
30
+ avg_cpu_load: 'avg-cpu-load',
31
+ min_free_ram: 'min-free-ram',
32
+ min_free_disk_space: 'min-free-disk-space',
33
+ user_variables: 'user-variables',
34
+ type: 'type',
35
+ default: 'default',
36
+ validation: 'validation',
37
+ ansible_configuration: 'ansible-configuration',
38
+ extra_vars: 'extra-vars',
39
+ key: 'key',
40
+ value: 'value',
41
+ source_url: 'source-url',
42
+ ansible_playbook: 'ansible-playbook',
43
+ groups: 'groups',
44
+ hostnames: 'hostnames'
45
+ }.freeze
46
+
47
+ # @params available_ram, available_disk
48
+ def initialize(available_ram, available_disk, mode)
49
+ @required_ram = 0
50
+ @required_disk = 0
51
+ @available_ram = available_ram
52
+ @available_disk = available_disk
53
+ @mode = mode
54
+
55
+ begin
56
+ @json = JSON.parse(File.read("#{Dir.pwd}/#{Configs::Blueprint::FILE_NAME}"))
57
+ rescue => e
58
+ Put.error e
59
+ exit!
60
+ end
61
+ end
62
+
63
+ # Gives Subutai.json user variables
64
+ # returns json object
65
+ def user_variables
66
+ hash = {}
67
+
68
+ if @json.key?(KEYS[:user_variables])
69
+ conf_user_variables = SubutaiConfig.get(:USER_VARIABLES)
70
+
71
+ if conf_user_variables.nil?
72
+ conf_user_variables = {}
73
+ else
74
+ if conf_user_variables.kind_of?(String)
75
+ begin
76
+ conf_user_variables = JSON.parse(SubutaiConfig.get(:USER_VARIABLES))
77
+ rescue JSON::ParserError => e
78
+ Put.error e
79
+ return
80
+ end
81
+ end
82
+ end
83
+
84
+ user_variables = @json[KEYS[:user_variables]]
85
+ keys = user_variables.keys
86
+
87
+ keys.each do |key|
88
+ if conf_user_variables[key].nil?
89
+ hash[key] = get_input(user_variables[key])
90
+ else
91
+ hash[key] = conf_user_variables[key]
92
+ end
93
+ end
94
+ end
95
+
96
+ @variables = hash
97
+ end
98
+
99
+ # This counts how mach quota(ram, disk) required for building environment from the Peer Os
100
+ def check_required_quota
101
+ if @json.key?(KEYS[:user_variables])
102
+ user_variables = @json[KEYS[:user_variables]]
103
+ keys = user_variables.keys
104
+
105
+ keys.each do |key|
106
+ if user_variables[key][KEYS[:type]] == 'enum' && Configs::Blueprint::CONTAINER_SIZES.include?(user_variables[key][KEYS[:default]])
107
+ @required_ram += (VagrantSubutai::Configs::Quota::RESOURCE[(user_variables[key][KEYS[:default]]).strip.to_sym][:RAM])
108
+ @required_disk += (VagrantSubutai::Configs::Quota::RESOURCE[(user_variables[key][KEYS[:default]]).strip.to_sym][:DISK])
109
+ end
110
+ end
111
+
112
+ @required_ram += VagrantSubutai::Configs::Quota::RESOURCE[:TINY][:RAM] if @json.key?(KEYS[:ansible_configuration]) # default ansible container ram
113
+ @required_disk += VagrantSubutai::Configs::Quota::RESOURCE[:TINY][:DISK] if @json.key?(KEYS[:ansible_configuration]) # default ansible container disk
114
+ else
115
+ @json[KEYS[:containers]].each do |container|
116
+ @required_ram += (VagrantSubutai::Configs::Quota::RESOURCE[(container[KEYS[:size]]).to_sym][:RAM])
117
+ @required_disk += (VagrantSubutai::Configs::Quota::RESOURCE[(container[KEYS[:size]]).to_sym][:DISK])
118
+ end
119
+ end
120
+ end
121
+
122
+ def check_quota?(resource)
123
+ resource = JSON.parse(resource)
124
+
125
+ @free_ram = resource['RAM']['free'].to_f / 1073741824 # convert bytes to gb
126
+ @free_disk = (resource['Disk']['total'].to_f - resource['Disk']['used'].to_f) / 1073741824 # convert bytes to gb
127
+
128
+ @free_ram = @free_ram.round(3)
129
+ @free_disk = @free_disk.round(2)
130
+
131
+ check_required_quota
132
+
133
+ if @free_ram >= @required_ram && @free_disk >= @required_disk
134
+ true
135
+ else
136
+ Put.warn "\nNo available resources on the Peer Os\n"
137
+ Put.info "--------------------------------------------------------------------"
138
+ if @free_ram >= @required_ram
139
+ Put.info "RAM: available = #{@free_ram} gb, required minimum = #{@required_ram} gb"
140
+ else
141
+ Put.error "RAM: available = #{@free_ram} gb, required minimum = #{@required_ram} gb"
142
+ end
143
+
144
+ Put.info "--------------------------------------------------------------------"
145
+
146
+ if @free_disk >= @required_disk
147
+ Put.info "DISK: available = #{@free_disk} gb, required minimum = #{@required_disk} gb"
148
+ else
149
+ Put.error "DISK: available = #{@free_disk} gb, required minimum = #{@required_disk} gb"
150
+ end
151
+ Put.info "--------------------------------------------------------------------"
152
+
153
+ false
154
+ end
155
+ end
156
+
157
+ def has_ansible?
158
+ if @json.key?(KEYS[:ansible_configuration])
159
+ true
160
+ else
161
+ false
162
+ end
163
+ end
164
+
165
+ def ansible
166
+
167
+ if has_ansible?
168
+ ansible = VagrantSubutai::Models::Ansible.new
169
+ ansible_configuration = @json[KEYS[:ansible_configuration]]
170
+
171
+ ansible.ansible_playbook = ansible_configuration[KEYS[:ansible_playbook]]
172
+ ansible.source_url = ansible_configuration[KEYS[:source_url]]
173
+ ansible.extra_vars = []
174
+ ansible.groups = []
175
+
176
+ ansible_configuration[KEYS[:groups]].each do |group|
177
+ temp = group
178
+ hostnames = []
179
+
180
+ group[KEYS[:hostnames]].each do |hostname|
181
+ hostnames << value(hostname)
182
+ end
183
+ temp[KEYS[:hostnames]] = hostnames
184
+ ansible.groups << temp
185
+ end
186
+
187
+ if ansible_configuration.key?(KEYS[:extra_vars])
188
+ ansible_configuration[KEYS[:extra_vars]].each do |obj|
189
+ hash = {}
190
+ hash[obj[KEYS[:key]]] = value(obj[KEYS[:value]])
191
+ ansible.extra_vars << hash
192
+ end
193
+ end
194
+
195
+ ansible
196
+ end
197
+ end
198
+
199
+ def params(rh_id, peer_id)
200
+ env = environment
201
+ containers = env.containers
202
+
203
+ hash = {}
204
+ nodes = []
205
+
206
+ if mode == Configs::Blueprint::MODE::PEER
207
+ containers.each do |container|
208
+ node = {}
209
+ node['hostname'] = container.hostname
210
+ node['quota'] = {'containerSize' => container.container_size}
211
+ node['templateId'] = Rest::Gorjun.template_id(container.name, container.owner)
212
+ node['resourceHostId'] = rh_id
213
+ node['peerId'] = peer_id
214
+ nodes << node
215
+ end
216
+
217
+ hash['name'] = env.name
218
+ hash['sshKey'] = ""
219
+ hash['nodes'] = nodes
220
+ elsif mode == Configs::Blueprint::MODE::BAZAAR
221
+ containers.each do |container|
222
+ node = {}
223
+ node['hostname'] = container.hostname
224
+ node['quota'] = {'containerSize' => container.container_size}
225
+ node['templateId'] = Rest::Gorjun.template_id(container.name, container.owner)
226
+ node['resourceHostId'] = rh_id
227
+ node['templateName'] = container.name
228
+ node['peerId'] = peer_id
229
+ nodes << node
230
+ end
231
+
232
+ hash['environmentName'] = env.name
233
+ hash['exchangeSshKeys'] = true
234
+ hash['registerHosts'] = true
235
+ hash['nodes'] = nodes
236
+ end
237
+
238
+
239
+ hash
240
+ end
241
+
242
+ def value(variable)
243
+ if is_variable?(variable)
244
+ @variables[variable[/\${(.*?)}/, 1]]
245
+ else
246
+ variable
247
+ end
248
+ end
249
+
250
+ def is_variable?(var)
251
+ if (var =~ /\${(.*?)}/).nil?
252
+ false
253
+ else
254
+ true
255
+ end
256
+ end
257
+
258
+ # Environment
259
+ # @return Environment model
260
+ def environment
261
+ env = VagrantSubutai::Models::Environment.new
262
+ env.name = value(@json[KEYS[:name]])
263
+ env.containers = containers
264
+ env
265
+ end
266
+
267
+ # Containers
268
+ # @return Container Models
269
+ def containers
270
+ arr = []
271
+
272
+ @json[KEYS[:containers]].each do |container|
273
+ cont = VagrantSubutai::Models::Container.new
274
+
275
+ cont.hostname = value(container[KEYS[:hostname]])
276
+ cont.container_size = value(container[KEYS[:size]])
277
+ cont.template = container[KEYS[:template]]
278
+ cont.peer_criteria = container[KEYS[:peer_criteria]]
279
+ cont.port_mapping = container[KEYS[:port_mapping]]
280
+
281
+ arr << cont
282
+ end
283
+
284
+ if @json.key?(KEYS[:ansible_configuration])
285
+ cont = VagrantSubutai::Models::Container.new
286
+ cont.ansible
287
+ arr << cont
288
+ end
289
+
290
+ arr
291
+ end
292
+
293
+ # Domain
294
+ # @return Domain Model or nil
295
+ def domain
296
+ @json[KEYS[:containers]].each do |container|
297
+ if container.key?(KEYS[:port_mapping])
298
+ container[KEYS[:port_mapping]].each do |port_map|
299
+ if port_map[KEYS[:protocol]] == 'HTTP' || port_map[KEYS[:protocol]] == 'http'
300
+ domain = VagrantSubutai::Models::Domain.new
301
+
302
+ domain.protocol = port_map[KEYS[:protocol]]
303
+ domain.name = value(port_map[KEYS[:domain]])
304
+ domain.internal_port = port_map[KEYS[:internal_port]]
305
+ domain.external_port = port_map[KEYS[:external_port]]
306
+ domain.container_hostname = value(container[KEYS[:hostname]])
307
+
308
+ return domain
309
+ end
310
+ end
311
+ end
312
+ end
313
+
314
+ nil
315
+ end
316
+
317
+ # Gets input variable
318
+ # @params variable json object
319
+ def get_input(variable_json)
320
+ if variable_json[KEYS[:type]] == 'enum'
321
+ Put.info "\nEnter your container size (Ex: #{variable_json[KEYS[:default]]}): "
322
+ validations = variable_json[KEYS[:validation]].split(',')
323
+
324
+ temp = nil
325
+ validations.each_with_index do |validation, index|
326
+ if @available_ram >= Configs::Quota::RESOURCE[validation.strip.to_sym][:RAM] && @available_disk >= Configs::Quota::RESOURCE[validation.strip.to_sym][:DISK]
327
+ Put.info " #{index}. #{validation}"
328
+ temp = index
329
+ end
330
+ end
331
+
332
+ Put.info "\nChoose your container size between ( 0 to #{temp}): "
333
+ input = STDIN.gets.strip.to_i
334
+ validations[input]
335
+ elsif mode == Configs::Blueprint::MODE::BAZAAR && variable_json[KEYS[:type]] == 'domain'
336
+ Put.info "\n#Create a new domain: (Ex: YOUR_DOMAIN_NAME.envs.subutai.cloud)"
337
+ reserve
338
+ else
339
+ Put.info "\n#{variable_json[KEYS[:description]]}: (Ex: #{variable_json[KEYS[:default]]})"
340
+
341
+ STDIN.gets.strip
342
+ end
343
+ end
344
+
345
+ def reserve
346
+ begin
347
+ @temp = STDIN.gets.strip
348
+ @response = VagrantSubutai::Rest::Bazaar.reserve(@cookies, @temp)
349
+
350
+ until @response.kind_of?(Net::HTTPOK)
351
+ Put.warn "\n-------------------------------------------------------------------"
352
+ Put.warn "Requested \"#{@temp}.envs.subutai.cloud\" sub-domain already exists"
353
+ Put.warn '-------------------------------------------------------------------'
354
+
355
+ Put.info "\n#Create a new domain: (Ex: YOUR_DOMAIN_NAME.envs.subutai.cloud)"
356
+ @temp = STDIN.gets.strip
357
+ @response = VagrantSubutai::Rest::Bazaar.reserve(@cookies, @temp)
358
+ end
359
+
360
+ res = VagrantSubutai::Rest::Bazaar.domains(@cookies)
361
+ json = JSON.parse(res.body)
362
+
363
+ json = json.find {|domain| domain['name'].split('.').first == @temp}
364
+
365
+ Put.info "\n Created a new domain: #{json['name']}"
366
+
367
+ json['name']
368
+ rescue => e
369
+ Put.error e
370
+ end
371
+ end
372
+
373
+ # Validate variable
374
+ # @params var, type, validation
375
+ def validate_variable(var, variable_json)
376
+ if (var =~ /#{Regexp.quote(variable_json[KEYS[:validation]])}/).nil?
377
+ false
378
+ else
379
+ true
380
+ end
381
+ end
382
+
383
+ def bazaar_params(variables)
384
+ variables.each do |variable|
385
+ Put.info variable['label']
386
+ end
387
+ end
388
+
389
+ def get_input_bazaar(variable)
390
+ Put.info "\n#{variable['label']}"
391
+ if variable.key?('acceptableValues')
392
+ if variable['type'] == 'enum'
393
+ arr = variable['acceptableValues'].split(',')
394
+ temp = nil
395
+
396
+ arr.each_with_index do |val, index|
397
+ Put.info " #{index}. #{val}"
398
+ temp = index
399
+ end
400
+
401
+ Put.info "\nChoose your container size between ( 0 to #{temp}): "
402
+ input = STDIN.gets.strip.to_i
403
+ arr[input]
404
+ elsif variable['type'] == 'domain'
405
+ arr = variable['acceptableValues'].split(',')
406
+ temp = nil
407
+
408
+ arr.each_with_index do |val, index|
409
+ Put.info " #{index}. #{val}"
410
+ temp = index
411
+ end
412
+ Put.info " #{temp+1}. Create a new domain: (Ex: YOUR_DOMAIN_NAME.envs.subutai.cloud)"
413
+
414
+ Put.info "\nChoose options: ( 0 to #{temp+1}) "
415
+ input = STDIN.gets.strip.to_i
416
+
417
+ if temp+1 == input
418
+ Put.success "\nCreate a new domain: (Ex: YOUR_DOMAIN_NAME.envs.subutai.cloud)"
419
+ reserve
420
+ else
421
+ Put.success "\n Chosen a domain: #{arr[input]}"
422
+ arr[input]
423
+ end
424
+
425
+ end
426
+ else
427
+ STDIN.gets.strip
428
+ end
429
+ end
430
+
431
+ # Validates Subutai.json file
432
+ def validate
433
+ scheme = Configs::Blueprint::SCHEME
434
+
435
+ # Check keys
436
+ @json.keys.each do |key|
437
+ unless scheme.key?(key.to_sym)
438
+ Put.error "Undefined key: \"#{key}\""
439
+ return false
440
+ end
441
+ end
442
+
443
+ scheme_container = scheme[:containers].first
444
+ scheme_port_mapping = scheme_container[KEYS[:port_mapping].to_sym].first
445
+ # Check container keys
446
+ @json[KEYS[:containers]].each do |container|
447
+ container.keys.each do |key|
448
+ unless scheme_container.key?(key.to_sym)
449
+ Put.error "Undefined key: \"#{key}\""
450
+ return false
451
+ end
452
+
453
+ if key == KEYS[:size] && !is_variable?(container[KEYS[:size]])
454
+ unless Configs::Blueprint::CONTAINER_SIZES.include?(container[KEYS[:size]])
455
+ Put.error "Undefined container size: #{container[KEYS[:size]]}"
456
+ return false
457
+ end
458
+ end
459
+
460
+ if container.key?(KEYS[:port_mapping])
461
+ container[KEYS[:port_mapping]].each do |port_map|
462
+ port_map.keys.each do |key|
463
+ unless scheme_port_mapping.key?(key.to_sym)
464
+ Put.error "Undefined port-mapping key: #{key}"
465
+ return false
466
+ end
467
+ end
468
+ end
469
+ end
470
+ end
471
+ end
472
+
473
+ # Check ansible configuration
474
+ if @json.key?(KEYS[:ansible_configuration])
475
+ @json[KEYS[:ansible_configuration]].keys.each do |key|
476
+ unless scheme[KEYS[:ansible_configuration].to_sym].key?(key.to_sym)
477
+ Put.error "Undefined ansible-configuration key: #{key}"
478
+ return false
479
+ end
480
+ end
481
+
482
+ # check extra-vars
483
+ if @json[KEYS[:ansible_configuration]].key?(KEYS[:extra_vars])
484
+ unless @json[KEYS[:ansible_configuration]][KEYS[:extra_vars]].kind_of?(Array)
485
+ Put.error "ansible-configuration extra-vals should be JSON array \"[]\""
486
+ return false
487
+ end
488
+
489
+ @json[KEYS[:ansible_configuration]][KEYS[:extra_vars]].each do |extra_var|
490
+ unless extra_var.key?(KEYS[:key])
491
+ Put.error "ansible-configuration extra-vals has no \"key\" key"
492
+ return false
493
+ end
494
+
495
+ unless extra_var.key?(KEYS[:value])
496
+ Put.error "ansible-configuration extra-vals has no \"value\" key"
497
+ return false
498
+ end
499
+ end
500
+ end
501
+
502
+ # check groups
503
+ if @json[KEYS[:ansible_configuration]].key?(KEYS[:groups])
504
+ unless @json[KEYS[:ansible_configuration]][KEYS[:groups]].kind_of?(Array)
505
+ Put.error "groups should be JSON array"
506
+ return false
507
+ end
508
+
509
+ @json[KEYS[:ansible_configuration]][KEYS[:groups]].each do |group|
510
+ group.keys.each do |key|
511
+ unless scheme[KEYS[:ansible_configuration].to_sym][KEYS[:groups].to_sym].first.key?(key.to_sym)
512
+ Put.error "Undefined groups key: #{key}"
513
+ return false
514
+ end
515
+ end
516
+ end
517
+ end
518
+ end
519
+
520
+ # check peer criteria
521
+ @json[KEYS[:peer_criteria]].each do |peer_criteria|
522
+ peer_criteria.keys.each do |key|
523
+ unless scheme[KEYS[:peer_criteria].to_sym].first.key?(key.to_sym)
524
+ Put.error "Undefined peer-criteria key: #{key}"
525
+ return false
526
+ end
527
+ end
528
+ end
529
+
530
+ # check user-variables
531
+ if @json.key?(KEYS[:user_variables])
532
+ @json[KEYS[:user_variables]].keys.each do |key|
533
+ user_variable = @json[KEYS[:user_variables]][key]
534
+ user_variable.keys.each do |key|
535
+ unless scheme[KEYS[:user_variables].to_sym][:any_name].key?(key.to_sym)
536
+ Put.error "Undefined user-variables key: #{key}"
537
+ return false
538
+ end
539
+ end
540
+ end
541
+ end
542
+
543
+ true
544
+ end
545
+ end
546
+ end
547
+ end