1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-02-03 13:47:01 +03:00

M #-: add monitoring plot to CLI (#4904)

This commit is contained in:
Alejandro Huertas Herrero 2020-06-09 16:52:46 +02:00 committed by GitHub
parent 7af497226f
commit 49e1901a5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 271 additions and 3 deletions

View File

@ -58,6 +58,7 @@ GEM
ffi-rzmq-core (>= 1.0.7)
ffi-rzmq-core (1.0.7)
ffi
gnuplot (2.6.2)
hashie (3.6.0)
highline (1.7.10)
http-cookie (1.0.3)

View File

@ -65,6 +65,7 @@ GEM
ffi-rzmq-core (>= 1.0.7)
ffi-rzmq-core (1.0.7)
ffi
gnuplot (2.6.2)
hashie (3.6.0)
highline (1.7.10)
http-cookie (1.0.3)

View File

@ -65,6 +65,7 @@ GEM
ffi-rzmq-core (>= 1.0.7)
ffi-rzmq-core (1.0.7)
ffi
gnuplot (2.6.2)
hashie (3.6.0)
highline (1.7.10)
http-cookie (1.0.3)

View File

@ -58,6 +58,7 @@ GEM
ffi-rzmq-core (>= 1.0.7)
ffi-rzmq-core (1.0.7)
ffi
gnuplot (2.6.2)
hashie (3.6.0)
highline (1.7.10)
http-cookie (1.0.3)

View File

@ -65,6 +65,7 @@ GEM
ffi-rzmq-core (>= 1.0.7)
ffi-rzmq-core (1.0.7)
ffi
gnuplot (2.6.2)
hashie (3.6.0)
highline (1.7.10)
http-cookie (1.0.3)

View File

@ -65,6 +65,7 @@ GEM
ffi-rzmq-core (>= 1.0.7)
ffi-rzmq-core (1.0.7)
ffi
gnuplot (2.6.2)
hashie (3.6.0)
highline (1.7.10)
http-cookie (1.0.3)

View File

@ -87,3 +87,4 @@ gem 'i18n', '~> 0.9' # packethost
gem 'ffi-rzmq', '~> 2.0.7' # onehem (hooks)
gem 'ipaddress', '~> 0.8.3' # sunstone, oneflow
gem 'augeas', '~> 0.6' # serversync
gem 'gnuplot' # monitoring host plot

View File

@ -58,6 +58,7 @@ GEM
ffi-rzmq-core (>= 1.0.7)
ffi-rzmq-core (1.0.7)
ffi
gnuplot (2.6.2)
hashie (3.6.0)
highline (1.7.10)
http-cookie (1.0.3)

View File

@ -65,6 +65,7 @@ GEM
ffi-rzmq-core (>= 1.0.7)
ffi-rzmq-core (1.0.7)
ffi
gnuplot (2.6.2)
hashie (3.6.0)
highline (1.7.10)
http-cookie (1.0.3)

View File

@ -65,6 +65,7 @@ GEM
ffi-rzmq-core (>= 1.0.7)
ffi-rzmq-core (1.0.7)
ffi
gnuplot (2.6.2)
hashie (3.6.0)
highline (1.7.10)
http-cookie (1.0.3)

View File

@ -65,6 +65,7 @@ GEM
ffi-rzmq-core (>= 1.0.7)
ffi-rzmq-core (1.0.7)
ffi
gnuplot (2.6.2)
hashie (3.6.0)
highline (1.7.10)
http-cookie (1.0.3)

View File

@ -59,7 +59,8 @@ GROUPS={
:market => 'aws-sdk',
:onedb => ['mysql2', PG],
:hooks => %w[zeromq ffi-rzmq],
:serversync => "augeas"
:serversync => "augeas",
:gnuplot => "gnuplot"
}
PACKAGES=GROUPS.keys
@ -83,7 +84,8 @@ DISTRIBUTIONS={
'thin' => ['g++'],
'json' => ['gcc'],
'zeromq' => ['gcc', 'libzmq5', 'libzmq3-dev'],
'augeas' => ['gcc', 'libaugeas-dev']
'augeas' => ['gcc', 'libaugeas-dev'],
'gnuplot' => ['gcc']
},
:install_command_interactive => 'apt-get install',
:install_command => 'apt-get -y install',
@ -105,7 +107,8 @@ DISTRIBUTIONS={
'thin' => ['gcc-c++'],
'json' => ['gcc'],
'zeromq' => ['gcc', 'zeromq', 'zeromq-devel'],
'augeas' => ['gcc', 'augeas-devel']
'augeas' => ['gcc', 'augeas-devel'],
'gnuplot' => ['gcc']
},
:install_command_interactive => 'yum install',
:install_command => 'yum -y install',

