opennebula-cli 6.6.1 → 6.6.3
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/onecluster +1 -1
- data/bin/onedatastore +1 -1
- data/bin/oneflow-template +10 -5
- data/bin/onegroup +1 -1
- data/bin/onehook +1 -1
- data/bin/onehost +1 -1
- data/bin/oneimage +1 -1
- data/bin/onemarket +1 -1
- data/bin/onemarketapp +1 -1
- data/bin/onesecgroup +1 -1
- data/bin/onetemplate +1 -1
- data/bin/oneuser +1 -1
- data/bin/onevdc +1 -1
- data/bin/onevm +24 -8
- data/bin/onevmgroup +1 -1
- data/bin/onevnet +1 -1
- data/bin/onevntemplate +1 -1
- data/bin/onevrouter +1 -1
- data/bin/onezone +1 -1
- data/lib/one_helper/oneflowtemplate_helper.rb +29 -0
- data/lib/one_helper/onetemplate_helper.rb +1 -0
- data/lib/one_helper/onevm_helper.rb +50 -57
- data/lib/one_helper.rb +321 -325
- data/share/schemas/xsd/vm_pool.xsd +1 -0
- metadata +4 -4
data/lib/one_helper.rb
CHANGED
@@ -21,27 +21,28 @@ require 'io/console'
|
|
21
21
|
begin
|
22
22
|
require 'opennebula'
|
23
23
|
rescue Exception => e
|
24
|
-
puts
|
24
|
+
puts 'Error: '+e.message.to_s
|
25
25
|
exit(-1)
|
26
26
|
end
|
27
27
|
|
28
28
|
include OpenNebula
|
29
29
|
|
30
30
|
module OpenNebulaHelper
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
|
32
|
+
ONE_VERSION=<<~EOT
|
33
|
+
OpenNebula #{OpenNebula::VERSION}
|
34
|
+
Copyright 2002-2023, OpenNebula Project, OpenNebula Systems
|
35
|
+
EOT
|
35
36
|
|
36
37
|
if ONE_LOCATION
|
37
|
-
TABLE_CONF_PATH = ONE_LOCATION +
|
38
|
-
VAR_LOCATION = ONE_LOCATION +
|
39
|
-
CLI_ADDONS_LOCATION = ONE_LOCATION +
|
38
|
+
TABLE_CONF_PATH = ONE_LOCATION + '/etc/cli'
|
39
|
+
VAR_LOCATION = ONE_LOCATION + '/var' unless defined?(VAR_LOCATION)
|
40
|
+
CLI_ADDONS_LOCATION = ONE_LOCATION + '/lib/ruby/cli/addons'
|
40
41
|
XSD_PATH = ONE_LOCATION + '/share/schemas/xsd'
|
41
42
|
else
|
42
|
-
TABLE_CONF_PATH =
|
43
|
-
VAR_LOCATION =
|
44
|
-
CLI_ADDONS_LOCATION =
|
43
|
+
TABLE_CONF_PATH = '/etc/one/cli'
|
44
|
+
VAR_LOCATION = '/var/lib/one' unless defined?(VAR_LOCATION)
|
45
|
+
CLI_ADDONS_LOCATION = '/usr/lib/one/ruby/cli/addons'
|
45
46
|
XSD_PATH = '/usr/share/one/schemas/xsd'
|
46
47
|
end
|
47
48
|
|
@@ -51,10 +52,10 @@ EOT
|
|
51
52
|
# Options
|
52
53
|
########################################################################
|
53
54
|
XML={
|
54
|
-
:name =>
|
55
|
-
:short =>
|
56
|
-
:large =>
|
57
|
-
:description =>
|
55
|
+
:name => 'xml',
|
56
|
+
:short => '-x',
|
57
|
+
:large => '--xml',
|
58
|
+
:description => 'Show the resource in xml format'
|
58
59
|
}
|
59
60
|
|
60
61
|
JSON = {
|
@@ -75,30 +76,30 @@ EOT
|
|
75
76
|
}
|
76
77
|
|
77
78
|
NUMERIC={
|
78
|
-
:name =>
|
79
|
-
:short =>
|
80
|
-
:large =>
|
81
|
-
:description =>
|
79
|
+
:name => 'numeric',
|
80
|
+
:short => '-n',
|
81
|
+
:large => '--numeric',
|
82
|
+
:description => 'Do not translate user and group IDs'
|
82
83
|
}
|
83
84
|
|
84
85
|
KILOBYTES={
|
85
|
-
:name =>
|
86
|
-
:short =>
|
87
|
-
:large =>
|
88
|
-
:description =>
|
86
|
+
:name => 'kilobytes',
|
87
|
+
:short => '-k',
|
88
|
+
:large => '--kilobytes',
|
89
|
+
:description => 'Show units in kilobytes'
|
89
90
|
}
|
90
91
|
|
91
92
|
DESCRIBE={
|
92
|
-
:name =>
|
93
|
-
:large =>
|
94
|
-
:description =>
|
93
|
+
:name => 'describe',
|
94
|
+
:large => '--describe',
|
95
|
+
:description => 'Describe list columns'
|
95
96
|
}
|
96
97
|
|
97
98
|
APPEND = {
|
98
|
-
:name =>
|
99
|
-
:short =>
|
100
|
-
:large =>
|
101
|
-
:description =>
|
99
|
+
:name => 'append',
|
100
|
+
:short => '-a',
|
101
|
+
:large => '--append',
|
102
|
+
:description => 'Append new attributes to the current template'
|
102
103
|
}
|
103
104
|
|
104
105
|
# Command line VM template options
|
@@ -122,7 +123,7 @@ EOT
|
|
122
123
|
:large => '--user name',
|
123
124
|
:description => 'User name used to connect to OpenNebula',
|
124
125
|
:format => String,
|
125
|
-
:proc => lambda do |o,
|
126
|
+
:proc => lambda do |o, _options|
|
126
127
|
OneHelper.set_user(o)
|
127
128
|
[0, o]
|
128
129
|
end
|
@@ -132,7 +133,7 @@ EOT
|
|
132
133
|
:large => '--password password',
|
133
134
|
:description => 'Password to authenticate with OpenNebula',
|
134
135
|
:format => String,
|
135
|
-
:proc => lambda do |o,
|
136
|
+
:proc => lambda do |o, _options|
|
136
137
|
OneHelper.set_password(o)
|
137
138
|
[0, o]
|
138
139
|
end
|
@@ -142,7 +143,7 @@ EOT
|
|
142
143
|
:large => '--endpoint endpoint',
|
143
144
|
:description => 'URL of OpenNebula xmlrpc frontend',
|
144
145
|
:format => String,
|
145
|
-
:proc => lambda do |o,
|
146
|
+
:proc => lambda do |o, _options|
|
146
147
|
OneHelper.set_endpoint(o)
|
147
148
|
[0, o]
|
148
149
|
end
|
@@ -153,7 +154,7 @@ EOT
|
|
153
154
|
{
|
154
155
|
:name => 'name',
|
155
156
|
:large => '--name name',
|
156
|
-
:short =>
|
157
|
+
:short => '-n',
|
157
158
|
:description =>
|
158
159
|
'Name for the new group',
|
159
160
|
:format => String
|
@@ -161,7 +162,7 @@ EOT
|
|
161
162
|
{
|
162
163
|
:name => 'admin_user',
|
163
164
|
:large => '--admin_user name',
|
164
|
-
:short =>
|
165
|
+
:short => '-u',
|
165
166
|
:description =>
|
166
167
|
'Creates an admin user for the group with name',
|
167
168
|
:format => String
|
@@ -169,7 +170,7 @@ EOT
|
|
169
170
|
{
|
170
171
|
:name => 'admin_password',
|
171
172
|
:large => '--admin_password pass',
|
172
|
-
:short =>
|
173
|
+
:short => '-p',
|
173
174
|
:description =>
|
174
175
|
'Password for the admin user of the group',
|
175
176
|
:format => String
|
@@ -177,7 +178,7 @@ EOT
|
|
177
178
|
{
|
178
179
|
:name => 'admin_driver',
|
179
180
|
:large => '--admin_driver driver',
|
180
|
-
:short =>
|
181
|
+
:short => '-d',
|
181
182
|
:description =>
|
182
183
|
'Auth driver for the admin user of the group',
|
183
184
|
:format => String
|
@@ -185,43 +186,43 @@ EOT
|
|
185
186
|
{
|
186
187
|
:name => 'resources',
|
187
188
|
:large => '--resources res_str',
|
188
|
-
:short =>
|
189
|
+
:short => '-r',
|
189
190
|
:description =>
|
190
|
-
|
191
|
-
|
191
|
+
'Which resources can be created by group users '<<
|
192
|
+
'(VM+NET+IMAGE+TEMPLATE by default)',
|
192
193
|
:format => String
|
193
194
|
}
|
194
195
|
]
|
195
196
|
|
196
197
|
AS_USER = {
|
197
|
-
|
198
|
+
:name => 'as_uid',
|
198
199
|
:large => '--as_uid uid',
|
199
200
|
:format => Integer,
|
200
201
|
:description => 'The User ID to instantiate the VM'
|
201
202
|
}
|
202
203
|
|
203
204
|
AS_GROUP = {
|
204
|
-
|
205
|
+
:name => 'as_gid',
|
205
206
|
:large => '--as_gid gid',
|
206
207
|
:format => Integer,
|
207
208
|
:description => 'The Group ID to instantiate the VM'
|
208
209
|
}
|
209
210
|
|
210
|
-
#NOTE: Other options defined using this array, add new options at the end
|
211
|
+
# NOTE: Other options defined using this array, add new options at the end
|
211
212
|
TEMPLATE_OPTIONS=[
|
212
213
|
{
|
213
214
|
:name => 'cpu',
|
214
215
|
:large => '--cpu cpu',
|
215
216
|
:description =>
|
216
217
|
"CPU percentage reserved for the VM (1=100% one\n"<<
|
217
|
-
|
218
|
+
' '*31<<'CPU)',
|
218
219
|
:format => Float
|
219
220
|
},
|
220
221
|
{
|
221
222
|
:name => 'vcpu',
|
222
223
|
:large => '--vcpu vcpu',
|
223
224
|
:description =>
|
224
|
-
|
225
|
+
'Number of virtualized CPUs',
|
225
226
|
:format => Integer
|
226
227
|
},
|
227
228
|
{
|
@@ -236,20 +237,20 @@ EOT
|
|
236
237
|
:large => '--memory memory',
|
237
238
|
:description => 'Memory amount given to the VM. By default the '<<
|
238
239
|
"unit is megabytes. To use gigabytes add a 'g', floats "<<
|
239
|
-
|
240
|
+
'can be used: 8g=8192, 0.5g=512',
|
240
241
|
:format => String,
|
241
|
-
:proc => lambda do |o,
|
242
|
+
:proc => lambda do |o, _options|
|
242
243
|
m=o.strip.match(/^(\d+(?:\.\d+)?)(m|mb|g|gb)?$/i)
|
243
244
|
|
244
245
|
if !m
|
245
246
|
[-1, 'Memory value malformed']
|
246
247
|
else
|
247
248
|
multiplier=case m[2]
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
249
|
+
when /(g|gb)/i
|
250
|
+
1024
|
251
|
+
else
|
252
|
+
1
|
253
|
+
end
|
253
254
|
|
254
255
|
value=m[1].to_f*multiplier
|
255
256
|
|
@@ -260,29 +261,29 @@ EOT
|
|
260
261
|
{
|
261
262
|
:name => 'disk',
|
262
263
|
:large => '--disk image0,image1',
|
263
|
-
:description =>
|
264
|
-
|
264
|
+
:description => 'Disks to attach. To use an image owned by'<<
|
265
|
+
' other user use user[disk]. Add any additional'<<
|
265
266
|
" attributes separated by ':' and in the shape of"<<
|
266
|
-
|
267
|
-
|
268
|
-
|
267
|
+
' KEY=VALUE. For example, if the disk must be'<<
|
268
|
+
' resized, use image0:size=1000 . Or'<<
|
269
|
+
' image0:size=1000:target=vda,image1:target=vdb',
|
269
270
|
:format => Array
|
270
271
|
},
|
271
272
|
{
|
272
273
|
:name => 'nic',
|
273
274
|
:large => '--nic network0,network1',
|
274
|
-
:description =>
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
275
|
+
:description => 'Networks to attach. To use a network owned by'<<
|
276
|
+
' other user use user[network]. Additional'<<
|
277
|
+
' attributes are supported like with the --disk'<<
|
278
|
+
' option. Also you can use auto if you want that' <<
|
279
|
+
' OpenNebula select automatically the network',
|
279
280
|
:format => Array
|
280
281
|
},
|
281
282
|
{
|
282
283
|
:name => 'raw',
|
283
284
|
:large => '--raw string',
|
284
285
|
:description => "Raw string to add to the template. Not to be\n"<<
|
285
|
-
|
286
|
+
' '*31<<'confused with the RAW attribute',
|
286
287
|
:format => String
|
287
288
|
},
|
288
289
|
{
|
@@ -338,9 +339,9 @@ EOT
|
|
338
339
|
:large => '--ssh [file]',
|
339
340
|
:description => "Add an ssh public key to the context. If the \n"<<
|
340
341
|
(' '*31) << "file is omited then the user variable \n"<<
|
341
|
-
(' '*31) <<
|
342
|
+
(' '*31) << 'SSH_PUBLIC_KEY will be used.',
|
342
343
|
:format => String,
|
343
|
-
:proc => lambda do |o,
|
344
|
+
:proc => lambda do |o, _options|
|
344
345
|
if !o
|
345
346
|
[0, true]
|
346
347
|
else
|
@@ -393,8 +394,8 @@ EOT
|
|
393
394
|
:name => 'vcenter_vm_folder',
|
394
395
|
:large => '--vcenter_vm_folder path',
|
395
396
|
:format => String,
|
396
|
-
:description =>
|
397
|
-
|
397
|
+
:description => 'In a vCenter environment sets the the VMs and Template folder where the VM will be placed in.' \
|
398
|
+
' The path uses slashes to separate folders. For example: --vcenter_vm_folder "/Management/VMs"'
|
398
399
|
},
|
399
400
|
{
|
400
401
|
:name => 'user_inputs',
|
@@ -442,19 +443,20 @@ EOT
|
|
442
443
|
TEMPLATE_OPTIONS_VM = [TEMPLATE_NAME_VM] + TEMPLATE_OPTIONS + [DRY]
|
443
444
|
|
444
445
|
CAPACITY_OPTIONS_VM = [TEMPLATE_OPTIONS[0], TEMPLATE_OPTIONS[1],
|
445
|
-
|
446
|
+
TEMPLATE_OPTIONS[3]]
|
446
447
|
|
447
448
|
UPDATECONF_OPTIONS_VM = TEMPLATE_OPTIONS[6..15] + [TEMPLATE_OPTIONS[2],
|
448
|
-
|
449
|
+
TEMPLATE_OPTIONS[17], TEMPLATE_OPTIONS[18]]
|
449
450
|
|
450
451
|
FORMAT = [XML, JSON, YAML]
|
451
452
|
|
452
453
|
OPTIONS = FORMAT, EXTENDED, NUMERIC, KILOBYTES
|
453
454
|
|
454
455
|
class OneHelper
|
456
|
+
|
455
457
|
attr_accessor :client
|
456
458
|
|
457
|
-
def self.get_client(options={}, force=false)
|
459
|
+
def self.get_client(options = {}, force = false)
|
458
460
|
if !force && defined?(@@client)
|
459
461
|
@@client
|
460
462
|
else
|
@@ -470,7 +472,7 @@ EOT
|
|
470
472
|
end
|
471
473
|
|
472
474
|
if user
|
473
|
-
password=password||options[:password]||
|
475
|
+
password=password||options[:password]||get_password
|
474
476
|
secret="#{user}:#{password}"
|
475
477
|
end
|
476
478
|
|
@@ -497,7 +499,7 @@ EOT
|
|
497
499
|
if defined?(@@client)
|
498
500
|
@@client
|
499
501
|
else
|
500
|
-
|
502
|
+
get_client
|
501
503
|
end
|
502
504
|
end
|
503
505
|
|
@@ -513,10 +515,10 @@ EOT
|
|
513
515
|
@@endpoint=endpoint
|
514
516
|
end
|
515
517
|
|
516
|
-
if RUBY_VERSION>=
|
518
|
+
if RUBY_VERSION>='1.9.3'
|
517
519
|
require 'io/console'
|
518
520
|
def self.get_password
|
519
|
-
print
|
521
|
+
print 'Password: '
|
520
522
|
pass=nil
|
521
523
|
STDIN.noecho {|io| pass=io.gets }
|
522
524
|
puts
|
@@ -528,25 +530,29 @@ EOT
|
|
528
530
|
else
|
529
531
|
# This function is copied from ruby net/imap.rb
|
530
532
|
def self.get_password
|
531
|
-
print
|
532
|
-
system(
|
533
|
+
print 'Password: '
|
534
|
+
system('stty', '-echo')
|
533
535
|
begin
|
534
536
|
@@password = STDIN.gets.chop
|
535
|
-
|
537
|
+
@@password
|
536
538
|
ensure
|
537
|
-
system(
|
539
|
+
system('stty', 'echo')
|
538
540
|
print "\n"
|
539
541
|
end
|
540
542
|
end
|
541
543
|
end
|
542
544
|
|
543
|
-
def
|
545
|
+
def self.list_layout_help
|
546
|
+
"The default columns and their layout can be configured in #{conf_file}"
|
547
|
+
end
|
548
|
+
|
549
|
+
def initialize(_secret = nil, _endpoint = nil)
|
544
550
|
@client=nil
|
545
551
|
|
546
552
|
@translation_hash = nil
|
547
553
|
end
|
548
554
|
|
549
|
-
def set_client(options, client=nil)
|
555
|
+
def set_client(options, client = nil)
|
550
556
|
if client.nil?
|
551
557
|
@client=OpenNebulaHelper::OneHelper.get_client(options, true)
|
552
558
|
else
|
@@ -554,15 +560,15 @@ EOT
|
|
554
560
|
end
|
555
561
|
end
|
556
562
|
|
557
|
-
def create_resource(
|
563
|
+
def create_resource(_options, &block)
|
558
564
|
resource = factory
|
559
565
|
|
560
566
|
rc = block.call(resource)
|
561
567
|
if OpenNebula.is_error?(rc)
|
562
|
-
|
568
|
+
[-1, rc.message]
|
563
569
|
else
|
564
|
-
puts "ID: #{resource.id
|
565
|
-
|
570
|
+
puts "ID: #{resource.id}"
|
571
|
+
0
|
566
572
|
end
|
567
573
|
end
|
568
574
|
|
@@ -596,7 +602,7 @@ EOT
|
|
596
602
|
p_w.close
|
597
603
|
p_r.close
|
598
604
|
|
599
|
-
|
605
|
+
lpid
|
600
606
|
end
|
601
607
|
|
602
608
|
def stop_pager(lpid)
|
@@ -605,7 +611,7 @@ EOT
|
|
605
611
|
begin
|
606
612
|
Process.wait(lpid)
|
607
613
|
rescue Interrupt
|
608
|
-
Process.kill(
|
614
|
+
Process.kill('TERM', lpid)
|
609
615
|
Process.wait(lpid)
|
610
616
|
rescue Errno::ECHILD
|
611
617
|
end
|
@@ -613,7 +619,7 @@ EOT
|
|
613
619
|
|
614
620
|
def print_page(pool, options)
|
615
621
|
elements = 0
|
616
|
-
page =
|
622
|
+
page = ''
|
617
623
|
|
618
624
|
if options[:xml]
|
619
625
|
elements += 1
|
@@ -640,20 +646,20 @@ EOT
|
|
640
646
|
end
|
641
647
|
end
|
642
648
|
|
643
|
-
|
649
|
+
[elements, page]
|
644
650
|
end
|
645
651
|
|
646
652
|
#-----------------------------------------------------------------------
|
647
653
|
# List the pool in table form, it uses pagination for interactive
|
648
654
|
# output
|
649
655
|
#-----------------------------------------------------------------------
|
650
|
-
def list_pool_table(table, pool, options,
|
651
|
-
if $stdout.isatty and (!options.key
|
656
|
+
def list_pool_table(table, pool, options, _filter_flag)
|
657
|
+
if $stdout.isatty and (!options.key? :no_pager)
|
652
658
|
size = $stdout.winsize[0] - 1
|
653
659
|
|
654
660
|
# ----------- First page, check if pager is needed -------------
|
655
661
|
rc = pool.get_page(size, 0, false, options[:state])
|
656
|
-
ps =
|
662
|
+
ps = ''
|
657
663
|
|
658
664
|
return -1, rc.message if OpenNebula.is_error?(rc)
|
659
665
|
|
@@ -719,21 +725,21 @@ EOT
|
|
719
725
|
table.show(hash, options)
|
720
726
|
end
|
721
727
|
|
722
|
-
|
728
|
+
0
|
723
729
|
end
|
724
730
|
|
725
731
|
#-----------------------------------------------------------------------
|
726
732
|
# List pool in XML format, pagination is used in interactive output
|
727
733
|
#-----------------------------------------------------------------------
|
728
|
-
def list_pool_xml(pool, options,
|
734
|
+
def list_pool_xml(pool, options, _filter_flag)
|
729
735
|
extended = options.include?(:extended) && options[:extended]
|
730
736
|
|
731
|
-
if $stdout.isatty and (!options.key
|
737
|
+
if $stdout.isatty and (!options.key? :no_pager)
|
732
738
|
size = $stdout.winsize[0] - 1
|
733
739
|
|
734
740
|
# ----------- First page, check if pager is needed -------------
|
735
741
|
rc = pool.get_page(size, 0, extended, options[:state])
|
736
|
-
ps =
|
742
|
+
ps = ''
|
737
743
|
|
738
744
|
return -1, rc.message if OpenNebula.is_error?(rc)
|
739
745
|
|
@@ -789,7 +795,7 @@ EOT
|
|
789
795
|
|
790
796
|
stop_pager(ppid)
|
791
797
|
else
|
792
|
-
if pool.pool_name ==
|
798
|
+
if pool.pool_name == 'VM_POOL' && extended
|
793
799
|
rc = pool.info_all_extended
|
794
800
|
else
|
795
801
|
rc = pool.info
|
@@ -800,21 +806,21 @@ EOT
|
|
800
806
|
puts pool.to_xml(true)
|
801
807
|
end
|
802
808
|
|
803
|
-
|
809
|
+
0
|
804
810
|
end
|
805
811
|
|
806
812
|
#-----------------------------------------------------------------------
|
807
813
|
# List pool in JSON format, pagination is used in interactive output
|
808
814
|
#-----------------------------------------------------------------------
|
809
|
-
def list_pool_format(pool, options,
|
815
|
+
def list_pool_format(pool, options, _filter_flag)
|
810
816
|
extended = options.include?(:extended) && options[:extended]
|
811
817
|
|
812
|
-
if $stdout.isatty and (!options.key
|
818
|
+
if $stdout.isatty and (!options.key? :no_pager)
|
813
819
|
size = $stdout.winsize[0] - 1
|
814
820
|
|
815
821
|
# ----------- First page, check if pager is needed -------------
|
816
822
|
rc = pool.get_page(size, 0, extended, options[:state])
|
817
|
-
ps =
|
823
|
+
ps = ''
|
818
824
|
|
819
825
|
return -1, rc.message if OpenNebula.is_error?(rc)
|
820
826
|
|
@@ -865,7 +871,7 @@ EOT
|
|
865
871
|
|
866
872
|
stop_pager(ppid)
|
867
873
|
else
|
868
|
-
if pool.pool_name ==
|
874
|
+
if pool.pool_name == 'VM_POOL' && extended
|
869
875
|
rc = pool.info_all_extended
|
870
876
|
else
|
871
877
|
rc = pool.info
|
@@ -876,26 +882,25 @@ EOT
|
|
876
882
|
yield(pool) if block_given?
|
877
883
|
end
|
878
884
|
|
879
|
-
|
885
|
+
0
|
880
886
|
end
|
881
887
|
|
882
888
|
#-----------------------------------------------------------------------
|
883
889
|
# List pool table in top-like form
|
884
890
|
#-----------------------------------------------------------------------
|
885
891
|
def list_pool_top(table, pool, options)
|
886
|
-
table.top(options)
|
892
|
+
table.top(options) do
|
887
893
|
array = pool.get_hash
|
888
894
|
|
889
895
|
return -1, array.message if OpenNebula.is_error?(array)
|
890
896
|
|
891
897
|
array
|
892
|
-
|
898
|
+
end
|
893
899
|
|
894
|
-
|
900
|
+
0
|
895
901
|
end
|
896
902
|
|
897
|
-
|
898
|
-
def list_pool(options, top=false, filter_flag=nil)
|
903
|
+
def list_pool(options, top = false, filter_flag = nil)
|
899
904
|
# Capture Broken pipe
|
900
905
|
Signal.trap('PIPE', 'EXIT')
|
901
906
|
|
@@ -919,19 +924,19 @@ EOT
|
|
919
924
|
return list_pool_xml(pool, options, filter_flag)
|
920
925
|
elsif options[:json]
|
921
926
|
list_pool_format(pool, options, filter_flag) do |pool|
|
922
|
-
hash
|
927
|
+
hash = check_resource_xsd(pool, pname)
|
923
928
|
puts ::JSON.pretty_generate(hash)
|
924
929
|
end
|
925
930
|
elsif options[:yaml]
|
926
931
|
list_pool_format(pool, options, filter_flag) do |pool|
|
927
|
-
hash
|
932
|
+
hash = check_resource_xsd(pool, pname)
|
928
933
|
puts hash.to_yaml(:indent => 4)
|
929
934
|
end
|
930
935
|
else
|
931
936
|
return list_pool_table(table, pool, options, filter_flag)
|
932
937
|
end
|
933
938
|
|
934
|
-
|
939
|
+
0
|
935
940
|
rescue SystemExit, Interrupt
|
936
941
|
# Rescue ctrl + c when paginated
|
937
942
|
0
|
@@ -972,21 +977,21 @@ EOT
|
|
972
977
|
return -1, rc.message if OpenNebula.is_error?(rc)
|
973
978
|
|
974
979
|
if options[:xml]
|
975
|
-
|
980
|
+
[0, resource.to_xml(true)]
|
976
981
|
elsif options[:json]
|
977
982
|
# If body is set, the resource contains a JSON inside
|
978
983
|
if options[:body]
|
979
|
-
|
984
|
+
[0, check_resource_xsd(resource)]
|
980
985
|
else
|
981
|
-
|
986
|
+
[0, ::JSON.pretty_generate(
|
982
987
|
check_resource_xsd(resource)
|
983
|
-
)
|
988
|
+
)]
|
984
989
|
end
|
985
990
|
elsif options[:yaml]
|
986
|
-
|
991
|
+
[0, check_resource_xsd(resource).to_yaml(:indent => 4)]
|
987
992
|
else
|
988
993
|
format_resource(resource, options)
|
989
|
-
|
994
|
+
0
|
990
995
|
end
|
991
996
|
end
|
992
997
|
|
@@ -995,19 +1000,19 @@ EOT
|
|
995
1000
|
|
996
1001
|
rc = block.call(resource)
|
997
1002
|
if OpenNebula.is_error?(rc)
|
998
|
-
|
1003
|
+
[-1, rc.message]
|
999
1004
|
else
|
1000
1005
|
if options[:verbose]
|
1001
1006
|
puts "#{self.class.rname} #{id}: #{verbose}"
|
1002
1007
|
end
|
1003
|
-
|
1008
|
+
0
|
1004
1009
|
end
|
1005
1010
|
end
|
1006
1011
|
|
1007
|
-
def perform_actions(ids,options,verbose
|
1012
|
+
def perform_actions(ids, options, verbose, &block)
|
1008
1013
|
exit_code = 0
|
1009
1014
|
ids.each do |id|
|
1010
|
-
rc = perform_action(id,options,verbose
|
1015
|
+
rc = perform_action(id, options, verbose, &block)
|
1011
1016
|
|
1012
1017
|
unless rc[0]==0
|
1013
1018
|
STDERR.puts rc[1]
|
@@ -1021,7 +1026,7 @@ EOT
|
|
1021
1026
|
########################################################################
|
1022
1027
|
# Id translation
|
1023
1028
|
########################################################################
|
1024
|
-
def user_name(resource, options={})
|
1029
|
+
def user_name(resource, options = {})
|
1025
1030
|
if options[:numeric]
|
1026
1031
|
resource['UID']
|
1027
1032
|
else
|
@@ -1029,7 +1034,7 @@ EOT
|
|
1029
1034
|
end
|
1030
1035
|
end
|
1031
1036
|
|
1032
|
-
def group_name(resource, options={})
|
1037
|
+
def group_name(resource, options = {})
|
1033
1038
|
if options[:numeric]
|
1034
1039
|
resource['GID']
|
1035
1040
|
else
|
@@ -1053,7 +1058,7 @@ EOT
|
|
1053
1058
|
end
|
1054
1059
|
|
1055
1060
|
def self.to_id_desc
|
1056
|
-
"OpenNebula #{
|
1061
|
+
"OpenNebula #{rname} name or id"
|
1057
1062
|
end
|
1058
1063
|
|
1059
1064
|
def list_to_id(names)
|
@@ -1063,7 +1068,7 @@ EOT
|
|
1063
1068
|
pool = rc[1]
|
1064
1069
|
poolname = self.class.rname
|
1065
1070
|
|
1066
|
-
result = names.split(',').collect
|
1071
|
+
result = names.split(',').collect do |name|
|
1067
1072
|
if name.match(/^[0123456789]+$/)
|
1068
1073
|
name.to_i
|
1069
1074
|
else
|
@@ -1075,76 +1080,70 @@ EOT
|
|
1075
1080
|
|
1076
1081
|
rc[1]
|
1077
1082
|
end
|
1078
|
-
|
1083
|
+
end
|
1079
1084
|
|
1080
|
-
|
1085
|
+
[0, result]
|
1081
1086
|
end
|
1082
1087
|
|
1083
1088
|
def self.list_to_id_desc
|
1084
|
-
"Comma-separated list of OpenNebula #{
|
1089
|
+
"Comma-separated list of OpenNebula #{rname} names or ids"
|
1085
1090
|
end
|
1086
1091
|
|
1087
1092
|
def self.name_to_id(name, pool, ename)
|
1088
|
-
if ename==
|
1089
|
-
return 0,
|
1093
|
+
if ename=='CLUSTER' and name.upcase=='ALL'
|
1094
|
+
return 0, 'ALL'
|
1090
1095
|
end
|
1091
1096
|
|
1092
1097
|
objects=pool.select {|object| object.name==name }
|
1093
1098
|
|
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
|
1099
|
+
return -1, "#{ename} named #{name} not found." unless objects.length>0
|
1100
|
+
return -1, "There are multiple #{ename}s with name #{name}." if objects.length>1
|
1103
1101
|
|
1104
|
-
|
1102
|
+
result = objects.first.id
|
1103
|
+
|
1104
|
+
[0, result]
|
1105
1105
|
end
|
1106
1106
|
|
1107
1107
|
def filterflag_to_i(str)
|
1108
1108
|
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
|
1109
|
+
when 'a', 'all' then OpenNebula::Pool::INFO_ALL
|
1110
|
+
when 'm', 'mine' then OpenNebula::Pool::INFO_MINE
|
1111
|
+
when 'g', 'group' then OpenNebula::Pool::INFO_GROUP
|
1112
|
+
when 'G', 'primary group' then OpenNebula::Pool::INFO_PRIMARY_GROUP
|
1113
|
+
else
|
1114
|
+
if str.match(/^[0123456789]+$/)
|
1115
|
+
str.to_i
|
1116
|
+
else
|
1117
|
+
rc = OpenNebulaHelper.rname_to_id(str, 'USER')
|
1118
|
+
return rc if rc.first==-1
|
1119
|
+
|
1120
|
+
rc[1]
|
1125
1121
|
|
1126
|
-
|
1122
|
+
end
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
[0, filter_flag]
|
1127
1126
|
end
|
1128
1127
|
|
1129
1128
|
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
|
-
|
1129
|
+
desc=<<~EOT
|
1130
|
+
a, all all the known #{rname}s
|
1131
|
+
m, mine the #{rname} belonging to the user in ONE_AUTH
|
1132
|
+
g, group 'mine' plus the #{rname} belonging to the groups
|
1133
|
+
the user is member of
|
1134
|
+
G, primary group the #{rname} owned the user's primary group
|
1135
|
+
uid #{rname} of the user identified by this uid
|
1136
|
+
user #{rname} of the user identified by the username
|
1137
|
+
EOT
|
1138
|
+
end
|
1139
|
+
|
1140
|
+
def self.table_conf(conf_file = self.conf_file)
|
1141
|
+
path = "#{ENV['HOME']}/.one/cli/#{conf_file}"
|
1142
|
+
|
1143
|
+
if File.exist?(path)
|
1144
|
+
path
|
1146
1145
|
else
|
1147
|
-
|
1146
|
+
"#{TABLE_CONF_PATH}/#{conf_file}"
|
1148
1147
|
end
|
1149
1148
|
end
|
1150
1149
|
|
@@ -1171,7 +1170,7 @@ EOT
|
|
1171
1170
|
phash = [phash["#{rname}_POOL"]["#{rname}"]]
|
1172
1171
|
end
|
1173
1172
|
else
|
1174
|
-
phash =
|
1173
|
+
phash = []
|
1175
1174
|
end
|
1176
1175
|
|
1177
1176
|
phash
|
@@ -1183,15 +1182,14 @@ EOT
|
|
1183
1182
|
|
1184
1183
|
rc = pool.info
|
1185
1184
|
if OpenNebula.is_error?(rc)
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
end
|
1185
|
+
return -1, rc.message unless rc.message.empty?
|
1186
|
+
|
1187
|
+
return -1, "OpenNebula #{self.class.rname} name not " <<
|
1188
|
+
'found, use the ID instead'
|
1189
|
+
|
1192
1190
|
end
|
1193
1191
|
|
1194
|
-
|
1192
|
+
[0, pool]
|
1195
1193
|
end
|
1196
1194
|
|
1197
1195
|
def get_format_size(pool, options)
|
@@ -1217,7 +1215,7 @@ EOT
|
|
1217
1215
|
# @return [Object] Hash with correct values
|
1218
1216
|
def check_resource_xsd(resource, ename = nil)
|
1219
1217
|
hash = resource.to_hash
|
1220
|
-
ename
|
1218
|
+
ename ||= hash.keys.first
|
1221
1219
|
xsd = read_xsd(ename)
|
1222
1220
|
|
1223
1221
|
return hash unless xsd
|
@@ -1228,14 +1226,13 @@ EOT
|
|
1228
1226
|
xsd = xsd['element']
|
1229
1227
|
end
|
1230
1228
|
|
1231
|
-
xsd = [
|
1229
|
+
xsd = [xsd] unless xsd.is_a? Array
|
1232
1230
|
|
1233
1231
|
check_xsd(hash[ename], xsd)
|
1234
1232
|
|
1235
1233
|
hash
|
1236
1234
|
end
|
1237
1235
|
|
1238
|
-
|
1239
1236
|
# Replaces refs in xsd definition
|
1240
1237
|
# limited func: only traverse hashes (not arrays), but works well for pools
|
1241
1238
|
#
|
@@ -1248,15 +1245,15 @@ EOT
|
|
1248
1245
|
if h.keys.include? 'ref'
|
1249
1246
|
ref_xsd = read_xsd(h['ref'])
|
1250
1247
|
return ref_xsd unless ref_xsd.nil?
|
1251
|
-
|
1248
|
+
|
1249
|
+
h
|
1252
1250
|
else
|
1253
|
-
h.each do |k,v|
|
1251
|
+
h.each do |k, v|
|
1254
1252
|
h[k] = replace_refs(v)
|
1255
1253
|
end
|
1256
1254
|
end
|
1257
1255
|
end
|
1258
1256
|
|
1259
|
-
|
1260
1257
|
# Read XSD file and parse to XML
|
1261
1258
|
#
|
1262
1259
|
# @param ename [String] Element name to read XSD
|
@@ -1275,16 +1272,14 @@ EOT
|
|
1275
1272
|
|
1276
1273
|
unless File.exist?(file)
|
1277
1274
|
STDERR.puts "WARNING: XSD for #{ename} not found, skipping check"
|
1278
|
-
return
|
1275
|
+
return
|
1279
1276
|
end
|
1280
1277
|
|
1281
1278
|
hash = Hash.from_xml(Nokogiri::XML(File.read(file)).to_s)
|
1282
1279
|
|
1283
1280
|
hash = hash['schema']['element']
|
1284
1281
|
|
1285
|
-
|
1286
|
-
|
1287
|
-
hash
|
1282
|
+
replace_refs(hash)
|
1288
1283
|
end
|
1289
1284
|
|
1290
1285
|
# Decides if given xsd definiton should be array in xml
|
@@ -1298,6 +1293,7 @@ EOT
|
|
1298
1293
|
def is_array?(e)
|
1299
1294
|
return false if e.nil?
|
1300
1295
|
return false unless e.is_a? Hash
|
1296
|
+
|
1301
1297
|
e['maxOccurs'] == 'unbounded' || e['maxOccurs'].to_i > 1
|
1302
1298
|
end
|
1303
1299
|
|
@@ -1338,16 +1334,15 @@ EOT
|
|
1338
1334
|
return unless hash or hash.empty?
|
1339
1335
|
|
1340
1336
|
hash.each do |k, v|
|
1341
|
-
|
1342
1337
|
# find the elem definition in xsd array
|
1343
|
-
xsd_elem = xsd.select {
|
1338
|
+
xsd_elem = xsd.select {|e| e['name'] == k }.first unless xsd.nil?
|
1344
1339
|
|
1345
1340
|
if xsd_complex_sequence?(xsd_elem) || xsd_complex_all?(xsd_elem)
|
1346
1341
|
|
1347
1342
|
# go deeper in xsd, xsd is ehter complex sequence or all
|
1348
1343
|
begin
|
1349
1344
|
inner_xsd = xsd_elem['complexType']['sequence']['element']
|
1350
|
-
rescue
|
1345
|
+
rescue StandardError
|
1351
1346
|
inner_xsd = xsd_elem['complexType']['all']['element']
|
1352
1347
|
end
|
1353
1348
|
|
@@ -1365,81 +1360,82 @@ EOT
|
|
1365
1360
|
end
|
1366
1361
|
|
1367
1362
|
# if XSD requires array, do so in resource if missing
|
1368
|
-
if is_array?(xsd_elem) && (!
|
1369
|
-
hash[k] = [
|
1363
|
+
if is_array?(xsd_elem) && (!v.is_a? Array)
|
1364
|
+
hash[k] = [v]
|
1370
1365
|
end
|
1371
1366
|
end
|
1372
1367
|
end
|
1368
|
+
|
1373
1369
|
end
|
1374
1370
|
|
1375
|
-
def
|
1371
|
+
def self.rname_to_id(name, poolname)
|
1376
1372
|
return 0, name.to_i if name.match(/^[0123456789]+$/)
|
1377
1373
|
|
1378
1374
|
client=OneHelper.client
|
1379
1375
|
|
1380
1376
|
pool = case poolname
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1377
|
+
when 'HOST' then OpenNebula::HostPool.new(client)
|
1378
|
+
when 'HOOK' then OpenNebula::HookPool.new(client)
|
1379
|
+
when 'GROUP' then OpenNebula::GroupPool.new(client)
|
1380
|
+
when 'USER' then OpenNebula::UserPool.new(client)
|
1381
|
+
when 'DATASTORE' then OpenNebula::DatastorePool.new(client)
|
1382
|
+
when 'CLUSTER' then OpenNebula::ClusterPool.new(client)
|
1383
|
+
when 'VNET' then OpenNebula::VirtualNetworkPool.new(client)
|
1384
|
+
when 'IMAGE' then OpenNebula::ImagePool.new(client)
|
1385
|
+
when 'VMTEMPLATE' then OpenNebula::TemplatePool.new(client)
|
1386
|
+
when 'VNTEMPLATES' then OpenNebula::VNTemplatePool.new(client)
|
1387
|
+
when 'VM' then OpenNebula::VirtualMachinePool.new(client)
|
1388
|
+
when 'ZONE' then OpenNebula::ZonePool.new(client)
|
1389
|
+
when 'MARKETPLACE' then OpenNebula::MarketPlacePool.new(client)
|
1390
|
+
when 'FLOWTEMPLATES' then OpenNebula::ServiceTemplatePool.new(client)
|
1391
|
+
end
|
1396
1392
|
|
1397
1393
|
rc = pool.info
|
1398
1394
|
if OpenNebula.is_error?(rc)
|
1399
1395
|
return -1, "OpenNebula #{poolname} name not found," <<
|
1400
|
-
|
1396
|
+
' use the ID instead'
|
1401
1397
|
end
|
1402
1398
|
|
1403
1399
|
OneHelper.name_to_id(name, pool, poolname)
|
1404
1400
|
end
|
1405
1401
|
|
1406
|
-
def
|
1402
|
+
def self.size_in_mb(size)
|
1407
1403
|
m = size.match(/^(\d+(?:\.\d+)?)(t|tb|m|mb|g|gb)?$/i)
|
1408
1404
|
|
1409
1405
|
if !m
|
1410
1406
|
# return OpenNebula::Error.new('Size value malformed')
|
1411
|
-
|
1407
|
+
[-1, 'Size value malformed']
|
1412
1408
|
else
|
1413
1409
|
multiplier=case m[2]
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1410
|
+
when /(t|tb)/i
|
1411
|
+
1024*1024
|
1412
|
+
when /(g|gb)/i
|
1413
|
+
1024
|
1414
|
+
else
|
1415
|
+
1
|
1416
|
+
end
|
1421
1417
|
|
1422
1418
|
value=m[1].to_f*multiplier
|
1423
1419
|
|
1424
1420
|
# return value.ceil
|
1425
|
-
|
1421
|
+
[0, value.ceil]
|
1426
1422
|
end
|
1427
1423
|
end
|
1428
1424
|
|
1429
|
-
def
|
1425
|
+
def self.rname_to_id_desc(poolname)
|
1430
1426
|
"OpenNebula #{poolname} name or id"
|
1431
1427
|
end
|
1432
1428
|
|
1433
|
-
def
|
1429
|
+
def self.boolean_to_str(str)
|
1434
1430
|
if str.to_i == 1
|
1435
|
-
|
1431
|
+
'Yes'
|
1436
1432
|
else
|
1437
|
-
|
1433
|
+
'No'
|
1438
1434
|
end
|
1439
1435
|
end
|
1440
1436
|
|
1441
|
-
def
|
1442
|
-
|
1437
|
+
def self.time_to_str(time, print_seconds = true,
|
1438
|
+
print_hours = true, print_years = false)
|
1443
1439
|
|
1444
1440
|
value = time.to_i
|
1445
1441
|
|
@@ -1449,63 +1445,63 @@ EOT
|
|
1449
1445
|
if print_hours
|
1450
1446
|
if print_seconds
|
1451
1447
|
if print_years
|
1452
|
-
value=Time.at(value).strftime(
|
1448
|
+
value=Time.at(value).strftime('%m/%d/%y %H:%M:%S')
|
1453
1449
|
else
|
1454
|
-
value=Time.at(value).strftime(
|
1450
|
+
value=Time.at(value).strftime('%m/%d %H:%M:%S')
|
1455
1451
|
end
|
1456
1452
|
else
|
1457
1453
|
if print_years
|
1458
|
-
value=Time.at(value).strftime(
|
1454
|
+
value=Time.at(value).strftime('%m/%d/%y %H:%M')
|
1459
1455
|
else
|
1460
|
-
value=Time.at(value).strftime(
|
1456
|
+
value=Time.at(value).strftime('%m/%d %H:%M')
|
1461
1457
|
end
|
1462
1458
|
end
|
1463
1459
|
else
|
1464
1460
|
if print_years
|
1465
|
-
value=Time.at(value).strftime(
|
1461
|
+
value=Time.at(value).strftime('%m/%d/%y')
|
1466
1462
|
else
|
1467
|
-
value=Time.at(value).strftime(
|
1463
|
+
value=Time.at(value).strftime('%m/%d')
|
1468
1464
|
end
|
1469
1465
|
end
|
1470
1466
|
end
|
1471
1467
|
|
1472
|
-
|
1468
|
+
value
|
1473
1469
|
end
|
1474
1470
|
|
1475
|
-
def
|
1471
|
+
def self.period_to_str(time, print_seconds = true)
|
1476
1472
|
seconds=time.to_i
|
1477
1473
|
minutes, seconds=seconds.divmod(60)
|
1478
1474
|
hours, minutes=minutes.divmod(60)
|
1479
1475
|
days, hours=hours.divmod(24)
|
1480
1476
|
|
1481
1477
|
if print_seconds
|
1482
|
-
|
1478
|
+
format('%3dd %02dh%02dm%02ds', days, hours, minutes, seconds)
|
1483
1479
|
else
|
1484
|
-
|
1480
|
+
format('%3dd %02dh%02dm', days, hours, minutes)
|
1485
1481
|
end
|
1486
1482
|
end
|
1487
1483
|
|
1488
|
-
def
|
1484
|
+
def self.short_period_to_str(time, print_seconds = true)
|
1489
1485
|
seconds=time.to_i
|
1490
1486
|
minutes, seconds=seconds.divmod(60)
|
1491
1487
|
hours, minutes=minutes.divmod(60)
|
1492
1488
|
|
1493
1489
|
if print_seconds
|
1494
|
-
|
1490
|
+
format('%3dh%02dm%02ds', hours, minutes, seconds)
|
1495
1491
|
else
|
1496
|
-
|
1492
|
+
format('%3dh%02dm', hours, minutes)
|
1497
1493
|
end
|
1498
1494
|
end
|
1499
1495
|
|
1500
|
-
BinarySufix = [
|
1496
|
+
BinarySufix = ['K', 'M', 'G', 'T']
|
1501
1497
|
|
1502
|
-
def
|
1498
|
+
def self.unit_to_str(value, options, unit = 'K')
|
1503
1499
|
if options[:kilobytes]
|
1504
1500
|
value
|
1505
1501
|
else
|
1506
1502
|
i=BinarySufix.index(unit).to_i
|
1507
1503
|
|
1508
|
-
while value > 1024 && i < 3
|
1504
|
+
while value > 1024 && i < 3
|
1509
1505
|
value /= 1024.0
|
1510
1506
|
i+=1
|
1511
1507
|
end
|
@@ -1517,11 +1513,11 @@ EOT
|
|
1517
1513
|
end
|
1518
1514
|
end
|
1519
1515
|
|
1520
|
-
def
|
1516
|
+
def self.bytes_to_unit(value, unit = 'K')
|
1521
1517
|
j = 0
|
1522
1518
|
i = BinarySufix.index(unit).to_i
|
1523
1519
|
|
1524
|
-
while j < i
|
1520
|
+
while j < i
|
1525
1521
|
value /= 1024.0
|
1526
1522
|
j += 1
|
1527
1523
|
end
|
@@ -1533,51 +1529,50 @@ EOT
|
|
1533
1529
|
#
|
1534
1530
|
# @param str [String || Hash] Cluster name, or empty Hash (when <CLUSTER/>)
|
1535
1531
|
# @return [String] the same Cluster name, or '-' if it is empty
|
1536
|
-
def
|
1537
|
-
if str
|
1532
|
+
def self.cluster_str(str)
|
1533
|
+
if !str.nil? && !str.empty?
|
1538
1534
|
str
|
1539
1535
|
else
|
1540
|
-
|
1536
|
+
'-'
|
1541
1537
|
end
|
1542
1538
|
end
|
1543
1539
|
|
1544
|
-
def
|
1540
|
+
def self.clusters_str(clusters)
|
1545
1541
|
if clusters.nil?
|
1546
|
-
|
1542
|
+
'-'
|
1547
1543
|
else
|
1548
1544
|
[clusters].flatten.join(',')
|
1549
1545
|
end
|
1550
|
-
|
1551
1546
|
end
|
1552
1547
|
|
1553
|
-
def
|
1554
|
-
|
1548
|
+
def self.update_template(id, resource, path = nil, xpath = 'TEMPLATE')
|
1549
|
+
update_template_helper(false, id, resource, path, xpath)
|
1555
1550
|
end
|
1556
1551
|
|
1557
|
-
def
|
1558
|
-
|
1552
|
+
def self.append_template(id, resource, path = nil, xpath = 'TEMPLATE')
|
1553
|
+
update_template_helper(true, id, resource, path, xpath)
|
1559
1554
|
end
|
1560
1555
|
|
1561
|
-
def
|
1556
|
+
def self.update_template_helper(append, _id, resource, path, xpath, update = true)
|
1562
1557
|
if path
|
1563
|
-
|
1558
|
+
File.read(path)
|
1564
1559
|
elsif append
|
1565
|
-
|
1560
|
+
editor_input
|
1566
1561
|
else
|
1567
1562
|
if update
|
1568
1563
|
rc = resource.info
|
1569
1564
|
|
1570
1565
|
if OpenNebula.is_error?(rc)
|
1571
1566
|
puts rc.message
|
1572
|
-
exit
|
1567
|
+
exit(-1)
|
1573
1568
|
end
|
1574
1569
|
end
|
1575
1570
|
|
1576
|
-
|
1571
|
+
editor_input(resource.template_like_str(xpath))
|
1577
1572
|
end
|
1578
1573
|
end
|
1579
1574
|
|
1580
|
-
def
|
1575
|
+
def self.update_obj(obj, file, plain = false)
|
1581
1576
|
rc = obj.info(true)
|
1582
1577
|
|
1583
1578
|
return rc if OpenNebula.is_error?(rc)
|
@@ -1614,28 +1609,27 @@ EOT
|
|
1614
1609
|
end
|
1615
1610
|
end
|
1616
1611
|
|
1617
|
-
def
|
1612
|
+
def self.editor_input(contents = nil)
|
1618
1613
|
require 'tempfile'
|
1619
1614
|
|
1620
|
-
tmp
|
1615
|
+
tmp = Tempfile.new('one_cli')
|
1621
1616
|
|
1622
1617
|
if contents
|
1623
1618
|
tmp << contents
|
1624
1619
|
tmp.flush
|
1625
1620
|
end
|
1626
1621
|
|
1627
|
-
editor_path = ENV[
|
1622
|
+
editor_path = ENV['EDITOR'] ? ENV['EDITOR'] : EDITOR_PATH
|
1628
1623
|
system("#{editor_path} #{tmp.path}")
|
1629
1624
|
|
1630
1625
|
unless $?.exitstatus == 0
|
1631
|
-
puts
|
1632
|
-
exit
|
1626
|
+
puts 'Editor not defined'
|
1627
|
+
exit(-1)
|
1633
1628
|
end
|
1634
1629
|
|
1635
1630
|
tmp.close
|
1636
1631
|
|
1637
|
-
|
1638
|
-
return str
|
1632
|
+
File.read(tmp.path)
|
1639
1633
|
end
|
1640
1634
|
|
1641
1635
|
def self.parse_user_object(user_object)
|
@@ -1643,7 +1637,7 @@ EOT
|
|
1643
1637
|
|
1644
1638
|
m=user_object.match(reg)
|
1645
1639
|
|
1646
|
-
return
|
1640
|
+
return unless m
|
1647
1641
|
|
1648
1642
|
user=nil
|
1649
1643
|
if m[2]
|
@@ -1660,7 +1654,7 @@ EOT
|
|
1660
1654
|
template=''
|
1661
1655
|
|
1662
1656
|
objects.each do |obj|
|
1663
|
-
obj, *extra_attributes = obj.split(
|
1657
|
+
obj, *extra_attributes = obj.split(':')
|
1664
1658
|
|
1665
1659
|
# When extra attributes do not contain = character include
|
1666
1660
|
# them in the previous value. Fixes adding MAC addresses. These
|
@@ -1673,26 +1667,27 @@ EOT
|
|
1673
1667
|
#
|
1674
1668
|
attrs = []
|
1675
1669
|
extra_attributes.each do |str|
|
1676
|
-
if str.include?(
|
1670
|
+
if str.include?('=')
|
1677
1671
|
attrs << str
|
1678
1672
|
else
|
1679
1673
|
attrs.last << ":#{str}"
|
1680
|
-
|
1674
|
+
end
|
1681
1675
|
end
|
1682
1676
|
|
1683
1677
|
extra_attributes = attrs
|
1684
1678
|
|
1685
1679
|
res=parse_user_object(obj)
|
1686
|
-
return [-1, "#{section.capitalize} \"#{obj}\" malformed"]
|
1680
|
+
return [-1, "#{section.capitalize} \"#{obj}\" malformed"] unless res
|
1681
|
+
|
1687
1682
|
user, object=*res
|
1688
1683
|
|
1689
1684
|
template<<"#{section.upcase}=[\n"
|
1690
|
-
if object.downcase ==
|
1685
|
+
if object.downcase == 'auto'
|
1691
1686
|
template<<" NETWORK_MODE=\"#{object}\"\n"
|
1692
1687
|
else
|
1693
1688
|
template<<" #{name.upcase}_UNAME=\"#{user}\",\n" if user
|
1694
1689
|
extra_attributes.each do |extra_attribute|
|
1695
|
-
key, value = extra_attribute.split(
|
1690
|
+
key, value = extra_attribute.split('=')
|
1696
1691
|
template<<" #{key.upcase}=\"#{value}\",\n"
|
1697
1692
|
end
|
1698
1693
|
if object.match(/^\d+$/)
|
@@ -1708,13 +1703,14 @@ EOT
|
|
1708
1703
|
end
|
1709
1704
|
|
1710
1705
|
def self.create_context(options)
|
1711
|
-
context_options = [:ssh, :net_context, :context, :init, :files_ds, :startscript,
|
1706
|
+
context_options = [:ssh, :net_context, :context, :init, :files_ds, :startscript,
|
1707
|
+
:report_ready]
|
1712
1708
|
if !(options.keys & context_options).empty?
|
1713
1709
|
lines=[]
|
1714
1710
|
|
1715
1711
|
if options[:ssh]
|
1716
1712
|
if options[:ssh]==true
|
1717
|
-
lines<<
|
1713
|
+
lines<<'SSH_PUBLIC_KEY="$USER[SSH_PUBLIC_KEY]"'
|
1718
1714
|
else
|
1719
1715
|
begin
|
1720
1716
|
key=File.read(options[:ssh]).strip
|
@@ -1727,7 +1723,7 @@ EOT
|
|
1727
1723
|
end
|
1728
1724
|
|
1729
1725
|
if options[:net_context]
|
1730
|
-
lines <<
|
1726
|
+
lines << 'NETWORK = "YES"'
|
1731
1727
|
end
|
1732
1728
|
|
1733
1729
|
lines+=options[:context] if options[:context]
|
@@ -1735,7 +1731,7 @@ EOT
|
|
1735
1731
|
if options[:files_ds]
|
1736
1732
|
text='FILES_DS="'
|
1737
1733
|
text << options[:files_ds].map do |file|
|
1738
|
-
%
|
1734
|
+
%($FILE[IMAGE=\\"#{file}\\"])
|
1739
1735
|
end.join(' ')
|
1740
1736
|
text << '"'
|
1741
1737
|
|
@@ -1743,7 +1739,7 @@ EOT
|
|
1743
1739
|
end
|
1744
1740
|
|
1745
1741
|
if options[:init]
|
1746
|
-
lines << %
|
1742
|
+
lines << %(INIT_SCRIPTS="#{options[:init].join(' ')}")
|
1747
1743
|
end
|
1748
1744
|
|
1749
1745
|
if options[:startscript]
|
@@ -1754,16 +1750,16 @@ EOT
|
|
1754
1750
|
STDERR.puts e.message
|
1755
1751
|
exit(-1)
|
1756
1752
|
end
|
1757
|
-
script = Base64
|
1753
|
+
script = Base64.strict_encode64(script)
|
1758
1754
|
lines<<"START_SCRIPT_BASE64=\"#{script}\""
|
1759
1755
|
end
|
1760
1756
|
|
1761
1757
|
if options[:report_ready]
|
1762
|
-
lines <<
|
1758
|
+
lines << 'REPORT_READY = "YES"'
|
1763
1759
|
end
|
1764
1760
|
|
1765
1761
|
if !lines.empty?
|
1766
|
-
"CONTEXT=[\n" << lines.map{|l|
|
1762
|
+
"CONTEXT=[\n" << lines.map {|l| ' ' << l }.join(",\n") << "\n]\n"
|
1767
1763
|
else
|
1768
1764
|
nil
|
1769
1765
|
end
|
@@ -1772,7 +1768,7 @@ EOT
|
|
1772
1768
|
end
|
1773
1769
|
end
|
1774
1770
|
|
1775
|
-
def self.create_template(options, template_obj=nil)
|
1771
|
+
def self.create_template(options, template_obj = nil)
|
1776
1772
|
template=''
|
1777
1773
|
|
1778
1774
|
template<<"NAME=\"#{options[:name]}\"\n" if options[:name]
|
@@ -1812,7 +1808,7 @@ EOT
|
|
1812
1808
|
end
|
1813
1809
|
|
1814
1810
|
if options[:vnc]
|
1815
|
-
vnc_listen=options[:vnc_listen] ||
|
1811
|
+
vnc_listen=options[:vnc_listen] || '0.0.0.0'
|
1816
1812
|
template<<"GRAPHICS=[ TYPE=\"vnc\", LISTEN=\"#{vnc_listen}\""
|
1817
1813
|
if options[:vnc_password]
|
1818
1814
|
template << ", PASSWD=\"#{options[:vnc_password]}\""
|
@@ -1824,7 +1820,7 @@ EOT
|
|
1824
1820
|
end
|
1825
1821
|
|
1826
1822
|
if options[:spice]
|
1827
|
-
spice_listen=options[:spice_listen] ||
|
1823
|
+
spice_listen=options[:spice_listen] || '0.0.0.0'
|
1828
1824
|
template<<"GRAPHICS=[ TYPE=\"spice\", LISTEN=\"#{spice_listen}\""
|
1829
1825
|
if options[:spice_password]
|
1830
1826
|
template << ", PASSWD=\"#{options[:spice_password]}\""
|
@@ -1840,15 +1836,15 @@ EOT
|
|
1840
1836
|
context=create_context(options)
|
1841
1837
|
template<<context if context
|
1842
1838
|
|
1843
|
-
if options[:userdata] && !template_obj.nil?
|
1844
|
-
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1839
|
+
if options[:userdata] && !template_obj.nil? && template_obj.has_elements?('TEMPLATE/EC2')
|
1840
|
+
template_obj.add_element(
|
1841
|
+
'TEMPLATE/EC2',
|
1842
|
+
'USERDATA' => options[:userdata]
|
1843
|
+
)
|
1848
1844
|
|
1849
|
-
|
1850
|
-
|
1851
|
-
|
1845
|
+
template << template_obj.template_like_str(
|
1846
|
+
'TEMPLATE', false, 'EC2'
|
1847
|
+
)
|
1852
1848
|
end
|
1853
1849
|
|
1854
1850
|
[0, template]
|
@@ -1946,21 +1942,21 @@ EOT
|
|
1946
1942
|
|
1947
1943
|
def self.sunstone_url
|
1948
1944
|
if (one_sunstone = ENV['ONE_SUNSTONE'])
|
1949
|
-
|
1945
|
+
one_sunstone
|
1950
1946
|
elsif (one_xmlrpc = ENV['ONE_XMLRPC'])
|
1951
1947
|
uri = URI(one_xmlrpc)
|
1952
1948
|
"#{uri.scheme}://#{uri.host}:9869"
|
1953
1949
|
else
|
1954
|
-
|
1950
|
+
'http://localhost:9869'
|
1955
1951
|
end
|
1956
1952
|
end
|
1957
1953
|
|
1958
|
-
def self.download_resource_sunstone(kind, id, path,
|
1954
|
+
def self.download_resource_sunstone(kind, id, path, _force)
|
1959
1955
|
client = OneHelper.client
|
1960
|
-
user, password = client.one_auth.split(
|
1956
|
+
user, password = client.one_auth.split(':', 2)
|
1961
1957
|
|
1962
1958
|
# Step 1: Build Session to get Cookie
|
1963
|
-
uri = URI(File.join(sunstone_url,
|
1959
|
+
uri = URI(File.join(sunstone_url, 'login'))
|
1964
1960
|
|
1965
1961
|
req = Net::HTTP::Post.new(uri)
|
1966
1962
|
req.basic_auth user, password
|
@@ -1969,14 +1965,14 @@ EOT
|
|
1969
1965
|
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
|
1970
1966
|
http.request(req)
|
1971
1967
|
end
|
1972
|
-
rescue
|
1968
|
+
rescue StandardError
|
1973
1969
|
return OpenNebula::Error.new("Error connecting to '#{uri}'.")
|
1974
1970
|
end
|
1975
1971
|
|
1976
1972
|
cookie = res.response['set-cookie'].split('; ')[0]
|
1977
1973
|
|
1978
1974
|
if cookie.nil?
|
1979
|
-
|
1975
|
+
return OpenNebula::Error.new('Unable to get Cookie. Is OpenNebula running?')
|
1980
1976
|
end
|
1981
1977
|
|
1982
1978
|
# Step 2: Open '/' to get the csrftoken
|
@@ -1989,7 +1985,7 @@ EOT
|
|
1989
1985
|
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
|
1990
1986
|
http.request(req)
|
1991
1987
|
end
|
1992
|
-
rescue
|
1988
|
+
rescue StandardError
|
1993
1989
|
return OpenNebula::Error.new("Error connecting to '#{uri}'.")
|
1994
1990
|
end
|
1995
1991
|
|
@@ -1997,7 +1993,7 @@ EOT
|
|
1997
1993
|
csrftoken = m[1] rescue nil
|
1998
1994
|
|
1999
1995
|
if csrftoken.nil?
|
2000
|
-
|
1996
|
+
return OpenNebula::Error.new('Unable to get csrftoken.')
|
2001
1997
|
end
|
2002
1998
|
|
2003
1999
|
# Step 3: Download resource
|
@@ -2009,7 +2005,7 @@ EOT
|
|
2009
2005
|
req = Net::HTTP::Get.new(uri)
|
2010
2006
|
|
2011
2007
|
req['Cookie'] = cookie
|
2012
|
-
req['User-Agent'] =
|
2008
|
+
req['User-Agent'] = 'OpenNebula CLI'
|
2013
2009
|
|
2014
2010
|
begin
|
2015
2011
|
File.open(path, 'wb') do |f|
|
@@ -2022,7 +2018,7 @@ EOT
|
|
2022
2018
|
end
|
2023
2019
|
end
|
2024
2020
|
rescue Errno::EACCES
|
2025
|
-
return OpenNebula::Error.new(
|
2021
|
+
return OpenNebula::Error.new('Target file not writable.')
|
2026
2022
|
end
|
2027
2023
|
|
2028
2024
|
error_message = nil
|
@@ -2039,30 +2035,30 @@ EOT
|
|
2039
2035
|
error_message = m[1] if m
|
2040
2036
|
end
|
2041
2037
|
|
2042
|
-
|
2043
|
-
|
2044
|
-
|
2045
|
-
|
2038
|
+
return unless error_message
|
2039
|
+
|
2040
|
+
File.unlink(path)
|
2041
|
+
OpenNebula::Error.new("Remote server error: #{error_message}")
|
2046
2042
|
end
|
2047
2043
|
|
2048
|
-
def
|
2044
|
+
def self.level_lock_to_str(str)
|
2049
2045
|
level = str.to_i
|
2050
2046
|
if level == 0
|
2051
|
-
|
2047
|
+
'None'
|
2052
2048
|
elsif level == 1
|
2053
|
-
|
2049
|
+
'Use'
|
2054
2050
|
elsif level == 2
|
2055
|
-
|
2051
|
+
'Manage'
|
2056
2052
|
elsif level == 3
|
2057
|
-
|
2053
|
+
'Admin'
|
2058
2054
|
elsif level == 4
|
2059
|
-
|
2055
|
+
'All'
|
2060
2056
|
else
|
2061
|
-
|
2057
|
+
'-'
|
2062
2058
|
end
|
2063
2059
|
end
|
2064
2060
|
|
2065
|
-
def
|
2061
|
+
def self.parse_user_inputs(inputs, keys = [])
|
2066
2062
|
unless inputs.keys == keys
|
2067
2063
|
puts 'There are some parameters that require user input. ' \
|
2068
2064
|
'Use the string <<EDITOR>> to launch an editor ' \
|
@@ -2132,7 +2128,7 @@ EOT
|
|
2132
2128
|
# use default in case it's empty
|
2133
2129
|
answer = initial if answer.empty?
|
2134
2130
|
|
2135
|
-
unless
|
2131
|
+
unless ['YES', 'NO'].include?(answer)
|
2136
2132
|
STDERR.puts "Invalid boolean '#{answer}'"
|
2137
2133
|
STDERR.puts 'Boolean has to be YES or NO'
|
2138
2134
|
exit(-1)
|
@@ -2160,8 +2156,8 @@ EOT
|
|
2160
2156
|
answer = STDIN.readline.chop
|
2161
2157
|
|
2162
2158
|
answer = initial if answer == ''
|
2163
|
-
|
2164
|
-
end while !noanswer && (answer =~ exp)
|
2159
|
+
noanswer = ((answer == '') && optional)
|
2160
|
+
end while !noanswer && (answer =~ exp).nil?
|
2165
2161
|
|
2166
2162
|
if noanswer
|
2167
2163
|
next
|
@@ -2198,7 +2194,7 @@ EOT
|
|
2198
2194
|
answer = initial if answer == ''
|
2199
2195
|
|
2200
2196
|
noanswer = (answer == '') && optional
|
2201
|
-
end while !noanswer && ((answer =~ exp)
|
2197
|
+
end while !noanswer && ((answer =~ exp).nil? ||
|
2202
2198
|
answer.to_f < min || answer.to_f > max)
|
2203
2199
|
|
2204
2200
|
if noanswer
|
@@ -2257,7 +2253,7 @@ EOT
|
|
2257
2253
|
# @param title [String] Plot title
|
2258
2254
|
#
|
2259
2255
|
# @return Gnuplot plot object
|
2260
|
-
def
|
2256
|
+
def self.get_plot(x, y, attr, title)
|
2261
2257
|
# Require gnuplot gem only here
|
2262
2258
|
begin
|
2263
2259
|
require 'gnuplot'
|
@@ -2307,7 +2303,7 @@ EOT
|
|
2307
2303
|
# @param perm [String] Permissions in human readbale format
|
2308
2304
|
#
|
2309
2305
|
# @return [String] Permissions in octet format
|
2310
|
-
def
|
2306
|
+
def self.to_octet(perm)
|
2311
2307
|
begin
|
2312
2308
|
Integer(perm)
|
2313
2309
|
perm
|