opennebula-cli 5.11.90.pre → 5.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d8f9d505a4c075609888337b302394b8ad09f564
4
- data.tar.gz: 5f91c8dd57ee2e6ebe71f6a5563d289e68ed97ee
3
+ metadata.gz: d3699c3c2d650493465035066057a79c20a59f40
4
+ data.tar.gz: 1577fe3f9e9ca3af5db399a0d9bc3587ad81cea4
5
5
  SHA512:
6
- metadata.gz: 45a4ca86499b26e4f8d94178384b7266da3764e0c0169226b1b8f3e582ba09298b01387f852dea0b9d05e66420ea74a2ee84295101aca55366d9adfa92d402e6
7
- data.tar.gz: 547591723faffe0aa33786c3f4ee863576348a618e30ede74df643e98c664afca036dae467f277cce9ef3f063f93391e64ae5e48123156968769ab0e255c6244
6
+ metadata.gz: 7c85e13cf698bcb599380d24b4eff94fc8c5ea157947ba42ec4bf2cd56bbf83043a4bc660fe78da567e35b3c4f0b9780fcbc1105886d79bc05d21e6495fdb295
7
+ data.tar.gz: 334c79870dd8a1dde30dfd971f0007d8f925cb1bccc870d863a0e86509f9840671754a4a4b17ec51d21f29cd458997e4a99140c3aa325c210018120e6e8012a9
@@ -97,6 +97,52 @@ CommandParser::CmdParser.new(ARGV) do
97
97
  CREAT_OPTIONS = [IM, VMM, OneClusterHelper::CLUSTER, TYPE]
98
98
  SYNC_OPTIONS = [OneClusterHelper::CLUSTER, FORCE, SSH]
99
99
 
100
+ ########################################################################
101
+ # Monitoring PLOT options
102
+ ########################################################################
103
+ START_T = {
104
+ :name => 'start',
105
+ :large => '--start date',
106
+ :description => 'Start date to show data',
107
+ :format => String
108
+ }
109
+
110
+ END_T = {
111
+ :name => 'end',
112
+ :large => '--end date',
113
+ :description => 'End date to show data',
114
+ :format => String
115
+ }
116
+
117
+ UNIT = {
118
+ :name => 'unit',
119
+ :large => '--unit unit',
120
+ :description => 'Unit to format data',
121
+ :format => String
122
+ }
123
+
124
+ TABLE = {
125
+ :name => 'table',
126
+ :large => '--table',
127
+ :description => 'Show monitoring information in table format'
128
+ }
129
+
130
+ N_ELEMS = {
131
+ :name => 'n_elems',
132
+ :large => '--n elements',
133
+ :description => 'Number of records to show',
134
+ :format => Integer
135
+ }
136
+
137
+ CSV_WITH_SEP = {
138
+ :name => 'csv',
139
+ :large => '--csv separator',
140
+ :description => 'Show data in CSV format',
141
+ :format => String
142
+ }
143
+
144
+ PLOT_OPTS = [START_T, END_T, UNIT, TABLE, N_ELEMS, CSV_WITH_SEP]
145
+
100
146
  ########################################################################
101
147
  # Formatters for arguments
102
148
  ########################################################################
@@ -304,4 +350,25 @@ CommandParser::CmdParser.new(ARGV) do
304
350
  :options => [OneClusterHelper::CLUSTER] do
305
351
  helper.forceupdate(args[0], options)
306
352
  end
353
+
354
+ monitoring_desc = <<-EOT.unindent
355
+ Show monitoring metrics in a graphic
356
+ EOT
357
+
358
+ command :monitoring,
359
+ monitoring_desc,
360
+ :hostid,
361
+ :attr,
362
+ :options => PLOT_OPTS do
363
+ helper.perform_action(args[0], options, 'monitoring') do |host|
364
+ rc = host.info
365
+
366
+ if OpenNebula.is_error?(rc)
367
+ STDERR.puts rc.message
368
+ exit(-1)
369
+ end
370
+
371
+ helper.monitoring(host, args[1], options)
372
+ end
373
+ end
307
374
  end
