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:
parent
7af497226f
commit
49e1901a5c
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user