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.
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 "Error: "+e.message.to_s
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
- ONE_VERSION=<<-EOT
32
- OpenNebula #{OpenNebula::VERSION}
33
- Copyright 2002-2023, OpenNebula Project, OpenNebula Systems
34
- EOT
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 + "/etc/cli"
38
- VAR_LOCATION = ONE_LOCATION + "/var" if !defined?(VAR_LOCATION)
39
- CLI_ADDONS_LOCATION = ONE_LOCATION + "/lib/ruby/cli/addons"
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 = "/etc/one/cli"
43
- VAR_LOCATION = "/var/lib/one" if !defined?(VAR_LOCATION)
44
- CLI_ADDONS_LOCATION = "/usr/lib/one/ruby/cli/addons"
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 => "xml",
55
- :short => "-x",
56
- :large => "--xml",
57
- :description => "Show the resource in xml format"
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 => "numeric",
79
- :short => "-n",
80
- :large => "--numeric",
81
- :description => "Do not translate user and group IDs"
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 => "kilobytes",
86
- :short => "-k",
87
- :large => "--kilobytes",
88
- :description => "Show units in kilobytes"
91
+ :name => 'kilobytes',
92
+ :short => '-k',
93
+ :large => '--kilobytes',
94
+ :description => 'Show units in kilobytes'
89
95
  }
90
96
 
91
97
  DESCRIBE={
92
- :name => "describe",
93
- :large => "--describe",
94
- :description => "Describe list columns"
98
+ :name => 'describe',
99
+ :large => '--describe',
100
+ :description => 'Describe list columns'
95
101
  }
96
102
 
97
103
  APPEND = {
98
- :name => "append",
99
- :short => "-a",
100
- :large => "--append",
101
- :description => "Append new attributes to the current template"
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, options|
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, options|
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, options|
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 => "-n",
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 => "-u",
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 => "-p",
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 => "-d",
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 => "-r",
210
+ :short => '-r',
189
211
  :description =>
190
- "Which resources can be created by group users "<<
191
- "(VM+NET+IMAGE+TEMPLATE by default)",
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
- :name => 'as_uid',
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
- :name => 'as_gid',
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
- " "*31<<"CPU)",
239
+ ' '*31<<'CPU)',
218
240
  :format => Float
219
241
  },
220
242
  {
221
243
  :name => 'vcpu',
222
244
  :large => '--vcpu vcpu',
223
245
  :description =>
224
- "Number of virtualized CPUs",
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
- "can be used: 8g=8192, 0.5g=512",
261
+ 'can be used: 8g=8192, 0.5g=512',
240
262
  :format => String,
241
- :proc => lambda do |o,options|
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
- when /(g|gb)/i
249
- 1024
250
- else
251
- 1
252
- end
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 => "Disks to attach. To use an image owned by"<<
264
- " other user use user[disk]. Add any additional"<<
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
- " KEY=VALUE. For example, if the disk must be"<<
267
- " resized, use image0:size=1000 . Or"<<
268
- " image0:size=1000:target=vda,image1:target=vdb",
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 => "Networks to attach. To use a network owned by"<<
275
- " other user use user[network]. Additional"<<
276
- " attributes are supported like with the --disk"<<
277
- " option. Also you can use auto if you want that" <<
278
- " OpenNebula select automatically the network",
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
- " "*31<<"confused with the RAW attribute",
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) << "SSH_PUBLIC_KEY will be used.",
363
+ (' '*31) << 'SSH_PUBLIC_KEY will be used.',
342
364
  :format => String,
343
- :proc => lambda do |o, options|
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 => "In a vCenter environment sets the the VMs and Template folder where the VM will be placed in." \
397
- " The path uses slashes to separate folders. For example: --vcenter_vm_folder \"/Management/VMs\""
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
- TEMPLATE_OPTIONS[3]]
587
+ TEMPLATE_OPTIONS[3]]
446
588
 
447
589
  UPDATECONF_OPTIONS_VM = TEMPLATE_OPTIONS[6..15] + [TEMPLATE_OPTIONS[2],
448
- TEMPLATE_OPTIONS[17], TEMPLATE_OPTIONS[18]]
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]||self.get_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
- self.get_client
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>="1.9.3"
659
+ if RUBY_VERSION>='1.9.3'
517
660
  require 'io/console'
518
661
  def self.get_password
519
- print "Password: "
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 "Password: "
532
- system("stty", "-echo")
674
+ print 'Password: '
675
+ system('stty', '-echo')
533
676
  begin
534
677
  @@password = STDIN.gets.chop
