opennebula-cli 6.6.2 → 6.7.80.pre
Sign up to get free protection for your applications and to get access to all the features.
- 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
|