View File

@ -1203,6 +1203,18 @@ EOT
end
end
def OpenNebulaHelper.bytes_to_unit(value, unit = 'K')
j = 0
i = BinarySufix.index(unit).to_i
while j < i do
value /= 1024.0
j += 1
end
value
end
# If the cluster name is empty, returns a '-' char.
#
# @param str [String || Hash] Cluster name, or empty Hash (when <CLUSTER/>)
@ -1887,4 +1899,57 @@ EOT
answers
end
# Returns plot object to print it on the CLI
#
# @param x [Array] Data to x axis (Time axis)
# @param y [Array] Data to y axis
# @param attr [String] Parameter to y axis
# @param title [String] Plot title
#
# @return Gnuplot plot object
def OpenNebulaHelper.get_plot(x, y, attr, title)
# Require gnuplot gem only here
begin
require 'gnuplot'
rescue LoadError, Gem::LoadError
STDERR.puts(
'Gnuplot gem is not installed, run `gem install gnuplot` '\
'to install it'
)
exit(-1)
end
# Check if gnuplot is installed on the system
unless system('gnuplot --version')
STDERR.puts(
'Gnuplot is not installed, install it depending on your distro'
)
exit(-1)
end
Gnuplot.open do |gp|
Gnuplot::Plot.new(gp) do |p|
p.title title
p.xlabel 'Time'
p.ylabel attr
p.xdata 'time'
p.timefmt "'%H:%M'"
p.format "x '%H:%M'"
p.style 'data lines'
p.terminal 'dumb'
p.data << Gnuplot::DataSet.new([x, y]) do |ds|
ds.with = 'linespoints'
ds.linewidth = '3'
ds.using = '1:2'
ds.notitle
end
end
end
end
end

View File