@@ -356,6 +356,12 @@ module CLIHelper
356
356
  # @param options [Hash] Object with CLI user options
357
357
  # @param top [Boolean] True to not update columns again
358
358
  def show(data, options = {}, top = false)
359
+ if options[:list]
360
+ @cli_columns = options[:list].collect {|o| o.upcase.to_sym }
361
+ else
362
+ @cli_columns = @default_columns
363
+ end
364
+
359
365
  update_columns(options) unless top
360
366
 
361
367
  if data.is_a? Hash
@@ -477,7 +483,7 @@ module CLIHelper
477
483
 
478
484
  # Get header in string format
479
485
  def header_str
480
- @default_columns.collect do |c|
486
+ @cli_columns.collect do |c|
481
487
  if @columns[c]
482
488
  format_str(c, c.to_s)
483
489
  else
@@ -495,12 +501,12 @@ module CLIHelper
495
501
 
496
502
  update_columns_size(options)
497
503
 
504
+ options[:csv_del] ? del = options[:csv_del] : del = ','
505
+
498
506
  if !options[:csv] && (!options.key? :no_header)
499
507
  CLIHelper.print_header(header_str)
500
- end
501
-
502
- if options[:csv] && (!options.key? :no_header)
503
- print_csv_data([@default_columns], options[:csv_del])
508
+ elsif options[:csv] && (!options.key? :no_header)
509
+ puts CSV.generate_line(@cli_columns, :col_sep => del)
504
510
  end
505
511
 
506
512
  @res_data ? print_data(@res_data, options) : puts
@@ -528,7 +534,13 @@ module CLIHelper
528
534
  del ? del = del : del = ','
529
535
 
530
536
  data.each do |l|
531
- puts CSV.generate_line(l, :col_sep => del)
537
+ result = []
538
+
539
+ @cli_columns.each do |col|
540
+ result << l[@default_columns.index(col)]
541
+ end
542
+
543
+ puts CSV.generate_line(result, :col_sep => del)
532
544
  end
533
545
  end
534
546
 
@@ -537,25 +549,26 @@ module CLIHelper
537
549
  # @param data [Array] Array with data to show
538
550
  # @param stat_column [String] Name of the state column
539
551
  def print_normal_data(data, stat_column)
540
- ncolumns = @default_columns.length
541
-
542
552
  if stat_column
543
- stat = stat_column.upcase.to_sym
544
- stat_column = @default_columns.index(stat)
553
+ stat = stat_column.upcase.to_sym
545
554
  else
546
- stat_column = @default_columns.index(:STAT)
555
+ stat = :STAT
547
556
  end
548
557
 
549
558
  data.each do |l|
550
559
  result = []
551
560
 
552
- ncolumns.times do |i|
561
+ @cli_columns.each do |col|
562
+ i = @default_columns.index(col)
563
+
564
+ # Column might not exist
565
+ next unless i
566
+
553
567
  dat = l[i]
554
- col = @default_columns[i]
555
568
 
556
569
  str = format_str(col, dat)
557
570
 
558
- str = CLIHelper.color_state(str) if i == stat_column
571
+ str = CLIHelper.color_state(str) if col == stat
559
572
 
560
573
  result << str
561
574
  end
@@ -570,8 +583,17 @@ module CLIHelper
570
583
  #
571
584
  # @return [Array] Array with selected columns information
572
585
  def data_array(data, options)
586
+ # Take default table columns and desired ones by the user
587
+ cols = @default_columns
588
+
589
+ @cli_columns.each do |col|
590
+ next if @default_columns.include?(col)
591
+
592
+ @default_columns.insert(@cli_columns.index(col) + 1, col)
593
+ end
594
+
573
595
  res_data = data.collect do |d|
