From: martyntaylor mtaylor@redhat.com
--- src/app/controllers/pool_controller.rb | 30 +++++- src/app/controllers/pool_controller.rb~ | 138 ++++++++++++++++++++++++ src/app/models/instance_observer.rb | 9 +- src/app/views/pool/new_quota.html.erb | 18 +++ src/app/views/pool/quota.html.erb | 3 +- src/app/views/pool/show.html.erb~ | 42 +++++++ src/config/environment.rb | 2 +- src/db/migrate/20090802000000_create_quotas.rb | 6 - src/spec/models/instance_observer_spec.rb | 60 ++++++++--- 9 files changed, 279 insertions(+), 29 deletions(-) create mode 100644 src/app/controllers/pool_controller.rb~ create mode 100644 src/app/views/pool/new_quota.html.erb create mode 100644 src/app/views/pool/show.html.erb~
diff --git a/src/app/controllers/pool_controller.rb b/src/app/controllers/pool_controller.rb index d5fc8af..632d0cb 100644 --- a/src/app/controllers/pool_controller.rb +++ b/src/app/controllers/pool_controller.rb @@ -143,8 +143,32 @@ class PoolController < ApplicationController
def quota @pool = Pool.find(params[:id]) - puts @pool.id - @quota = Quota.find(:first, :conditions => { :pool_id => @pool }) + if Quota.exists?(@pool.quota_id) + @quota = Quota.find(@pool.quota_id) + end + require_privilege(Privilege::QUOTA_VIEW, @pool) + end + + def new_quota + @pool = Pool.find(params[:id]) + require_privilege(Privilege::POOL_MODIFY, @pool) + end + + def create_quota + if !Quota.exists?(@pool.quota_id) + @pool = Pool.find(params[:pool][:id]) + require_privilege(Privilege::POOL_MODIFY, @pool) + + @quota = Quota.new(params[:quota]) + @quota.save! + + @pool.quota_id = @quota + @pool.save! + + redirect_to :action => 'quota', :id => @pool + else + errors.add("quota_id", "There already exists a quota for this pool") + end end
-end +end \ No newline at end of file diff --git a/src/app/controllers/pool_controller.rb~ b/src/app/controllers/pool_controller.rb~ new file mode 100644 index 0000000..137fdd5 --- /dev/null +++ b/src/app/controllers/pool_controller.rb~ @@ -0,0 +1,138 @@ +# +# 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. + +require 'util/taskomatic' + +class PoolController < ApplicationController + before_filter :require_user + + def index + render :action => 'new' + end + + def show + #FIXME: clean this up, many error cases here + @pool = Pool.find(params[:id]) + require_privilege(Privilege::INSTANCE_VIEW,@pool) + # pass nil into Taskomatic as we're not working off a task here + Taskomatic.new(nil,logger).pool_refresh(@pool) + @pool.reload + @instances = @pool.instances + end + + def hardware_profiles + @pool = Pool.find(params[:id]) + @hardware_profiles = @pool.hardware_profiles + require_privilege(Privilege::POOL_VIEW, @pool) + end + + def realms + @pool = Pool.find(params[:id]) + @realm_names = @pool.realms + require_privilege(Privilege::POOL_VIEW,@pool) + end + + def new + require_privilege(Privilege::POOL_MODIFY) + @pool = Pool.new + end + + def create + require_privilege(Privilege::POOL_MODIFY) + + #FIXME: owner is set to current user for self-service account creation, + # but in the more general case we need a way for the admin to pick + # a user + params[:pool][:owner_id] = @current_user.id + + #FIXME: This should probably be in a transaction + @pool = Pool.new(params[:pool]) + perm = Permission.new(:user => @pool.owner, + :role => Role.find_by_name("Instance Creator and User"), + :permission_object => @pool) + perm.save! + # FIXME: do we need any more handling around save failures? What if perm + # creation fails? + flash[:notice] = "Pool added." + redirect_to :action => 'show', :id => @pool.id + end + + def delete + end + + def instances_paginate + @pool = Pool.find(params[:id]) + require_privilege(Privilege::POOL_VIEW, @pool) + + # datatables sends pagination in format: + # iDisplayStart - start index + # iDisplayLength - num of recs + # => we need to count page num + page = params[:iDisplayStart].to_i / Instance::per_page + + order_col_rec = Instance::COLUMNS[params[:iSortCol_0].to_i] + order_col = Instance::COLUMNS[2] unless order_col_rec && order_col_rec[:opts][:searchable] + order = order_col[:id] + " " + (params[:sSortDir_0] == 'desc' ? 'desc' : 'asc') + + @instances = Instance.search_filter(params[:sSearch], Instance::SEARCHABLE_COLUMNS).paginate( + :page => page + 1, + :order => order, + :conditions => {:pool_id => @pool.id} + ) + + render :json => { + :sEcho => params[:sEcho], + :iTotalRecords => @instances.total_entries, + :iTotalDisplayRecords => @instances.total_entries, + :aaData => @instances.map {|i| [i.id, "", i.name, i.state, i.hardware_profile.name, i.image.name]} + } + end + + + def accounts_for_pool + @pool = Pool.find(params[:pool_id]) + require_privilege(Privilege::ACCOUNT_VIEW,@pool) + @cloud_accounts = [] + all_accounts = CloudAccount.list_for_user(@current_user, Privilege::ACCOUNT_ADD) + all_accounts.each {|account| + @cloud_accounts << account unless @pool.cloud_accounts.map{|x| x.id}.include?(account.id) + } + end + + def add_account + @pool = Pool.find(params[:pool]) + @cloud_account = CloudAccount.find(params[:cloud_account]) + require_privilege(Privilege::ACCOUNT_ADD,@pool) + require_privilege(Privilege::ACCOUNT_ADD,@cloud_account) + Pool.transaction do + @pool.cloud_accounts << @cloud_account unless @pool.cloud_accounts.map{|x| x.id}.include?(@cloud_account.id) + @pool.save! + @pool.populate_realms_and_images([@cloud_account]) + end + redirect_to :action => 'show', :id => @pool.id + end + + def quota + @pool = Pool.find(params[:id]) + puts @pool.id + @quota = Quota.find(:first, :conditions => { :pool_id => @pool }) + end +end diff --git a/src/app/models/instance_observer.rb b/src/app/models/instance_observer.rb index ab1a095..9b0461a 100644 --- a/src/app/models/instance_observer.rb +++ b/src/app/models/instance_observer.rb @@ -1,6 +1,6 @@ class InstanceObserver < ActiveRecord::Observer
- ACTIVE_STATES = [ Instance::STATE_PENDING, Instance::STATE_RUNNING, Instance::STATE_SHUTTING_DOWN ] + ACTIVE_STATES = [ Instance::STATE_NEW, Instance::STATE_PENDING, Instance::STATE_RUNNING, Instance::STATE_SHUTTING_DOWN, Instance::STATE_STOPPED ]
def before_save(an_instance) if an_instance.changed? @@ -34,7 +34,8 @@ class InstanceObserver < ActiveRecord::Observer def update_quota(state_from, state_to, an_instance)
hwp = HardwareProfile.find(an_instance.hardware_profile_id) - quota = Quota.find(:first, :conditions => { :pool_id => an_instance.pool_id }) + pool = Pool.find(an_instance.pool_id) + quota = Quota.find(pool.quota_id)
if(quota) if state_to == Instance::STATE_RUNNING @@ -43,7 +44,7 @@ class InstanceObserver < ActiveRecord::Observer quota.running_memory += hwp.memory elsif state_from == Instance::STATE_RUNNING #TODO update running cpus - quota.running_instances -- + quota.running_instances -= 1 quota.running_memory -= hwp.memory end
@@ -55,7 +56,7 @@ class InstanceObserver < ActiveRecord::Observer quota.total_instances -= 1 end
- quota.save + quota.save! end end end diff --git a/src/app/views/pool/new_quota.html.erb b/src/app/views/pool/new_quota.html.erb new file mode 100644 index 0000000..4290862 --- /dev/null +++ b/src/app/views/pool/new_quota.html.erb @@ -0,0 +1,18 @@ +<div class="dcloud_form"> + <%= error_messages_for 'pool' %> + + <h2>Create Quota for Pool: <%= @pool.name %></legend></h2><br /> + + <% form_tag :action => 'create_quota' do -%> + <%=hidden_field :pool, :id %> + <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/pool/quota.html.erb b/src/app/views/pool/quota.html.erb index edd4463..09b227e 100644 --- a/src/app/views/pool/quota.html.erb +++ b/src/app/views/pool/quota.html.erb @@ -1,5 +1,6 @@ -<% if @quota == nil %> +<% if !@quota %> <h1>There is no quota associated with this pool</h1> +<%= link_to "Create a Quota", {:controller => "pool", :action => "new_quota", :id => @pool}, :class=>"actionlink"%> <% else %> <table> <thead> diff --git a/src/app/views/pool/show.html.erb~ b/src/app/views/pool/show.html.erb~ new file mode 100644 index 0000000..2b46781 --- /dev/null +++ b/src/app/views/pool/show.html.erb~ @@ -0,0 +1,42 @@ +<script type="text/javascript"> + function clickRow(ev) { + var box = $('input[name="image_id[]"]', ev.target.parentNode); + if ($(ev.target).attr('name') != "image_id[]") + box.attr('checked', !box.attr('checked')); + } + + function checkAll(ev) { + $('input[name="image_id[]"]', dataTable_instances_table).attr('checked', $(ev.target).attr('checked')) + } +</script> + +<%= datatable( + Instance::COLUMNS.map {|c| c[:opts]}, + { + :table_dom_id => 'instances_table', + :per_page => Instance::per_page, + :sort_by => "[3, 'asc']", + :serverside => true, + :ajax_source => url_for(:controller => 'pool', :action => 'instances_paginate') + "/#{@pool.id}", + :append => ".fnSetFilteringDelay()", + :persist_state => false, + :click_callback => "function(ev) {clickRow(ev);}", + } +) %> + +<table class="datatable display" id="instances_table"> + <thead> + <tr> + <% Instance::COLUMNS.each do |c| %> + <%= "<th>#{c[:header]}</th>" %> + <% end %> + </tr> + </thead> + <tbody> + </tbody> +</table> +<%= link_to "Add a new instance", {:controller => "instance", :action => "new", "instance[pool_id]" => @pool}, :class=>"actionlink"%> +<%= 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", {:action => "quota", :id => @pool}, :class=>"actionlink"%> diff --git a/src/config/environment.rb b/src/config/environment.rb index 1721126..9f0fa4f 100644 --- a/src/config/environment.rb +++ b/src/config/environment.rb @@ -43,7 +43,7 @@ Rails::Initializer.run do |config| config.gem "gettext", :lib => "gettext_rails" config.gem "gettext", :lib => "gettext_activerecord" config.gem "authlogic" - config.gem "deltacloud-client", :lib => "deltacloud" + config.gem "deltacloud-cliesrc/config/environment.rbnt", :lib => "deltacloud" config.gem "haml" config.gem "will_paginate"
diff --git a/src/db/migrate/20090802000000_create_quotas.rb b/src/db/migrate/20090802000000_create_quotas.rb index 29cc5bf..01c885d 100644 --- a/src/db/migrate/20090802000000_create_quotas.rb +++ b/src/db/migrate/20090802000000_create_quotas.rb @@ -20,12 +20,6 @@ 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 :pool_id t.integer :running_cpus, :default => 0 t.integer :running_memory, :default => 0 t.integer :running_instances, :default => 0 diff --git a/src/spec/models/instance_observer_spec.rb b/src/spec/models/instance_observer_spec.rb index 76514e5..7051955 100644 --- a/src/spec/models/instance_observer_spec.rb +++ b/src/spec/models/instance_observer_spec.rb @@ -4,7 +4,10 @@ describe InstanceObserver do
before(:each) do @timestamp = Time.now - @instance = Factory :new_instance + @hwp = Factory :mock_hwp1 + @pool = Factory :pool + @quota = Factory(:quota, :pool_id => @pool.id) + @instance = Factory(:new_instance, :pool => @pool, :hardware_profile => @hwp) end
it "should set started at timestamp when instance goes to state pending" do @@ -55,7 +58,7 @@ describe InstanceObserver do sleep(1)
@instance.state = Instance::STATE_SHUTTING_DOWN - @instance.save + @instance.save!
@instance.acc_running_time.should >= 1 @instance.acc_running_time.should <= 2 @@ -87,22 +90,51 @@ describe InstanceObserver do @instance.acc_stopped_time.should <= 2 end
- it "should update a pools quota when an associated instance state goes to running" do - hwp = Factory :mock_hwp1 - pool = Factory :pool - quota = Factory(:quota, :pool_id => pool.id) - instance = Factory(:new_instance, :pool => pool, :hardware_profile => hwp) + it "should update quota when a new pool instance is created" do + @quota = Quota.find(@quota) + + @quota.total_instances.should == 1 + @quota.total_storage.should == @hwp.storage + end + + it "should update quota when a pool instance goes into an inactive state" do + @quota = Quota.find(@quota) + + @instance.state = Instance::STATE_CREATE_FAILED + @quota.total_instances.should == 0 + @quota.total_storage.should == 0 + end + + it "should update a pools quota when an pool instance state goes to running" do + @quota = Quota.find(@quota) + + @instance.state = Instance::STATE_RUNNING + @instance.save!
- instance.state = Instance::STATE_RUNNING - instance.save + @quota = Quota.find(@quota.id) + @quota.running_instances.should == 1 + @quota.running_memory.should == @hwp.memory + #TODO test for cpus + + @quota.total_storage.should == @hwp.storage + @quota.total_instances.should == 1 + end + + it "should update a pools quota when an associated instance state goes from running another active state" do + @quota = Quota.find(@quota) + + @instance.state = Instance::STATE_RUNNING + @instance.save! + + @instance.state = Instance::STATE_SHUTTING_DOWN + @instance.save!
- quota = Quota.find_by_id(quota.id) - quota.running_instances.should == 1 - quota.running_memory.should == hwp.memory #TODO test for cpus + @quota.running_instances.should == 0 + @quota.running_memory.should == 0
- quota.total_storage.should == hwp.storage - quota.total_instances.should == 1 + @quota.total_storage.should == @hwp.storage + @quota.total_instances.should == 1 end
end \ No newline at end of file