From: martyntaylor mtaylor@redhat.com
--- src/app/controllers/quota_controller.rb | 134 ++++++++++++++++++++++++ src/app/views/pool/show.html.erb | 1 + src/app/views/provider/accounts.html.erb | 1 + src/app/views/quota/edit.html.erb | 21 ++++ src/app/views/quota/new.html.erb | 21 ++++ src/app/views/quota/show.html.erb | 58 ++++++++++ src/db/migrate/20090802000000_create_quotas.rb | 15 ++- src/features/pool.feature | 27 +++++ src/features/step_definitions/pool_steps.rb | 10 ++ 9 files changed, 283 insertions(+), 5 deletions(-) create mode 100644 src/app/controllers/quota_controller.rb create mode 100644 src/app/views/quota/edit.html.erb create mode 100644 src/app/views/quota/new.html.erb create mode 100644 src/app/views/quota/show.html.erb
diff --git a/src/app/controllers/quota_controller.rb b/src/app/controllers/quota_controller.rb new file mode 100644 index 0000000..cf84ee9 --- /dev/null +++ b/src/app/controllers/quota_controller.rb @@ -0,0 +1,134 @@ +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +# Filters added to this controller apply to all controllers in the application. +# Likewise, all the methods added will be available for all controllers. + + +class QuotaController < ApplicationController + + def show + @parent = get_parent_object(params) + @parent_type = params[:parent_type] + if Quota.exists?(@parent.quota_id) + @quota = Quota.find(@parent.quota_id) + end + require_privilege(Privilege::QUOTA_VIEW, @parent) + end + + def new + @parent = get_parent_object(params) + @parent_type = params[:parent_type] + @name = get_parent_name(@parent, @parent_type) + require_privilege(Privilege::QUOTA_MODIFY, @parent) + end + + def create + #TODO Should be in Transaction + @parent = get_parent_object(params) + @parent_type = params[:parent_type] + + if !Quota.exists?(@parent.quota_id) + require_privilege(Privilege::QUOTA_MODIFY, @parent) + @quota = Quota.new(params[:quota]) + + # Populate Current Pool totals to Quota + @parent.instances.each do |instance| + hwp = HardwareProfile.find(instance.hardware_profile_id) + if instance.state == Instance::STATE_RUNNING + @quota.running_instances += 1 + @quota.running_memory += hwp.memory + #TODO Add CPUs + end + + if InstanceObserver::ACTIVE_STATES.include?(instance.state) + @quota.total_instances += 1 + @quota.total_storage += hwp.storage + end + end + + if @quota.save + flash[:notice] = "Quota Created!" + @parent.quota_id = @quota.id + @parent.save! + redirect_to :action => 'show', :id => @parent, :parent_type => @parent_type + else + render :action => :new + end + else + @parent.errors.add("quota_id", "There already exists a quota for this " + @parent_type) + render :action => :new + end + end + + def edit + @parent = get_parent_object(params) + @parent_type = params[:parent_type] + @name = get_parent_name(@parent, @parent_type) + + @quota = Quota.find(@parent.quota_id) + + require_privilege(Privilege::QUOTA_MODIFY, @parent) + end + + def update + @parent = @parent = get_parent_object(params) + @parent_type = params[:parent_type] + require_privilege(Privilege::QUOTA_MODIFY, @parent) + + @quota = Quota.find(@parent.quota_id) + if @quota.update_attributes(params[:quota]) + flash[:notice] = "Quota updated!" + redirect_to :action => 'show', :id => @parent, :parent_type => @parent_type + else + render :action => :edit + end + end + + def delete + @parent = get_parent_object(params) + @parent_type = params[:parent_type] + @quota = Quota.find(@parent.quota_id) + require_privilege(Privilege::QUOTA_MODIFY, @parent) + if @quota.delete + flash[:notice] = "Quota Deleted!" + redirect_to :action => 'show', :id => @parent, :parent_type => @parent_type + end + end + + private + def get_parent_object(params) + if params[:parent_type] == "pool" + return Pool.find(params[:id]) + elsif params[:parent_type] == "cloud_account" + return CloudAccount.find(params[:id]) + end + #TODO Throw no match to pool or cloud account exception + end + + def get_parent_name(parent, parent_type) + if parent_type == "pool" + return parent.name + elsif parent_type == "cloud_account" + return parent.username + end + #TODO Throw no match to pool or cloud account exception + end + + +end diff --git a/src/app/views/pool/show.html.erb b/src/app/views/pool/show.html.erb index 1230c64..8fb8fb6 100644 --- a/src/app/views/pool/show.html.erb +++ b/src/app/views/pool/show.html.erb @@ -39,3 +39,4 @@ <%= link_to "User access", {:controller => "permissions", :action => "list", :pool_id => @pool.id}, :class=>"actionlink" if has_view_perms? %> <%= link_to "Hardware Profiles", {:action => "hardware_profiles", :id => @pool.id}, :class=>"actionlink"%> <%= link_to "Realms", {:action => "realms", :id => @pool.id}, :class=>"actionlink"%> +<%= link_to "Quota", {:controller => "quota", :action => "show", :id => @pool, :parent_type => "pool"}, :class=>"actionlink"%> diff --git a/src/app/views/provider/accounts.html.erb b/src/app/views/provider/accounts.html.erb index ddc8f27..8ce98a8 100644 --- a/src/app/views/provider/accounts.html.erb +++ b/src/app/views/provider/accounts.html.erb @@ -15,6 +15,7 @@ <td><%= acct.username %></td> <td><%= link_to "Edit", {:controller => 'cloud_accounts', :action => 'edit', :id => acct.id} if has_account_modify?(@provider) %></td> <td><%= link_to "Delete", {:controller => 'cloud_accounts', :action => 'destroy', :id => acct.id} if has_account_modify?(@provider) && acct.destroyable? %></td> + <td><%= link_to "Quota", {:controller => 'quota', :action => 'show', :id => acct.id, :parent_type => "cloud_account"} %></td> </tr> <% } %> </tbody> diff --git a/src/app/views/quota/edit.html.erb b/src/app/views/quota/edit.html.erb new file mode 100644 index 0000000..5e742bb --- /dev/null +++ b/src/app/views/quota/edit.html.erb @@ -0,0 +1,21 @@ +<div class="dcloud_form"> + <%= error_messages_for @parent_type %> + <%= error_messages_for 'quota' %> + + <h2>Edit Quota for <%= @parent_type + ": " + @name%></legend></h2><br /> + + <% form_tag :action => 'update' do -%> + <%=hidden_field_tag 'id', @parent.id %> + <%=hidden_field_tag 'parent_type', @parent_type %> + + <ul> + <li><label>Max Running Instances</label><%= text_field :quota, :maximum_running_instances %></li> + <li><label>Max Running Memory</label><%= text_field :quota, :maximum_running_memory %></li> + <li><label>Max Running CPUs</label><%= text_field :quota, :maximum_running_cpus %></li> + <li><label>Max Total Instances</label><%= text_field :quota, :maximum_total_instances %></li> + <li><label>Max Total Storage</label><%= text_field :quota, :maximum_total_storage %></li> + </ul> + </fieldset> + <%= submit_tag "Save", :class => "submit" %> + <% end %> +</div> \ No newline at end of file diff --git a/src/app/views/quota/new.html.erb b/src/app/views/quota/new.html.erb new file mode 100644 index 0000000..1f99bd9 --- /dev/null +++ b/src/app/views/quota/new.html.erb @@ -0,0 +1,21 @@ +<div class="dcloud_form"> + <%= error_messages_for @parent_type %> + <%= error_messages_for 'quota' %> + + <h2>Create Quota for <%= @parent_type + ": " + @name%></legend></h2><br /> + + <% form_tag :action => 'create' do -%> + <%=hidden_field_tag 'id', @parent.id %> + <%=hidden_field_tag 'parent_type', @parent_type %> + + <ul> + <li><label>Max Running Instances</label><%= text_field :quota, :maximum_running_instances %></li> + <li><label>Max Running Memory</label><%= text_field :quota, :maximum_running_memory %></li> + <li><label>Max Running CPUs</label><%= text_field :quota, :maximum_running_cpus %></li> + <li><label>Max Total Instances</label><%= text_field :quota, :maximum_total_instances %></li> + <li><label>Max Total Storage</label><%= text_field :quota, :maximum_total_storage %></li> + </ul> + </fieldset> + <%= submit_tag "Save", :class => "submit" %> + <% end %> +</div> \ No newline at end of file diff --git a/src/app/views/quota/show.html.erb b/src/app/views/quota/show.html.erb new file mode 100644 index 0000000..a3b6366 --- /dev/null +++ b/src/app/views/quota/show.html.erb @@ -0,0 +1,58 @@ +<% if !@quota %> +<h1>There is no Quota on this <%= @parent_type %></h1> +<%= link_to "Create Quota", {:action => "new", :id => @parent, :parent_type => @parent_type}, :class=>"actionlink"%> +<% else %> + <table> + <thead> + <tr> + <th scope="col">Resource</th> + <th scope="col">Max Capacity</th> + <th scope="col">Used Capacity</th> + <th scope="col">Free Capacity</th> + <th scope="col">% Used</th> + </tr> + </thead> + <tbody> + <tr> + <td>Running Instances</td> + <td><%= @quota.maximum_running_instances %></td> + <td><%= @quota.running_instances %></td> + <td><%= @quota.maximum_running_instances - @quota.running_instances %></td> + <td><%= percentage = (100.to_f / @quota.maximum_running_instances) * @quota.running_instances + sprintf("%.2f", percentage) %></td> + </tr> + <tr> + <td>Running Memory</td> + <td><%= @quota.maximum_running_memory %></td> + <td><%= @quota.running_memory %></td> + <td><%= @quota.maximum_running_memory - @quota.running_memory %></td> + <td><%= percentage = (100.to_f / @quota.maximum_running_memory) * @quota.running_memory + sprintf("%.2f", percentage) %></td> + <td></td> + </tr> + <tr> + <td>Running CPUs</td> + <td><%= @quota.maximum_running_cpus %></td> + <td><%= @quota.running_cpus %></td> + <td><%= @quota.maximum_running_cpus - @quota.running_cpus %></td> + <td><%= sprintf("%.2f", (100.to_f / @quota.maximum_running_cpus) * @quota.running_cpus) %></td> + </tr> + <tr> + <td>Total Instances</td> + <td><%= @quota.maximum_total_instances %></td> + <td><%= @quota.total_instances %></td> + <td><%= @quota.maximum_total_instances - @quota.total_instances %></td> + <td><%= sprintf("%.2f", (100.to_f / @quota.maximum_total_instances) * @quota.total_instances) %></td> + </tr> + <tr> + <td>Total Storage</td> + <td><%= @quota.maximum_total_storage %></td> + <td><%= @quota.total_storage %></td> + <td><%= @quota.maximum_total_storage - @quota.total_storage %></td> + <td><%= sprintf("%.2f", (100.to_f / @quota.maximum_total_storage) * @quota.total_storage) %></td> + </tr> + </tbody> + </table> + <%= link_to "Edit", {:action => "edit", :id => @parent, :parent_type => @parent_type}, :class=>"actionlink"%> + <%= link_to "Delete", {:action => "delete", :id => @parent, :parent_type => @parent_type}, :class=>"actionlink"%> +<% end %> \ No newline at end of file diff --git a/src/db/migrate/20090802000000_create_quotas.rb b/src/db/migrate/20090802000000_create_quotas.rb index c61fb16..01c885d 100644 --- a/src/db/migrate/20090802000000_create_quotas.rb +++ b/src/db/migrate/20090802000000_create_quotas.rb @@ -20,11 +20,16 @@ class CreateQuotas < ActiveRecord::Migration def self.up create_table :quotas do |t| - t.integer :running_cpus - t.integer :running_memory - t.integer :running_instances - t.integer :total_storage - t.integer :total_instances + t.integer :running_cpus, :default => 0 + t.integer :running_memory, :default => 0 + t.integer :running_instances, :default => 0 + t.integer :total_storage, :default => 0 + t.integer :total_instances, :default => 0 + t.integer :maximum_running_cpus + t.integer :maximum_running_memory + t.integer :maximum_running_instances + t.integer :maximum_total_storage + t.integer :maximum_total_instances t.integer :lock_version, :default => 0 t.timestamps end diff --git a/src/features/pool.feature b/src/features/pool.feature index 5d8f219..7308dd4 100644 --- a/src/features/pool.feature +++ b/src/features/pool.feature @@ -45,3 +45,30 @@ Feature: Manage Pools When I follow "Realms" Then I should see "Europe" And I should see "United States" + + @tag + Scenario: View Pool's Quota Usage + Given I own a pool named "mockpool" + And the Pool has a quota with following capacities: + | resource | capacity | + | maximum_running_instances | 10 | + | maximum_running_memory | 10240 | + | maximum_running_cpus | 20 | + | maximum_total_instances | 15 | + | maximum_total_storage | 8500 | + | running_instances | 8 | + | running_memory | 9240 | + | running_cpus | 16 | + | total_instances | 15 | + | total_storage | 8400 | + And I am on the homepage + When I follow "mockpool" + Then I should be on the show pool page + When I follow "Quota" + Then I should see the following: + | Resource | Max Capacity | Used Capacity | Free Capacity | % Used | + | Running Instances | 10 | 8 | 2 | 80.00 | + | Running Memory | 10240 | 9240 | 1000 | 90.23 | + | Running CPUs | 20 | 16 | 4 | 80.00 | + | Total Instances | 15 | 15 | 0 | 100.00 | + | Total Storage | 8500 | 8400 | 100 | 98.82 | \ No newline at end of file diff --git a/src/features/step_definitions/pool_steps.rb b/src/features/step_definitions/pool_steps.rb index 3d97da6..3be5808 100644 --- a/src/features/step_definitions/pool_steps.rb +++ b/src/features/step_definitions/pool_steps.rb @@ -48,3 +48,13 @@ Then /^I should see the following:$/ do |table| end end end + +Given /^the Pool has a quota with following capacities:$/ do |table| + quota_hash = { "pool_id" => @pool, "pool" => @pool} + table.hashes.each do |hash| + quota_hash[hash["resource"]] = hash["capacity"] + end + + @quota = Factory(:quota, quota_hash) + puts @quota.pool_id +end \ No newline at end of file