535
- return @@password
678
+ @@password
536
679
  ensure
537
- system("stty", "echo")
680
+ system('stty', 'echo')
538
681
  print "\n"
539
682
  end
540
683
  end
541
684
  end
542
685
 
543
- def initialize(secret=nil, endpoint=nil)
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(options, &block)
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
- return -1, rc.message
713
+ [-1, rc.message]
563
714
  else
564
- puts "ID: #{resource.id.to_s}"
565
- return 0
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
- return lpid
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("TERM", lpid)
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
- return elements, page
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, filter_flag)
651
- if $stdout.isatty and (!options.key?:no_pager)
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
- return 0
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, filter_flag)
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?:no_pager)
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 == "VM_POOL" && extended
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
- return 0
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, filter_flag)
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?:no_pager)
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 == "VM_POOL" && extended
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
- return 0
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
- return 0
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 = check_resource_xsd(pool, pname)
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 = check_resource_xsd(pool, pname)
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
- return 0
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
- return 0, resource.to_xml(true)
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
- return 0, check_resource_xsd(resource)
1129
+ [0, check_resource_xsd(resource)]
980
1130
  else
981
- return 0, ::JSON.pretty_generate(
1131
+ [0, ::JSON.pretty_generate(
982
1132
  check_resource_xsd(resource)
983
- )
1133
+ )]
984
1134
  end
985
1135
  elsif options[:yaml]
986
- return 0, check_resource_xsd(resource).to_yaml(:indent => 4)
1136
+ [0, check_resource_xsd(resource).to_yaml(:indent => 4)]
987
1137
  else
988
1138
  format_resource(resource, options)
989
- return 0
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
- return -1, rc.message
1148
+ [-1, rc.message]
999
1149
  else
1000
1150
  if options[:verbose]
1001
1151
  puts "#{self.class.rname} #{id}: #{verbose}"
1002
1152
  end
1003
- return 0
1153
+ 0
1004
1154
  end
1005
1155
  end
1006
1156
 
1007
- def perform_actions(ids,options,verbose,&block)
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,&block)
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 #{self.rname} name or id"
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 { |name|
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
- return 0, result
1230
+ [0, result]
1081
1231
  end
1082
1232
 
1083
1233
  def self.list_to_id_desc
1084
- "Comma-separated list of OpenNebula #{self.rname} names or ids"
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=="CLUSTER" and name.upcase=="ALL"
1089
- return 0, "ALL"
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
- if objects.length>0
1095
- if objects.length>1
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
- return 0, result
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
- 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
- if rc.first==-1
1119
- return rc
1120
- else
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
- return 0, filter_flag
1267
+ end
1268
+ end
1269
+
1270
+ [0, filter_flag]
1127
1271
  end
1128
1272
 
1129
1273
  def self.filterflag_to_i_desc
1130
- desc=<<-EOT
1131
- a, all all the known #{self.rname}s
1132
- m, mine the #{self.rname} belonging to the user in ONE_AUTH
1133
- g, group 'mine' plus the #{self.rname} belonging to the groups
1134
- the user is member of
1135
- G, primary group the #{self.rname} owned the user's primary group
1136
- uid #{self.rname} of the user identified by this uid
1137
- user #{self.rname} of the user identified by the username
1138
- EOT
1139
- end
1140
-
1141
- def self.table_conf(conf_file=self.conf_file)
1142
- path = "#{ENV["HOME"]}/.one/cli/#{conf_file}"
1143
-
1144
- if File.exists?(path)
1145
- return path
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
- return "#{TABLE_CONF_PATH}/#{conf_file}"
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 = Array.new
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
- if rc.message.empty?
1187
- return -1, "OpenNebula #{self.class.rname} name not " <<
1188
- "found, use the ID instead"
1189
- else
1190
- return -1,rc.message
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
- return 0, pool
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 = hash.keys.first unless 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 = [ xsd ] unless xsd.is_a? Array
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
- return h
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 nil
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
- hash = replace_refs(hash)
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 { |e| e['name'] == k }.first unless xsd.nil?
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) && (! v.is_a? Array)
1369
- hash[k] = [ v ]
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 OpenNebulaHelper.rname_to_id(name, poolname)
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
- when "HOST" then OpenNebula::HostPool.new(client)
1382
- when "HOOK" then OpenNebula::HookPool.new(client)
1383
- when "GROUP" then OpenNebula::GroupPool.new(client)
1384
- when "USER" then OpenNebula::UserPool.new(client)
1385
- when "DATASTORE" then OpenNebula::DatastorePool.new(client)
1386
- when "CLUSTER" then OpenNebula::ClusterPool.new(client)
1387
- when "VNET" then OpenNebula::VirtualNetworkPool.new(client)
1388
- when "IMAGE" then OpenNebula::ImagePool.new(client)
1389
- when "VMTEMPLATE" then OpenNebula::TemplatePool.new(client)
1390
- when "VNTEMPLATES" then OpenNebula::VNTemplatePool.new(client)
1391
- when "VM" then OpenNebula::VirtualMachinePool.new(client)
1392
- when "ZONE" then OpenNebula::ZonePool.new(client)
1393
- when "MARKETPLACE" then OpenNebula::MarketPlacePool.new(client)
1394
- when "FLOWTEMPLATES" then OpenNebula::ServiceTemplatePool.new(client)
1395
- end
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
- " use the ID instead"
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 OpenNebulaHelper.size_in_mb(size)
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
- return -1, 'Size value malformed'
1553
+ [-1, 'Size value malformed']
1412
1554
  else
