mtaylor(a)redhat.com wrote:
From: martyntaylor <mtaylor(a)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 @@
+
+
+class QuotaController < ApplicationController
+
I'm thinking for these quota controller methods, :id should refer to the
quota ID. For show/edit you won't need parent_type since that can be
inferred from the quota object -- and for new/create, there would be no
id, but there'd be either a pool_id or cloud_account ID
+ def show
+ @parent = get_parent_object(params)
+ @parent_type = params[:parent_type]
+ if Quota.exists?((a)parent.quota_id)
+ @quota = Quota.find((a)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?((a)parent.quota_id)
+ require_privilege(Privilege::QUOTA_MODIFY, @parent)
+ @quota = Quota.new(params[:quota])
+
+ # Populate Current Pool totals to Quota
So if I'm understanding correctly, the updated model stores the defined
quota as well as caching the current usage towards the quota in the same
row.
Although not part of this patch, we've got to resolve how we're going to
keep this in sync, as it needs update every time an instance changes state.
+ @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)
Wherever we are aggregating current resource usage (which I don't think
will be this form), ACTIVE_STATES should apply towards the running_*
quota parameters, and _all_ instances, regardless of state should apply
to the total_* parameters
+ @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!
If @quota defines pool or cloud_account, then just saving the quota
should also save the association (at least that's how I think it should
work)
+ 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((a)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((a)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((a)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"%>
Since you're going to the quota controller, and the action is
'show',
I'd think the ID passed in should be the quota ID. This would need some
logic around the link to only include 'show' links where one is defined
-- with a 'create' link otherwise. In addition, the link should be
filtered on permission -- i.e. 'show' link if the user has quota view
permissions, etc.
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>
Similar comments here to the link on the pool show view.
</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
If we only render the 'show' link for pools/accounts with a quota
defined ,we won't need to have the 'there is no quota/create one here' bits.
@@ -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