opennebula-cli 6.6.2 → 6.7.80.pre
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 +4 -4
- data/bin/oneacl +1 -1
- data/bin/onebackupjob +373 -0
- data/bin/onecluster +1 -1
- data/bin/onedatastore +7 -4
- data/bin/oneflow-template +24 -6
- data/bin/onegroup +1 -1
- data/bin/onehook +1 -1
- data/bin/onehost +1 -1
- data/bin/oneimage +9 -4
- data/bin/onemarket +6 -4
- data/bin/onemarketapp +6 -2
- data/bin/onesecgroup +1 -1
- data/bin/onetemplate +16 -9
- data/bin/oneuser +1 -1
- data/bin/onevdc +1 -1
- data/bin/onevm +118 -152
- data/bin/onevmgroup +65 -2
- data/bin/onevnet +7 -4
- data/bin/onevntemplate +1 -1
- data/bin/onevrouter +22 -16
- data/bin/onezone +1 -1
- data/lib/command_parser.rb +2 -9
- data/lib/one_helper/oneacl_helper.rb +6 -4
- data/lib/one_helper/onebackupjob_helper.rb +284 -0
- data/lib/one_helper/onevm_helper.rb +42 -230
- data/lib/one_helper/onevmgroup_helper.rb +15 -0
- data/lib/one_helper.rb +622 -325
- data/share/schemas/xsd/acct.xsd +2 -19
- data/share/schemas/xsd/backupjob.xsd +42 -0
- data/share/schemas/xsd/backupjob_pool.xsd +12 -0
- data/share/schemas/xsd/index.xsd +3 -0
- data/share/schemas/xsd/opennebula_configuration.xsd +3 -0
- data/share/schemas/xsd/shared.xsd +46 -0
- data/share/schemas/xsd/vm.xsd +18 -47
- data/share/schemas/xsd/vm_group.xsd +1 -0
- data/share/schemas/xsd/vm_pool.xsd +1 -0
- metadata +12 -6
data/lib/one_helper.rb
CHANGED
@@ -17,44 +17,50 @@
|
|
17
17
|
require 'cli_helper'
|
18
18
|
require 'open3'
|
19
19
|
require 'io/console'
|
20
|
+
require 'time'
|
21
|
+
require 'io/wait'
|
20
22
|
|
21
23
|
begin
|
22
24
|
require 'opennebula'
|
23
25
|
rescue Exception => e
|
24
|
-
puts
|
26
|
+
puts 'Error: '+e.message.to_s
|
25
27
|
exit(-1)
|
26
28
|
end
|
27
29
|
|
28
30
|
include OpenNebula
|
29
31
|
|
30
32
|
module OpenNebulaHelper
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
|
34
|
+
ONE_VERSION=<<~EOT
|
35
|
+
OpenNebula #{OpenNebula::VERSION}
|
36
|
+
Copyright 2002-2023, OpenNebula Project, OpenNebula Systems
|
37
|
+
EOT
|
35
38
|
|
36
39
|
if ONE_LOCATION
|
37
|
-
TABLE_CONF_PATH = ONE_LOCATION +
|
38
|
-
VAR_LOCATION = ONE_LOCATION +
|
39
|
-
CLI_ADDONS_LOCATION = ONE_LOCATION +
|
40
|
+
TABLE_CONF_PATH = ONE_LOCATION + '/etc/cli'
|
41
|
+
VAR_LOCATION = ONE_LOCATION + '/var' unless defined?(VAR_LOCATION)
|
42
|
+
CLI_ADDONS_LOCATION = ONE_LOCATION + '/lib/ruby/cli/addons'
|
40
43
|
XSD_PATH = ONE_LOCATION + '/share/schemas/xsd'
|
41
44
|
else
|
42
|
-
TABLE_CONF_PATH =
|
43
|
-
VAR_LOCATION =
|
44
|
-
CLI_ADDONS_LOCATION =
|
45
|
+
TABLE_CONF_PATH = '/etc/one/cli'
|
46
|
+
VAR_LOCATION = '/var/lib/one' unless defined?(VAR_LOCATION)
|
47
|
+
CLI_ADDONS_LOCATION = '/usr/lib/one/ruby/cli/addons'
|
45
48
|
XSD_PATH = '/usr/share/one/schemas/xsd'
|
46
49
|
end
|
47
50
|
|
48
51
|
EDITOR_PATH='/usr/bin/vi'
|
49
52
|
|
53
|
+
TEMPLATE_INPUT = 'A template can be passed as a file with or the content via STDIN
|
54
|
+
Bash symbols must be escaped on STDIN passing'
|
55
|
+
|
50
56
|
########################################################################
|
51
57
|
# Options
|
52
58
|
########################################################################
|
53
59
|
XML={
|
54
|
-
:name =>
|
55
|
-
:short =>
|
56
|
-
:large =>
|
57
|
-
:description =>
|
60
|
+
:name => 'xml',
|
61
|
+
:short => '-x',
|
62
|
+
:large => '--xml',
|
63
|
+
:description => 'Show the resource in xml format'
|
58
64
|
}
|
59
65
|
|
60
66
|
JSON = {
|
@@ -75,30 +81,46 @@ EOT
|
|
75
81
|
}
|
76
82
|
|
77
83
|
NUMERIC={
|
78
|
-
:name =>
|
79
|
-
:short =>
|
80
|
-
:large =>
|
81
|
-
:description =>
|
84
|
+
:name => 'numeric',
|
85
|
+
:short => '-n',
|
86
|
+
:large => '--numeric',
|
87
|
+
:description => 'Do not translate user and group IDs'
|
82
88
|
}
|
83
89
|
|
84
90
|
KILOBYTES={
|
85
|
-
:name =>
|
86
|
-
:short =>
|
87
|
-
:large =>
|
88
|
-
:description =>
|
91
|
+
:name => 'kilobytes',
|
92
|
+
:short => '-k',
|
93
|
+
:large => '--kilobytes',
|
94
|
+
:description => 'Show units in kilobytes'
|
89
95
|
}
|
90
96
|
|
91
97
|
DESCRIBE={
|
92
|
-
:name =>
|
93
|
-
:large =>
|
94
|
-
:description =>
|
98
|
+
:name => 'describe',
|
99
|
+
:large => '--describe',
|
100
|
+
:description => 'Describe list columns'
|
95
101
|
}
|
96
102
|
|
97
103
|
APPEND = {
|
98
|
-
:name =>
|
99
|
-
:short =>
|
100
|
-
:large =>
|
101
|
-
:description =>
|
104
|
+
:name => 'append',
|
105
|
+
:short => '-a',
|
106
|
+
:large => '--append',
|
107
|
+
:description => 'Append new attributes to the current template'
|
108
|
+
}
|
109
|
+
|
110
|
+
FILE = {
|
111
|
+
:name => 'file',
|
112
|
+
:short => '-f file',
|
113
|
+
:large => '--file file',
|
114
|
+
:description => 'Selects the template file',
|
115
|
+
:format => String,
|
116
|
+
:proc => lambda {|o, options|
|
117
|
+
if File.file?(o)
|
118
|
+
options[:file] = o
|
119
|
+
else
|
120
|
+
STDERR.puts "File `#{options[:file]}` doesn't exist"
|
121
|
+
exit(-1)
|
122
|
+
end
|
123
|
+
}
|
102
124
|
}
|
103
125
|
|
104
126
|
# Command line VM template options
|
@@ -122,7 +144,7 @@ EOT
|
|
122
144
|
:large => '--user name',
|
123
145
|
:description => 'User name used to connect to OpenNebula',
|
124
146
|
:format => String,
|
125
|
-
:proc => lambda do |o,
|
147
|
+
:proc => lambda do |o, _options|
|
126
148
|
OneHelper.set_user(o)
|
127
149
|
[0, o]
|
128
150
|
end
|
@@ -132,7 +154,7 @@ EOT
|
|
132
154
|
:large => '--password password',
|
133
155
|
:description => 'Password to authenticate with OpenNebula',
|
134
156
|
:format => String,
|
135
|
-
:proc => lambda do |o,
|
157
|
+
:proc => lambda do |o, _options|
|
136
158
|
OneHelper.set_password(o)
|
137
159
|
[0, o]
|
138
160
|
end
|
@@ -142,7 +164,7 @@ EOT
|
|
142
164
|
:large => '--endpoint endpoint',
|
143
165
|
:description => 'URL of OpenNebula xmlrpc frontend',
|
144
166
|
:format => String,
|
145
|
-
:proc => lambda do |o,
|
167
|
+
:proc => lambda do |o, _options|
|
146
168
|
OneHelper.set_endpoint(o)
|
147
169
|
[0, o]
|
148
170
|
end
|
@@ -153,7 +175,7 @@ EOT
|
|
153
175
|
{
|
154
176
|
:name => 'name',
|
155
177
|
:large => '--name name',
|
156
|
-
:short =>
|
178
|
+
:short => '-n',
|
157
179
|
:description =>
|
158
180
|
'Name for the new group',
|
159
181
|
:format => String
|
@@ -161,7 +183,7 @@ EOT
|
|
161
183
|
{
|
162
184
|
:name => 'admin_user',
|
163
185
|
:large => '--admin_user name',
|
164
|
-
:short =>
|
186
|
+
:short => '-u',
|
165
187
|
:description =>
|
166
188
|
'Creates an admin user for the group with name',
|
167
189
|
:format => String
|
@@ -169,7 +191,7 @@ EOT
|
|
169
191
|
{
|
170
192
|
:name => 'admin_password',
|
171
193
|
:large => '--admin_password pass',
|
172
|
-
:short =>
|
194
|
+
:short => '-p',
|
173
195
|
:description =>
|
174
196
|
'Password for the admin user of the group',
|
175
197
|
:format => String
|
@@ -177,7 +199,7 @@ EOT
|
|
177
199
|
{
|
178
200
|
:name => 'admin_driver',
|
179
201
|
:large => '--admin_driver driver',
|
180
|
-
:short =>
|
202
|
+
:short => '-d',
|
181
203
|
:description =>
|
182
204
|
'Auth driver for the admin user of the group',
|
183
205
|
:format => String
|
@@ -185,43 +207,43 @@ EOT
|
|
185
207
|
{
|
186
208
|
:name => 'resources',
|
187
209
|
:large => '--resources res_str',
|
188
|
-
:short =>
|
210
|
+
:short => '-r',
|
189
211
|
:description =>
|
190
|
-
|
191
|
-
|
212
|
+
'Which resources can be created by group users '<<
|
213
|
+
'(VM+NET+IMAGE+TEMPLATE by default)',
|
192
214
|
:format => String
|
193
215
|
}
|
194
216
|
]
|
195
217
|
|
196
218
|
AS_USER = {
|
197
|
-
|
219
|
+
:name => 'as_uid',
|
198
220
|
:large => '--as_uid uid',
|
199
221
|
:format => Integer,
|
200
222
|
:description => 'The User ID to instantiate the VM'
|
201
223
|
}
|
202
224
|
|
203
225
|
AS_GROUP = {
|
204
|
-
|
226
|
+
:name => 'as_gid',
|
205
227
|
:large => '--as_gid gid',
|
206
228
|
:format => Integer,
|
207
229
|
:description => 'The Group ID to instantiate the VM'
|
208
230
|
}
|
209
231
|
|
210
|
-
#NOTE: Other options defined using this array, add new options at the end
|
232
|
+
# NOTE: Other options defined using this array, add new options at the end
|
211
233
|
TEMPLATE_OPTIONS=[
|
212
234
|
{
|
213
235
|
:name => 'cpu',
|
214
236
|
:large => '--cpu cpu',
|
215
237
|
:description =>
|
216
238
|
"CPU percentage reserved for the VM (1=100% one\n"<<
|
217
|
-
|
239
|
+
' '*31<<'CPU)',
|
218
240
|
:format => Float
|
219
241
|
},
|
220
242
|
{
|
221
243
|
:name => 'vcpu',
|
222
244
|
:large => '--vcpu vcpu',
|
223
245
|
:description =>
|
224
|
-
|
246
|
+
'Number of virtualized CPUs',
|
225
247
|
:format => Integer
|
226
248
|
},
|
227
249
|
{
|
@@ -236,20 +258,20 @@ EOT
|
|
236
258
|
:large => '--memory memory',
|
237
259
|
:description => 'Memory amount given to the VM. By default the '<<
|
238
260
|
"unit is megabytes. To use gigabytes add a 'g', floats "<<
|
239
|
-
|
261
|
+
'can be used: 8g=8192, 0.5g=512',
|
240
262
|
:format => String,
|
241
|
-
:proc => lambda do |o,
|
263
|
+
:proc => lambda do |o, _options|
|
242
264
|
m=o.strip.match(/^(\d+(?:\.\d+)?)(m|mb|g|gb)?$/i)
|
243
265
|
|
244
266
|
if !m
|
245
267
|
[-1, 'Memory value malformed']
|
246
268
|
else
|
247
269
|
multiplier=case m[2]
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
270
|
+
when /(g|gb)/i
|
271
|
+
1024
|
272
|
+
else
|
273
|
+
1
|
274
|
+
end
|
253
275
|
|
254
276
|
value=m[1].to_f*multiplier
|
255
277
|
|
@@ -260,29 +282,29 @@ EOT
|
|
260
282
|
{
|
261
283
|
:name => 'disk',
|
262
284
|
:large => '--disk image0,image1',
|
263
|
-
:description =>
|
264
|
-
|
285
|
+
:description => 'Disks to attach. To use an image owned by'<<
|
286
|
+
' other user use user[disk]. Add any additional'<<
|
265
287
|
" attributes separated by ':' and in the shape of"<<
|
266
|
-
|
267
|
-
|
268
|
-
|
288
|
+
' KEY=VALUE. For example, if the disk must be'<<
|
289
|
+
' resized, use image0:size=1000 . Or'<<
|
290
|
+
' image0:size=1000:target=vda,image1:target=vdb',
|
269
291
|
:format => Array
|
270
292
|
},
|
271
293
|
{
|
272
294
|
:name => 'nic',
|
273
295
|
:large => '--nic network0,network1',
|
274
|
-
:description =>
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
296
|
+
:description => 'Networks to attach. To use a network owned by'<<
|
297
|
+
' other user use user[network]. Additional'<<
|
298
|
+
' attributes are supported like with the --disk'<<
|
299
|
+
' option. Also you can use auto if you want that' <<
|
300
|
+
' OpenNebula select automatically the network',
|
279
301
|
:format => Array
|
280
302
|
},
|
281
303
|
{
|
282
304
|
:name => 'raw',
|
283
305
|
:large => '--raw string',
|
284
306
|
:description => "Raw string to add to the template. Not to be\n"<<
|
285
|
-
|
307
|
+
' '*31<<'confused with the RAW attribute',
|
286
308
|
:format => String
|
287
309
|
},
|
288
310
|
{
|
@@ -338,9 +360,9 @@ EOT
|
|
338
360
|
:large => '--ssh [file]',
|
339
361
|
:description => "Add an ssh public key to the context. If the \n"<<
|
340
362
|
(' '*31) << "file is omited then the user variable \n"<<
|
341
|
-
(' '*31) <<
|
363
|
+
(' '*31) << 'SSH_PUBLIC_KEY will be used.',
|
342
364
|
:format => String,
|
343
|
-
:proc => lambda do |o,
|
365
|
+
:proc => lambda do |o, _options|
|
344
366
|
if !o
|
345
367
|
[0, true]
|
346
368
|
else
|
@@ -393,8 +415,8 @@ EOT
|
|
393
415
|
:name => 'vcenter_vm_folder',
|
394
416
|
:large => '--vcenter_vm_folder path',
|
395
417
|
:format => String,
|
396
|
-
:description =>
|
397
|
-
|
418
|
+
:description => 'In a vCenter environment sets the the VMs and Template folder where the VM will be placed in.' \
|
419
|
+
' The path uses slashes to separate folders. For example: --vcenter_vm_folder "/Management/VMs"'
|
398
420
|
},
|
399
421
|
{
|
400
422
|
:name => 'user_inputs',
|
@@ -417,6 +439,59 @@ EOT
|
|
417
439
|
options[:user_inputs] = o.join("\n")
|
418
440
|
end
|
419
441
|
},
|
442
|
+
{
|
443
|
+
:name => 'video',
|
444
|
+
:large => '--video type',
|
445
|
+
:format => String,
|
446
|
+
:description => 'Add a custom video device (none, vga, cirrus, virtio)'
|
447
|
+
},
|
448
|
+
{
|
449
|
+
:name => 'video_iommu',
|
450
|
+
:large => '--video-iommu',
|
451
|
+
:description => 'Enable IOMMU (I/O Memory Management Unit) for the video device'
|
452
|
+
},
|
453
|
+
{
|
454
|
+
:name => 'video_ats',
|
455
|
+
:large => '--video-ats',
|
456
|
+
:description => 'Enable ATS (Address Translation Services) for the video device'
|
457
|
+
},
|
458
|
+
{
|
459
|
+
:name => 'video_vram',
|
460
|
+
:large => '--video-vram vram',
|
461
|
+
:description => 'VRAM allocated to the video device. By default the ' +
|
462
|
+
"unit is megabytes. To use gigabytes add a 'g', floats " +
|
463
|
+
'can be used: 8g=8192, 0.5g=512',
|
464
|
+
:format => String,
|
465
|
+
:proc => lambda do |o, _options|
|
466
|
+
m=o.strip.match(/^(\d+(?:\.\d+)?)(m|mb|g|gb)?$/i)
|
467
|
+
|
468
|
+
if !m
|
469
|
+
[-1, 'VRAM value malformed']
|
470
|
+
else
|
471
|
+
multiplier=case m[2]
|
472
|
+
when /(g|gb)/i
|
473
|
+
1048576 # = 1024 * 1024
|
474
|
+
else
|
475
|
+
1024
|
476
|
+
end
|
477
|
+
|
478
|
+
value=m[1].to_f*multiplier
|
479
|
+
|
480
|
+
[0, value.floor]
|
481
|
+
end
|
482
|
+
end
|
483
|
+
},
|
484
|
+
{
|
485
|
+
:name => 'video_resolution',
|
486
|
+
:large => '--video-resolution resolution',
|
487
|
+
:format => String,
|
488
|
+
:description => 'Video resolution, in format like: 1280x720 or 1920x1080',
|
489
|
+
:proc => lambda do |_o, _options|
|
490
|
+
if !m.match?(/\d{3,4}x\d{3,4}/)
|
491
|
+
[-1, 'Video Resolution value malformed']
|
492
|
+
end
|
493
|
+
end
|
494
|
+
},
|
420
495
|
AS_GROUP,
|
421
496
|
AS_USER
|
422
497
|
]
|
@@ -439,22 +514,90 @@ EOT
|
|
439
514
|
:description => 'Get decrypted attributes'
|
440
515
|
}
|
441
516
|
|
517
|
+
SCHEDULE_OPTIONS=[
|
518
|
+
SCHEDULE = {
|
519
|
+
:name => 'schedule',
|
520
|
+
:large => '--schedule TIME',
|
521
|
+
:description => 'Schedules this action to be executed after' \
|
522
|
+
'the given time. For example: onevm resume 0 --schedule "09/23 14:15"',
|
523
|
+
:format => String,
|
524
|
+
:proc => lambda {|o, options|
|
525
|
+
if o[0] == '+'
|
526
|
+
options[:schedule] = o
|
527
|
+
elsif o == 'now'
|
528
|
+
options[:schedule] = Time.now.to_i
|
529
|
+
else
|
530
|
+
begin
|
531
|
+
options[:schedule] = Time.parse(o).to_i
|
532
|
+
rescue StandardError
|
533
|
+
STDERR.puts "Error parsing time spec: #{o}"
|
534
|
+
exit(-1)
|
535
|
+
end
|
536
|
+
end
|
537
|
+
}
|
538
|
+
},
|
539
|
+
|
540
|
+
WEEKLY = {
|
541
|
+
:name => 'weekly',
|
542
|
+
:large => '--weekly days',
|
543
|
+
:description => 'Repeats the schedule action the days of the week ' \
|
544
|
+
'specified, it can be a number between 0 (Sunday) to 6 (Saturday) ' \
|
545
|
+
'separated with commas. ' \
|
546
|
+
'For example: onevm resume 0 --schedule "09/23 14:15" --weekly 0,2,4',
|
547
|
+
:format => String
|
548
|
+
},
|
549
|
+
|
550
|
+
MONTHLY = {
|
551
|
+
:name => 'monthly',
|
552
|
+
:large => '--monthly days',
|
553
|
+
:description => 'Repeats the schedule action the days of the month ' \
|
554
|
+
'specified, it can be a number between 1,31 separated with commas. ' \
|
555
|
+
'For example: onevm resume 0 --schedule "09/23 14:15" --monthly 1,14',
|
556
|
+
:format => String
|
557
|
+
},
|
558
|
+
|
559
|
+
YEARLY = {
|
560
|
+
:name => 'yearly',
|
561
|
+
:large => '--yearly days',
|
562
|
+
:description => 'Repeats the schedule action the days of the year ' \
|
563
|
+
'specified, it can be a number between 0,365 separated with commas. ' \
|
564
|
+
'For example: onevm resume 0 --schedule "09/23 14:15" --yearly 30,60',
|
565
|
+
:format => String
|
566
|
+
},
|
567
|
+
|
568
|
+
HOURLY = {
|
569
|
+
:name => 'hourly',
|
570
|
+
:large => '--hourly hour',
|
571
|
+
:description => 'Repeats the schedule action with the given hourly frequency. ' \
|
572
|
+
'For example (every 5 hours): onevm resume 0 --schedule "09/23 14:15" --hourly 5',
|
573
|
+
:format => Numeric
|
574
|
+
},
|
575
|
+
|
576
|
+
END_TIME = {
|
577
|
+
:name => 'end',
|
578
|
+
:large => '--end number|TIME',
|
579
|
+
:description => '----',
|
580
|
+
:format => String
|
581
|
+
}
|
582
|
+
]
|
583
|
+
|
442
584
|
TEMPLATE_OPTIONS_VM = [TEMPLATE_NAME_VM] + TEMPLATE_OPTIONS + [DRY]
|
443
585
|
|
444
586
|
CAPACITY_OPTIONS_VM = [TEMPLATE_OPTIONS[0], TEMPLATE_OPTIONS[1],
|
445
|
-
|
587
|
+
TEMPLATE_OPTIONS[3]]
|
446
588
|
|
447
589
|
UPDATECONF_OPTIONS_VM = TEMPLATE_OPTIONS[6..15] + [TEMPLATE_OPTIONS[2],
|
448
|
-
|
590
|
+
TEMPLATE_OPTIONS[17], TEMPLATE_OPTIONS[18]]
|
449
591
|
|
450
592
|
FORMAT = [XML, JSON, YAML]
|
451
593
|
|
452
594
|
OPTIONS = FORMAT, EXTENDED, NUMERIC, KILOBYTES
|
453
595
|
|
454
596
|
class OneHelper
|
597
|
+
|
455
598
|
attr_accessor :client
|
456
599
|
|
457
|
-
def self.get_client(options={}, force=false)
|
600
|
+
def self.get_client(options = {}, force = false)
|
458
601
|
if !force && defined?(@@client)
|
459
602
|
@@client
|
460
603
|
else
|
@@ -470,7 +613,7 @@ EOT
|
|
470
613
|
end
|
471
614
|
|
472
615
|
if user
|
473
|
-
password=password||options[:password]||
|
616
|
+
password=password||options[:password]||get_password
|
474
617
|
secret="#{user}:#{password}"
|
475
618
|
end
|
476
619
|
|
@@ -497,7 +640,7 @@ EOT
|
|
497
640
|
if defined?(@@client)
|
498
641
|
@@client
|
499
642
|
else
|
500
|
-
|
643
|
+
get_client
|
501
644
|
end
|
502
645
|
end
|
503
646
|
|
@@ -513,10 +656,10 @@ EOT
|
|
513
656
|
@@endpoint=endpoint
|
514
657
|
end
|
515
658
|
|
516
|
-
if RUBY_VERSION>=
|
659
|
+
if RUBY_VERSION>='1.9.3'
|
517
660
|
require 'io/console'
|
518
661
|
def self.get_password
|
519
|
-
print
|
662
|
+
print 'Password: '
|
520
663
|
pass=nil
|
521
664
|
STDIN.noecho {|io| pass=io.gets }
|
522
665
|
puts
|
@@ -528,25 +671,33 @@ EOT
|
|
528
671
|
else
|
529
672
|
# This function is copied from ruby net/imap.rb
|
530
673
|
def self.get_password
|
531
|
-
print
|
532
|
-
system(
|
674
|
+
print 'Password: '
|
675
|
+
system('stty', '-echo')
|
533
676
|
begin
|
534
677
|
@@password = STDIN.gets.chop
|
535
|
-
|
678
|
+
@@password
|
536
679
|
ensure
|
537
|
-
system(
|
680
|
+
system('stty', 'echo')
|
538
681
|
print "\n"
|
539
682
|
end
|
540
683
|
end
|
541
684
|
end
|
542
685
|
|
543
|
-
def
|
686
|
+
def self.list_layout_help
|
687
|
+
"The default columns and their layout can be configured in #{conf_file}"
|
688
|
+
end
|
689
|
+
|
690
|
+
def self.template_input_help(object_name)
|
691
|
+
"#{TEMPLATE_INPUT}\nWhen using a template add only one #{object_name} instance."
|
692
|
+
end
|
693
|
+
|
694
|
+
def initialize(_secret = nil, _endpoint = nil)
|
544
695
|
@client=nil
|
545
696
|
|
546
697
|
@translation_hash = nil
|
547
698
|
end
|
548
699
|
|
549
|
-
def set_client(options, client=nil)
|
700
|
+
def set_client(options, client = nil)
|
550
701
|
if client.nil?
|
551
702
|
@client=OpenNebulaHelper::OneHelper.get_client(options, true)
|
552
703
|
else
|
@@ -554,15 +705,15 @@ EOT
|
|
554
705
|
end
|
555
706
|
end
|
556
707
|
|
557
|
-
def create_resource(
|
708
|
+
def create_resource(_options, &block)
|
558
709
|
resource = factory
|
559
710
|
|
560
711
|
rc = block.call(resource)
|
561
712
|
if OpenNebula.is_error?(rc)
|
562
|
-
|
713
|
+
[-1, rc.message]
|
563
714
|
else
|
564
|
-
puts "ID: #{resource.id
|
565
|
-
|
715
|
+
puts "ID: #{resource.id}"
|
716
|
+
0
|
566
717
|
end
|
567
718
|
end
|
568
719
|
|
@@ -596,7 +747,7 @@ EOT
|
|
596
747
|
p_w.close
|
597
748
|
p_r.close
|
598
749
|
|
599
|
-
|
750
|
+
lpid
|
600
751
|
end
|
601
752
|
|
602
753
|
def stop_pager(lpid)
|
@@ -605,7 +756,7 @@ EOT
|
|
605
756
|
begin
|
606
757
|
Process.wait(lpid)
|
607
758
|
rescue Interrupt
|
608
|
-
Process.kill(
|
759
|
+
Process.kill('TERM', lpid)
|
609
760
|
Process.wait(lpid)
|
610
761
|
rescue Errno::ECHILD
|
611
762
|
end
|
@@ -613,7 +764,7 @@ EOT
|
|
613
764
|
|
614
765
|
def print_page(pool, options)
|
615
766
|
elements = 0
|
616
|
-
page =
|
767
|
+
page = ''
|
617
768
|
|
618
769
|
if options[:xml]
|
619
770
|
elements += 1
|
@@ -640,20 +791,20 @@ EOT
|
|
640
791
|
end
|
641
792
|
end
|
642
793
|
|
643
|
-
|
794
|
+
[elements, page]
|
644
795
|
end
|
645
796
|
|
646
797
|
#-----------------------------------------------------------------------
|
647
798
|
# List the pool in table form, it uses pagination for interactive
|
648
799
|
# output
|
649
800
|
#-----------------------------------------------------------------------
|
650
|
-
def list_pool_table(table, pool, options,
|
651
|
-
if $stdout.isatty and (!options.key
|
801
|
+
def list_pool_table(table, pool, options, _filter_flag)
|
802
|
+
if $stdout.isatty and (!options.key? :no_pager)
|
652
803
|
size = $stdout.winsize[0] - 1
|
653
804
|
|
654
805
|
# ----------- First page, check if pager is needed -------------
|
655
806
|
rc = pool.get_page(size, 0, false, options[:state])
|
656
|
-
ps =
|
807
|
+
ps = ''
|
657
808
|
|
658
809
|
return -1, rc.message if OpenNebula.is_error?(rc)
|
659
810
|
|
@@ -719,21 +870,21 @@ EOT
|
|
719
870
|
table.show(hash, options)
|
720
871
|
end
|
721
872
|
|
722
|
-
|
873
|
+
0
|
723
874
|
end
|
724
875
|
|
725
876
|
#-----------------------------------------------------------------------
|
726
877
|
# List pool in XML format, pagination is used in interactive output
|
727
878
|
#-----------------------------------------------------------------------
|
728
|
-
def list_pool_xml(pool, options,
|
879
|
+
def list_pool_xml(pool, options, _filter_flag)
|
729
880
|
extended = options.include?(:extended) && options[:extended]
|
730
881
|
|
731
|
-
if $stdout.isatty and (!options.key
|
882
|
+
if $stdout.isatty and (!options.key? :no_pager)
|
732
883
|
size = $stdout.winsize[0] - 1
|
733
884
|
|
734
885
|
# ----------- First page, check if pager is needed -------------
|
735
886
|
rc = pool.get_page(size, 0, extended, options[:state])
|
736
|
-
ps =
|
887
|
+
ps = ''
|
737
888
|
|
738
889
|
return -1, rc.message if OpenNebula.is_error?(rc)
|
739
890
|
|
@@ -789,7 +940,7 @@ EOT
|
|
789
940
|
|
790
941
|
stop_pager(ppid)
|
791
942
|
else
|
792
|
-
if pool.pool_name ==
|
943
|
+
if pool.pool_name == 'VM_POOL' && extended
|
793
944
|
rc = pool.info_all_extended
|
794
945
|
else
|
795
946
|
rc = pool.info
|
@@ -800,21 +951,21 @@ EOT
|
|
800
951
|
puts pool.to_xml(true)
|
801
952
|
end
|
802
953
|
|
803
|
-
|
954
|
+
0
|
804
955
|
end
|
805
956
|
|
806
957
|
#-----------------------------------------------------------------------
|
807
958
|
# List pool in JSON format, pagination is used in interactive output
|
808
959
|
#-----------------------------------------------------------------------
|
809
|
-
def list_pool_format(pool, options,
|
960
|
+
def list_pool_format(pool, options, _filter_flag)
|
810
961
|
extended = options.include?(:extended) && options[:extended]
|
811
962
|
|
812
|
-
if $stdout.isatty and (!options.key
|
963
|
+
if $stdout.isatty and (!options.key? :no_pager)
|
813
964
|
size = $stdout.winsize[0] - 1
|
814
965
|
|
815
966
|
# ----------- First page, check if pager is needed -------------
|
816
967
|
rc = pool.get_page(size, 0, extended, options[:state])
|
817
|
-
ps =
|
968
|
+
ps = ''
|
818
969
|
|
819
970
|
return -1, rc.message if OpenNebula.is_error?(rc)
|
820
971
|
|
@@ -865,7 +1016,7 @@ EOT
|
|
865
1016
|
|
866
1017
|
stop_pager(ppid)
|
867
1018
|
else
|
868
|
-
if pool.pool_name ==
|
1019
|
+
if pool.pool_name == 'VM_POOL' && extended
|
869
1020
|
rc = pool.info_all_extended
|
870
1021
|
else
|
871
1022
|
rc = pool.info
|
@@ -876,26 +1027,25 @@ EOT
|
|
876
1027
|
yield(pool) if block_given?
|
877
1028
|
end
|
878
1029
|
|
879
|
-
|
1030
|
+
0
|
880
1031
|
end
|
881
1032
|
|
882
1033
|
#-----------------------------------------------------------------------
|
883
1034
|
# List pool table in top-like form
|
884
1035
|
#-----------------------------------------------------------------------
|
885
1036
|
def list_pool_top(table, pool, options)
|
886
|
-
table.top(options)
|
1037
|
+
table.top(options) do
|
887
1038
|
array = pool.get_hash
|
888
1039
|
|
889
1040
|
return -1, array.message if OpenNebula.is_error?(array)
|
890
1041
|
|
891
1042
|
array
|
892
|
-
|
1043
|
+
end
|
893
1044
|
|
894
|
-
|
1045
|
+
0
|
895
1046
|
end
|
896
1047
|
|
897
|
-
|
898
|
-
def list_pool(options, top=false, filter_flag=nil)
|
1048
|
+
def list_pool(options, top = false, filter_flag = nil)
|
899
1049
|
# Capture Broken pipe
|
900
1050
|
Signal.trap('PIPE', 'EXIT')
|
901
1051
|
|
@@ -919,19 +1069,19 @@ EOT
|
|
919
1069
|
return list_pool_xml(pool, options, filter_flag)
|
920
1070
|
elsif options[:json]
|
921
1071
|
list_pool_format(pool, options, filter_flag) do |pool|
|
922
|
-
hash
|
1072
|
+
hash = check_resource_xsd(pool, pname)
|
923
1073
|
puts ::JSON.pretty_generate(hash)
|
924
1074
|
end
|
925
1075
|
elsif options[:yaml]
|
926
1076
|
list_pool_format(pool, options, filter_flag) do |pool|
|
927
|
-
hash
|
1077
|
+
hash = check_resource_xsd(pool, pname)
|
928
1078
|
puts hash.to_yaml(:indent => 4)
|
929
1079
|
end
|
930
1080
|
else
|
931
1081
|
return list_pool_table(table, pool, options, filter_flag)
|
932
1082
|
end
|
933
1083
|
|
934
|
-
|
1084
|
+
0
|
935
1085
|
rescue SystemExit, Interrupt
|
936
1086
|
# Rescue ctrl + c when paginated
|
937
1087
|
0
|
@@ -972,21 +1122,21 @@ EOT
|
|
972
1122
|
return -1, rc.message if OpenNebula.is_error?(rc)
|
973
1123
|
|
974
1124
|
if options[:xml]
|
975
|
-
|
1125
|
+
[0, resource.to_xml(true)]
|
976
1126
|
elsif options[:json]
|
977
1127
|
# If body is set, the resource contains a JSON inside
|
978
1128
|
if options[:body]
|
979
|
-
|
1129
|
+
[0, check_resource_xsd(resource)]
|
980
1130
|
else
|
981
|
-
|
1131
|
+
[0, ::JSON.pretty_generate(
|
982
1132
|
check_resource_xsd(resource)
|
983
|
-
)
|
1133
|
+
)]
|
984
1134
|
end
|
985
1135
|
elsif options[:yaml]
|
986
|
-
|
1136
|
+
[0, check_resource_xsd(resource).to_yaml(:indent => 4)]
|
987
1137
|
else
|
988
1138
|
format_resource(resource, options)
|
989
|
-
|
1139
|
+
0
|
990
1140
|
end
|
991
1141
|
end
|
992
1142
|
|
@@ -995,19 +1145,19 @@ EOT
|
|
995
1145
|
|
996
1146
|
rc = block.call(resource)
|
997
1147
|
if OpenNebula.is_error?(rc)
|
998
|
-
|
1148
|
+
[-1, rc.message]
|
999
1149
|
else
|
1000
1150
|
if options[:verbose]
|
1001
1151
|
puts "#{self.class.rname} #{id}: #{verbose}"
|
1002
1152
|
end
|
1003
|
-
|
1153
|
+
0
|
1004
1154
|
end
|
1005
1155
|
end
|
1006
1156
|
|
1007
|
-
def perform_actions(ids,options,verbose
|
1157
|
+
def perform_actions(ids, options, verbose, &block)
|
1008
1158
|
exit_code = 0
|
1009
1159
|
ids.each do |id|
|
1010
|
-
rc = perform_action(id,options,verbose
|
1160
|
+
rc = perform_action(id, options, verbose, &block)
|
1011
1161
|
|
1012
1162
|
unless rc[0]==0
|
1013
1163
|
STDERR.puts rc[1]
|
@@ -1021,7 +1171,7 @@ EOT
|
|
1021
1171
|
########################################################################
|
1022
1172
|
# Id translation
|
1023
1173
|
########################################################################
|
1024
|
-
def user_name(resource, options={})
|
1174
|
+
def user_name(resource, options = {})
|
1025
1175
|
if options[:numeric]
|
1026
1176
|
resource['UID']
|
1027
1177
|
else
|
@@ -1029,7 +1179,7 @@ EOT
|
|
1029
1179
|
end
|
1030
1180
|
end
|
1031
1181
|
|
1032
|
-
def group_name(resource, options={})
|
1182
|
+
def group_name(resource, options = {})
|
1033
1183
|
if options[:numeric]
|
1034
1184
|
resource['GID']
|
1035
1185
|
else
|
@@ -1053,7 +1203,7 @@ EOT
|
|
1053
1203
|
end
|
1054
1204
|
|
1055
1205
|
def self.to_id_desc
|
1056
|
-
"OpenNebula #{
|
1206
|
+
"OpenNebula #{rname} name or id"
|
1057
1207
|
end
|
1058
1208
|
|
1059
1209
|
def list_to_id(names)
|
@@ -1063,7 +1213,7 @@ EOT
|
|
1063
1213
|
pool = rc[1]
|
1064
1214
|
poolname = self.class.rname
|
1065
1215
|
|
1066
|
-
result = names.split(',').collect
|
1216
|
+
result = names.split(',').collect do |name|
|
1067
1217
|
if name.match(/^[0123456789]+$/)
|
1068
1218
|
name.to_i
|
1069
1219
|
else
|
@@ -1075,76 +1225,70 @@ EOT
|
|
1075
1225
|
|
1076
1226
|
rc[1]
|
1077
1227
|
end
|
1078
|
-
|
1228
|
+
end
|
1079
1229
|
|
1080
|
-
|
1230
|
+
[0, result]
|
1081
1231
|
end
|
1082
1232
|
|
1083
1233
|
def self.list_to_id_desc
|
1084
|
-
"Comma-separated list of OpenNebula #{
|
1234
|
+
"Comma-separated list of OpenNebula #{rname} names or ids"
|
1085
1235
|
end
|
1086
1236
|
|
1087
1237
|
def self.name_to_id(name, pool, ename)
|
1088
|
-
if ename==
|
1089
|
-
return 0,
|
1238
|
+
if ename=='CLUSTER' and name.upcase=='ALL'
|
1239
|
+
return 0, 'ALL'
|
1090
1240
|
end
|
1091
1241
|
|
1092
1242
|
objects=pool.select {|object| object.name==name }
|
1093
1243
|
|
1094
|
-
|
1095
|
-
|
1096
|
-
return -1, "There are multiple #{ename}s with name #{name}."
|
1097
|
-
else
|
1098
|
-
result = objects.first.id
|
1099
|
-
end
|
1100
|
-
else
|
1101
|
-
return -1, "#{ename} named #{name} not found."
|
1102
|
-
end
|
1244
|
+
return -1, "#{ename} named #{name} not found." unless objects.length>0
|
1245
|
+
return -1, "There are multiple #{ename}s with name #{name}." if objects.length>1
|
1103
1246
|
|
1104
|
-
|
1247
|
+
result = objects.first.id
|
1248
|
+
|
1249
|
+
[0, result]
|
1105
1250
|
end
|
1106
1251
|
|
1107
1252
|
def filterflag_to_i(str)
|
1108
1253
|
filter_flag = case str
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
rc[1]
|
1122
|
-
end
|
1123
|
-
end
|
1124
|
-
end
|
1254
|
+
when 'a', 'all' then OpenNebula::Pool::INFO_ALL
|
1255
|
+
when 'm', 'mine' then OpenNebula::Pool::INFO_MINE
|
1256
|
+
when 'g', 'group' then OpenNebula::Pool::INFO_GROUP
|
1257
|
+
when 'G', 'primary group' then OpenNebula::Pool::INFO_PRIMARY_GROUP
|
1258
|
+
else
|
1259
|
+
if str.match(/^[0123456789]+$/)
|
1260
|
+
str.to_i
|
1261
|
+
else
|
1262
|
+
rc = OpenNebulaHelper.rname_to_id(str, 'USER')
|
1263
|
+
return rc if rc.first==-1
|
1264
|
+
|
1265
|
+
rc[1]
|
1125
1266
|
|
1126
|
-
|
1267
|
+
end
|
1268
|
+
end
|
1269
|
+
|
1270
|
+
[0, filter_flag]
|
1127
1271
|
end
|
1128
1272
|
|
1129
1273
|
def self.filterflag_to_i_desc
|
1130
|
-
desc
|
1131
|
-
a, all all the known #{
|
1132
|
-
m, mine the #{
|
1133
|
-
g, group 'mine' plus the #{
|
1134
|
-
|
1135
|
-
G, primary group the #{
|
1136
|
-
uid #{
|
1137
|
-
user #{
|
1138
|
-
EOT
|
1139
|
-
end
|
1140
|
-
|
1141
|
-
def self.table_conf(conf_file=self.conf_file)
|
1142
|
-
path = "#{ENV[
|
1143
|
-
|
1144
|
-
if File.
|
1145
|
-
|
1274
|
+
desc=<<~EOT
|
1275
|
+
a, all all the known #{rname}s
|
1276
|
+
m, mine the #{rname} belonging to the user in ONE_AUTH
|
1277
|
+
g, group 'mine' plus the #{rname} belonging to the groups
|
1278
|
+
the user is member of
|
1279
|
+
G, primary group the #{rname} owned the user's primary group
|
1280
|
+
uid #{rname} of the user identified by this uid
|
1281
|
+
user #{rname} of the user identified by the username
|
1282
|
+
EOT
|
1283
|
+
end
|
1284
|
+
|
1285
|
+
def self.table_conf(conf_file = self.conf_file)
|
1286
|
+
path = "#{ENV['HOME']}/.one/cli/#{conf_file}"
|
1287
|
+
|
1288
|
+
if File.exist?(path)
|
1289
|
+
path
|
1146
1290
|
else
|
1147
|
-
|
1291
|
+
"#{TABLE_CONF_PATH}/#{conf_file}"
|
1148
1292
|
end
|
1149
1293
|
end
|
1150
1294
|
|
@@ -1171,7 +1315,7 @@ EOT
|
|
1171
1315
|
phash = [phash["#{rname}_POOL"]["#{rname}"]]
|
1172
1316
|
end
|
1173
1317
|
else
|
1174
|
-
phash =
|
1318
|
+
phash = []
|
1175
1319
|
end
|
1176
1320
|
|
1177
1321
|
phash
|
@@ -1183,15 +1327,14 @@ EOT
|
|
1183
1327
|
|
1184
1328
|
rc = pool.info
|
1185
1329
|
if OpenNebula.is_error?(rc)
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
end
|
1330
|
+
return -1, rc.message unless rc.message.empty?
|
1331
|
+
|
1332
|
+
return -1, "OpenNebula #{self.class.rname} name not " <<
|
1333
|
+
'found, use the ID instead'
|
1334
|
+
|
1192
1335
|
end
|
1193
1336
|
|
1194
|
-
|
1337
|
+
[0, pool]
|
1195
1338
|
end
|
1196
1339
|
|
1197
1340
|
def get_format_size(pool, options)
|
@@ -1217,7 +1360,7 @@ EOT
|
|
1217
1360
|
# @return [Object] Hash with correct values
|
1218
1361
|
def check_resource_xsd(resource, ename = nil)
|
1219
1362
|
hash = resource.to_hash
|
1220
|
-
ename
|
1363
|
+
ename ||= hash.keys.first
|
1221
1364
|
xsd = read_xsd(ename)
|
1222
1365
|
|
1223
1366
|
return hash unless xsd
|
@@ -1228,14 +1371,13 @@ EOT
|
|
1228
1371
|
xsd = xsd['element']
|
1229
1372
|
end
|
1230
1373
|
|
1231
|
-
xsd = [
|
1374
|
+
xsd = [xsd] unless xsd.is_a? Array
|
1232
1375
|
|
1233
1376
|
check_xsd(hash[ename], xsd)
|
1234
1377
|
|
1235
1378
|
hash
|
1236
1379
|
end
|
1237
1380
|
|
1238
|
-
|
1239
1381
|
# Replaces refs in xsd definition
|
1240
1382
|
# limited func: only traverse hashes (not arrays), but works well for pools
|
1241
1383
|
#
|
@@ -1248,21 +1390,22 @@ EOT
|
|
1248
1390
|
if h.keys.include? 'ref'
|
1249
1391
|
ref_xsd = read_xsd(h['ref'])
|
1250
1392
|
return ref_xsd unless ref_xsd.nil?
|
1251
|
-
|
1393
|
+
|
1394
|
+
h
|
1252
1395
|
else
|
1253
|
-
h.each do |k,v|
|
1396
|
+
h.each do |k, v|
|
1254
1397
|
h[k] = replace_refs(v)
|
1255
1398
|
end
|
1256
1399
|
end
|
1257
1400
|
end
|
1258
1401
|
|
1259
|
-
|
1260
1402
|
# Read XSD file and parse to XML
|
1261
1403
|
#
|
1262
1404
|
# @param ename [String] Element name to read XSD
|
1263
1405
|
#
|
1264
1406
|
# @return [Hash] XSD in hash format, nil if not found
|
1265
1407
|
def read_xsd(ename)
|
1408
|
+
require 'active_support'
|
1266
1409
|
require 'active_support/core_ext/hash/conversions'
|
1267
1410
|
|
1268
1411
|
# Try GEM directory
|
@@ -1275,16 +1418,14 @@ EOT
|
|
1275
1418
|
|
1276
1419
|
unless File.exist?(file)
|
1277
1420
|
STDERR.puts "WARNING: XSD for #{ename} not found, skipping check"
|
1278
|
-
return
|
1421
|
+
return
|
1279
1422
|
end
|
1280
1423
|
|
1281
1424
|
hash = Hash.from_xml(Nokogiri::XML(File.read(file)).to_s)
|
1282
1425
|
|
1283
1426
|
hash = hash['schema']['element']
|
1284
1427
|
|
1285
|
-
|
1286
|
-
|
1287
|
-
hash
|
1428
|
+
replace_refs(hash)
|
1288
1429
|
end
|
1289
1430
|
|
1290
1431
|
# Decides if given xsd definiton should be array in xml
|
@@ -1298,6 +1439,7 @@ EOT
|
|
1298
1439
|
def is_array?(e)
|
1299
1440
|
return false if e.nil?
|
1300
1441
|
return false unless e.is_a? Hash
|
1442
|
+
|
1301
1443
|
e['maxOccurs'] == 'unbounded' || e['maxOccurs'].to_i > 1
|
1302
1444
|
end
|
1303
1445
|
|
@@ -1338,16 +1480,15 @@ EOT
|
|
1338
1480
|
return unless hash or hash.empty?
|
1339
1481
|
|
1340
1482
|
hash.each do |k, v|
|
1341
|
-
|
1342
1483
|
# find the elem definition in xsd array
|
1343
|
-
xsd_elem = xsd.select {
|
1484
|
+
xsd_elem = xsd.select {|e| e['name'] == k }.first unless xsd.nil?
|
1344
1485
|
|
1345
1486
|
if xsd_complex_sequence?(xsd_elem) || xsd_complex_all?(xsd_elem)
|
1346
1487
|
|
1347
1488
|
# go deeper in xsd, xsd is ehter complex sequence or all
|
1348
1489
|
begin
|
1349
1490
|
inner_xsd = xsd_elem['complexType']['sequence']['element']
|
1350
|
-
rescue
|
1491
|
+
rescue StandardError
|
1351
1492
|
inner_xsd = xsd_elem['complexType']['all']['element']
|
1352
1493
|
end
|
1353
1494
|
|
@@ -1365,81 +1506,82 @@ EOT
|
|
1365
1506
|
end
|
1366
1507
|
|
1367
1508
|
# if XSD requires array, do so in resource if missing
|
1368
|
-
if is_array?(xsd_elem) && (!
|
1369
|
-
hash[k] = [
|
1509
|
+
if is_array?(xsd_elem) && (!v.is_a? Array)
|
1510
|
+
hash[k] = [v]
|
1370
1511
|
end
|
1371
1512
|
end
|
1372
1513
|
end
|
1514
|
+
|
1373
1515
|
end
|
1374
1516
|
|
1375
|
-
def
|
1517
|
+
def self.rname_to_id(name, poolname)
|
1376
1518
|
return 0, name.to_i if name.match(/^[0123456789]+$/)
|
1377
1519
|
|
1378
1520
|
client=OneHelper.client
|
1379
1521
|
|
1380
1522
|
pool = case poolname
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1523
|
+
when 'HOST' then OpenNebula::HostPool.new(client)
|
1524
|
+
when 'HOOK' then OpenNebula::HookPool.new(client)
|
1525
|
+
when 'GROUP' then OpenNebula::GroupPool.new(client)
|
1526
|
+
when 'USER' then OpenNebula::UserPool.new(client)
|
1527
|
+
when 'DATASTORE' then OpenNebula::DatastorePool.new(client)
|
1528
|
+
when 'CLUSTER' then OpenNebula::ClusterPool.new(client)
|
1529
|
+
when 'VNET' then OpenNebula::VirtualNetworkPool.new(client)
|
1530
|
+
when 'IMAGE' then OpenNebula::ImagePool.new(client)
|
1531
|
+
when 'VMTEMPLATE' then OpenNebula::TemplatePool.new(client)
|
1532
|
+
when 'VNTEMPLATES' then OpenNebula::VNTemplatePool.new(client)
|
1533
|
+
when 'VM' then OpenNebula::VirtualMachinePool.new(client)
|
1534
|
+
when 'ZONE' then OpenNebula::ZonePool.new(client)
|
1535
|
+
when 'MARKETPLACE' then OpenNebula::MarketPlacePool.new(client)
|
1536
|
+
when 'FLOWTEMPLATES' then OpenNebula::ServiceTemplatePool.new(client)
|
1537
|
+
end
|
1396
1538
|
|
1397
1539
|
rc = pool.info
|
1398
1540
|
if OpenNebula.is_error?(rc)
|
1399
1541
|
return -1, "OpenNebula #{poolname} name not found," <<
|
1400
|
-
|
1542
|
+
' use the ID instead'
|
1401
1543
|
end
|
1402
1544
|
|
1403
1545
|
OneHelper.name_to_id(name, pool, poolname)
|
1404
1546
|
end
|
1405
1547
|
|
1406
|
-
def
|
1548
|
+
def self.size_in_mb(size)
|
1407
1549
|
m = size.match(/^(\d+(?:\.\d+)?)(t|tb|m|mb|g|gb)?$/i)
|
1408
1550
|
|
1409
1551
|
if !m
|
1410
1552
|
# return OpenNebula::Error.new('Size value malformed')
|
1411
|
-
|
1553
|
+
[-1, 'Size value malformed']
|
1412
1554
|
else
|
1413
1555
|
multiplier=case m[2]
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1556
|
+
when /(t|tb)/i
|
1557
|
+
1024*1024
|
1558
|
+
when /(g|gb)/i
|
1559
|
+
1024
|
1560
|
+
else
|
1561
|
+
1
|
1562
|
+
end
|
1421
1563
|
|
1422
1564
|
value=m[1].to_f*multiplier
|
1423
1565
|
|
1424
1566
|
# return value.ceil
|
1425
|
-
|
1567
|
+
[0, value.ceil]
|
1426
1568
|
end
|
1427
1569
|
end
|
1428
1570
|
|
1429
|
-
def
|
1571
|
+
def self.rname_to_id_desc(poolname)
|
1430
1572
|
"OpenNebula #{poolname} name or id"
|
1431
1573
|
end
|
1432
1574
|
|
1433
|
-
def
|
1575
|
+
def self.boolean_to_str(str)
|
1434
1576
|
if str.to_i == 1
|
1435
|
-
|
1577
|
+
'Yes'
|
1436
1578
|
else
|
1437
|
-
|
1579
|
+
'No'
|
1438
1580
|
end
|
1439
1581
|
end
|
1440
1582
|
|
1441
|
-
def
|
1442
|
-
|
1583
|
+
def self.time_to_str(time, print_seconds = true,
|
1584
|
+
print_hours = true, print_years = false)
|
1443
1585
|
|
1444
1586
|
value = time.to_i
|
1445
1587
|
|
@@ -1449,63 +1591,63 @@ EOT
|
|
1449
1591
|
if print_hours
|
1450
1592
|
if print_seconds
|
1451
1593
|
if print_years
|
1452
|
-
value=Time.at(value).strftime(
|
1594
|
+
value=Time.at(value).strftime('%m/%d/%y %H:%M:%S')
|
1453
1595
|
else
|
1454
|
-
value=Time.at(value).strftime(
|
1596
|
+
value=Time.at(value).strftime('%m/%d %H:%M:%S')
|
1455
1597
|
end
|
1456
1598
|
else
|
1457
1599
|
if print_years
|
1458
|
-
value=Time.at(value).strftime(
|
1600
|
+
value=Time.at(value).strftime('%m/%d/%y %H:%M')
|
1459
1601
|
else
|
1460
|
-
value=Time.at(value).strftime(
|
1602
|
+
value=Time.at(value).strftime('%m/%d %H:%M')
|
1461
1603
|
end
|
1462
1604
|
end
|
1463
1605
|
else
|
1464
1606
|
if print_years
|
1465
|
-
value=Time.at(value).strftime(
|
1607
|
+
value=Time.at(value).strftime('%m/%d/%y')
|
1466
1608
|
else
|
1467
|
-
value=Time.at(value).strftime(
|
1609
|
+
value=Time.at(value).strftime('%m/%d')
|
1468
1610
|
end
|
1469
1611
|
end
|
1470
1612
|
end
|
1471
1613
|
|
1472
|
-
|
1614
|
+
value
|
1473
1615
|
end
|
1474
1616
|
|
1475
|
-
def
|
1617
|
+
def self.period_to_str(time, print_seconds = true)
|
1476
1618
|
seconds=time.to_i
|
1477
1619
|
minutes, seconds=seconds.divmod(60)
|
1478
1620
|
hours, minutes=minutes.divmod(60)
|
1479
1621
|
days, hours=hours.divmod(24)
|
1480
1622
|
|
1481
1623
|
if print_seconds
|
1482
|
-
|
1624
|
+
format('%3dd %02dh%02dm%02ds', days, hours, minutes, seconds)
|
1483
1625
|
else
|
1484
|
-
|
1626
|
+
format('%3dd %02dh%02dm', days, hours, minutes)
|
1485
1627
|
end
|
1486
1628
|
end
|
1487
1629
|
|
1488
|
-
def
|
1630
|
+
def self.short_period_to_str(time, print_seconds = true)
|
1489
1631
|
seconds=time.to_i
|
1490
1632
|
minutes, seconds=seconds.divmod(60)
|
1491
1633
|
hours, minutes=minutes.divmod(60)
|
1492
1634
|
|
1493
1635
|
if print_seconds
|
1494
|
-
|
1636
|
+
format('%3dh%02dm%02ds', hours, minutes, seconds)
|
1495
1637
|
else
|
1496
|
-
|
1638
|
+
format('%3dh%02dm', hours, minutes)
|
1497
1639
|
end
|
1498
1640
|
end
|
1499
1641
|
|
1500
|
-
BinarySufix = [
|
1642
|
+
BinarySufix = ['K', 'M', 'G', 'T']
|
1501
1643
|
|
1502
|
-
def
|
1644
|
+
def self.unit_to_str(value, options, unit = 'K')
|
1503
1645
|
if options[:kilobytes]
|
1504
1646
|
value
|
1505
1647
|
else
|
1506
1648
|
i=BinarySufix.index(unit).to_i
|
1507
1649
|
|
1508
|
-
while value > 1024 && i < 3
|
1650
|
+
while value > 1024 && i < 3
|
1509
1651
|
value /= 1024.0
|
1510
1652
|
i+=1
|
1511
1653
|
end
|
@@ -1517,11 +1659,11 @@ EOT
|
|
1517
1659
|
end
|
1518
1660
|
end
|
1519
1661
|
|
1520
|
-
def
|
1662
|
+
def self.bytes_to_unit(value, unit = 'K')
|
1521
1663
|
j = 0
|
1522
1664
|
i = BinarySufix.index(unit).to_i
|
1523
1665
|
|
1524
|
-
while j < i
|
1666
|
+
while j < i
|
1525
1667
|
value /= 1024.0
|
1526
1668
|
j += 1
|
1527
1669
|
end
|
@@ -1533,51 +1675,52 @@ EOT
|
|
1533
1675
|
#
|
1534
1676
|
# @param str [String || Hash] Cluster name, or empty Hash (when <CLUSTER/>)
|
1535
1677
|
# @return [String] the same Cluster name, or '-' if it is empty
|
1536
|
-
def
|
1537
|
-
if str
|
1678
|
+
def self.cluster_str(str)
|
1679
|
+
if !str.nil? && !str.empty?
|
1538
1680
|
str
|
1539
1681
|
else
|
1540
|
-
|
1682
|
+
'-'
|
1541
1683
|
end
|
1542
1684
|
end
|
1543
1685
|
|
1544
|
-
def
|
1686
|
+
def self.clusters_str(clusters)
|
1545
1687
|
if clusters.nil?
|
1546
|
-
|
1688
|
+
'-'
|
1547
1689
|
else
|
1548
1690
|
[clusters].flatten.join(',')
|
1549
1691
|
end
|
1550
|
-
|
1551
1692
|
end
|
1552
1693
|
|
1553
|
-
def
|
1554
|
-
|
1694
|
+
def self.update_template(id, resource, path = nil, xpath = 'TEMPLATE')
|
1695
|
+
update_template_helper(false, id, resource, path, xpath)
|
1555
1696
|
end
|
1556
1697
|
|
1557
|
-
def
|
1558
|
-
|
1698
|
+
def self.append_template(id, resource, path = nil, xpath = 'TEMPLATE')
|
1699
|
+
update_template_helper(true, id, resource, path, xpath)
|
1559
1700
|
end
|
1560
1701
|
|
1561
|
-
def
|
1702
|
+
def self.update_template_helper(append, _id, resource, path, xpath, update = true)
|
1562
1703
|
if path
|
1563
|
-
|
1704
|
+
File.read(path)
|
1705
|
+
elsif STDIN.wait_readable(0)
|
1706
|
+
STDIN.read
|
1564
1707
|
elsif append
|
1565
|
-
|
1708
|
+
editor_input
|
1566
1709
|
else
|
1567
1710
|
if update
|
1568
1711
|
rc = resource.info
|
1569
1712
|
|
1570
1713
|
if OpenNebula.is_error?(rc)
|
1571
1714
|
puts rc.message
|
1572
|
-
exit
|
1715
|
+
exit(-1)
|
1573
1716
|
end
|
1574
1717
|
end
|
1575
1718
|
|
1576
|
-
|
1719
|
+
editor_input(resource.template_like_str(xpath))
|
1577
1720
|
end
|
1578
1721
|
end
|
1579
1722
|
|
1580
|
-
def
|
1723
|
+
def self.update_obj(obj, file, plain = false)
|
1581
1724
|
rc = obj.info(true)
|
1582
1725
|
|
1583
1726
|
return rc if OpenNebula.is_error?(rc)
|
@@ -1614,28 +1757,27 @@ EOT
|
|
1614
1757
|
end
|
1615
1758
|
end
|
1616
1759
|
|
1617
|
-
def
|
1760
|
+
def self.editor_input(contents = nil)
|
1618
1761
|
require 'tempfile'
|
1619
1762
|
|
1620
|
-
tmp
|
1763
|
+
tmp = Tempfile.new('one_cli')
|
1621
1764
|
|
1622
1765
|
if contents
|
1623
1766
|
tmp << contents
|
1624
1767
|
tmp.flush
|
1625
1768
|
end
|
1626
1769
|
|
1627
|
-
editor_path = ENV[
|
1770
|
+
editor_path = ENV['EDITOR'] ? ENV['EDITOR'] : EDITOR_PATH
|
1628
1771
|
system("#{editor_path} #{tmp.path}")
|
1629
1772
|
|
1630
1773
|
unless $?.exitstatus == 0
|
1631
|
-
puts
|
1632
|
-
exit
|
1774
|
+
puts 'Editor not defined'
|
1775
|
+
exit(-1)
|
1633
1776
|
end
|
1634
1777
|
|
1635
1778
|
tmp.close
|
1636
1779
|
|
1637
|
-
|
1638
|
-
return str
|
1780
|
+
File.read(tmp.path)
|
1639
1781
|
end
|
1640
1782
|
|
1641
1783
|
def self.parse_user_object(user_object)
|
@@ -1643,7 +1785,7 @@ EOT
|
|
1643
1785
|
|
1644
1786
|
m=user_object.match(reg)
|
1645
1787
|
|
1646
|
-
return
|
1788
|
+
return unless m
|
1647
1789
|
|
1648
1790
|
user=nil
|
1649
1791
|
if m[2]
|
@@ -1660,7 +1802,7 @@ EOT
|
|
1660
1802
|
template=''
|
1661
1803
|
|
1662
1804
|
objects.each do |obj|
|
1663
|
-
obj, *extra_attributes = obj.split(
|
1805
|
+
obj, *extra_attributes = obj.split(':')
|
1664
1806
|
|
1665
1807
|
# When extra attributes do not contain = character include
|
1666
1808
|
# them in the previous value. Fixes adding MAC addresses. These
|
@@ -1673,26 +1815,27 @@ EOT
|
|
1673
1815
|
#
|
1674
1816
|
attrs = []
|
1675
1817
|
extra_attributes.each do |str|
|
1676
|
-
if str.include?(
|
1818
|
+
if str.include?('=')
|
1677
1819
|
attrs << str
|
1678
1820
|
else
|
1679
1821
|
attrs.last << ":#{str}"
|
1680
|
-
|
1822
|
+
end
|
1681
1823
|
end
|
1682
1824
|
|
1683
1825
|
extra_attributes = attrs
|
1684
1826
|
|
1685
1827
|
res=parse_user_object(obj)
|
1686
|
-
return [-1, "#{section.capitalize} \"#{obj}\" malformed"]
|
1828
|
+
return [-1, "#{section.capitalize} \"#{obj}\" malformed"] unless res
|
1829
|
+
|
1687
1830
|
user, object=*res
|
1688
1831
|
|
1689
1832
|
template<<"#{section.upcase}=[\n"
|
1690
|
-
if object.downcase ==
|
1833
|
+
if object.downcase == 'auto'
|
1691
1834
|
template<<" NETWORK_MODE=\"#{object}\"\n"
|
1692
1835
|
else
|
1693
1836
|
template<<" #{name.upcase}_UNAME=\"#{user}\",\n" if user
|
1694
1837
|
extra_attributes.each do |extra_attribute|
|
1695
|
-
key, value = extra_attribute.split(
|
1838
|
+
key, value = extra_attribute.split('=')
|
1696
1839
|
template<<" #{key.upcase}=\"#{value}\",\n"
|
1697
1840
|
end
|
1698
1841
|
if object.match(/^\d+$/)
|
@@ -1708,13 +1851,14 @@ EOT
|
|
1708
1851
|
end
|
1709
1852
|
|
1710
1853
|
def self.create_context(options)
|
1711
|
-
context_options = [:ssh, :net_context, :context, :init, :files_ds, :startscript,
|
1854
|
+
context_options = [:ssh, :net_context, :context, :init, :files_ds, :startscript,
|
1855
|
+
:report_ready]
|
1712
1856
|
if !(options.keys & context_options).empty?
|
1713
1857
|
lines=[]
|
1714
1858
|
|
1715
1859
|
if options[:ssh]
|
1716
1860
|
if options[:ssh]==true
|
1717
|
-
lines<<
|
1861
|
+
lines<<'SSH_PUBLIC_KEY="$USER[SSH_PUBLIC_KEY]"'
|
1718
1862
|
else
|
1719
1863
|
begin
|
1720
1864
|
key=File.read(options[:ssh]).strip
|
@@ -1727,7 +1871,7 @@ EOT
|
|
1727
1871
|
end
|
1728
1872
|
|
1729
1873
|
if options[:net_context]
|
1730
|
-
lines <<
|
1874
|
+
lines << 'NETWORK = "YES"'
|
1731
1875
|
end
|
1732
1876
|
|
1733
1877
|
lines+=options[:context] if options[:context]
|
@@ -1735,7 +1879,7 @@ EOT
|
|
1735
1879
|
if options[:files_ds]
|
1736
1880
|
text='FILES_DS="'
|
1737
1881
|
text << options[:files_ds].map do |file|
|
1738
|
-
%
|
1882
|
+
%($FILE[IMAGE=\\"#{file}\\"])
|
1739
1883
|
end.join(' ')
|
1740
1884
|
text << '"'
|
1741
1885
|
|
@@ -1743,7 +1887,7 @@ EOT
|
|
1743
1887
|
end
|
1744
1888
|
|
1745
1889
|
if options[:init]
|
1746
|
-
lines << %
|
1890
|
+
lines << %(INIT_SCRIPTS="#{options[:init].join(' ')}")
|
1747
1891
|
end
|
1748
1892
|
|
1749
1893
|
if options[:startscript]
|
@@ -1754,16 +1898,16 @@ EOT
|
|
1754
1898
|
STDERR.puts e.message
|
1755
1899
|
exit(-1)
|
1756
1900
|
end
|
1757
|
-
script = Base64
|
1901
|
+
script = Base64.strict_encode64(script)
|
1758
1902
|
lines<<"START_SCRIPT_BASE64=\"#{script}\""
|
1759
1903
|
end
|
1760
1904
|
|
1761
1905
|
if options[:report_ready]
|
1762
|
-
lines <<
|
1906
|
+
lines << 'REPORT_READY = "YES"'
|
1763
1907
|
end
|
1764
1908
|
|
1765
1909
|
if !lines.empty?
|
1766
|
-
"CONTEXT=[\n" << lines.map{|l|
|
1910
|
+
"CONTEXT=[\n" << lines.map {|l| ' ' << l }.join(",\n") << "\n]\n"
|
1767
1911
|
else
|
1768
1912
|
nil
|
1769
1913
|
end
|
@@ -1772,7 +1916,7 @@ EOT
|
|
1772
1916
|
end
|
1773
1917
|
end
|
1774
1918
|
|
1775
|
-
def self.create_template(options, template_obj=nil)
|
1919
|
+
def self.create_template(options, template_obj = nil)
|
1776
1920
|
template=''
|
1777
1921
|
|
1778
1922
|
template<<"NAME=\"#{options[:name]}\"\n" if options[:name]
|
@@ -1812,7 +1956,7 @@ EOT
|
|
1812
1956
|
end
|
1813
1957
|
|
1814
1958
|
if options[:vnc]
|
1815
|
-
vnc_listen=options[:vnc_listen] ||
|
1959
|
+
vnc_listen=options[:vnc_listen] || '0.0.0.0'
|
1816
1960
|
template<<"GRAPHICS=[ TYPE=\"vnc\", LISTEN=\"#{vnc_listen}\""
|
1817
1961
|
if options[:vnc_password]
|
1818
1962
|
template << ", PASSWD=\"#{options[:vnc_password]}\""
|
@@ -1824,7 +1968,7 @@ EOT
|
|
1824
1968
|
end
|
1825
1969
|
|
1826
1970
|
if options[:spice]
|
1827
|
-
spice_listen=options[:spice_listen] ||
|
1971
|
+
spice_listen=options[:spice_listen] || '0.0.0.0'
|
1828
1972
|
template<<"GRAPHICS=[ TYPE=\"spice\", LISTEN=\"#{spice_listen}\""
|
1829
1973
|
if options[:spice_password]
|
1830
1974
|
template << ", PASSWD=\"#{options[:spice_password]}\""
|
@@ -1835,20 +1979,29 @@ EOT
|
|
1835
1979
|
template<<' ]' << "\n"
|
1836
1980
|
end
|
1837
1981
|
|
1982
|
+
if options[:video]
|
1983
|
+
template<<"VIDEO=[ TYPE=\"#{options[:video]}\""
|
1984
|
+
template<<', IOMMU="YES"' if options[:video_iommu]
|
1985
|
+
template<<', ATS="YES"' if options[:video_ats]
|
1986
|
+
template<<", VRAM=\"#{options[:video_vram]}\"" if options[:video_vram]
|
1987
|
+
template<<", RESOLUTION=\"#{options[:video_resolution]}\""
|
1988
|
+
template<<' ]' << "\n"
|
1989
|
+
end
|
1990
|
+
|
1838
1991
|
template<<"VCENTER_VM_FOLDER=#{options[:vcenter_vm_folder]}\n" if options[:vcenter_vm_folder]
|
1839
1992
|
|
1840
1993
|
context=create_context(options)
|
1841
1994
|
template<<context if context
|
1842
1995
|
|
1843
|
-
if options[:userdata] && !template_obj.nil?
|
1844
|
-
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1996
|
+
if options[:userdata] && !template_obj.nil? && template_obj.has_elements?('TEMPLATE/EC2')
|
1997
|
+
template_obj.add_element(
|
1998
|
+
'TEMPLATE/EC2',
|
1999
|
+
'USERDATA' => options[:userdata]
|
2000
|
+
)
|
1848
2001
|
|
1849
|
-
|
1850
|
-
|
1851
|
-
|
2002
|
+
template << template_obj.template_like_str(
|
2003
|
+
'TEMPLATE', false, 'EC2'
|
2004
|
+
)
|
1852
2005
|
end
|
1853
2006
|
|
1854
2007
|
[0, template]
|
@@ -1946,21 +2099,21 @@ EOT
|
|
1946
2099
|
|
1947
2100
|
def self.sunstone_url
|
1948
2101
|
if (one_sunstone = ENV['ONE_SUNSTONE'])
|
1949
|
-
|
2102
|
+
one_sunstone
|
1950
2103
|
elsif (one_xmlrpc = ENV['ONE_XMLRPC'])
|
1951
2104
|
uri = URI(one_xmlrpc)
|
1952
2105
|
"#{uri.scheme}://#{uri.host}:9869"
|
1953
2106
|
else
|
1954
|
-
|
2107
|
+
'http://localhost:9869'
|
1955
2108
|
end
|
1956
2109
|
end
|
1957
2110
|
|
1958
|
-
def self.download_resource_sunstone(kind, id, path,
|
2111
|
+
def self.download_resource_sunstone(kind, id, path, _force)
|
1959
2112
|
client = OneHelper.client
|
1960
|
-
user, password = client.one_auth.split(
|
2113
|
+
user, password = client.one_auth.split(':', 2)
|
1961
2114
|
|
1962
2115
|
# Step 1: Build Session to get Cookie
|
1963
|
-
uri = URI(File.join(sunstone_url,
|
2116
|
+
uri = URI(File.join(sunstone_url, 'login'))
|
1964
2117
|
|
1965
2118
|
req = Net::HTTP::Post.new(uri)
|
1966
2119
|
req.basic_auth user, password
|
@@ -1969,14 +2122,14 @@ EOT
|
|
1969
2122
|
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
|
1970
2123
|
http.request(req)
|
1971
2124
|
end
|
1972
|
-
rescue
|
2125
|
+
rescue StandardError
|
1973
2126
|
return OpenNebula::Error.new("Error connecting to '#{uri}'.")
|
1974
2127
|
end
|
1975
2128
|
|
1976
2129
|
cookie = res.response['set-cookie'].split('; ')[0]
|
1977
2130
|
|
1978
2131
|
if cookie.nil?
|
1979
|
-
|
2132
|
+
return OpenNebula::Error.new('Unable to get Cookie. Is OpenNebula running?')
|
1980
2133
|
end
|
1981
2134
|
|
1982
2135
|
# Step 2: Open '/' to get the csrftoken
|
@@ -1989,7 +2142,7 @@ EOT
|
|
1989
2142
|
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
|
1990
2143
|
http.request(req)
|
1991
2144
|
end
|
1992
|
-
rescue
|
2145
|
+
rescue StandardError
|
1993
2146
|
return OpenNebula::Error.new("Error connecting to '#{uri}'.")
|
1994
2147
|
end
|
1995
2148
|
|
@@ -1997,7 +2150,7 @@ EOT
|
|
1997
2150
|
csrftoken = m[1] rescue nil
|
1998
2151
|
|
1999
2152
|
if csrftoken.nil?
|
2000
|
-
|
2153
|
+
return OpenNebula::Error.new('Unable to get csrftoken.')
|
2001
2154
|
end
|
2002
2155
|
|
2003
2156
|
# Step 3: Download resource
|
@@ -2009,7 +2162,7 @@ EOT
|
|
2009
2162
|
req = Net::HTTP::Get.new(uri)
|
2010
2163
|
|
2011
2164
|
req['Cookie'] = cookie
|
2012
|
-
req['User-Agent'] =
|
2165
|
+
req['User-Agent'] = 'OpenNebula CLI'
|
2013
2166
|
|
2014
2167
|
begin
|
2015
2168
|
File.open(path, 'wb') do |f|
|
@@ -2022,7 +2175,7 @@ EOT
|
|
2022
2175
|
end
|
2023
2176
|
end
|
2024
2177
|
rescue Errno::EACCES
|
2025
|
-
return OpenNebula::Error.new(
|
2178
|
+
return OpenNebula::Error.new('Target file not writable.')
|
2026
2179
|
end
|
2027
2180
|
|
2028
2181
|
error_message = nil
|
@@ -2039,30 +2192,30 @@ EOT
|
|
2039
2192
|
error_message = m[1] if m
|
2040
2193
|
end
|
2041
2194
|
|
2042
|
-
|
2043
|
-
|
2044
|
-
|
2045
|
-
|
2195
|
+
return unless error_message
|
2196
|
+
|
2197
|
+
File.unlink(path)
|
2198
|
+
OpenNebula::Error.new("Remote server error: #{error_message}")
|
2046
2199
|
end
|
2047
2200
|
|
2048
|
-
def
|
2201
|
+
def self.level_lock_to_str(str)
|
2049
2202
|
level = str.to_i
|
2050
2203
|
if level == 0
|
2051
|
-
|
2204
|
+
'None'
|
2052
2205
|
elsif level == 1
|
2053
|
-
|
2206
|
+
'Use'
|
2054
2207
|
elsif level == 2
|
2055
|
-
|
2208
|
+
'Manage'
|
2056
2209
|
elsif level == 3
|
2057
|
-
|
2210
|
+
'Admin'
|
2058
2211
|
elsif level == 4
|
2059
|
-
|
2212
|
+
'All'
|
2060
2213
|
else
|
2061
|
-
|
2214
|
+
'-'
|
2062
2215
|
end
|
2063
2216
|
end
|
2064
2217
|
|
2065
|
-
def
|
2218
|
+
def self.parse_user_inputs(inputs, keys = [])
|
2066
2219
|
unless inputs.keys == keys
|
2067
2220
|
puts 'There are some parameters that require user input. ' \
|
2068
2221
|
'Use the string <<EDITOR>> to launch an editor ' \
|
@@ -2132,7 +2285,7 @@ EOT
|
|
2132
2285
|
# use default in case it's empty
|
2133
2286
|
answer = initial if answer.empty?
|
2134
2287
|
|
2135
|
-
unless
|
2288
|
+
unless ['YES', 'NO'].include?(answer)
|
2136
2289
|
STDERR.puts "Invalid boolean '#{answer}'"
|
2137
2290
|
STDERR.puts 'Boolean has to be YES or NO'
|
2138
2291
|
exit(-1)
|
@@ -2160,8 +2313,8 @@ EOT
|
|
2160
2313
|
answer = STDIN.readline.chop
|
2161
2314
|
|
2162
2315
|
answer = initial if answer == ''
|
2163
|
-
|
2164
|
-
end while !noanswer && (answer =~ exp)
|
2316
|
+
noanswer = ((answer == '') && optional)
|
2317
|
+
end while !noanswer && (answer =~ exp).nil?
|
2165
2318
|
|
2166
2319
|
if noanswer
|
2167
2320
|
next
|
@@ -2198,7 +2351,7 @@ EOT
|
|
2198
2351
|
answer = initial if answer == ''
|
2199
2352
|
|
2200
2353
|
noanswer = (answer == '') && optional
|
2201
|
-
end while !noanswer && ((answer =~ exp)
|
2354
|
+
end while !noanswer && ((answer =~ exp).nil? ||
|
2202
2355
|
answer.to_f < min || answer.to_f > max)
|
2203
2356
|
|
2204
2357
|
if noanswer
|
@@ -2257,7 +2410,7 @@ EOT
|
|
2257
2410
|
# @param title [String] Plot title
|
2258
2411
|
#
|
2259
2412
|
# @return Gnuplot plot object
|
2260
|
-
def
|
2413
|
+
def self.get_plot(x, y, attr, title)
|
2261
2414
|
# Require gnuplot gem only here
|
2262
2415
|
begin
|
2263
2416
|
require 'gnuplot'
|
@@ -2307,7 +2460,7 @@ EOT
|
|
2307
2460
|
# @param perm [String] Permissions in human readbale format
|
2308
2461
|
#
|
2309
2462
|
# @return [String] Permissions in octet format
|
2310
|
-
def
|
2463
|
+
def self.to_octet(perm)
|
2311
2464
|
begin
|
2312
2465
|
Integer(perm)
|
2313
2466
|
perm
|
@@ -2353,4 +2506,148 @@ EOT
|
|
2353
2506
|
end
|
2354
2507
|
end
|
2355
2508
|
|
2509
|
+
def self.schedule_action_tmpl(options, action, warning = nil)
|
2510
|
+
str_periodic = ''
|
2511
|
+
|
2512
|
+
if options.key?(:weekly)
|
2513
|
+
str_periodic << ", REPEAT = 0, DAYS = \"#{options[:weekly]}\""
|
2514
|
+
elsif options.key?(:monthly)
|
2515
|
+
str_periodic << ", REPEAT = 1, DAYS = \"#{options[:monthly]}\""
|
2516
|
+
elsif options.key?(:yearly)
|
2517
|
+
str_periodic << ", REPEAT = 2, DAYS = \"#{options[:yearly]}\""
|
2518
|
+
elsif options.key?(:hourly)
|
2519
|
+
str_periodic << ", REPEAT = 3, DAYS = \"#{options[:hourly]}\""
|
2520
|
+
end
|
2521
|
+
|
2522
|
+
if options.key?(:end)
|
2523
|
+
begin
|
2524
|
+
end_date = Date.parse(options[:end])
|
2525
|
+
str_periodic << ", END_TYPE = 2, END_VALUE = #{end_date.to_time.to_i}"
|
2526
|
+
rescue ArgumentError
|
2527
|
+
if options[:end].to_i > 0
|
2528
|
+
str_periodic << ", END_TYPE = 1, END_VALUE = #{options[:end].to_i}"
|
2529
|
+
end
|
2530
|
+
end
|
2531
|
+
elsif str_periodic != ''
|
2532
|
+
str_periodic << ', END_TYPE = 0'
|
2533
|
+
end
|
2534
|
+
|
2535
|
+
tmp_str = 'SCHED_ACTION = ['
|
2536
|
+
tmp_str << "ACTION = #{action}, " if action
|
2537
|
+
tmp_str << "WARNING = #{warning}," if warning
|
2538
|
+
tmp_str << "ARGS = \"#{options[:args]}\"," if options[:args]
|
2539
|
+
tmp_str << "TIME = #{options[:schedule]}"
|
2540
|
+
tmp_str << str_periodic << ']'
|
2541
|
+
|
2542
|
+
tmp_str
|
2543
|
+
end
|
2544
|
+
|
2545
|
+
def self.scheduled_action_table(object)
|
2546
|
+
CLIHelper::ShowTable.new(nil, object) do
|
2547
|
+
column :ID, '', :adjust => true do |d|
|
2548
|
+
d['ID']
|
2549
|
+
end
|
2550
|
+
|
2551
|
+
column :ACTION, '', :adjust => true do |d|
|
2552
|
+
d['ACTION']
|
2553
|
+
end
|
2554
|
+
|
2555
|
+
column :ARGS, '', :adjust => true do |d|
|
2556
|
+
d['ARGS'] && !d['ARGS'].empty? ? d['ARGS'] : '-'
|
2557
|
+
end
|
2558
|
+
|
2559
|
+
column :SCHEDULED, '', :adjust => true do |d|
|
2560
|
+
t = d['TIME'].to_i
|
2561
|
+
|
2562
|
+
# relative action for VMs
|
2563
|
+
if d['TIME'] !~ /^[0-9].*/ && !object['STIME'].nil?
|
2564
|
+
t += object['STIME'].to_i
|
2565
|
+
end
|
2566
|
+
|
2567
|
+
OpenNebulaHelper.time_to_str(t, false) unless d.nil?
|
2568
|
+
end
|
2569
|
+
|
2570
|
+
column :REPEAT, '', :adjust => true do |d|
|
2571
|
+
begin
|
2572
|
+
str_rep = ''
|
2573
|
+
|
2574
|
+
case d['REPEAT']
|
2575
|
+
when '0'
|
2576
|
+
str_rep << 'Weekly '
|
2577
|
+
when '1'
|
2578
|
+
str_rep << 'Monthly '
|
2579
|
+
when '2'
|
2580
|
+
str_rep << 'Yearly '
|
2581
|
+
when '3'
|
2582
|
+
str_rep << 'Each ' << d['DAYS'] << ' hours'
|
2583
|
+
end
|
2584
|
+
|
2585
|
+
if d['REPEAT'] != '3'
|
2586
|
+
str_rep << d['DAYS']
|
2587
|
+
end
|
2588
|
+
|
2589
|
+
str_rep
|
2590
|
+
rescue StandardError
|
2591
|
+
''
|
2592
|
+
end
|
2593
|
+
end
|
2594
|
+
|
2595
|
+
column :END, '', :adjust => true do |d|
|
2596
|
+
begin
|
2597
|
+
str_end = ''
|
2598
|
+
|
2599
|
+
case d['END_TYPE']
|
2600
|
+
when '0'
|
2601
|
+
str_end << 'None'
|
2602
|
+
when '1'
|
2603
|
+
str_end << 'After ' << d['END_VALUE'] << ' times'
|
2604
|
+
when '2'
|
2605
|
+
str_end << 'On ' << \
|
2606
|
+
OpenNebulaHelper.time_to_str(d['END_VALUE'], false, false, true)
|
2607
|
+
end
|
2608
|
+
|
2609
|
+
str_end
|
2610
|
+
rescue StandardError
|
2611
|
+
''
|
2612
|
+
end
|
2613
|
+
end
|
2614
|
+
|
2615
|
+
column :STATUS, '', :left, :size => 50 do |d|
|
2616
|
+
begin
|
2617
|
+
if d['DONE'].to_i > 0 && d['REPEAT'].to_i < 0
|
2618
|
+
"Done on #{OpenNebulaHelper.time_to_str(d['DONE'], false)}"
|
2619
|
+
elsif d['MESSAGE'] && !d['MESSAGE'].empty?
|
2620
|
+
"Error! #{d['MESSAGE']}"
|
2621
|
+
else
|
2622
|
+
t1 = Time.now
|
2623
|
+
t2 = d['TIME'].to_i
|
2624
|
+
|
2625
|
+
# relative action for VMs
|
2626
|
+
if (d['TIME'] !~ /^[0-9].*/) && !object['STIME'].nil?
|
2627
|
+
t2 += object['STIME'].to_i
|
2628
|
+
end
|
2629
|
+
|
2630
|
+
t2 = Time.at(t2)
|
2631
|
+
|
2632
|
+
days = ((t2 - t1) / (24 * 3600)).round(2)
|
2633
|
+
hours = ((t2 - t1) / 3600).round(2)
|
2634
|
+
minutes = ((t2 - t1) / 60).round(2)
|
2635
|
+
|
2636
|
+
if days > 1
|
2637
|
+
"Next in #{days} days"
|
2638
|
+
elsif days <= 1 && hours > 1
|
2639
|
+
"Next in #{hours} hours"
|
2640
|
+
elsif minutes > 0
|
2641
|
+
"Next in #{minutes} minutes"
|
2642
|
+
else
|
2643
|
+
'Overdue!'
|
2644
|
+
end
|
2645
|
+
end
|
2646
|
+
rescue StandardError
|
2647
|
+
''
|
2648
|
+
end
|
2649
|
+
end
|
2650
|
+
end
|
2651
|
+
end
|
2652
|
+
|
2356
2653
|
end
|