1413
1555
  multiplier=case m[2]
1414
- when /(t|tb)/i
1415
- 1024*1024
1416
- when /(g|gb)/i
1417
- 1024
1418
- else
1419
- 1
1420
- end
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
- return 0, value.ceil
1567
+ [0, value.ceil]
1426
1568
  end
1427
1569
  end
1428
1570
 
1429
- def OpenNebulaHelper.rname_to_id_desc(poolname)
1571
+ def self.rname_to_id_desc(poolname)
1430
1572
  "OpenNebula #{poolname} name or id"
1431
1573
  end
1432
1574
 
1433
- def OpenNebulaHelper.boolean_to_str(str)
1575
+ def self.boolean_to_str(str)
1434
1576
  if str.to_i == 1
1435
- "Yes"
1577
+ 'Yes'
1436
1578
  else
1437
- "No"
1579
+ 'No'
1438
1580
  end
1439
1581
  end
1440
1582
 
1441
- def OpenNebulaHelper.time_to_str(time, print_seconds=true,
1442
- print_hours=true, print_years=false)
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("%m/%d/%y %H:%M:%S")
1594
+ value=Time.at(value).strftime('%m/%d/%y %H:%M:%S')
1453
1595
  else
1454
- value=Time.at(value).strftime("%m/%d %H:%M:%S")
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("%m/%d/%y %H:%M")
1600
+ value=Time.at(value).strftime('%m/%d/%y %H:%M')
1459
1601
  else
1460
- value=Time.at(value).strftime("%m/%d %H:%M")
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("%m/%d/%y")
1607
+ value=Time.at(value).strftime('%m/%d/%y')
1466
1608
  else
1467
- value=Time.at(value).strftime("%m/%d")
1609
+ value=Time.at(value).strftime('%m/%d')
1468
1610
  end
1469
1611
  end
1470
1612
  end
1471
1613
 
1472
- return value
1614
+ value
1473
1615
  end
1474
1616
 
1475
- def OpenNebulaHelper.period_to_str(time, print_seconds=true)
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
- "%3dd %02dh%02dm%02ds" % [days, hours, minutes, seconds]
1624
+ format('%3dd %02dh%02dm%02ds', days, hours, minutes, seconds)
1483
1625
  else
1484
- "%3dd %02dh%02dm" % [days, hours, minutes]
1626
+ format('%3dd %02dh%02dm', days, hours, minutes)
1485
1627
  end
1486
1628
  end
1487
1629
 
1488
- def OpenNebulaHelper.short_period_to_str(time, print_seconds=true)
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
- "%3dh%02dm%02ds" % [hours, minutes, seconds]
1636
+ format('%3dh%02dm%02ds', hours, minutes, seconds)
1495
1637
  else
1496
- "%3dh%02dm" % [hours, minutes]
1638
+ format('%3dh%02dm', hours, minutes)
1497
1639
  end
1498
1640
  end
1499
1641
 
1500
- BinarySufix = ["K", "M", "G", "T" ]
1642
+ BinarySufix = ['K', 'M', 'G', 'T']
1501
1643
 
1502
- def OpenNebulaHelper.unit_to_str(value, options, unit="K")
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 do
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 OpenNebulaHelper.bytes_to_unit(value, unit = 'K')
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 do
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 OpenNebulaHelper.cluster_str(str)
1537
- if str != nil && !str.empty?
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 OpenNebulaHelper.clusters_str(clusters)
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 OpenNebulaHelper.update_template(id, resource, path=nil, xpath='TEMPLATE')
1554
- return update_template_helper(false, id, resource, path, xpath)
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 OpenNebulaHelper.append_template(id, resource, path=nil, xpath='TEMPLATE')
1558
- return update_template_helper(true, id, resource, path, xpath)
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 OpenNebulaHelper.update_template_helper(append, id, resource, path, xpath, update=true)
1702
+ def self.update_template_helper(append, _id, resource, path, xpath, update = true)
1562
1703
  if path
