[PATCH] RM3509 re-establish providers/ index page, with useful statistics and graphs; generic-ize chart code for ease of reuse
Tzu-Mainn Chen
tzumainn at redhat.com
Fri Jul 20 19:14:33 UTC 2012
---
src/app/controllers/logs_controller.rb | 41 +----
src/app/controllers/providers_controller.rb | 194 +++++++++++++++++++++---
src/app/stylesheets/layout.scss | 15 ++
src/app/util/chart_datasets.rb | 59 +++++++
src/app/views/charts/_time_chart.html.haml | 25 +++
src/app/views/logs/_pretty_view.html.haml | 25 +---
src/app/views/providers/_page_header.html.haml | 4 +
src/app/views/providers/index.html.haml | 53 ++++++-
src/config/locales/en.yml | 7 +
src/config/routes.rb | 1 +
src/features/provider.feature | 11 --
11 files changed, 345 insertions(+), 90 deletions(-)
create mode 100644 src/app/util/chart_datasets.rb
create mode 100644 src/app/views/charts/_time_chart.html.haml
diff --git a/src/app/controllers/logs_controller.rb b/src/app/controllers/logs_controller.rb
index 316b375..06ff76a 100644
--- a/src/app/controllers/logs_controller.rb
+++ b/src/app/controllers/logs_controller.rb
@@ -99,7 +99,7 @@ class LogsController < ApplicationController
Date.civil(params[:from_date][:year].to_i,
params[:from_date][:month].to_i,
params[:from_date][:day].to_i)
- @to_date = params[:to_date].nil? ? Date.today + 1.days :
+ @to_date = params[:to_date].nil? ? Date.today :
Date.civil(params[:to_date][:year].to_i,
params[:to_date][:month].to_i,
params[:to_date][:day].to_i)
@@ -283,27 +283,17 @@ class LogsController < ApplicationController
find(:all, :conditions => initial_conditions)
end
- @datasets = Hash.new
- counts = Hash.new
-
- counts["All"] = @initial_sources.count
+ @datasets = ChartDatasets.new(@from_date, @to_date)
+ @datasets.increment_count("All", at initial_sources.count)
if @group_options.include?(@group)
@initial_sources.each do |source|
label = get_source_label(source, @group)
-
- if counts.has_key?(label)
- counts[label] = counts[label] + 1
- else
- counts[label] = 1
- end
+ @datasets.increment_count(label,1)
end
end
- counts.each.map{ |label,count|
- @datasets[label] = [[@from_date.to_datetime.beginning_of_day.to_i * 1000,
- count]]
- }
+ @datasets.initialize_datasets
@events.each do |event|
event_timestamp = event.event_time.to_i * 1000
@@ -311,23 +301,21 @@ class LogsController < ApplicationController
if event.status_code == start_code || event.status_code == end_code
increment = (event.status_code == end_code) ? -1 : 1
- add_dataset_point("All", event_timestamp, increment, counts)
+ @datasets.add_dataset_point("All", event_timestamp, increment)
if @group_options.include?(@group)
label = get_source_label(event.source, @group)
- add_dataset_point(label, event_timestamp, increment, counts)
+ @datasets.add_dataset_point(label, event_timestamp, increment)
end
end
end
- counts.each.map{ |label,count|
- @datasets[label] << [@to_date.to_datetime.end_of_day.to_i * 1000, count]
- }
+ @datasets.finalize_datasets
end
def get_source_label(source, label_type)
label = "Unknown"
- case @group
+ case label_type
when t('logs.index.pool')
label = source.pool.name unless source.pool.nil?
when t('logs.index.provider')
@@ -338,15 +326,4 @@ class LogsController < ApplicationController
label
end
-
- def add_dataset_point(label, timestamp, increment, counts)
- if !@datasets.has_key?(label)
- counts[label] = 0
- @datasets[label] = [ [timestamp - 1, 0] ]
- end
-
- @datasets[label] << [timestamp - 1, counts[label]]
- counts[label] = counts[label] + increment
- @datasets[label] << [timestamp, counts[label]]
- end
end
diff --git a/src/app/controllers/providers_controller.rb b/src/app/controllers/providers_controller.rb
index 81ac69f..2f78e9f 100644
--- a/src/app/controllers/providers_controller.rb
+++ b/src/app/controllers/providers_controller.rb
@@ -21,27 +21,28 @@ class ProvidersController < ApplicationController
before_filter :parse_provider_type, :only => [:create, :update]
def index
- @params = params
- begin
- @provider = Provider.find(session[:current_provider_id]) if session[:current_provider_id]
- rescue ActiveRecord::RecordNotFound => exc
- logger.error(exc.message)
- ensure
- @provider ||= @providers.first
- end
+ @from_date = params[:from_date].nil? ? Date.today - 7.days :
+ Date.civil(params[:from_date][:year].to_i,
+ params[:from_date][:month].to_i,
+ params[:from_date][:day].to_i)
+ @to_date = params[:to_date].nil? ? Date.today :
+ Date.civil(params[:to_date][:year].to_i,
+ params[:to_date][:month].to_i,
+ params[:to_date][:day].to_i)
+
+ load_headers
+ statistics
respond_to do |format|
- format.html do
- if @providers.present?
- flash.keep
- redirect_to edit_provider_path(@provider)
- else
- render :action => :index
- end
- end
+ format.html
+ format.js { render :partial => 'list' }
format.xml { render :partial => 'list.xml' }
end
+ end
+ def filter
+ redirect_to_original({ "from_date" => params[:from_date],
+ "to_date" => params[:to_date] })
end
def new
@@ -221,8 +222,8 @@ class ProvidersController < ApplicationController
end
def load_providers
- @providers = Provider.list_for_user(current_session, current_user,
- Privilege::VIEW)
+ @providers = Provider.includes(:provider_type).list_for_user(current_session, current_user,
+ Privilege::VIEW).order("name")
end
def disable_provider
@@ -322,4 +323,161 @@ class ProvidersController < ApplicationController
@view = @details_tab[:view]
end
+
+ def load_headers
+ @header = [
+ { :name => t('providers.index.provider_name'), :class => 'center',
+ :sortable => false },
+ { :name => t('providers.index.provider_type'), :class => 'center',
+ :sortable => false },
+ { :name => t('providers.index.running_instances'), :class => 'center',
+ :sortable => false },
+ { :name => t('providers.index.pending_instances'), :class => 'center',
+ :sortable => false },
+ { :name => t('providers.index.error_instances'), :class => 'center',
+ :sortable => false },
+ { :name => t('providers.index.historical_running_instances'), :class => 'center',
+ :sortable => false },
+ { :name => t('providers.index.historical_error_instances'), :class => 'center',
+ :sortable => false },
+ ]
+ end
+
+ def statistics
+ @statistics = Hash.new
+ @providers.each do |provider|
+ @statistics[provider.id] = {
+ "running_instances" => 0,
+ "pending_instances" => 0,
+ "error_instances" => 0,
+ "historical_running_instances" => 0,
+ "historical_error_instances" => 0,
+ }
+ end
+
+ # Queries are NOT permissioned by instance, as info is
+ # used purely for statistical purposes.
+
+ # current instances
+ provider_counts = ProviderAccount.joins(:instances).
+ merge(Instance.scoped).
+ select("provider_id, state, count(*) as count").
+ where(:provider_id => @providers.map{|provider| provider.id}).
+ group("provider_id, state")
+
+ provider_counts.each do |provider_count|
+ provider_id = provider_count["provider_id"]
+ state = provider_count["state"]
+ count = provider_count["count"]
+
+ if Instance::FAILED_STATES.include?(state)
+ @statistics[provider_id]["error_instances"] += count.to_i
+ elsif [Instance::STATE_RUNNING, Instance::STATE_SHUTTING_DOWN].
+ include?(state)
+ @statistics[provider_id]["running_instances"] += count.to_i
+ elsif [Instance::STATE_NEW, Instance::STATE_PENDING].
+ include?(state)
+ @statistics[provider_id]["pending_instances"] += count.to_i
+ end
+ end
+
+ # instances that were running between historical date range
+ historical_running_provider_counts = ProviderAccount.joins(:instances).
+ merge(Instance.unscoped).
+ select("provider_id, state, count(*) as count").
+ where(:provider_id => @providers.map{|provider| provider.id}).
+ where("time_last_running <= :to_date and
+ (time_last_stopped is null
+ or time_last_stopped >= :from_date)",
+ :to_date => @to_date.to_datetime.end_of_day,
+ :from_date => @from_date.to_datetime.beginning_of_day
+ ).
+ group("provider_id, state")
+
+ historical_running_provider_counts.each do |provider_count|
+ provider_id = provider_count["provider_id"]
+ count = provider_count["count"]
+
+ @statistics[provider_id]["historical_running_instances"] += count.to_i
+ end
+
+ # instances that threw an error between historical date range
+ historical_error_provider_counts = ProviderAccount.joins(:instances).
+ merge(Instance.unscoped).
+ select("provider_id, count(*) as count").
+ where(:provider_id => @providers.map{|provider| provider.id}).
+ where("instances.state" => Instance::FAILED_STATES).
+ where("instances.updated_at between :from_date and :to_date",
+ :states => Instance::FAILED_STATES,
+ :to_date => @to_date.to_datetime.end_of_day,
+ :from_date => @from_date.to_datetime.beginning_of_day
+ ).
+ group("provider_id")
+
+ historical_error_provider_counts.each do |provider_count|
+ provider_id = provider_count["provider_id"]
+ count = provider_count["count"]
+
+ @statistics[provider_id]["historical_error_instances"] += count.to_i
+ end
+
+ # all running instances during historical date range
+ historical_instances = Instance.unscoped.
+ find(:all,
+ :conditions => ["time_last_running <= ? and
+ (time_last_stopped is null
+ or time_last_stopped >= ?)",
+ @to_date.to_datetime.end_of_day,
+ @from_date.to_datetime.beginning_of_day],
+ :include => {:provider_account => [:provider]}
+ )
+
+ @datasets = ChartDatasets.new(@from_date, @to_date)
+ events = Array.new
+
+ historical_instances.each do |instance|
+ provider_account = instance.provider_account
+
+ if check_privilege(Privilege::VIEW, provider_account)
+ label = provider_account.nil? ?
+ 'Unknown' :
+ provider_account.provider.name +
+ " (" + provider_account.name + ")"
+
+ # see if instance started before from_date
+ if instance.time_last_running <= @from_date.to_datetime.beginning_of_day
+ @datasets.increment_count(label,1)
+ @datasets.increment_count("All",1)
+ else
+ events << {
+ "time" => instance.time_last_running,
+ "label" => label,
+ "increment" => 1
+ }
+ end
+
+ if !instance.time_last_stopped.nil? &&
+ instance.time_last_stopped <= @to_date.to_datetime.end_of_day
+ events << {
+ "time" => instance.time_last_stopped,
+ "label" => label,
+ "increment" => -1
+ }
+ end
+ end
+ end
+
+ @datasets.initialize_datasets
+
+ events.sort_by {|event| event["time"]}.each do |event|
+ timestamp = event["time"].to_i * 1000
+ increment = event["increment"]
+
+ [ event["label"], "All" ].each { |label|
+ @datasets.add_dataset_point(label,timestamp,increment)
+ }
+ end
+
+ @datasets.finalize_datasets
+ end
end
diff --git a/src/app/stylesheets/layout.scss b/src/app/stylesheets/layout.scss
index e161f65..45127fd 100644
--- a/src/app/stylesheets/layout.scss
+++ b/src/app/stylesheets/layout.scss
@@ -1962,6 +1962,10 @@ table.flat, form.filterable-data table{
&.checkbox{ width: 21px; text-align: center; padding: 0px 3px; }
&.alert{ width: 16px; text-align: center; padding: 0px 4px;}
&.center{ text-align: center;}
+
+ &.running_instances{ color: green; text-align: center; }
+ &.pending_instances{ color: brown; text-align: center; }
+ &.error_instances{ color: red; text-align: center; }
}
tr.stripe{
@@ -2290,6 +2294,17 @@ ul.listing, ol.listing{
}
}
+/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Charts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
+
+div.provider-history-graph {
+ width: 100%; height: 250px;
+}
+
+div.log-history-graph {
+ width: 100%; height: 600px;
+}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Page Footer -- v.0.0.1 [footer] (footer.scss)
diff --git a/src/app/util/chart_datasets.rb b/src/app/util/chart_datasets.rb
new file mode 100644
index 0000000..3e9d3e6
--- /dev/null
+++ b/src/app/util/chart_datasets.rb
@@ -0,0 +1,59 @@
+#
+# Copyright 2011 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class ChartDatasets
+ attr_accessor :data
+
+ def initialize(from_date, to_date)
+ @data = Hash.new
+ @counts = Hash.new
+ @from_date = from_date
+ @to_date = to_date
+ end
+
+ def initialize_datasets()
+ @counts.each.map{ |label,count|
+ @data[label] = [[@from_date.to_datetime.beginning_of_day.to_i * 1000,
+ count]]
+ }
+ end
+
+ def finalize_datasets()
+ @counts.each.map{ |label,count|
+ @data[label] << [@to_date.to_datetime.end_of_day.to_i * 1000, count]
+ }
+ end
+
+ def increment_count(label, increment)
+ if !@counts.has_key?(label)
+ @counts[label] = 0
+ end
+
+ @counts[label] = @counts[label] + increment
+ end
+
+ def add_dataset_point(label, timestamp, increment)
+ if !@data.has_key?(label)
+ @data[label] = [[timestamp - 1, 0]]
+ else
+ @data[label] << [timestamp - 1, @counts[label]]
+ end
+
+ increment_count(label,increment)
+ @data[label] << [timestamp, @counts[label]]
+ end
+
+end
diff --git a/src/app/views/charts/_time_chart.html.haml b/src/app/views/charts/_time_chart.html.haml
new file mode 100644
index 0000000..c1db5fb
--- /dev/null
+++ b/src/app/views/charts/_time_chart.html.haml
@@ -0,0 +1,25 @@
+%div{:class => "#{name}", :id => "#{name}"}
+
+:javascript
+ $(function () {
+ var datasets = [
+ #{ datasets.reject{|label,data| label != "All"}.each.map{ |label,data| "{
+ label: \"" + label + "\",
+ data: [#{ data.each.map{ |p| "[#{p[0]}, #{p[1]}]"}.join(", ") }]
+ },
+ "} }
+ #{ datasets.reject{|label,data| label == "All"}.each.map{ |label,data| "{
+ label: \"" + label + "\",
+ data: [#{ data.each.map{ |p| "[#{p[0]}, #{p[1]}]"}.join(", ") }]
+ }" }.join(",
+ ") }
+ ];
+
+ $.plot($("##{name}"), datasets, {
+ xaxis: {
+ mode: "time",
+ min: #{from_date.to_datetime.beginning_of_day.to_i * 1000},
+ max: #{to_date.to_datetime.end_of_day.to_i * 1000}
+ }
+ });
+ });
diff --git a/src/app/views/logs/_pretty_view.html.haml b/src/app/views/logs/_pretty_view.html.haml
index d559a4f..ac8d223 100644
--- a/src/app/views/logs/_pretty_view.html.haml
+++ b/src/app/views/logs/_pretty_view.html.haml
@@ -14,27 +14,4 @@
= restful_submit_tag t("filter_table.apply_filters"), "index", filter_logs_path, 'POST', :class => 'button', :id => 'apply_logs_filter'
%br
-%div{:id => 'placeholder', :style => 'width:925px;height:600px;'}
-
-:javascript
- $(function () {
- var datasets = [
- {
- label: "All",
- data: [#{ @datasets["All"].each.map{ |p| "[#{p[0]}, #{p[1]}]"}.join(", ") }]
- },
- #{ @datasets.reject{|label,data| label == "All"}.each.map{ |label,data| "{
- label: \"" + label + "\",
- data: [#{ data.each.map{ |p| "[#{p[0]}, #{p[1]}]"}.join(", ") }]
- }" }.join(",
- ") }
- ];
-
- $.plot($("#placeholder"), datasets, {
- xaxis: {
- mode: "time",
- min: #{@from_date.to_datetime.beginning_of_day.to_i * 1000},
- max: #{@to_date.to_datetime.end_of_day.to_i * 1000}
- }
- });
- });
+= render :partial => 'charts/time_chart', :locals => { :datasets => @datasets.data, :from_date => @from_date, :to_date => @to_date, :name => 'log-history-graph' }
diff --git a/src/app/views/providers/_page_header.html.haml b/src/app/views/providers/_page_header.html.haml
index f48f2ac..7752c6f 100644
--- a/src/app/views/providers/_page_header.html.haml
+++ b/src/app/views/providers/_page_header.html.haml
@@ -12,4 +12,8 @@
= form_for @provider, :url => provider_path(@provider), :html => { :method => :put, :class => 'generic horizontal' } do |f|
= f.hidden_field :enabled, :value => !@provider.enabled
= f.submit "", :confirm => "#{@provider.enabled ? t("providers.disable_provider") : t("providers.enable_provider")}", :class => "provider_toggle #{@provider.enabled ? "on" : "off"}"
+ %br
+ .return_to
+ =t'return_to'
+ = link_to "#{t'providers.providers'}", providers_path
%h1.providers{:class => @provider.enabled ? 'enabled' : 'disabled'}= @provider.name.blank? ? t("providers.editing_provider") : @provider.name
diff --git a/src/app/views/providers/index.html.haml b/src/app/views/providers/index.html.haml
index c45d064..7ea0da6 100644
--- a/src/app/views/providers/index.html.haml
+++ b/src/app/views/providers/index.html.haml
@@ -1,6 +1,49 @@
= render :partial => 'layouts/admin_nav'
-%section.content-section.provider
- = t("providers.index.no_providers_available")
- - if check_privilege(Privilege::CREATE, Provider)
- = t("providers.index.create_one")
- = link_to t("providers.index.create_new_provider"), new_provider_path, :class => 'button', :id => "create_new_provider"
+%section.content-section
+ %header
+ %h2.providers #{t'providers.providers'}
+ .content
+ - content_for :form_header do
+ - if check_privilege(Privilege::CREATE, Provider)
+ %li= link_to t("providers.index.create_new_provider"), new_provider_path, :class => 'button', :id => "create_new_provider"
+
+ - content_for :filter_controls do
+ %li
+ = hidden_field_tag :current_path, request.fullpath
+ = t('filter_table.from')
+ = select_date @from_date, :prefix => :from_date
+ = t('filter_table.to')
+ = select_date @to_date, :prefix => :to_date
+ = restful_submit_tag t("filter_table.apply_filters"), "index", filter_providers_path, 'POST', :class => 'button', :id => 'apply_provider_index_filter'
+ %span.label.badge.dark= @providers.count
+ = filter_table(@header, @providers) do |provider|
+ - statistics = @statistics[provider.id]
+ %tr{:class => cycle('nostripe','stripe')}
+ %td= link_to provider.name, edit_provider_path(provider)
+ %td= provider.provider_type.name
+ - if statistics["running_instances"] > 0
+ %td{:class => 'running_instances'}= statistics["running_instances"]
+ - else
+ %td.center= statistics["running_instances"]
+ - if statistics["pending_instances"] > 0
+ %td{:class => 'pending_instances'}= statistics["pending_instances"]
+ - else
+ %td.center= statistics["pending_instances"]
+ - if statistics["error_instances"] > 0
+ %td{:class => 'error_instances'}= statistics["error_instances"]
+ - else
+ %td.center= statistics["error_instances"]
+ - if statistics["historical_running_instances"] > 0
+ %td{:class => 'running_instances'}= statistics["historical_running_instances"]
+ - else
+ %td.center= statistics["historical_running_instances"]
+ - if statistics["historical_error_instances"] > 0
+ %td{:class => 'error_instances'}= statistics["historical_error_instances"]
+ - else
+ %td.center= statistics["historical_error_instances"]
+
+%section.content-section
+ %header
+ %h2.activity #{t'activity.activity'}
+ .content
+ = render :partial => 'charts/time_chart', :locals => { :datasets => @datasets.data, :from_date => @from_date, :to_date => @to_date, :name => 'provider-history-graph' }
diff --git a/src/config/locales/en.yml b/src/config/locales/en.yml
index 42b15f5..1fcd708 100644
--- a/src/config/locales/en.yml
+++ b/src/config/locales/en.yml
@@ -10,6 +10,8 @@ en:
other: "%{count} hours"
search:
no_results: "No matching results."
+ activity:
+ activity: Activity
logs:
options:
default_users: All Users
@@ -1111,6 +1113,11 @@ en:
provider_name: Provider Name
provider_url: Provider URL
provider_type: Provider Type
+ running_instances: Running Instances (Current)
+ pending_instances: Pending (Current)
+ error_instances: Errors (Current)
+ historical_running_instances: Running (Historical)
+ historical_error_instances: Errors (Historical)
x_deltacloud_driver: X-Deltacloud-Driver
x_deltacloud_provider: X-Deltacloud-Provider
no_providers_available: No providers available.
diff --git a/src/config/routes.rb b/src/config/routes.rb
index e7f26c5..3e81885 100644
--- a/src/config/routes.rb
+++ b/src/config/routes.rb
@@ -139,6 +139,7 @@ Conductor::Application.routes.draw do
resources :providers do
delete 'multi_destroy', :on => :collection
+ post :filter, :on => :collection
resources :provider_accounts do
collection do
diff --git a/src/features/provider.feature b/src/features/provider.feature
index 2ba7081..e5ca8dc 100644
--- a/src/features/provider.feature
+++ b/src/features/provider.feature
@@ -92,17 +92,6 @@ Feature: Manage Providers
And I should see "Provider is disabled."
And provider "provider1" should have all instances stopped
- Scenario: Persist selected provider
- Given there is a provider named "provider1"
- And there is a provider named "provider2"
- When I am on the provider1's edit provider page
- And I click on the Providers icon in the menu
- Then I should be on the provider1's edit provider page
- When I am on the provider2's edit provider page
- And I click on the Providers icon in the menu
- Then I should be on the provider2's edit provider page
-
-
# Scenario: Search for hardware profiles
# Given there are these providers:
# | name | url |
--
1.7.6.5
More information about the aeolus-devel
mailing list