@ -17,6 +17,7 @@
require 'one_helper'
require 'one_helper/onevm_helper'
require 'rubygems'
require 'time'
# implements onehost command
class OneHostHelper < OpenNebulaHelper::OneHelper
@ -75,6 +76,15 @@ class OneHostHelper < OpenNebulaHelper::OneHelper
VERSION_XPATH = "#{TEMPLATE_XPATH}/VERSION"
MONITORING = {
'FREE_CPU' => 'CAPACITY',
'FREE_MEMORY' => 'CAPACITY',
'USED_CPU' => 'CAPACITY',
'USED_MEMORY' => 'CAPACITY',
'NETRX' => 'SYSTEM',
'NETTX' => 'SYSTEM'
}
def self.rname
'HOST'
end
@ -457,6 +467,92 @@ class OneHostHelper < OpenNebulaHelper::OneHelper
end
end
def monitoring(host, attr, options)
unit = options[:unit] || 'G'
start_d = options[:start]
end_d = options[:end]
n_elems = options[:n_elems] || 8
# Different available size units
units = %w[K M G T]
# Attrs that need units conversion
attrs = %w[FREE_MEMORY USED_MEMORY]
if unit && !units.include?(unit)
STDERR.puts "Invalid unit `#{unit}`"
exit(-1)
end
attr = attr.upcase
start_d = Time.parse(start_d) if start_d
end_d = Time.parse(end_d) if end_d
# Get monitoring data from user path
#
# 0 -> timestamp
# 1 -> data retrieved
monitoring_data = host.monitoring(["#{MONITORING[attr]}/#{attr}"])
monitoring_data = monitoring_data["#{MONITORING[attr]}/#{attr}"]
if monitoring_data.empty?
STDERR.puts 'No monitoring data found'
return
end
# Get data max and min date
start_d ||= Time.at(monitoring_data.min {|v| v[0].to_i }[0].to_i)
end_d ||= Time.at(monitoring_data.max {|v| v[0].to_i }[0].to_i)
# Filter data betwen dates
monitoring_data.reject! do |v|
v[0].to_i < start_d.to_i || v[0].to_i > end_d.to_i
end
if monitoring_data.empty?
STDERR.puts "No monitoring data found between #{start_d} " \
"and #{end_d}"
return
end
start_d = start_d.strftime('%d/%m/%Y %H:%M')
end_d = end_d.strftime('%d/%m/%Y %H:%M')
# Parse dcollected data
x = monitoring_data.collect {|v| Time.at(v[0].to_i).strftime('%H:%M') }
y = monitoring_data.collect do |v|
if attrs.include?(attr)
# GB is the default unit
v = OpenNebulaHelper.bytes_to_unit(v[1].to_i, unit).round(2)
"#{v} #{unit}B"
else
v[1]
end
end
title = ''
title << "Host #{host.id} #{attr} "
title << "in #{unit}B " if unit && attrs.include?(attr)
title << "from #{start_d} to #{end_d}"
x = x.last(n_elems)
y = y.last(n_elems)
if options[:table]
print_monitoring_table(x, y, title)
elsif options[:csv]
csv = ''
csv << "TIME#{options[:csv]}VALUE\n"
x.zip(y) {|x_v, y_v| csv << "#{x_v}#{options[:csv]}#{y_v}\n" }
puts csv
else
puts OpenNebulaHelper.get_plot(x, y, attr, title)
end
end
private
def print_update_info(current, total, host)
@ -876,4 +972,30 @@ class OneHostHelper < OpenNebulaHelper::OneHelper
table.show(hugepages)
end
def print_monitoring_table(x, y, title)
puts
CLIHelper.print_header(title, true)
puts
table = CLIHelper::ShowTable.new(nil, self) do
column :TIME, 'Timestamp', :size => 8, :left => false do |d|
d['TIME']
end
column :VALUE, 'Value', :size => 8, :left => false do |d|
d['VALUE']
end
default :TIME, :VALUE
end
data = []
x.zip(y) do |x_v, y_v|
data << { 'TIME' => x_v, 'VALUE' => y_v }
end
table.show(data)
end
end

View File

@ -97,6 +97,52 @@ CommandParser::CmdParser.new(ARGV) do
CREAT_OPTIONS = [IM, VMM, OneClusterHelper::CLUSTER, TYPE]
SYNC_OPTIONS = [OneClusterHelper::CLUSTER, FORCE, SSH]
########################################################################
# Monitoring PLOT options
########################################################################
START_T = {
:name => 'start',
:large => '--start date',
:description => 'Start date to show data',
:format => String
}
END_T = {
:name => 'end',
:large => '--end date',
:description => 'End date to show data',
:format => String
}
UNIT = {
:name => 'unit',
:large => '--unit unit',
:description => 'Unit to format data',
:format => String
}
TABLE = {
:name => 'table',
:large => '--table',
:description => 'Show monitoring information in table format'
}
N_ELEMS = {
:name => 'n_elems',
:large => '--n elements',
:description => 'Number of records to show',
:format => Integer
}
CSV_WITH_SEP = {
:name => 'csv',
:large => '--csv separator',
:description => 'Show data in CSV format',
:format => String
}
PLOT_OPTS = [START_T, END_T, UNIT, TABLE, N_ELEMS, CSV_WITH_SEP]
########################################################################
# Formatters for arguments
########################################################################
@ -304,4 +350,25 @@ CommandParser::CmdParser.new(ARGV) do
:options => [OneClusterHelper::CLUSTER] do
helper.forceupdate(args[0], options)
end
monitoring_desc = <<-EOT.unindent
Show monitoring metrics in a graphic
EOT
command :monitoring,
monitoring_desc,
:hostid,
:attr,
:options => PLOT_OPTS do
helper.perform_action(args[0], options, 'monitoring') do |host|
rc = host.info
if OpenNebula.is_error?(rc)
STDERR.puts rc.message
exit(-1)
end
helper.monitoring(host, args[1], options)
end
end
end