1563
- return File.read(path)
1704
+ File.read(path)
1705
+ elsif STDIN.wait_readable(0)
1706
+ STDIN.read
1564
1707
  elsif append
1565
- return editor_input()
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 -1
1715
+ exit(-1)
1573
1716
  end
1574
1717
  end
1575
1718
 
1576
- return editor_input(resource.template_like_str(xpath))
1719
+ editor_input(resource.template_like_str(xpath))
1577
1720
  end
1578
1721
  end
1579
1722
 
1580
- def OpenNebulaHelper.update_obj(obj, file, plain = false)
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 OpenNebulaHelper.editor_input(contents=nil)
1760
+ def self.editor_input(contents = nil)
1618
1761
  require 'tempfile'
1619
1762
 
1620
- tmp = Tempfile.new("one_cli")
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["EDITOR"] ? ENV["EDITOR"] : EDITOR_PATH
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 "Editor not defined"
1632
- exit -1
1774
+ puts 'Editor not defined'
1775
+ exit(-1)
1633
1776
  end
1634
1777
 
1635
1778
  tmp.close
1636
1779
 
1637
- str = File.read(tmp.path)
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 nil if !m
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
- end
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"] if !res
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 == "auto"
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, :report_ready]
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<<"SSH_PUBLIC_KEY=\"$USER[SSH_PUBLIC_KEY]\""
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 << "NETWORK = \"YES\""
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
- %Q<$FILE[IMAGE=\\"#{file}\\"]>
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 << %Q<INIT_SCRIPTS="#{options[:init].join(' ')}">
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::strict_encode64(script)
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 << "REPORT_READY = \"YES\""
1906
+ lines << 'REPORT_READY = "YES"'
1763
1907
  end
1764
1908
 
1765
1909
  if !lines.empty?
1766
- "CONTEXT=[\n" << lines.map{|l| " " << l }.join(",\n") << "\n]\n"
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] || "0.0.0.0"
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] || "0.0.0.0"
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
- if template_obj.has_elements?('TEMPLATE/EC2')
1845
- template_obj.add_element(
1846
- 'TEMPLATE/EC2',
1847
- 'USERDATA' => options[:userdata])
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
- template << template_obj.template_like_str(
1850
- 'TEMPLATE', false, 'EC2')
1851
- end
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
- one_sunstone
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
- "http://localhost:9869"
2107
+ 'http://localhost:9869'
1955
2108
  end
1956
2109
  end
1957
2110
 
1958
- def self.download_resource_sunstone(kind, id, path, force)
2111
+ def self.download_resource_sunstone(kind, id, path, _force)
1959
2112
  client = OneHelper.client
1960
- user, password = client.one_auth.split(":", 2)
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,"login"))
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
- return OpenNebula::Error.new("Unable to get Cookie. Is OpenNebula running?")
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
- return OpenNebula::Error.new("Unable to get csrftoken.")
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'] = "OpenNebula CLI"
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("Target file not writable.")
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
- if error_message
2043
- File.unlink(path)
2044
- return OpenNebula::Error.new("Remote server error: #{error_message}")
2045
- end
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 OpenNebulaHelper.level_lock_to_str(str)
2201
+ def self.level_lock_to_str(str)
2049
2202
  level = str.to_i
2050
2203
  if level == 0
2051
- "None"
2204
+ 'None'
2052
2205
  elsif level == 1
2053
- "Use"
2206
+ 'Use'
2054
2207
  elsif level == 2
2055
- "Manage"
2208
+ 'Manage'
2056
2209
  elsif level == 3
2057
- "Admin"
2210
+ 'Admin'
2058
2211
  elsif level == 4
2059
- "All"
2212
+ 'All'
2060
2213
  else
2061
- "-"
2214
+ '-'
2062
2215
  end
2063
2216
  end
2064
2217
 
2065
- def OpenNebulaHelper.parse_user_inputs(inputs, keys = [])
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 %w[YES NO].include?(answer)
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
- noanswer = ((answer == '') && optional)
2164
- end while !noanswer && (answer =~ exp) == nil
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) == nil ||
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 OpenNebulaHelper.get_plot(x, y, attr, title)
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 OpenNebulaHelper.to_octet(perm)
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