opennebula-cli 5.11.90.pre → 5.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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