574
- @default_columns.collect do |c|
596
+ cols.collect do |c|
575
597
  @columns[c][:proc].call(d).to_s if @columns[c]
576
598
  end
577
599
  end
@@ -580,10 +602,6 @@ module CLIHelper
580
602
  filter_data!(res_data, options) if options[:filter]
581
603
  end
582
604
 
583
- return res_data unless options[:list]
584
-
585
- @default_columns = options[:list].collect {|o| o.upcase.to_sym }
586
-
587
605
  res_data
588
606
  end
589
607
 
@@ -612,7 +630,7 @@ module CLIHelper
612
630
  def config_expand_data
613
631
  ret = []
614
632
 
615
- @default_columns.each do |column|
633
+ @cli_columns.each do |column|
616
634
  expand_c = @columns[column][:expand]
617
635
 
618
636
  next unless expand_c
@@ -633,7 +651,7 @@ module CLIHelper
633
651
  def config_adjust_data
634
652
  ret = []
635
653
 
636
- @default_columns.each do |column|
654
+ @cli_columns.each do |column|
637
655
  next unless @columns[column][:adjust]
638
656
 
639
657
  ret << column.to_s.downcase
@@ -657,13 +675,13 @@ module CLIHelper
657
675
 
658
676
  return if terminal_size.nil?
659
677
 
660
- default_columns = columns_info(@default_columns)
678
+ default_columns = columns_info(@cli_columns)
661
679
  expand_columns = columns_info(expand_columns)
662
680
 
663
681
  total_size = total_columns_size(default_columns)
664
682
  columns_size = total_columns_size(expand_columns)
665
683
 
666
- terminal_size -= (@default_columns.size - 1)
684
+ terminal_size -= (@cli_columns.size - 1)
667
685
  left_size = terminal_size - total_size
668
686
  remaining_size = left_size
669
687
 
@@ -735,7 +753,7 @@ module CLIHelper
735
753
  expand_data = []
736
754
 
737
755
  if expand_all
738
- expand_data = @default_columns
756
+ expand_data = @cli_columns
739
757
  elsif expand
740
758
  expand_data += options[:expand]
741
759
  end
@@ -749,7 +767,7 @@ module CLIHelper
749
767
  unless expand_all
750
768
  adjust.each do |column|
751
769
  column = column.upcase.to_sym
752
- size = max_size(@default_columns.index(column))
770
+ size = max_size(@cli_columns.index(column))
753
771
 
754
772
  if size && size > @columns[column][:size]
755
773
  @columns[column][:adjust] = true
