src/app/controllers/providers_controller.rb
#
# 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 ProvidersController < ApplicationController
before_filter :require_user
before_filter :load_providers, :only => [:index, :show, :new, :edit, :create, :update]
before_filter :load_providers_types, :only => [:new, :edit, :update, :create]
before_filter ResourceLinkFilter.new({ :provider => :provider_type }),
:only => [:create, :update]
def index
@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)
if @to_date < @from_date
flash[:error] = _('\'From date\' cannot be after \'To date\'')
end
load_headers
statistics
respond_to do |format|
format.html
format.js { render :partial => 'list' }
format.xml { render :partial => 'list.xml' , :locals => { :minimal => params[:minimal] }}
end
end
def filter
redirect_to_original({ "from_date" => params[:from_date],
"to_date" => params[:to_date] })
end
def new
require_privilege(Alberich::Privilege::CREATE, Provider)
@provider = Provider.new
@provider.url = Provider::DEFAULT_DELTACLOUD_URL
@provider.provider_type = ProviderType.find_by_deltacloud_driver('ec2')
@title = _('New Provider')
end
def edit
@provider = Provider.find(params[:id])
@title = _('Cloud Providers')
# requiring VIEW rather than MODIFY since edit doubles as the 'show' page
# here -- actions must be hidden explicitly in template
require_privilege(Alberich::Privilege::VIEW, @provider)
if params.delete :test_provider
test_connection(@provider)
end
respond_to do |format|
format.html
format.json { render :json => @provider }
end
end
def show
@provider = Provider.find(params[:id])
require_privilege(Alberich::Privilege::VIEW, @provider)
@alerts = provider_alerts(@provider)
load_provider_tabs
respond_to do |format|
format.html
format.js do
if params.delete :details_pane
render :partial => 'layouts/details_pane'
else
render :partial => @view
end
end
format.xml { render :partial => 'detail', :locals => { :provider => @provider } }
end
end
def create
@title = _('New Provider')
require_privilege(Alberich::Privilege::CREATE, Provider)
@provider = Provider.new(params[:provider])
if @provider.save
@provider.assign_owner_roles(current_user)
respond_to do |format|
format.html do
flash[:notice] = _('Provider added.')
redirect_to provider_path(@provider)
end
format.xml do
render :partial => 'detail',
:status => :created,
:locals => { :provider => @provider }
end
end
else
respond_to do |format|
format.html { render :action => "new" }
format.xml { render_api_error(@provider.errors) }
end
end
rescue Errno::EACCES
Provider.skip_callback :save, :check_name
@provider.save
@provider.assign_owner_roles(current_user)
respond_to do |format|
format.html do
flash[:notice] = _('Provider added.')
flash[:warning] = _('Cannot check if provider name is right. Please check config file')
redirect_to provider_path(@provider)
end
end
end
def update
@provider = Provider.find(params[:id])
require_privilege(Alberich::Privilege::MODIFY, @provider)
@provider.assign_attributes(params[:provider])
provider_disabled = @provider.enabled_changed? && !@provider.enabled
if provider_disabled
disable_provider
return
end
if @provider.save
@provider.update_availability
respond_to do |format|
format.html do
flash[:notice] = _('Provider updated.')
redirect_to provider_path(@provider)
end
format.xml { render :partial => 'detail', :locals => { :provider => @provider } }
end
else
# we reset 'enabled' attribute to real state
# if save failed
@provider.reset_enabled!
respond_to do |format|
format.html do
load_provider_tabs
@alerts = provider_alerts(@provider)
render :action => "edit"
end
format.xml { render_api_error(@provider.errors) }
end
end
rescue Errno::EACCES
Provider.skip_callback :save, :check_name
@provider.save
respond_to do |format|
format.html do
flash[:notice] = _('Provider updated.')
flash[:warning] = _('Cannot check if provider name is right. Please check config file')
redirect_to provider_path(@provider)
end
end
end
def destroy
provider = Provider.find(params[:id])
require_privilege(Alberich::Privilege::MODIFY, provider)
respond_to do |format|
if provider.safe_destroy
session[:current_provider_id] = nil
format.html do
flash[:notice] = _('Provider has been deleted.')
redirect_to providers_path
end
format.xml { render :nothing => true, :status => :no_content }
else
format.html do
flash[:error] = _('Provider was not deleted: %s') % provider.errors.full_messages.join(', ')
redirect_to providers_path
end
# FIXME: what to return in body of response, if anything?
format.xml do
raise(Aeolus::Conductor::API::Error.new(500, error))
end
end
end
end
protected
def test_connection(provider)
@provider.errors.clear
if @provider.update_availability
flash.now[:notice] = _('Successfully connected to Provider')
else
flash.now[:warning] = _('Failed to connect to Provider')
@provider.errors.add :url
end
end
def load_providers
@providers = Provider.includes(:provider_type).list_for_user(current_session, current_user,
Alberich::Privilege::VIEW).order("providers.name")
end
def disable_provider
@instances_to_terminate = @provider.instances_to_terminate
if @instances_to_terminate.any? and not params[:terminate]
render :action => "confirm_terminate"
return
end
res = @provider.disable(current_user)
if res[:failed_to_stop].present?
flash[:error] = {
:summary => _('Provider was not disabled. Failed to stop following instances:'),
:failures => res[:failed_to_stop]
}
elsif res[:failed_to_terminate].present?
flash[:error] = {
:summary => _('Provider was not disabled. Failed to change status to \'stopped\' for following instances:'),
:failures => res[:failed_to_terminate].map {|i| i.name}
}
else
flash[:notice] = _('Provider disabled.')
end
redirect_to edit_provider_path(@provider)
end
def provider_alerts(provider)
alerts = []
# Quota Alerts
provider.provider_accounts.each do |provider_account|
unless provider_account.quota.maximum_running_instances == nil
if provider_account.quota.maximum_running_instances < provider_account.quota.running_instances
alerts << {
:class => "critical",
:subject => "#{_('Quota')}",
:alert_type => "#{_('Account Quota Exceeded')}",
:path => edit_provider_provider_account_path(@provider,provider_account),
:description => _('Quota limit of running Instances for %s account has been exceeded.') % provider_account.name,
:account_id => provider_account.id
}
end
if (70..100) === provider_account.quota.percentage_used.round
alerts << {
:class => "warning",
:subject => "#{_('Quota')}",
:alert_type => "#{provider_account.quota.percentage_used.round}% #{_('Account Quota Reached')}",
:path => provider_provider_account_path(@provider,provider_account),
:description => "#{provider_account.quota.percentage_used.round}% "+ _('of Quota limit for running Instances for %s account has been reached.') % provider_account.name,
:account_id => provider_account.id
}
end
end
end
return alerts
end
def load_providers_types
provider_types = ProviderType.all.map do |type|
begin
label = I18n.translate!("providers.form.x_deltacloud_provider.#{type.deltacloud_driver}")
rescue
end
{ :id => type.id,
:label => label,
:name => type.name }
end
@labeled_provider_types = provider_types.select {|type| type[:label]}
@provider_type_options = provider_types.map {|type| [type[:name], type[:id]]}
end
def load_provider_tabs
@realms = @provider.provider_realms.apply_filters(:preset_filter_id => params[:provider_realms_preset_filter], :search_filter => params[:provider_realms_search])
#TODO add links to real data for history,properties,permissions
@tabs = [{ :name => _('Properties'),
:view => 'properties',
:id => 'properties' },
{ :name => _('Accounts'),
:view => 'provider_accounts/list',
:id => 'accounts',
:count => @provider.provider_accounts.count },
{ :name => _('Provider Realms'),
:view => 'provider_realms/list',
:id => 'realms',
:count => @realms.count },
{ :name => _('Hardware Profiles'),
:view => 'hardware_profiles',
:id => 'hardware_profiles',
:count => @provider.hardware_profiles.count}
]
add_permissions_tab(@provider, "edit_")
details_tab_name = params[:details_tab].blank? ? 'properties' : params[:details_tab]
details_tab_name = 'properties' unless
['accounts', 'realms', 'hardware_profiles', 'permissions'].include?(details_tab_name)
@details_tab = @tabs.find {|t| t[:id] == details_tab_name} || @tabs.first[:name].downcase
if @details_tab[:id] == 'accounts'
@provider_accounts = @provider.provider_accounts.
apply_filters(:preset_filter_id =>
params[:provider_accounts_preset_filter],
:search_filter => params[:provider_accounts_search]).
list_for_user(current_session, current_user, Alberich::Privilege::VIEW)
elsif @details_tab[:id] == 'hardware_profiles'
@hardware_profiles = @provider.hardware_profiles
end
#@permissions = @provider.permissions if @details_tab[:id] == 'roles'
@view = @details_tab[:view]
end
def load_headers
@header = [
{ :name => _('Provider Name'), :class => 'center',
:sortable => false },
{ :name => _('Provider Type'), :class => 'center',
:sortable => false },
{ :name => _('Running Instances (Current)'), :class => 'center',
:sortable => false },
{ :name => _('Pending (Current)'), :class => 'center',
:sortable => false },
{ :name => _('Errors (Current)'), :class => 'center',
:sortable => false },
{ :name => _('Running (Historical)'), :class => 'center',
:sortable => false },
{ :name => _('Errors (Historical)'), :class => 'center',
:sortable => false },
{ :name => _('Enabled'), :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(Alberich::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 >= instance.time_last_running &&
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