app/controllers/application_controller/performance.rb
module ApplicationController::Performance
extend ActiveSupport::Concern
CHARTS_REPORTS_FOLDER = Rails.root.join("product", "charts", "miq_reports")
CHARTS_LAYOUTS_FOLDER = Rails.root.join("product", "charts", "layouts")
# Process changes to performance charts
# Accessible from VM details --> Monitoring/utilization --> Performance
def perf_chart_chooser
assert_privileges("perf_reload")
@record = identify_tl_or_perf_record
@perf_record = @record.kind_of?(MiqServer) ? @record.vm : @record # Use related server vm record
unless params[:task_id] # first time thru, gather options changed by the user
@perf_options.update_from_params(params)
perf_set_or_fix_dates(!params[:perf_typ]) if @perf_options[:chart_type] == :performance
end
if !@no_util_data && @perf_options[:chart_type] == :performance
perf_gen_data # generate the task
return unless @charts # no charts got created (first time thru async rpt gen)
end
if @perf_options[:no_rollups]
add_flash(_("No Hourly or Daily data is available, real time data " \
"from the Most Recent Hour is being displayed"), :warning)
end
add_flash(_('No Daily data is available'), :warning) if @perf_options[:no_daily] && @perf_options[:typ] == 'Daily'
button_changes =
if %w[host vm vm_or_template].include?(params[:controller])
pfx = params[:controller] == "vm_or_template" ? "vm_" : ""
if @perf_options[:typ] == "realtime"
{
"#{pfx}perf_refresh" => {:hidden => false, :enabled => true},
"#{pfx}perf_reload" => {:hidden => false, :enabled => true}
}
else
{
"#{pfx}perf_refresh" => {:hidden => true},
"#{pfx}perf_reload" => {:hidden => true}
}
end
end
render :update do |page|
page << javascript_prologue
page << if @parent_chart_data
'ManageIQ.charts.chartData = ' + {
"candu" => @chart_data,
"parent" => @parent_chart_data
}.to_json + ';'
else
'ManageIQ.charts.chartData = ' + {
"candu" => @chart_data
}.to_json + ';'
end
# Turn on/off individual buttons.
page << "ManageIQ.toolbars.applyChanges(#{button_changes.to_json})" if button_changes.present?
page.replace("flash_msg_div", :partial => "layouts/flash_msg")
page << "miqScrollTop();" if @flash_array.present?
page.replace("perf_options_div", :partial => "layouts/perf_options")
page.replace("candu_charts_div", :partial => "layouts/perf_charts",
:locals => {:chart_data => @chart_data, :chart_set => "candu"})
unless @no_util_data
page << js_build_calendar(@perf_options.to_calendar)
page << ManageIQ::Reporting::Charting.js_load_statement
end
page << 'miqSparkle(false);'
if request.parameters["controller"] == "storage"
page << javascript_disable_field('perf_typ') if @perf_options[:cat]
page << javascript_disable_field('perf_cat') if @perf_options[:typ] == 'Daily' && @perf_options[:no_daily]
end
end
end
# Generate a chart with the top CIs for a given timestamp
def perf_top_chart
return if perfmenu_click?
@record = identify_tl_or_perf_record
@perf_record = @record.kind_of?(MiqServer) ? @record.vm : @record # Use related server vm record
if params[:menu_choice]
chart_click_data = parse_chart_click(params[:menu_choice])
report = @sb[:chart_reports].kind_of?(Array) ? @sb[:chart_reports][chart_click_data.chart_index] : @sb[:chart_reports]
data_row = report.table.data[chart_click_data.data_index]
top_ids = if @perf_options[:cat]
data_row["assoc_ids_#{report.extras[:group_by_tags][chart_click_data.legend_index]}"][chart_click_data.model.downcase.to_sym][:on]
else
data_row["assoc_ids"][chart_click_data.model.downcase.to_sym][:on]
end
@perf_options[:top_model] = chart_click_data.model.singularize.capitalize
@perf_options[:top_type] = chart_click_data.type # day or hour
@perf_options[:top_ts] = data_row["timestamp"].utc
@perf_options[:top_ids] = top_ids
end
@perf_options[:index] = params[:chart_idx] == "clear" ? nil : params[:chart_idx] if params[:chart_idx]
@showtype = "performance"
if request.xml_http_request? # Is this an Ajax request?
perf_gen_top_data # Generate top data
return unless @charts # Return if no charts got created (first time thru async rpt gen)
render :update do |page|
page << javascript_prologue
page << 'ManageIQ.charts.chartData = ' + {"candu" => @chart_data}.to_json + ';'
page.replace("candu_charts_div",
:partial => "layouts/perf_charts",
:locals => {:chart_data => @chart_data, :chart_set => "candu"})
page << ManageIQ::Reporting::Charting.js_load_statement
page << 'miqSparkle(false);'
end
else
drop_breadcrumb(
:name => params[:bc],
:url => url_for_only_path(
:id => @perf_record.id,
:action => "perf_top_chart",
:bc => params[:bc],
:escape => false
)
)
@ajax_action = "perf_top_chart"
render :action => "show"
end
end
private
def perf_breadcrumb
name = @perf_record.respond_to?(:evm_display_name) ? @perf_record.evm_display_name : @perf_record.name
url = url_for_only_path(
:action => "show",
:id => @perf_record,
:display => "performance",
:refresh => "n"
)
if @perf_options.cat
drop_breadcrumb(
:name => _("%{name} Capacity & Utilization (by %{option}:%{model})") %
{
:name => name,
:option => @perf_options.cats[@perf_options.cat_model],
:model => @perf_options.cat
},
:url => url
)
else
drop_breadcrumb(:name => _("%{name} Capacity & Utilization") % {:name => name}, :url => url)
end
end
# Initiate the backend refresh of realtime c&u data
def perf_refresh_data
assert_privileges("perf_refresh")
@record = identify_tl_or_perf_record
@perf_record = @record.kind_of?(MiqServer) ? @record.vm : @record # Use related server vm record
@perf_record.perf_capture_realtime_now
add_flash(_("Refresh of recent C&U data has been initiated"))
end
# Correct any date that is out of the date/range or not allowed in a profile
def perf_set_or_fix_dates(allow_interval_override = true)
start_date, end_date = @perf_record.first_and_last_capture('hourly')
start_date, end_date = @perf_record.first_and_last_capture('realtime') if (realtime = start_date.nil?)
if start_date.nil? && realtime
add_flash(_("No Utilization data available"), :warning)
@no_util_data = true
return
elsif realtime
@perf_options[:typ] = "realtime"
@perf_options[:no_rollups] = true
end
@perf_options.set_dates(start_date, end_date, allow_interval_override)
end
def skip_days_from_time_profile(time_profile_days)
(1..7).to_a.delete_if do |d|
# time_profile_days has 0 for sunday, skip_days needs 7 for sunday
time_profile_days.include?(d % 7)
end
end
# Handle actions for performance chart context menu clicks
def perf_menu_click
# Parse the clicked item to get indexes and selection variables
chart_click_data = parse_chart_click(params[:menu_click])
# Swap in 'Instances' for 'VMs' in AZ breadcrumbs (poor man's cloud/infra split hack)
bc_model = %w[availability_zone host_aggregate].include?(request.parameters['controller']) && chart_click_data.model == 'VMs' ? 'Instances' : chart_click_data.model
report = @sb[:chart_reports].kind_of?(Array) ? @sb[:chart_reports][chart_click_data.chart_index] : @sb[:chart_reports]
data_row = report.table.data[chart_click_data.data_index]
ts = data_row["timestamp"].in_time_zone(@perf_options[:tz]) # Grab the timestamp from the row in selected tz
request_displayed, unavailability_reason = process_chart_action(chart_click_data, data_row, report, ts, bc_model)
if request_displayed
return
elsif unavailability_reason.present?
add_flash(unavailability_reason, :warning)
else
add_flash(_("Unknown error has occurred"), :error)
end
javascript_flash(:spinner_off => true)
end
# Params:
# chart_click_data - information about clicked position (cmd, model, type)
# data_row - a single datapoint from the data that the chart was
# generated from
# report - instance of MiqReport that is displayed by the graph
# ts - timestamp of the clicked datapoint
# bc_model - information about the model described by the chart used
# for creating breadcrumbs
# Returns:
# an information about whether the request was displayed, or the reason why
# it was not
#
def process_chart_action(chart_click_data, data_row, report, ts, bc_model)
request_displayed, unavailability_reason =
case chart_click_data.cmd
when "Display"
process_display_click(chart_click_data, data_row, report, ts, bc_model)
when "Timeline"
process_timeline_click(chart_click_data, data_row, ts)
when "Chart"
process_chart_click(chart_click_data, data_row, report, ts, bc_model)
else
[false, _("Chart menu selection not yet implemented")]
end
[request_displayed, unavailability_reason]
end
def process_display_click(chart_click_data, data_row, report, ts, bc_model)
request_displayed, unavailability_reason =
if chart_click_data.model == "Current" && chart_click_data.type == "Top"
display_current_top(data_row)
elsif chart_click_data.type == "bytag"
display_by_tag(chart_click_data, data_row, report, ts, bc_model)
else
display_selected(chart_click_data, ts, data_row, bc_model)
end
[request_displayed, unavailability_reason]
end
def process_timeline_click(chart_click_data, data_row, ts)
request_displayed, unavailability_reason =
if chart_click_data.model == "Current"
timeline_current(chart_click_data, ts)
elsif chart_click_data.model == "Selected"
timeline_selected(chart_click_data, data_row, ts)
end
[request_displayed, unavailability_reason]
end
def process_chart_click(chart_click_data, data_row, report, ts, bc_model)
request_displayed, unavailability_reason =
if chart_click_data.model == "Current" && chart_click_data.type == "Hourly"
chart_current_hourly(ts)
elsif chart_click_data.model == "Current" && chart_click_data.type == "Daily"
chart_current_daily
elsif chart_click_data.model == "Selected"
chart_selected(chart_click_data, data_row, ts)
elsif chart_click_data.type.starts_with?("top") && @perf_options[:cat]
chart_top_by_tag(chart_click_data, data_row, report, ts, bc_model)
elsif chart_click_data.type.starts_with?("top")
chart_top(chart_click_data, data_row, ts, bc_model)
end
[request_displayed, unavailability_reason]
end
# display the CI selected from a Top chart
def display_current_top(data_row)
# Record needs to exist and user must have rights to access it
record = find_record_with_rbac(data_row["resource_type"].constantize, data_row["resource_id"])
javascript_redirect(:controller => data_row["resource_type"].underscore,
:action => "show",
:id => record.id,
:escape => false)
[true, nil]
end
# display selected resources from a tag chart
def display_by_tag(chart_click_data, data_row, report, ts, bc_model)
top_ids = data_row["assoc_ids_#{report.extras[:group_by_tags][chart_click_data.legend_index]}"][chart_click_data.model.downcase.to_sym][:on]
bc_tag = breadcrumb_tag(report, chart_click_data.legend_index)
if top_ids.blank?
message = _("No %{tag} %{model} were running %{time}") % {:tag => bc_tag, :model => bc_model, :time => date_time_running_msg(chart_click_data.type, ts)}
[false, message]
else
bc = if request.parameters["controller"] == "storage"
_("%{model} (%{tag} %{time})") %
{:tag => bc_tag, :model => bc_model, :time => date_time_running_msg(chart_click_data.type, ts)}
else
_("%{model} (%{tag} running %{time})") %
{:tag => bc_tag, :model => bc_model, :time => date_time_running_msg(chart_click_data.type, ts)}
end
javascript_redirect(:controller => chart_click_data.model.downcase.singularize,
:action => "show_list",
:menu_click => params[:menu_click],
:sb_controller => request.parameters["controller"],
:bc => bc,
:escape => false)
[true, nil]
end
end
# display selected resources
def display_selected(chart_click_data, ts, data_row, bc_model)
dt = @perf_options[:typ] == "Hourly" ? "on #{ts.to_date} at #{ts.strftime("%H:%M:%S %Z")}" : "on #{ts.to_date}"
state = chart_click_data.type == "on" ? _("running") : _("stopped")
if data_row["assoc_ids"][chart_click_data.model.downcase.to_sym][chart_click_data.type.to_sym].blank?
message = _("No %{model} were %{state} %{time}") % {:model => chart_click_data.model, :state => state, :time => dt}
[false, message]
else
bc = request.parameters["controller"] == "storage" ? "#{bc_model} #{dt}" : "#{bc_model} #{state} #{dt}"
javascript_redirect(:controller => chart_click_data.model.downcase.singularize,
:action => "show_list",
:menu_click => params[:menu_click],
:sb_controller => request.parameters["controller"],
:bc => bc,
:no_checkboxes => true,
:escape => false)
[true, nil]
end
end
# display timeline for the current CI
def timeline_current(chart_click_data, ts)
@record = identify_tl_or_perf_record
@perf_record = @record.kind_of?(MiqServer) ? @record.vm : @record # Use related server vm record
new_opts = tl_session_data(request.parameters["controller"]) || ApplicationController::Timelines::Options.new
new_opts[:model] = @perf_record.class.base_class.to_s
new_opts.date.typ = chart_click_data.type
new_opts.date.end_date = @perf_options[:daily_date] if chart_click_data.type == "Daily"
new_opts.date.end_date = [ts.month, ts.day, ts.year].join("/") if chart_click_data.type == "Hourly"
new_opts[:tl_show] = "timeline"
set_tl_session_data(new_opts, request.parameters["controller"])
f = @perf_record.first_event
if f.nil?
message = if new_opts[:model] == "EmsCluster"
_("No events available for this Cluster")
else
_("No events available for this %{model}") % {:model => new_opts[:model]}
end
[false, message]
elsif @record.kind_of?(MiqServer) # For server charts in OPS
change_tab("diagnostics_timelines") # Switch to the Timelines tab
[true, nil]
else
if @explorer
@_params[:id] = @perf_record.id
@_params[:refresh] = "n"
show_timeline
else
javascript_redirect(:id => @perf_record.id,
:action => "show",
:display => "timeline",
:controller => model_to_controller(@perf_record),
:refresh => "n",
:escape => false)
end
[true, nil]
end
end
# display timeline for the selected CI
def timeline_selected(chart_click_data, data_row, ts)
@record = find_record_with_rbac(data_row["resource_type"].constantize, data_row["resource_id"])
return [true, nil] unless @record
controller = data_row["resource_type"].underscore
new_opts = tl_session_data(controller) || ApplicationController::Timelines::Options.new
new_opts[:model] = data_row["resource_type"]
new_opts.date.typ = chart_click_data.type
new_opts.date.daily = @perf_options[:daily_date] if chart_click_data.type == "Daily"
new_opts.date.hourly = [ts.month, ts.day, ts.year].join("/") if chart_click_data.type == "Hourly"
new_opts[:tl_show] = "timeline"
set_tl_session_data(new_opts, controller)
f = @record.first_event
if f.nil?
message = if chart_click_data.model == "EmsCluster"
_("No events available for this Cluster")
else
_("No events available for this %{model}") % {:model => chart_click_data.model}
end
[false, message]
elsif @record.kind_of?(MiqServer) # For server charts in OPS
change_tab("diagnostics_timelines") # Switch to the Timelines tab
[true, nil]
else
if @explorer
@_params[:id] = data_row["resource_id"]
@_params[:refresh] = "n"
show_timeline
elsif data_row["resource_type"] == "VmOrTemplate"
tree_node_id = TreeBuilder.build_node_id(@record)
session[:exp_parms] = {:display => "timeline", :refresh => "n", :id => tree_node_id}
javascript_redirect(:controller => data_row["resource_type"].underscore.downcase.singularize,
:action => "explorer")
else
javascript_redirect(:controller => data_row["resource_type"].underscore.downcase.singularize,
:action => "show",
:display => "timeline",
:id => data_row["resource_id"],
:refresh => "n",
:escape => false)
end
[true, nil]
end
end
# create hourly chart for selected day
def chart_current_hourly(ts)
@record = identify_tl_or_perf_record
@perf_record = @record.kind_of?(MiqServer) ? @record.vm : @record # Use related server vm record
@perf_options[:typ] = "Hourly"
@perf_options[:hourly_date] = [ts.month, ts.day, ts.year].join("/")
perf_set_or_fix_dates unless params[:task_id] # Set dates if first time thru
perf_gen_data
return [true, nil] unless @charts # Return if no charts got created (first time thru async rpt gen)
render :update do |page|
page << javascript_prologue
page << if @parent_chart_data
'ManageIQ.charts.chartData = ' + {
"candu" => @chart_data,
"parent" => @parent_chart_data
}.to_json + ';'
else
'ManageIQ.charts.chartData = ' + {
"candu" => @chart_data
}.to_json + ';'
end
page.replace("perf_options_div", :partial => "layouts/perf_options")
page.replace("candu_charts_div", :partial => "layouts/perf_charts", :locals => {:chart_data => @chart_data, :chart_set => "candu"})
page << js_build_calendar(@perf_options.to_calendar)
page << ManageIQ::Reporting::Charting.js_load_statement
page << 'miqSparkle(false);'
end
[true, nil]
end
# go back to the daily chart
def chart_current_daily
@record = identify_tl_or_perf_record
@perf_record = @record.kind_of?(MiqServer) ? @record.vm : @record # Use related server vm record
@perf_options[:typ] = "Daily"
perf_set_or_fix_dates(false) unless params[:task_id] # Set dates if first time thru
perf_gen_data
return [true, nil] unless @charts # Return if no charts got created (first time thru async rpt gen)
render :update do |page|
page << javascript_prologue
page << if @parent_chart_data
'ManageIQ.charts.chartData = ' + {
"candu" => @chart_data,
"parent" => @parent_chart_data
}.to_json + ';'
else
'ManageIQ.charts.chartData = ' + {
"candu" => @chart_data
}.to_json + ';'
end
page.replace("perf_options_div", :partial => "layouts/perf_options")
page.replace("candu_charts_div", :partial => "layouts/perf_charts", :locals => {:chart_data => @chart_data, :chart_set => "candu"})
page << js_build_calendar(@perf_options.to_calendar)
page << ManageIQ::Reporting::Charting.js_load_statement
page << 'miqSparkle(false);'
end
[true, nil]
end
# Create daily/hourly chart for selected CI
def chart_selected(chart_click_data, data_row, ts)
@record = find_record_with_rbac(data_row["resource_type"].constantize, data_row["resource_id"])
return [true, nil] unless @record
# Set the perf options in the selected controller's sandbox
cont = data_row["resource_type"].underscore.downcase.to_sym
session[:sandboxes][cont] ||= {}
session[:sandboxes][cont][:perf_options] ||= {}
# Copy general items from the current perf_options
session[:sandboxes][cont][:perf_options][:index] = @perf_options[:index]
session[:sandboxes][cont][:perf_options][:tz] = @perf_options[:tz]
session[:sandboxes][cont][:perf_options][:time_profile] = @perf_options[:time_profile]
session[:sandboxes][cont][:perf_options][:time_profile_days] = @perf_options[:time_profile_days]
session[:sandboxes][cont][:perf_options][:time_profile_tz] = @perf_options[:time_profile_tz]
# Set new perf options based on what was selected
session[:sandboxes][cont][:perf_options][:model] = data_row["resource_type"]
session[:sandboxes][cont][:perf_options][:typ] = chart_click_data.type
session[:sandboxes][cont][:perf_options][:daily_date] = @perf_options[:daily_date] if chart_click_data.type == "Daily"
session[:sandboxes][cont][:perf_options][:days] = @perf_options[:days] if chart_click_data.type == "Daily"
session[:sandboxes][cont][:perf_options][:hourly_date] = [ts.month, ts.day, ts.year].join("/") if chart_click_data.type == "Hourly"
if data_row["resource_type"] == "VmOrTemplate"
prefix = TreeBuilder.get_prefix_for_model(@record.class.base_model)
tree_node_id = "#{prefix}-#{@record.id}" # Build the tree node id
session[:exp_parms] = {:display => "performance", :refresh => "n", :id => tree_node_id}
javascript_redirect(:controller => data_row["resource_type"].underscore.downcase.singularize,
:action => "explorer")
else
javascript_redirect(:controller => data_row["resource_type"].underscore.downcase.singularize,
:action => "show",
:id => data_row["resource_id"],
:display => "performance",
:refresh => "n",
:escape => false)
end
[true, nil]
end
# create top chart for selected timestamp/model by tag
def chart_top_by_tag(chart_click_data, data_row, report, ts, bc_model)
@record = identify_tl_or_perf_record
@perf_record = @record.kind_of?(MiqServer) ? @record.vm : @record # Use related server vm record
top_ids = data_row["assoc_ids_#{report.extras[:group_by_tags][chart_click_data.legend_index]}"][chart_click_data.model.downcase.to_sym][:on]
bc_tag = breadcrumb_tag(report, chart_click_data.legend_index)
if top_ids.blank?
message = _("No %{tag} %{model} were running %{time}") % {:tag => bc_tag, :model => bc_model, :time => date_time_running_msg(chart_click_data.type, ts)}
[false, message]
else
javascript_redirect(:id => @perf_record.id,
:action => "perf_top_chart",
:menu_choice => params[:menu_click],
:bc => "#{@perf_record.name} top #{bc_model} (#{bc_tag} #{date_time_running_msg(chart_click_data.type, ts)})",
:escape => false)
[true, nil]
end
end
# create top chart for selected timestamp/model
def chart_top(chart_click_data, data_row, ts, bc_model)
@record = identify_tl_or_perf_record
@perf_record = @record.kind_of?(MiqServer) ? @record.vm : @record # Use related server vm record
top_ids = data_row["assoc_ids"][chart_click_data.model.downcase.to_sym][:on]
if top_ids.blank?
message = _("No %{model} were running %{time}") % {:model => chart_click_data.model, :time => date_time_running_msg(chart_click_data.type, ts)}
[false, message]
else
javascript_redirect(:id => @perf_record.id,
:action => "perf_top_chart",
:menu_choice => params[:menu_click],
:bc => "#{@perf_record.name} top #{bc_model} (#{date_time_running_msg(chart_click_data.type, ts)})",
:escape => false)
[true, nil]
end
end
def date_time_running_msg(typ, timestamp)
if typ == "tophour"
_("on %{date} at %{time}") %
{:date => timestamp.to_date,
:time => timestamp.strftime("%h:%m:%s %z")}
else
_("on %{date}") % {:date => timestamp.to_date}
end
end
# Load a chart miq_report object from YML
def perf_get_chart_rpt(chart_rpt)
MiqReport.new(YAML.load(File.read("#{CHARTS_REPORTS_FOLDER}/#{chart_rpt}.yaml")))
end
# Load a chart layout from YML
def perf_get_chart_layout(layout, fname = nil)
charts = ChartsLayoutService.layout(@perf_record, CHARTS_LAYOUTS_FOLDER, layout, fname)
@perf_options[:index] = nil unless @perf_options[:index].nil? || charts[@perf_options[:index].to_i]
charts
end
# Init options for performance charts
def perf_gen_init_options(refresh = nil)
@perf_record = @record.kind_of?(MiqServer) ? @record.vm : @record # Use related server vm record
unless refresh == "n" || params[:refresh] == "n"
@perf_options = Options.new
tzs = TimeProfile.rollup_daily_metrics.all_timezones
@perf_options[:tz_daily] = tzs.include?(session[:user_tz]) ? session[:user_tz] : tzs.first
@perf_options[:typ] = "Daily"
# TODO: Remove next line once daily is available for Vmdb tables
@perf_options[:typ] = "Hourly" if @perf_record.class.name.starts_with?("Vmdb")
@perf_options[:days] = "7"
@perf_options[:rt_minutes] = 15.minutes
@perf_options[:model] = @perf_record.class.base_class.to_s
end
@perf_options[:rt_minutes] ||= 15.minutes
get_time_profiles(@perf_record) # Get time profiles list (global and user specific). Pass record so that profiles can be limited to its region.
# Get the time zone from the time profile, if one is in use
if @perf_options[:time_profile]
tp = TimeProfile.find_by(:id => @perf_options[:time_profile])
set_time_profile_vars(tp, @perf_options)
else
set_time_profile_vars(selected_time_profile_for_pull_down, @perf_options)
end
# Get start/end dates in selected timezone, but only right before displaying the chart options screen
perf_set_or_fix_dates if params[:action] == "perf_chart_chooser"
@perf_options[:days] ||= "7"
@perf_options[:ght_type] ||= "hybrid"
@perf_options[:chart_type] = :performance
perf_breadcrumb
@ajax_action = "perf_chart_chooser"
end
# Generate performance data for a model's charts
def perf_gen_data
perf_breadcrumb
unless @perf_options[:typ] == "realtime"
if @perf_options[:cat] # If a category was chosen, generate charts by tag
perf_gen_tag_data
return
end
end
# First time thru, kick off the report generate task
params[:task_id] ? perf_gen_data_after_wait : perf_gen_data_before_wait
end
# Generate performance data for a model's charts - kick off report task
def perf_gen_data_before_wait
interval_type = @perf_options[:typ].downcase
case interval_type
when "hourly", "daily"
# Set from/to datetimes
if interval_type == "hourly"
from_dt = create_time_in_utc(@perf_options[:hourly_date] + " 00", @perf_options[:tz]) # Get tz 12am in UTC
to_dt = create_time_in_utc(@perf_options[:hourly_date] + " 23", @perf_options[:tz]) # Get tz 11pm in UTC
elsif interval_type == "daily"
f = Date.parse(@perf_options[:daily_date]) - (@perf_options[:days].to_i - 1)
st = @perf_options[:sdate_daily]
s = Date.parse("#{st.year}/#{st.month}/#{st.day}")
f = s if f < s # Use later date
from_dt = create_time_in_utc("#{f.year}/#{f.month}/#{f.day} 00", @perf_options[:tz]) # Get tz 12am in UTC
to_dt = create_time_in_utc("#{@perf_options[:daily_date]} 23", @perf_options[:tz]) # Get tz 11pm in UTC
end
# Get the report definition (yaml) and set the where clause based on the record type
# Doing VIM performance on a normal CI
suffix = @perf_record.kind_of?(AvailabilityZone) || @perf_record.kind_of?(HostAggregate) ? "_cloud" : "" # Get special cloud version with 'Instances' headers
rpt = perf_get_chart_rpt("vim_perf_#{interval_type}#{suffix}")
rpt.where_clause = [
"resource_type = ? and resource_id = ? and timestamp >= ? and timestamp <= ? and capture_interval_name = ?",
@perf_options[:model],
@perf_record.id,
from_dt,
to_dt,
interval_type
]
rpt.tz = @perf_options[:tz]
rpt.time_profile_id = @perf_options[:time_profile]
when "realtime"
_, to_dt = @perf_record.first_and_last_capture("realtime")
from_dt = to_dt.nil? ? nil : to_dt - @perf_options[:rt_minutes]
rpt = perf_get_chart_rpt("vim_perf_realtime")
rpt.tz = @perf_options[:tz]
rpt.extras = {}
rpt.extras[:realtime] = true
@perf_options[:range] = if to_dt.nil?
nil
else
_("%{date_from} to %{date_to}") %
{:date_from => format_timezone(from_dt, @perf_options[:tz], "gtl"),
:date_to => format_timezone(to_dt, @perf_options[:tz], "gtl")}
end
rpt.where_clause = [
"resource_type = ? and resource_id = ? and timestamp >= ? and timestamp <= ? and capture_interval_name = ?",
@perf_options[:model],
@perf_record.id,
from_dt,
to_dt,
"realtime"
]
end
rpts = [rpt]
if perf_parent? # Build the parent report, if asked for
p_rpt = Marshal.load(Marshal.dump(rpt)) # Deep clone the main report
p_rpt.where_clause[1] = @perf_options[:parent]
p_rpt.where_clause[2] = @perf_record.send(ApplicationHelper::VALID_PERF_PARENTS[@perf_options[:parent]]).id
rpts.push(p_rpt)
end
initiate_wait_for_task(:task_id => MiqReport.async_generate_tables(:reports => rpts, :userid => session[:userid]))
end
# Generate performance data for a model's charts - generate charts from report task results
def perf_gen_data_after_wait
miq_task = MiqTask.find(params[:task_id]) # Not first time, read the task record
rpt = miq_task.task_results.first # Grab the only report in the array of reports returned
p_rpt = miq_task.task_results[1] if perf_parent? # Grab the parent report in the array of reports returned
miq_task.destroy # Get rid of the task and results
@charts, @chart_data = perf_gen_charts(rpt, @perf_options)
if perf_parent?
@parent_charts, @parent_chart_data = perf_gen_charts(p_rpt, @perf_options, true)
end
@sb[:chart_reports] = rpt # Hang on to the report data for these charts
@html = perf_report_to_html
@p_html = perf_report_to_html(p_rpt, @parent_charts[0]) if perf_parent?
end
# Return the column in the chart that starts with "trend_"
def perf_get_chart_trendcol(chart)
chart[:columns].each do |c|
return c if c.starts_with?("trend_")
end
nil
end
# Remove columns from chart based on model and/or options
def perf_remove_chart_cols(chart)
if @perf_options[:model] == "Host" && !@perf_record.owning_cluster.nil?
chart[:columns].delete_if { |col| col.include?("reserved") }
chart[:trends]&.delete_if { |trend| trend.include?("reserved") }
end
if chart[:title].include?("by Type") && @perf_options[:vmtype] && @perf_options[:vmtype] != "<All>"
chart[:columns].delete_if do |col|
!col.include?("_" + @perf_options[:vmtype])
end
end
end
# Generate performance data by tag for a model's charts
def perf_gen_tag_data
@perf_options[:chart_type] = :performance
# First time thru, kick off the report generate task
params[:task_id] ? perf_gen_tag_data_after_wait : perf_gen_tag_data_before_wait
end
# Generate performance data by tag - kick off report task
def perf_gen_tag_data_before_wait
case @perf_options[:typ]
when "Hourly"
from_dt = create_time_in_utc(@perf_options[:hourly_date] + " 00:00:00", @perf_options[:tz]) # Get tz 12am in UTC
to_dt = create_time_in_utc(@perf_options[:hourly_date] + " 23:59:59", @perf_options[:tz]) # Get tz 11:59pm in UTC
rpt = perf_get_chart_rpt("vim_perf_tag_hourly")
rpt.performance = {:group_by_category => @perf_options[:cat]}
rpt.tz = @perf_options[:tz]
rpt.time_profile_id = @perf_options[:time_profile]
rpt.where_clause = ["resource_type = ? and resource_id = ? and timestamp >= ? and timestamp <= ? and capture_interval_name = ?",
@perf_record.class.base_class.name,
@perf_record.id,
from_dt,
to_dt,
'hourly']
when "Daily"
f = Date.parse(@perf_options[:daily_date]) - (@perf_options[:days].to_i - 1)
from_dt = create_time_in_utc("#{f.year}/#{f.month}/#{f.day} 00", @perf_options[:tz]) # Get tz 12am in UTC
to_dt = create_time_in_utc("#{@perf_options[:daily_date]} 23", @perf_options[:tz]) # Get tz 11pm in UTC
rpt = perf_get_chart_rpt("vim_perf_tag_daily")
rpt.time_profile_id = @perf_options[:time_profile]
chart_layout = perf_get_chart_layout("daily_tag_charts", @perf_options[:model]) if @perf_options[:index]
if @perf_options[:index] # If only looking at 1 chart, trim report columns for less daily rollups
chart = chart_layout[@perf_options[:index].to_i]
perf_trim_report_cols(rpt, chart)
end
rpt.tz = @perf_options[:tz]
rpt.performance = {:group_by_category => @perf_options[:cat]}
rpt.where_clause = ["resource_type = ? and resource_id = ? and timestamp >= ? and timestamp <= ?",
@perf_record.class.base_class.name,
@perf_record.id,
from_dt,
to_dt]
end
initiate_wait_for_task(:task_id => rpt.async_generate_table(:userid => session[:userid],
:session_id => request.session_options[:id],
:cat_model => @perf_options[:cat_model],
:mode => "charts"))
end
def prepare_perf_tag_chart(chart, rpt, cat_desc)
# Remove opposite menu items
chart[:menu].delete_if { |m| m.include?(@perf_options[:cat_model] == "Host" ? "VMs for" : "Hosts for") }
# Substitue category description + ':<series>' into menus
chart[:menu].each { |m| m.gsub!(/<cat>/, cat_desc + " <series>") }
# Grab the first (and should be only) chart column
col = chart[:columns].first
# Create the new chart columns for each tag
chart[:columns] = rpt.extras[:group_by_tags].collect { |t| col + "_" + t }
end
def gen_perf_chart(chart, rpt, idx, zoom_action)
options = chart.merge(
:zoom_url => zoom_url = perf_zoom_url(zoom_action, idx.nil? ? 'clear' : idx.to_s),
:link_data_url => "javascript:miqChartLinkData( _col_, _row_, _value_, _category_, _series_, _id_ )",
:axis_skip => 3
)
if idx.nil?
options[:width] = 1000
options[:height] = 700
end
process_chart_trends(chart, rpt, options)
generated_chart = perf_gen_chart(rpt, options).merge(:menu => chart[:menu], :zoom_url => zoom_url)
# Grab title from chart in case formatting added units
chart[:title] = rpt.title
generated_chart
end
# Generate performance data by tag - generate charts from report task results
def perf_gen_tag_data_after_wait
miq_task = MiqTask.find(params[:task_id]) # Not first time, read the task record
rpt = miq_task.miq_report_result.report_results # Grab the report object from the blob
miq_task.destroy # Get rid of the task and results
charts = []
chart_data = []
cat_desc = Classification.lookup_by_name(@perf_options[:cat]).description
layout_name = case @perf_options[:typ]
when "Hourly" then 'hourly_tag_charts'
when "Daily" then 'daily_tag_charts'
end
chart_layout = perf_get_chart_layout(layout_name, @perf_options[:model])
if @perf_options[:index]
chart_index = @perf_options[:index].to_i
chart = chart_layout[chart_index]
prepare_perf_tag_chart(chart, rpt, cat_desc)
chart_data.push(gen_perf_chart(chart, rpt, nil, 'perf_chart_chooser'))
charts.push(chart)
else
chart_layout.each_with_index do |chart, idx|
prepare_perf_tag_chart(chart, rpt, cat_desc)
chart_data.push(gen_perf_chart(chart, rpt, idx, 'perf_chart_chooser'))
charts.push(chart)
end
end
@sb[:chart_reports] = rpt # Hang on to the report data for these charts
@chart_data = chart_data
@charts = charts
@html = perf_report_to_html
end
# Generate top 10 chart data
def perf_gen_top_data
if params[:task_id]
perf_gen_top_data_after_wait
else # First time thru, kick off the report generate task
perf_gen_top_data_before_wait
end
end
# Generate top 10 chart data - kick off report task
def perf_gen_top_data_before_wait
@perf_options[:ght_type] ||= "hybrid"
@perf_options[:chart_type] = :performance
cont_plus_model = request.parameters["controller"] + "-" + @perf_options[:top_model]
metric_model = @perf_options[:top_model] == "Vm" ? "VmOrTemplate" : @perf_options[:top_model]
rpts = [] # Store all reports for the async task to work on
case @perf_options[:top_type]
when "topday"
chart_layout = perf_get_chart_layout("day_top_charts", cont_plus_model)
if @perf_options[:index]
rpt = perf_get_chart_rpt("vim_perf_topday")
rpt.tz = @perf_options[:tz]
rpt.time_profile_id = @perf_options[:time_profile]
rpt.where_clause = ["resource_type = ? and resource_id IN (?) and timestamp >= ? and timestamp < ?",
metric_model,
@perf_options[:top_ids],
@perf_options[:top_ts].utc,
@perf_options[:top_ts].utc + 1.day]
rpts.push(rpt)
else
chart_layout.each_with_index do |chart, _idx|
next if chart.nil?
rpt = perf_get_chart_rpt("vim_perf_topday")
rpt.tz = @perf_options[:tz]
rpt.time_profile_id = @perf_options[:time_profile]
rpt.where_clause = ["resource_type = ? and resource_id IN (?) and timestamp >= ? and timestamp < ?",
metric_model,
@perf_options[:top_ids],
@perf_options[:top_ts].utc,
@perf_options[:top_ts].utc + 1.day]
rpts.push(rpt)
end
end
when "tophour"
chart_layout = perf_get_chart_layout("hour_top_charts", cont_plus_model)
if @perf_options[:index]
rpt = perf_get_chart_rpt("vim_perf_tophour")
rpt.tz = @perf_options[:tz]
rpt.time_profile_id = @perf_options[:time_profile]
rpt.where_clause = ["resource_type = ? and resource_id IN (?) and timestamp = ? and capture_interval_name = ?",
metric_model,
@perf_options[:top_ids],
@perf_options[:top_ts].utc,
'hourly']
rpts.push(rpt)
else
chart_layout.each_with_index do |chart, _idx|
next if chart.nil?
rpt = perf_get_chart_rpt("vim_perf_tophour")
rpt.tz = @perf_options[:tz]
rpt.time_profile_id = @perf_options[:time_profile]
rpt.where_clause = ["resource_type = ? and resource_id IN (?) and timestamp = ? and capture_interval_name = ?",
metric_model,
@perf_options[:top_ids],
@perf_options[:top_ts].utc,
'hourly']
rpts.push(rpt)
end
end
end
if rpts.length == 1
initiate_wait_for_task(:task_id => rpts.first.async_generate_table(:userid => session[:userid],
:session_id => request.session_options[:id],
:mode => "charts"))
else
initiate_wait_for_task(:task_id => MiqReport.async_generate_tables(:reports => rpts, :userid => session[:userid]))
end
end
# Generate top 10 chart data - generate charts from report task results
def perf_gen_top_data_after_wait
miq_task = MiqTask.find(params[:task_id]) # Not first time, read the task record
if miq_task.task_results.kind_of?(Array)
rpts = miq_task.task_results.reverse # Grab the array of report objects (reversed so reports can be popped off)
else
rpt = miq_task.miq_report_result.report_results # Grab the report object from the blob
end
miq_task.destroy # Get rid of the task and results
@chart_reports = []
@charts = []
@chart_data = []
@perf_options[:ght_type] ||= "hybrid"
@perf_options[:chart_type] = :performance
cont_plus_model = request.parameters["controller"] + "-" + @perf_options[:top_model]
layout_name = case @perf_options[:top_type]
when 'topday' then 'day_top_charts'
when 'tophour' then 'hour_top_charts'
end
chart_layouts = perf_get_chart_layout(layout_name, cont_plus_model)
if @perf_options[:index]
chart = chart_layouts[@perf_options[:index].to_i]
@chart_data.push(gen_perf_chart(chart, rpt, nil, 'perf_top_chart'))
@chart_reports.push(rpt)
@charts.push(chart)
else
chart_layouts.each_with_index do |chart, idx|
next if chart.nil?
rpt = rpts.pop # Get the next report object from the array
@chart_data.push(gen_perf_chart(chart, rpt, idx, 'perf_top_chart'))
@chart_reports.push(rpt)
@charts.push(chart)
end
end
@sb[:chart_reports] = @chart_reports # Hang on to the reports for these charts
@charts = @charts
@chart_data = @chart_data
@top_chart = true
@html = perf_report_to_html(rpt)
end
# Generate daily utilization data for a model's charts
def perf_util_daily_gen_data(_refresh = nil)
@perf_record ||= @record
@sb[:summary] = nil # Clear out existing summary report
@sb[:trend_charts] = nil # Clear out the charts to be generated
# Get start/end dates in selected timezone
s, e = @perf_record.first_and_last_capture
return if s.nil? # Nothing to do if no util data
sdate = s.in_time_zone(@sb[:options][:tz])
edate = e.in_time_zone(@sb[:options][:tz])
# Eliminate partial start or end days
sdate = sdate.hour.zero? ? sdate : sdate + 1.day
edate = edate.hour < 23 ? edate - 1.day : edate
return if sdate > edate # Don't have a full day's data
charts = []
chart_data = []
chart_layouts = perf_get_chart_layout("daily_util_charts")
if params[:miq_date_1] || params[:miq_date_2] # Only changed date for the timestamp charts, no need to rebuild the report object
rpt = @sb[:trend_rpt]
else
unless params[:task_id] # First time thru, generate report async
rpt = perf_get_chart_rpt("vim_perf_util_daily")
rpt.tz = @sb[:options][:tz]
rpt.time_profile_id = @sb[:options][:time_profile]
rpt.db_options = {
:rpt_type => "utilization",
:interval => "daily",
:start_date => @sb[:options][:trend_start], # Midnight on start day
:end_date => @sb[:options][:trend_end], # 11pm on end day
:resource_type => @perf_record.class.base_class.to_s,
:resource_id => @perf_record.id,
:tag => @sb[:options][:tag],
}
initiate_wait_for_task(
:task_id => rpt.async_generate_table(
:userid => session[:userid],
:session_id => request.session_options[:id],
:mode => "charts"
)
)
@waiting = true
return
end
end
if params[:task_id] # Came in after async report generation
miq_task = MiqTask.find(params[:task_id]) # Not first time, read the task record
begin
unless miq_task.results_ready?
add_flash(_("Error while generating report: %{error_message}") % {:error_message => miq_task.message}, :error)
return
end
rpt = miq_task.miq_report_result.report_results # Grab the report object from the blob
ensure
miq_task.destroy # Get rid of the task and results
end
end
if @sb[:options][:index]
chart = chart_layouts[@sb[:options][:model].to_sym][@sb[:options][:index].to_i]
perf_remove_chart_cols(chart)
options = chart.merge(:axis_skip => 3)
options[:chart_type] = chart[:chart_type].to_sym if chart[:chart_type] # Override :summary chart type if specified in chart definition
options[:chart_date] = @sb[:options][:chart_date]
chart_data.push(perf_gen_chart(rpt, options).merge(:menu => chart[:menu]))
chart[:title] = rpt.title # Grab title from chart in case formatting added units
charts.push(chart)
else
chart_layouts[@sb[:options][:model].to_sym].each_with_index do |chart, _idx|
tag_class = @sb[:options][:tag].split("/").first if @sb[:options][:tag]
if chart[:type] == "None" || # No chart is available for this slot
(@sb[:options][:tag] && chart[:allowed_child_tag] && !chart[:allowed_child_tag].include?(tag_class)) # Tag not allowed
chart_data.push(nil) # Push a placeholder onto the chart data array
else
perf_remove_chart_cols(chart)
options = chart.merge(:axis_skip => 3)
options[:chart_type] = chart[:chart_type].to_sym if chart[:chart_type] # Override :summary chart type if specified in chart definition
options[:chart_date] = @sb[:options][:chart_date]
chart_data.push(perf_gen_chart(rpt, options).merge(:menu => chart[:menu]))
chart[:title] = rpt.title # Grab title from chart in case formatting added units
end
charts.push(chart)
end
end
@sb[:trend_rpt] = rpt # Hang on to the report data for the trend charts
@sb[:trend_charts] = charts
@sb[:chart_data] = {}
@sb[:chart_data]["utiltrend"] = chart_data
# Generate the report and chart for the selected trend row (single day chart)
ts_rpt = perf_get_chart_rpt("vim_perf_util_4_ts")
tz = @sb[:options][:time_profile_tz] || @sb[:options][:tz] # Use tz in time profile or chosen tz, if no profile tz
ts_rpt.db_options = {:report => rpt, :row_col => "timestamp", :row_val => create_time_in_tz(@sb[:options][:chart_date] + " 00", tz)}
ts_rpt.generate_table(:userid => session[:userid])
@sb[:ts_rpt] = ts_rpt # Hang on to the timestamp report data
ts_chart_layouts = perf_get_chart_layout("ts_util_charts")
ts_chart = ts_chart_layouts[:MiqReport][0] # For now, just use first chart
@sb[:ts_charts] = [ts_chart] # Hang on to chart (as an array)
ts_options = ts_chart
ts_options[:chart_type] = ts_chart[:chart_type].to_sym if ts_chart[:chart_type] # Override chart type if specified in chart definition
ts_chart_data = [perf_gen_chart(ts_rpt, ts_options)]
@sb[:chart_data]["utilts"] = ts_chart_data # Hang on to chart data
@html = perf_report_to_html(rpt) # Generate html version of the report
@sb[:summary] = perf_util_summary_info
end
# Generate a set of charts based on a report object
def perf_gen_charts(rpt, perf_options, parent = false)
model_title = parent ? "Parent-#{perf_options[:parent]}" : perf_options[:model]
charts = []
chart_data = []
case perf_options[:typ]
when "Hourly"
chart_layout = perf_get_chart_layout("hourly_perf_charts", model_title)
if perf_options[:index]
chart = chart_layout[perf_options[:index].to_i]
perf_remove_chart_cols(chart)
options = chart.merge(:zoom_url => zoom_url = perf_zoom_url("perf_chart_chooser", "clear"),
:link_data_url => "javascript:miqChartLinkData( _col_, _row_, _value_, _category_, _series_, _id_ )",
:axis_skip => 3,
:width => 1000, :height => 700)
menu_opts = parent ? {} : {:menu => chart[:menu], :zoom_url => zoom_url}
chart_data.push(perf_gen_chart(rpt, options).merge(menu_opts))
chart[:title] = rpt.title # Grab title from chart in case formatting added units
charts.push(chart)
else
chart_layout.each_with_index do |chart, idx|
if chart[:type] == "None" # No chart is available for this slot
chart_data.push(nil) # Push a placeholder onto the chart data array
else
perf_remove_chart_cols(chart)
options = chart.merge(:zoom_url => zoom_url = perf_zoom_url("perf_chart_chooser", idx.to_s),
:link_data_url => "javascript:miqChartLinkData( _col_, _row_, _value_, _category_, _series_, _id_ )",
:axis_skip => 3)
menu_opts = parent ? {} : {:menu => chart[:menu], :zoom_url => zoom_url}
chart_data.push(perf_gen_chart(rpt, options).merge(menu_opts))
chart[:title] = rpt.title # Grab title from chart in case formatting added units
end
charts.push(chart)
end
end
when "realtime"
chart_layout = perf_get_chart_layout("realtime_perf_charts", model_title)
if perf_options[:index]
chart = chart_layout[perf_options[:index].to_i]
perf_remove_chart_cols(chart)
options = chart.merge(:zoom_url => zoom_url = perf_zoom_url("perf_chart_chooser", "clear"),
:axis_skip => 29,
:width => 1000,
:height => 700)
menu_opts = parent ? {} : {:menu => chart[:menu], :zoom_url => zoom_url}
chart_data.push(perf_gen_chart(rpt, options).merge(menu_opts))
chart[:title] = rpt.title # Grab title from chart in case formatting added units
charts.push(chart)
else
chart_layout.each_with_index do |chart, idx|
if chart[:type] == "None" # No chart is available for this slot
chart_data.push(nil) # Push a placeholder onto the chart data array
else
perf_remove_chart_cols(chart)
options = chart.merge(:zoom_url => zoom_url = perf_zoom_url("perf_chart_chooser", idx.to_s),
:axis_skip => 29)
menu_opts = parent ? {} : {:menu => chart[:menu], :zoom_url => zoom_url}
chart_data.push(perf_gen_chart(rpt, options).merge(menu_opts))
chart[:title] = rpt.title # Grab title from chart in case formatting added units
end
charts.push(chart)
end
end
when "Daily"
chart_layout = perf_get_chart_layout("daily_perf_charts", model_title)
if perf_options[:index]
chart = chart_layout[perf_options[:index].to_i]
if chart
perf_remove_chart_cols(chart)
options = chart.merge(:zoom_url => zoom_url = perf_zoom_url("perf_chart_chooser", "clear"),
:link_data_url => "javascript:miqChartLinkData( _col_, _row_, _value_, _category_, _series_, _id_ )",
:axis_skip => 3,
:width => 1000, :height => 700)
process_chart_trends(chart, rpt, options)
menu_opts = parent ? {} : {:menu => chart[:menu], :zoom_url => zoom_url}
chart_data.push(perf_gen_chart(rpt, options).merge(menu_opts))
chart[:title] = rpt.title # Grab title from chart in case formatting added units
charts.push(chart)
end
else
chart_layout.each_with_index do |chart, idx|
if chart[:type] == "None" # No chart is available for this slot
chart_data.push(nil) # Push a placeholder onto the chart data array
else
perf_remove_chart_cols(chart)
options = chart.merge(:zoom_url => zoom_url = perf_zoom_url("perf_chart_chooser", idx.to_s),
:link_data_url => "javascript:miqChartLinkData( _col_, _row_, _value_, _category_, _series_, _id_ )",
:axis_skip => 3)
process_chart_trends(chart, rpt, options)
menu_opts = parent ? {} : {:menu => chart[:menu], :zoom_url => zoom_url}
chart_data.push(perf_gen_chart(rpt, options).merge(menu_opts))
chart[:title] = rpt.title # Grab title from chart in case formatting added units
end
charts.push(chart)
end
end
end
return charts, chart_data
end
# Generate performance chart data for a report based on passed in options
def perf_gen_chart(report, options)
options[:chart_type] ||= @perf_options[:chart_type] # Set chart_type for the set of charts, unless overridden already for this chart
options[:width] ||= 350
options[:height] ||= 250
report.title = options[:title]
report.graph ||= {}
report.graph[:type] = options[:type]
report.graph[:columns] = options[:columns]
report.graph[:legends] = options[:legends]
report.graph[:max_col_size] = options[:max_value]
report.to_chart(settings(:display, :reporttheme), false,
MiqReport.graph_options(options))
chart_data = {
:data => report.chart, # Save the graph data
:main_col => options[:columns].first # And the main (first) column of the chart
}
if options[:chart2]
report.graph[:type] = options[:chart2][:type]
report.graph[:columns] = options[:chart2][:columns]
report.to_chart(settings(:display, :reporttheme), false,
MiqReport.graph_options(options.merge(:composite => true)))
chart_data[:data2] = report.chart
end
chart_data
end
# Build the chart zoom url
def perf_zoom_url(action, idx)
url = "javascript:miqAsyncAjax('" +
url_for_only_path(:action => action,
:id => @perf_record.id,
:chart_idx => idx) +
"')"
url
end
# Generate the html view of the chart report
def perf_report_to_html(rpt = nil, charts = nil)
rpt ||= @sb[:chart_reports] # Set default if not passed in
title = rpt.title
rpt.title = @title.gsub(/Capacity & Utilization/, "#{@perf_options[:typ]} C & U") + " - #{title}"
return if @perf_options[:index].nil? # Don't show html for graph setting or if multiple charts are showing
report = rpt.class == Array ? rpt.first : rpt # Get the first or only report
report = perf_remove_report_cols(report, charts) # Remove cols that are not in the current chart
report.headers.map! { |header| _(header) } # Translate report headers
report.to_html # Create html from the chart report
end
# Generate @summary_info array from report and chart data
def perf_util_summary_info
si = {}
si[:info] = []
si[:info].push([_("Utilization Trend Summary for"), @sb[:options][:model] == "MiqEnterprise" ? "Enterprise" : "#{ui_lookup(:model => @sb[:options][:model])} [#{@perf_record.name}]"])
si[:info].push([_("Trend Interval"), "#{format_timezone(@sb[:options][:trend_start], @sb[:options][:tz], "date")} - #{format_timezone(@sb[:options][:trend_end], @sb[:options][:tz], "date")}"])
si[:info].push([_("Selected Day"), format_timezone(@sb[:options][:chart_date].to_time, "UTC", "date")])
si[:info].push([_("Time Profile"), session[:time_profiles][@sb[:options][:time_profile]]]) if @sb[:options][:time_profile]
si[:info].push([_("Time Zone"), @sb[:options][:time_profile_tz] || @sb[:options][:tz]])
si[:info].push([_("Classification"), @sb[:tags][@sb[:options][:tag]]]) if @sb[:options][:tag]
if @sb[:trend_charts]
si[:cpu] = perf_util_summary_section("cpu") # Get the cpu section
si[:memory] = perf_util_summary_section("memory") # Get the memory section
si[:storage] = perf_util_summary_section("disk") # Get the disk section
end
si
end
# Build a section of the summary info hash
def perf_util_summary_section(s)
ss = []
# Fill in the single day data from the timestamp report
ts_rpt = @sb[:ts_rpt]
total_vals = 0.0
ts_rpt.table.data.each do |r|
next unless r[0].downcase.include?(s)
ts_rpt.col_order.each_with_index do |col, col_idx|
next unless col.ends_with?("_percent")
# Do NOT show reserve (available) column for Host and Storage nodes
next if col.include?("_reserve") && %w[Host Storage].include?(@sb[:options][:model])
tip = case s # Override the formatting for certain column groups on single day percent utilization chart
when "cpu"
ts_rpt.format(col + '_tip', r[col + '_tip'],
:format => {
:function => {
:name => "mhz_to_human_size",
:precision => 1
}
})
when "memory"
ts_rpt.format(col + '_tip', r[col + '_tip'].to_f * 1024 * 1024,
:format => {
:function => {
:name => "bytes_to_human_size",
:precision => 1
}
})
when "disk"
ts_rpt.format(col + '_tip', r[col + '_tip'],
:format => {
:function => {
:name => "bytes_to_human_size",
:precision => 1
}
})
else
ts_rpt.format(col + '_tip', r[col + '_tip'])
end
val = ts_rpt.format(col, r[col], :format => {:function => {:name => "number_with_delimiter", :suffix => "%"}, :precision => "0"})
ss.push([_(ts_rpt.headers[col_idx]), "#{tip} (#{val})"])
total_vals += r[col].to_f # Total up the values for this section
end
end
return nil if total_vals.zero? # If no values, return nil so this section won't show on the screen
# Get the trend information from the trend charts/report
@sb[:trend_charts].each do |c|
s = "storage" if s == "disk" # disk fields have 'storage' in them
next unless c[:columns].first.include?("#{s}_")
next unless c[:trends]
c[:trends].each do |t|
c[:columns].each do |trendcol|
next unless trendcol.starts_with?("trend_")
ss.push([Dictionary.gettext(trendcol, :type => :column, :notfound => :titleize) + ": " + t.split(":").last,
@sb[:trend_rpt].extras[:trend][trendcol + "|" + t.split(":").first]])
end
end
end
ss
end
# Remove cols from report object cols and col_order that are not in a chart before the report is run
def perf_trim_report_cols(report, chart)
keepcols = []
keepcols += %w[timestamp resource_name assoc_ids]
keepcols += chart[:columns]
keepcols += chart[:chart2][:columns] if chart[:chart2]
# First remove columns from the col_order and header arrays
report.cols.delete_if { |c| !keepcols.include?(c) } # Remove columns
cols = report.col_order.length # Remove col_order and header elements
(1..cols).each do |c|
idx = cols - c # Go thru arrays in reverse
unless keepcols.include?(report.col_order[idx])
report.col_order.delete_at(idx)
report.headers.delete_at(idx)
end
end
end
# Remove cols from report object that are not in the current chart after the report is run
def perf_remove_report_cols(report, charts = nil)
charts ||= @charts.first
new_rpt = MiqReport.new(report.attributes) # Make a copy of the report
new_rpt.table = Marshal.load(Marshal.dump(report.table))
keepcols = []
keepcols += %w[timestamp statistic_time] unless @top_chart
keepcols += ["resource_name"] if charts[:type].include?("Pie")
keepcols += charts[:columns]
keepcols += charts[:chart2][:columns] if charts[:chart2]
# First remove columns from the col_order and header arrays
cols = new_rpt.col_order.length
(1..cols).each do |c|
idx = cols - c # Go thru arrays in reverse
unless keepcols.include?(new_rpt.col_order[idx])
new_rpt.col_order.delete_at(idx)
new_rpt.headers.delete_at(idx)
end
end
# Now remove columns from the cols array so we don't include them in the CSV download
new_rpt.cols.each do |c|
unless keepcols.include?(c)
new_rpt.table.remove_column(c)
end
end
new_rpt
end
def process_chart_trends(chart, rpt, options)
if chart[:trends] && rpt.extras && rpt.extras[:trend]
trendcol = perf_get_chart_trendcol(chart)
if trendcol.present?
options[:trendtip] = chart[:trends].collect do |t|
t.split(":").last + ": " + rpt.extras[:trend][trendcol + "|" + t.split(":").first]
end.join("\r")
end
end
end
# get JSON encoded in Base64
def parse_chart_click(click_params)
click_parts = JSON.parse(Base64.decode64(click_params))
ApplicationController::Performance::ChartClickData.new(
click_parts['row'].to_i, # legend_index
click_parts['column'].to_i, # data_index
click_parts['chart_index'].to_i, # chart_index
click_parts['chart_name'].split("-").first, # cmd
click_parts['chart_name'].split("-").second, # model
click_parts['chart_name'].split("-").third # type
)
end
def breadcrumb_tag(report, legend_index)
category = Classification.lookup_by_name(@perf_options[:cat]).description
group_by = report.extras[:group_by_tag_descriptions][legend_index]
"#{category}:#{group_by}"
end
end