@@ -821,7 +839,7 @@ module CLIHelper
821
839
  m = s.match(/^(.*?)#{operators}(.*?)$/)
822
840
 
823
841
  if m
824
- index = @columns.keys.index(m[1].to_sym)
842
+ index = @default_columns.index(m[1].upcase.to_sym)
825
843
 
826
844
  if index
827
845
  {
@@ -649,7 +649,7 @@ module CommandParser
649
649
  print_formatters
650
650
  puts
651
651
  if @version
652
- puts "## LICENSE"
652
+ puts "## VERSION"
653
653
  puts @version
654
654
  end
655
655
  end
@@ -31,10 +31,6 @@ module OpenNebulaHelper
31
31
  ONE_VERSION=<<-EOT
32
32
  OpenNebula #{OpenNebula::VERSION}
33
33
  Copyright 2002-2020, OpenNebula Project, OpenNebula Systems
34
-
35
- Licensed under the Apache License, Version 2.0 (the "License"); you may
36
- not use this file except in compliance with the License. You may obtain
37
- a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
38
34
  EOT
39
35
 
40
36
  if ONE_LOCATION
@@ -1203,6 +1199,18 @@ EOT
1203
1199
  end
1204
1200
  end
1205
1201
 
1202
+ def OpenNebulaHelper.bytes_to_unit(value, unit = 'K')
1203
+ j = 0
1204
+ i = BinarySufix.index(unit).to_i
1205
+
1206
+ while j < i do
1207
+ value /= 1024.0
1208
+ j += 1
1209
+ end
1210
+
1211
+ value
1212
+ end
1213
+
1206
1214
  # If the cluster name is empty, returns a '-' char.
1207
1215
  #
1208
1216
  # @param str [String || Hash] Cluster name, or empty Hash (when <CLUSTER/>)
@@ -1887,4 +1895,57 @@ EOT
1887
1895
  answers
1888
1896
  end
1889
1897
 
1898
+ # Returns plot object to print it on the CLI
1899
+ #
1900
+ # @param x [Array] Data to x axis (Time axis)
1901
+ # @param y [Array] Data to y axis
1902
+ # @param attr [String] Parameter to y axis
1903
+ # @param title [String] Plot title
1904
+ #
1905
+ # @return Gnuplot plot object
1906
+ def OpenNebulaHelper.get_plot(x, y, attr, title)
1907
+ # Require gnuplot gem only here
1908
+ begin
1909
+ require 'gnuplot'
1910
+ rescue LoadError, Gem::LoadError
1911
+ STDERR.puts(
1912
+ 'Gnuplot gem is not installed, run `gem install gnuplot` '\
1913
+ 'to install it'
1914
+ )
1915
+ exit(-1)
1916
+ end
1917
+
1918
+ # Check if gnuplot is installed on the system
1919
+ unless system('gnuplot --version')
1920
+ STDERR.puts(
1921
+ 'Gnuplot is not installed, install it depending on your distro'
1922
+ )
1923
+ exit(-1)
1924
+ end
1925
+
1926
+ Gnuplot.open do |gp|
1927
+ Gnuplot::Plot.new(gp) do |p|
1928
+ p.title title
1929
+
1930
+ p.xlabel 'Time'
1931
+ p.ylabel attr
1932
+
1933
+ p.xdata 'time'
1934
+ p.timefmt "'%H:%M'"
1935
+ p.format "x '%H:%M'"
1936
+
1937
+ p.style 'data lines'
1938
+ p.terminal 'dumb'
1939
+
1940
+ p.data << Gnuplot::DataSet.new([x, y]) do |ds|
1941
+ ds.with = 'linespoints'
1942
+ ds.linewidth = '3'
1943
+ ds.using = '1:2'
1944
+
1945
+ ds.notitle
1946
+ end
1947
+ end
1948
+ end
1949
+ end
1950
+
1890
1951
  end
@@ -17,6 +17,7 @@
17
17
  require 'one_helper'
18
18
  require 'one_helper/onevm_helper'
19
19
  require 'rubygems'
20
+ require 'time'
20
21
 
21
22
  # implements onehost command
22
23
  class OneHostHelper < OpenNebulaHelper::OneHelper
@@ -75,6 +76,15 @@ class OneHostHelper < OpenNebulaHelper::OneHelper
75
76
 
76
77
  VERSION_XPATH = "#{TEMPLATE_XPATH}/VERSION"
77
78
 
79
+ MONITORING = {
80
+ 'FREE_CPU' => 'CAPACITY',
81
+ 'FREE_MEMORY' => 'CAPACITY',
82
+ 'USED_CPU' => 'CAPACITY',
83
+ 'USED_MEMORY' => 'CAPACITY',
84
+ 'NETRX' => 'SYSTEM',
85
+ 'NETTX' => 'SYSTEM'
86
+ }
87
+
78
88
  def self.rname
79
89
  'HOST'
80
90
  end
@@ -425,8 +435,7 @@ class OneHostHelper < OpenNebulaHelper::OneHelper
425
435
  rc = pool.info
426
436
  return -1, rc.message if OpenNebula.is_error?(rc)
427
437
 
428
- # Assign hosts to threads
429
- queue = []
438
+ host_errors = []
430
439
 
431
440
  pool.each do |host|
432
441
  if host_ids
@@ -435,7 +444,6 @@ class OneHostHelper < OpenNebulaHelper::OneHelper
435
444
  next if host['CLUSTER_ID'].to_i != cluster_id
436
445
  end
437
446
 
438
- vm_mad = host['VM_MAD'].downcase
439
447
  state = host['STATE']
440
448
 
441
449
  # Skip this host from remote syncing if it's a PUBLIC_CLOUD host
@@ -444,60 +452,104 @@ class OneHostHelper < OpenNebulaHelper::OneHelper
444
452
  # Skip this host from remote syncing if it's OFFLINE
445
453
  next if Host::HOST_STATES[state.to_i] == 'OFFLINE'
446
454
 
447
- # Skip this host if it is a vCenter cluster
448
- next if vm_mad == 'vcenter'
455
+ rc = host.forceupdate
449
456
 
450
- queue << host
457
+ host_errors << host['NAME'] if OpenNebula.is_error?(rc)
451
458
  end
452
459
 
453
- # Run the jobs in threads
454
- host_errors = []
455
- queue_lock = Mutex.new
456
- error_lock = Mutex.new
457
- total = queue.length
460
+ if host_errors.empty?
461
+ puts 'All hosts updated successfully.'
462
+ 0
463
+ else
464
+ STDERR.puts 'Failed to update the following hosts:'
465
+ host_errors.each {|h| STDERR.puts "* #{h}" }
466
+ -1
467
+ end
468
+ end
458
469
 
459
- if total.zero?
460
- puts 'No hosts are going to be forced.'
461
- exit(0)
470
+ def monitoring(host, attr, options)
471
+ unit = options[:unit] || 'G'
472
+ start_d = options[:start]
473
+ end_d = options[:end]
474
+ n_elems = options[:n_elems] || 8
475
+
476
+ # Different available size units
477
+ units = %w[K M G T]
478
+
479
+ # Attrs that need units conversion
480
+ attrs = %w[FREE_MEMORY USED_MEMORY]
481
+
482
+ if unit && !units.include?(unit)
483
+ STDERR.puts "Invalid unit `#{unit}`"
484
+ exit(-1)
462
485
  end
463
486
 
464
- ts = (1..NUM_THREADS).map do |_t|
465
- Thread.new do
466
- loop do
467
- host = nil
468
- size = 0
487
+ attr = attr.upcase
488
+ start_d = Time.parse(start_d) if start_d
489
+ end_d = Time.parse(end_d) if end_d
469
490
 
470
- queue_lock.synchronize do
471
- host = queue.shift
472
- size = queue.length
473
- end
491
+ # Get monitoring data from user path
492
+ #
493
+ # 0 -> timestamp
494
+ # 1 -> data retrieved
495
+ monitoring_data = host.monitoring(["#{MONITORING[attr]}/#{attr}"])
496
+ monitoring_data = monitoring_data["#{MONITORING[attr]}/#{attr}"]
474
497
 
475
- break unless host
498
+ if monitoring_data.empty?
499
+ STDERR.puts 'No monitoring data found'
500
+ return
501
+ end
476
502
 
477
- hn = host['NAME']
503
+ # Get data max and min date
504
+ start_d ||= Time.at(monitoring_data.min {|v| v[0].to_i }[0].to_i)
505
+ end_d ||= Time.at(monitoring_data.max {|v| v[0].to_i }[0].to_i)
478
506
 
479
- system("onehost offline #{hn} && onehost enable #{hn}")
480
- if !$CHILD_STATUS.success?
481
- error_lock.synchronize do
482
- host_errors << hn
483
- end
484
- else
485
- puts "#{hn} monitoring forced"
486
- end
487
- end
507
+ # Filter data betwen dates
508
+ monitoring_data.reject! do |v|
509
+ v[0].to_i < start_d.to_i || v[0].to_i > end_d.to_i
510
+ end
511
+
512
+ if monitoring_data.empty?
513
+ STDERR.puts "No monitoring data found between #{start_d} " \
514
+ "and #{end_d}"
515
+ return
516
+ end
517
+
518
+ start_d = start_d.strftime('%d/%m/%Y %H:%M')
519
+ end_d = end_d.strftime('%d/%m/%Y %H:%M')
520
+
521
+ # Parse dcollected data
522
+ x = monitoring_data.collect {|v| Time.at(v[0].to_i).strftime('%H:%M') }
523
+ y = monitoring_data.collect do |v|
524
+ if attrs.include?(attr)
525
+ # GB is the default unit
526
+ v = OpenNebulaHelper.bytes_to_unit(v[1].to_i, unit).round(2)
527
+ "#{v} #{unit}B"
528
+ else
529
+ v[1]
488
530
  end
489
531
  end
490
532
 
491
- # Wait for threads to finish
492
- ts.each {|t| t.join }
533
+ title = ''
534
+ title << "Host #{host.id} #{attr} "
535
+ title << "in #{unit}B " if unit && attrs.include?(attr)
536
+ title << "from #{start_d} to #{end_d}"
493
537
 
494
- if host_errors.empty?
495
- puts 'All hosts updated successfully.'
496
- 0
538
+ x = x.last(n_elems)
539
+ y = y.last(n_elems)
540
+
541
+ if options[:table]
542
+ print_monitoring_table(x, y, title)
543
+ elsif options[:csv]
544
+ csv = ''
545
+
546
+ csv << "TIME#{options[:csv]}VALUE\n"
547
+
548
+ x.zip(y) {|x_v, y_v| csv << "#{x_v}#{options[:csv]}#{y_v}\n" }
549
+
550
+ puts csv
497
551
  else
498
- STDERR.puts 'Failed to update the following hosts:'
499
- host_errors.each {|h| STDERR.puts "* #{h}" }
500
- -1
552
+ puts OpenNebulaHelper.get_plot(x, y, attr, title)
501
553
  end
502
554
  end
503
555
 
@@ -920,4 +972,30 @@ class OneHostHelper < OpenNebulaHelper::OneHelper
920
972
  table.show(hugepages)
921
973
  end
922
974
 
975
+ def print_monitoring_table(x, y, title)
976
+ puts
977
+ CLIHelper.print_header(title, true)
978
+ puts
979
+
980
+ table = CLIHelper::ShowTable.new(nil, self) do
981
+ column :TIME, 'Timestamp', :size => 8, :left => false do |d|
982
+ d['TIME']
983
+ end
984
+
985
+ column :VALUE, 'Value', :size => 8, :left => false do |d|
986
+ d['VALUE']
987
+ end
988
+
989
+ default :TIME, :VALUE
990
+ end
991
+
992
+ data = []
993
+
994
+ x.zip(y) do |x_v, y_v|
995
+ data << { 'TIME' => x_v, 'VALUE' => y_v }
996
+ end
997
+
998
+ table.show(data)
999
+ end
1000
+
923
1001
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opennebula-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.11.90.pre
4
+ version: 5.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenNebula
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-01 00:00:00.000000000 Z
11
+ date: 2020-06-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: opennebula
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 5.11.90.pre
19
+ version: 5.12.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 5.11.90.pre
26
+ version: 5.12.0
27
27
  description: Commands used to talk to OpenNebula
28
28
  email: contact@opennebula.io
29
29
  executables:
@@ -123,9 +123,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
123
123
  version: '0'
124
124
  required_rubygems_version: !ruby/object:Gem::Requirement
125
125
  requirements:
126
- - - '>'
126
+ - - '>='
127
127
  - !ruby/object:Gem::Version
128
- version: 1.3.1
128
+ version: '0'
129
129
  requirements: []
130
130
  rubyforge_project:
131
131
  rubygems_version: 2.0.14.1