[Ovirt-devel] [PATCH] initial push of smart pool code.

Scott Seago
2008-09-03T15:04:00Z

This isn't complete, but I want to get it committed before the repo
refactoring. This patch includes the controller refactoring needed for adding
the new smart pool controllers, but I have not yet added any code to the new
controllers. The models are in place for smart pools, but they aren't used
anywhere yet. In addition, I've added a new DirectoryPool pool type that is
used solely as a placeholder -- so  that we can have a common root for all
hardware and smart pools, and that we can organize smart pools by user.

Since we need this pushed before the repo refactoring, this patch should be
acked if it doesn't break anything. Separate review comments are, of course,
welcome -- but anything that doesn't break the WUI will be fixed in a
subsequent (post-repo-reorg) patch)

Signed-off-by: Scott Seago <sseago at redhat.com>
---
 wui/src/app/controllers/application.rb             |    3 +-
 wui/src/app/controllers/hardware_controller.rb     |  111 ++--------------
 wui/src/app/controllers/pool_controller.rb         |  132 ++++++++++++++++++++
 wui/src/app/controllers/resources_controller.rb    |  128 ++++---------------
 wui/src/app/controllers/smart_pool_controller.rb   |   22 ++++
 wui/src/app/controllers/tree_controller.rb         |    4 +-
 wui/src/app/models/directory_pool.rb               |   60 +++++++++
 wui/src/app/models/hardware_pool.rb                |   18 +--
 wui/src/app/models/host.rb                         |    3 +
 wui/src/app/models/pool.rb                         |   21 +++
 wui/src/app/models/smart_pool.rb                   |   47 +++++++
 wui/src/app/models/smart_pool_tag.rb               |   37 ++++++
 wui/src/app/models/storage_pool.rb                 |    3 +
 wui/src/app/models/vm.rb                           |    4 +
 wui/src/app/views/hardware/show_hosts.rhtml        |    2 +-
 wui/src/app/views/hardware/show_storage.rhtml      |    2 +-
 wui/src/app/views/layouts/_navigation_tabs.rhtml   |   10 +-
 wui/src/app/views/resources/edit.rhtml             |    4 +-
 wui/src/app/views/resources/quick_summary.rhtml    |   20 ++--
 wui/src/app/views/resources/show.rhtml             |   12 +-
 wui/src/app/views/resources/show_tasks.rhtml       |    4 +-
 wui/src/app/views/resources/show_users.rhtml       |    2 +-
 wui/src/app/views/resources/show_vms.rhtml         |   12 +-
 wui/src/db/migrate/017_add_smart_pools.rb          |   62 +++++++++
 wui/src/dutils/active_record_env.rb                |    4 +
 wui/src/script/grant_admin_privileges              |    4 +-
 .../betternestedset/lib/better_nested_set.rb       |   37 ++++--
 27 files changed, 503 insertions(+), 265 deletions(-)
 create mode 100644 wui/src/app/controllers/pool_controller.rb
 create mode 100644 wui/src/app/controllers/smart_pool_controller.rb
 create mode 100644 wui/src/app/models/directory_pool.rb
 create mode 100644 wui/src/app/models/smart_pool.rb
 create mode 100644 wui/src/app/models/smart_pool_tag.rb
 create mode 100644 wui/src/db/migrate/017_add_smart_pools.rb

diff --git a/wui/src/app/controllers/application.rb
b/wui/src/app/controllers/application.rb
index b95577a..3126748 100644
--- a/wui/src/app/controllers/application.rb
+++ b/wui/src/app/controllers/application.rb
@@ -29,8 +29,7 @@ class ApplicationController < ActionController::Base
   before_filter :pre_new, :only => [:new]
   before_filter :pre_create, :only => [:create]
   before_filter :pre_edit, :only => [:edit, :update, :destroy]
-  before_filter :pre_show, :only => [:show, :show_vms, :show_users,
-                                     :show_hosts, :show_storage]
+  before_filter :pre_show, :only => [:show]
   before_filter :authorize_admin, :only => [:new, :create, :edit, :update,
:destroy]
   before_filter :is_logged_in

diff --git a/wui/src/app/controllers/hardware_controller.rb
b/wui/src/app/controllers/hardware_controller.rb
index 7c1d916..091ce97 100644
--- a/wui/src/app/controllers/hardware_controller.rb
+++ b/wui/src/app/controllers/hardware_controller.rb
@@ -18,7 +18,7 @@
 # also available at http://www.gnu.org/copyleft/gpl.html.
 #

-class HardwareController < ApplicationController
+class HardwareController < PoolController

   XML_OPTS  = {
     :include => [ :storage_pools, :hosts, :quota ]
@@ -31,10 +31,8 @@ class HardwareController < ApplicationController
   verify :method => [:post, :delete], :only => :destroy,
          :redirect_to => { :action => :list }

-  before_filter :pre_json, :only => [:vm_pools_json, :users_json,
-                                     :storage_volumes_json, :show_tasks,
:tasks]
-  before_filter :pre_modify, :only => [:add_hosts, :move_hosts,
-                                       :add_storage, :move_storage,
+  before_filter :pre_modify, :only => [:add_hosts, :move_hosts,
+                                       :add_storage, :move_storage,
                                        :create_storage, :delete_storage]

   def index
@@ -60,27 +58,6 @@ class HardwareController < ApplicationController
     end
   end

-  def show
-    set_perms(@perm_obj)
-    unless @can_view
-      flash[:notice] = 'You do not have permission to view this hardware
pool: redirecting to top level'
-      respond_to do |format|
-        format.html { redirect_to :controller => "dashboard" }
-        format.xml { head :forbidden }
-      end
-      return
-    end
-    respond_to do |format|
-      format.html {
-        render :layout => 'tabs-and-content' if params[:ajax]
-        render :layout => false if params[:nolayout]
-      }
-      format.xml {
-        render :xml => @pool.to_xml(XML_OPTS)
-      }
-    end
-  end
-
   def json_view_tree
     json_tree_internal(Permission::PRIV_VIEW, false)
   end
@@ -121,11 +98,6 @@ class HardwareController < ApplicationController
     show
   end

-  def show_users
-    @roles = Permission::ROLES.keys
-    show
-  end
-
   def show_hosts
     @hardware_pools = HardwarePool.find :all
     show
@@ -145,53 +117,23 @@ class HardwareController < ApplicationController
                    ["Host Task", "HostTask"],
                    ["Storage Task", "StorageTask",
"break"],
                    ["Show All", ""]]
-    @task_states = [["Queued", Task::STATE_QUEUED],
-                    ["Running", Task::STATE_RUNNING],
-                    ["Paused", Task::STATE_PAUSED],
-                    ["Finished", Task::STATE_FINISHED],
-                    ["Failed", Task::STATE_FAILED],
-                    ["Canceled", Task::STATE_CANCELED,
"break"],
-                    ["Show All", ""]]
-    params[:page]=1
-    params[:sortname]="tasks.created_at"
-    params[:sortorder]="desc"
-    @tasks = tasks_internal
-    show
-  end
-
-  def tasks
-    render :json => tasks_internal.to_json
+    super
   end

   def tasks_internal
     @task_type = params[:task_type]
     @task_type ||=""
-    @task_state = params[:task_state]
-    @task_state ||=Task::STATE_QUEUED
-    conditions = {}
-    conditions[:type] = @task_type unless @task_type.empty?
-    conditions[:state] = @task_state unless @task_state.empty?
-    find_opts = {:include => [:storage_pool, :host, :vm]}
-    find_opts[:conditions] = conditions unless conditions.empty?
-    attr_list = []
-    attr_list << :id if params[:checkboxes]
-    attr_list += [:type_label, :task_obj, :action, :state, :user, :created_at,
:args, :message]
-    json_hash(@pool.tasks, attr_list, [:all], find_opts)
-  end
-
-  def quick_summary
-    pre_show
-    render :layout => 'selection'
+    super
   end

   def hosts_json
     if params[:exclude_host]
-      pre_json
+      pre_show
       hosts = @pool.hosts
       find_opts = {:conditions => ["id != ?",
params[:exclude_host]]}
       include_pool = false
     elsif params[:id]
-      pre_json
+      pre_show
       hosts = @pool.hosts
       find_opts = {}
       include_pool = false
@@ -219,14 +161,9 @@ class HardwareController < ApplicationController
               {:finder => 'call_finder', :conditions =>
["type = 'VmResourcePool'"]})
   end

-  def users_json
-    json_list(@pool.permissions,
-              [:grid_id, :uid, :user_role, :source])
-  end
-
   def storage_pools_json
     if params[:id]
-      pre_json
+      pre_show
       storage_pools = @pool.storage_pools
       find_opts = {}
       include_pool = false
@@ -258,7 +195,7 @@ class HardwareController < ApplicationController
   def new
     @resource_type = params[:resource_type]
     @resource_ids = params[:resource_ids]
-    render :layout => 'popup'
+    super
   end

   def create
@@ -293,10 +230,6 @@ class HardwareController < ApplicationController
     end
   end

-  def edit
-    render :layout => 'popup'
-  end
-
   def update
     if params[:hardware_pool]
       # FIXME: For the REST API, we allow moving hosts/storage through
@@ -455,28 +388,23 @@ class HardwareController < ApplicationController
                                       :alert => alert } }
       format.xml { head status }
     end
-  end
+   end

-  private
+  protected
   #filter methods
   def pre_new
     @pool = HardwarePool.new
-    @parent = Pool.find(params[:parent_id])
-    @perm_obj = @parent
-    @current_pool_id=@parent.id
+    super
   end
   def pre_create
     # FIXME: REST and browsers send params differently. Should be fixed
     # in the views
     if params[:pool]
       @pool = HardwarePool.new(params[:pool])
-      @parent = Pool.find(params[:parent_id])
     else
       @pool = HardwarePool.new(params[:hardware_pool])
-      @parent = Pool.find(params[:hardware_pool][:parent_id])
     end
-    @perm_obj = @parent
-    @current_pool_id=@parent.id
+    super
   end
   def pre_edit
     @pool = HardwarePool.find(params[:id])
@@ -486,18 +414,7 @@ class HardwareController < ApplicationController
   end
   def pre_show
     @pool = HardwarePool.find(params[:id])
-    @perm_obj = @pool
-    @current_pool_id=@pool.id
-    set_perms(@perm_obj)
-    unless @can_view
-      flash[:notice] = 'You do not have permission to view this Hardware
Pool: redirecting to top level'
-      # FIXME: figure out the return type and render appropriately
-      redirect_to :action => 'list'
-      return
-    end
-  end
-  def pre_json
-    pre_show
+    super
   end
   def pre_modify
     pre_edit
diff --git a/wui/src/app/controllers/pool_controller.rb
b/wui/src/app/controllers/pool_controller.rb
new file mode 100644
index 0000000..ce41701
--- /dev/null
+++ b/wui/src/app/controllers/pool_controller.rb
@@ -0,0 +1,132 @@
+#
+# Copyright (C) 2008 Red Hat, Inc.
+# Written by Scott Seago <sseago at redhat.com>
+#
+# 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.
+#
+
+class PoolController < ApplicationController
+
+  before_filter :pre_show_pool, :only => [:show_vms, :show_users,
+                                          :show_hosts, :show_storage,
+                                          :users_json, :show_tasks, :tasks,
+                                          :vms_json, :vm_pools_json,
+                                          :storage_volumes_json,
:quick_summary]
+
+  def show
+    respond_to do |format|
+      format.html {
+        render :layout => 'tabs-and-content' if params[:ajax]
+        render :layout => false if params[:nolayout]
+      }
+      format.xml {
+        render :xml => @pool.to_xml(XML_OPTS)
+      }
+    end
+  end
+
+  def quick_summary
+    render :layout => 'selection'
+  end
+
+  # resource's users list page
+  def show_users
+    @roles = Permission::ROLES.keys
+    show
+  end
+
+  def users_json
+    json_list(@pool.permissions,
+              [:grid_id, :uid, :user_role, :source])
+  end
+
+  def show_tasks
+    @task_states = [["Queued", Task::STATE_QUEUED],
+                    ["Running", Task::STATE_RUNNING],
+                    ["Paused", Task::STATE_PAUSED],
+                    ["Finished", Task::STATE_FINISHED],
+                    ["Failed", Task::STATE_FAILED],
+                    ["Canceled", Task::STATE_CANCELED,
"break"],
+                    ["Show All", ""]]
+    params[:page]=1
+    params[:sortname]="tasks.created_at"
+    params[:sortorder]="desc"
+    @tasks = tasks_internal
+    show
+  end
+
+  def tasks
+    render :json => tasks_internal.to_json
+  end
+
+  def tasks_internal
+    @task_state = params[:task_state]
+    @task_state ||=Task::STATE_QUEUED
+    conditions = {}
+    conditions[:type] = @task_type unless @task_type.empty?
+    conditions[:state] = @task_state unless @task_state.empty?
+    find_opts = {:include => [:storage_pool, :host, :vm]}
+    find_opts[:conditions] = conditions unless conditions.empty?
+    attr_list = []
+    attr_list << :id if params[:checkboxes]
+    attr_list += [:type_label, :task_obj, :action, :state, :user, :created_at,
:args, :message]
+    json_hash(@pool.tasks, attr_list, [:all], find_opts)
+  end
+
+  def new
+    render :layout => 'popup'
+  end
+
+  def edit
+    render :layout => 'popup'
+  end
+
+  protected
+  def pre_new
+    @parent = Pool.find(params[:parent_id])
+    @perm_obj = @parent
+    @redir_controller = @perm_obj.get_controller
+    @current_pool_id=@parent.id
+  end
+  def pre_create
+    #this is currently only true for the rest API for hardware pools
+    if params[:hardware_pool]
+      @parent = Pool.find(params[:hardware_pool][:parent_id])
+    else
+      @parent = Pool.find(params[:parent_id])
+    end
+    @perm_obj = @parent
+    @redir_controller = @perm_obj.get_controller
+    @current_pool_id=@parent.id
+  end
+  def pre_show_pool
+    pre_show
+  end
+  def pre_show
+    @perm_obj = @pool
+    @current_pool_id=@pool.id
+    set_perms(@perm_obj)
+    unless @can_view
+      flash[:notice] = 'You do not have permission to view this pool pool:
redirecting to top level'
+      respond_to do |format|
+        format.html { redirect_to :controller => "dashboard" }
+        format.xml { head :forbidden }
+      end
+      return
+    end
+  end
+
+end
diff --git a/wui/src/app/controllers/resources_controller.rb
b/wui/src/app/controllers/resources_controller.rb
index 5a8276c..a0a65a6 100644
--- a/wui/src/app/controllers/resources_controller.rb
+++ b/wui/src/app/controllers/resources_controller.rb
@@ -17,14 +17,12 @@
 # MA  02110-1301, USA.  A copy of the GNU General Public License is
 # also available at http://www.gnu.org/copyleft/gpl.html.

-class ResourcesController < ApplicationController
+class ResourcesController < PoolController
   def index
     list
     render :action => 'list'
   end

-  before_filter :pre_json, :only => [:vms_json, :users_json,
-                                     :show_tasks, :tasks]
   before_filter :pre_vm_actions, :only => [:vm_actions]

   # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
@@ -49,17 +47,7 @@ class ResourcesController < ApplicationController
                       ["Resume", VmTask::ACTION_RESUME_VM],
                       ["Save", VmTask::ACTION_SAVE_VM],
                       ["Restore", VmTask::ACTION_RESTORE_VM]]
-    if params[:ajax]
-      render :layout => 'tabs-and-content'
-    end
-    if params[:nolayout]
-      render :layout => false
-    end
-  end
-
-  def quick_summary
-    pre_show
-    render :layout => 'selection'
+    super
   end

   # resource's vms list page
@@ -73,81 +61,36 @@ class ResourcesController < ApplicationController
     show
   end

-  # resource's users list page
-  def show_users
-    @roles = Permission::ROLES.keys
-    show
-  end
-
-  def show_tasks
-    @task_states = [["Queued", Task::STATE_QUEUED],
-                    ["Running", Task::STATE_RUNNING],
-                    ["Paused", Task::STATE_PAUSED],
-                    ["Finished", Task::STATE_FINISHED],
-                    ["Failed", Task::STATE_FAILED],
-                    ["Canceled", Task::STATE_CANCELED,
"break"],
-                    ["Show All", ""]]
-    params[:page]=1
-    params[:sortname]="tasks.created_at"
-    params[:sortorder]="desc"
-    @tasks = tasks_internal
-    show
-  end
-
-  def tasks
-    render :json => tasks_internal.to_json
-  end
-
   def tasks_internal
+    @task_type = ""
     @task_state = params[:task_state]
-    @task_state ||=Task::STATE_QUEUED
-    conditions = {}
-    conditions[:state] = @task_state unless @task_state.empty?
-    find_opts = {:include => [:storage_pool, :host, :vm]}
-    find_opts[:conditions] = conditions unless conditions.empty?
-    attr_list = []
-    attr_list << :id if params[:checkboxes]
-    attr_list += [:type_label, :task_obj, :action, :state, :user, :created_at,
:args, :message]
-    json_hash(@vm_resource_pool.tasks, attr_list, [:all], find_opts)
+    super
   end

   def vms_json
-    json_list(@vm_resource_pool.vms,
+    json_list(@pool.vms,
               [:id, :description, :uuid, :num_vcpus_allocated,
:memory_allocated_in_mb, :vnic_mac_addr, :state, :id])
   end

-  def users_json
-    json_list(@vm_resource_pool.permissions,
-              [:grid_id, :uid, :user_role, :source])
-  end
-
-  def new
-    render :layout => 'popup'
-  end
-
   def create
     begin
-      @vm_resource_pool.create_with_parent(@parent)
+      @pool.create_with_parent(@parent)
       render :json => { :object => "vm_resource_pool", :success
=> true,
                         :alert => "Virtual Machine Pool was
successfully created." }
     rescue
       render :json => { :object => "vm_resource_pool", :success
=> false,
-                        :errors =>
@vm_resource_pool.errors.localize_error_messages.to_a}
+                        :errors =>
@pool.errors.localize_error_messages.to_a}
     end
   end

-  def edit
-    render :layout => 'popup'
-  end
-
   def update
     begin
-      @vm_resource_pool.update_attributes!(params[:vm_resource_pool])
+      @pool.update_attributes!(params[:vm_resource_pool])
       render :json => { :object => "vm_resource_pool", :success
=> true,
                         :alert => "Virtual Machine Pool was
successfully modified." }
     rescue
       render :json => { :object => "vm_resource_pool", :success
=> false,
-                        :errors =>
@vm_resource_pool.errors.localize_error_messages.to_a}
+                        :errors =>
@pool.errors.localize_error_messages.to_a}
     end
   end

@@ -174,7 +117,7 @@ class ResourcesController < ApplicationController
   end

   def destroy
-    if @vm_resource_pool.destroy
+    if @pool.destroy
       alert="Virtual Machine Pool was successfully deleted."
       success=true
     else
@@ -192,7 +135,7 @@ class ResourcesController < ApplicationController
     @success_list = []
     @failure_list = []
     begin
-      @vm_resource_pool.transaction do
+      @pool.transaction do
         @vms.each do |vm|
           if vm.queue_action(@user, @action)
             @success_list << vm
@@ -212,45 +155,30 @@ class ResourcesController < ApplicationController

   protected
   def pre_new
-    @vm_resource_pool = VmResourcePool.new
-    @parent = Pool.find(params[:parent_id])
-    @perm_obj = @parent
-    @redir_controller = @perm_obj.get_controller
-    @current_pool_id=@parent.id
+    @pool = VmResourcePool.new
+    super
   end
   def pre_create
-    @vm_resource_pool = VmResourcePool.new(params[:vm_resource_pool])
-    @parent = Pool.find(params[:parent_id])
-    @perm_obj = @parent
-    @redir_controller = @perm_obj.get_controller
-    @current_pool_id=@parent.id
-  end
-  def pre_show
-    @vm_resource_pool = VmResourcePool.find(params[:id])
-    @perm_obj = @vm_resource_pool
-    @current_pool_id=@vm_resource_pool.id
-    set_perms(@perm_obj)
-    @is_hwpool_admin = @vm_resource_pool.parent.can_modify(@user)
-    unless @can_view
-      flash[:notice] = 'You do not have permission to view this VM Resource
Pool: redirecting to top level'
-      redirect_to :action => 'dashboard'
-    end
+    @pool = VmResourcePool.new(params[:vm_resource_pool])
+    super
   end
   def pre_edit
-    @vm_resource_pool = VmResourcePool.find(params[:id])
-    @parent = @vm_resource_pool.parent
-    @perm_obj = @vm_resource_pool.parent
-    @redir_obj = @vm_resource_pool
-    @current_pool_id=@vm_resource_pool.id
+    @pool = VmResourcePool.find(params[:id])
+    @parent = @pool.parent
+    @perm_obj = @pool.parent
+    @redir_obj = @pool
+    @current_pool_id=@pool.id
   end
-  def pre_json
-    pre_show
+  def pre_show
+    @pool = VmResourcePool.find(params[:id])
+    @is_hwpool_admin = @pool.parent.can_modify(@user)
+    super
   end
   def pre_vm_actions
-    @vm_resource_pool = VmResourcePool.find(params[:id])
-    @parent = @vm_resource_pool.parent
-    @perm_obj = @vm_resource_pool
-    @redir_obj = @vm_resource_pool
+    @pool = VmResourcePool.find(params[:id])
+    @parent = @pool.parent
+    @perm_obj = @pool
+    @redir_obj = @pool
     authorize_user
   end

diff --git a/wui/src/app/controllers/smart_pool_controller.rb
b/wui/src/app/controllers/smart_pool_controller.rb
new file mode 100644
index 0000000..eb087a6
--- /dev/null
+++ b/wui/src/app/controllers/smart_pool_controller.rb
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2008 Red Hat, Inc.
+# Written by Scott Seago <sseago at redhat.com>
+#
+# 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.
+#
+
+class SmartPoolController < ApplicationController
+end
diff --git a/wui/src/app/controllers/tree_controller.rb
b/wui/src/app/controllers/tree_controller.rb
index c7dbe35..5ad8426 100644
--- a/wui/src/app/controllers/tree_controller.rb
+++ b/wui/src/app/controllers/tree_controller.rb
@@ -1,10 +1,10 @@
 class TreeController < ApplicationController

   def fetch_nav
-    @pools = Pool.root.full_set_nested(:method => :json_hash_element)
+    @pools = HardwarePool.get_default_pool.full_set_nested(:method =>
:json_hash_element)
   end

   def fetch_json
-    render :json => Pool.root.full_set_nested(:method =>
:json_hash_element).to_json
+    render :json => HardwarePool.get_default_pool.full_set_nested(:method
=> :json_hash_element).to_json
   end
 end
diff --git a/wui/src/app/models/directory_pool.rb
b/wui/src/app/models/directory_pool.rb
new file mode 100644
index 0000000..f62d980
--- /dev/null
+++ b/wui/src/app/models/directory_pool.rb
@@ -0,0 +1,60 @@
+#
+# Copyright (C) 2008 Red Hat, Inc.
+# Written by Scott Seago <sseago at redhat.com>
+#
+# 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.
+
+class DirectoryPool < Pool
+
+  ROOT_NAME     = "root"
+  HARDWARE_ROOT = "hardware"
+  SMART_ROOT    = "users"
+
+  def self.get_directory_root
+    self.root(:conditions=>"type='DirectoryPool'")
+  end
+
+  def self.get_hardware_root
+    dir_root = get_directory_root
+    dir_root ? dir_root.named_child(HARDWARE_ROOT) : nil
+  end
+
+  def self.get_smart_root
+    dir_root = get_directory_root
+    dir_root ? dir_root.named_child(SMART_ROOT) : nil
+  end
+
+  def self.get_user_root(user)
+    smart_root = get_smart_root
+    smart_root ? smart_root.named_child(user) : nil
+  end
+
+  def self.get_or_create_user_root(user)
+    user_root = get_user_root(user)
+    unless user_root
+      DirectoryPool.transaction do
+        user_root = DirectoryPool.new(:name => user)
+        user_root.create_with_parent(get_smart_root)
+        permission = Permission.new({:pool_id => user_root.id,
+                                     :uid => user,
+                                     :user_role =>
Permission::ROLE_SUPER_ADMIN})
+        #we don't need save_with_new_children here since there are no
children yet
+        permission.save!
+      end
+    end
+  end
+
+end
diff --git a/wui/src/app/models/hardware_pool.rb
b/wui/src/app/models/hardware_pool.rb
index 5409862..160f663 100644
--- a/wui/src/app/models/hardware_pool.rb
+++ b/wui/src/app/models/hardware_pool.rb
@@ -19,6 +19,8 @@

 class HardwarePool < Pool

+  DEFAULT_POOL_NAME = "default"
+
   has_many :tasks, :dependent => :nullify
   def all_storage_volumes
     StorageVolume.find(:all, :include => {:storage_pool =>
:hardware_pool}, :conditions => "pools.id = #{id}")
@@ -34,7 +36,8 @@ class HardwarePool < Pool

   # note: doesn't currently join w/ permissions
   def self.get_default_pool
-    self.root
+    hw_root = DirectoryPool.get_hardware_root
+    hw_root ? hw_root.named_child(DEFAULT_POOL_NAME) : nil
   end

   def create_with_resources(parent, resource_type= nil, resource_ids=[])
@@ -98,17 +101,4 @@ class HardwarePool < Pool
     return {:total => total, :labels => labels}
   end

-  def self.find_by_path(path)
-    segs = path.split("/")
-    unless segs.shift.empty?
-      raise "Path must be absolute, but is #{path}"
-    end
-    default_pool = get_default_pool
-    if segs.shift == default_pool.name
-      segs.inject(default_pool) do |pool, seg|
-        pool.sub_hardware_pools.find { |p| p.name == seg } if pool
-      end
-    end
-  end
-
 end
diff --git a/wui/src/app/models/host.rb b/wui/src/app/models/host.rb
index 5b32653..3fc3abb 100644
--- a/wui/src/app/models/host.rb
+++ b/wui/src/app/models/host.rb
@@ -40,6 +40,9 @@ class Host < ActiveRecord::Base
     end
   end

+  has_many :smart_pool_tags, :as => :tagged, :dependent => :destroy
+  has_many :smart_pools, :through => :smart_pool_tags
+
   acts_as_xapian :texts => [ :hostname, :uuid, :hypervisor_type, :arch ],
                  :values => [ [ :created_at, 0, "created_at",
:date ],
                               [ :updated_at, 1, "updated_at", :date ]
],
diff --git a/wui/src/app/models/pool.rb b/wui/src/app/models/pool.rb
index 6599c72..9d71fa5 100644
--- a/wui/src/app/models/pool.rb
+++ b/wui/src/app/models/pool.rb
@@ -42,12 +42,15 @@ class Pool < ActiveRecord::Base
     end
   end

+  has_many :smart_pool_tags, :as => :tagged, :dependent => :destroy
+  has_many :smart_pools, :through => :smart_pool_tags

   # used to allow parent traversal before obj is saved to the db
   # (needed for view code 'create' form)
   attr_accessor :tmp_parent

   validates_presence_of :name
+  validates_uniqueness_of :name, :scope => :parent_id

   # overloading this method such that we can use permissions.admins to get all
the admins for an object
   has_many :permissions, :dependent => :destroy, :order => "id
ASC" do
@@ -125,6 +128,11 @@ class Pool < ActiveRecord::Base
     self_and_siblings.select {|pool| pool[:type] == self.class.name}
   end

+  def named_child(child_name)
+    matches =
children(:conditions=>"name='#{child_name}'")
+    matches ? matches[0] : nil
+  end
+
   def can_view(user)
     has_privilege(user, Permission::PRIV_VIEW)
   end
@@ -252,6 +260,19 @@ class Pool < ActiveRecord::Base
     permissions.collect {|perm| perm.uid}
   end

+  def self.find_by_path(path)
+    segs = path.split("/")
+    unless segs.shift.empty?
+      raise "Path must be absolute, but is #{path}"
+    end
+    default_pool = DirectoryPool.get_directory_root
+    if segs.shift == default_pool.name
+      segs.inject(default_pool) do |pool, seg|
+        pool.children.find { |p| p.name == seg } if pool
+      end
+    end
+  end
+
   protected
   def traverse_parents
     if id
diff --git a/wui/src/app/models/smart_pool.rb b/wui/src/app/models/smart_pool.rb
new file mode 100644
index 0000000..e672c5b
--- /dev/null
+++ b/wui/src/app/models/smart_pool.rb
@@ -0,0 +1,47 @@
+#
+# Copyright (C) 2008 Red Hat, Inc.
+# Written by Scott Seago <sseago at redhat.com>
+#
+# 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.
+
+class SmartPool < Pool
+  has_many :smart_pool_tags, :dependent => :destroy
+  has_many :tagged_pools, :through => :smart_pool_tags, :source => :pool,
+                   :conditions => "smart_pool_tags.tagged_type =
'Pool'"
+  has_many :tagged_storage_pools, :through => :smart_pool_tags,
+                           :source => :storage_pool,
+                   :conditions => "smart_pool_tags.tagged_type =
'StoragePool'"
+  has_many :tagged_hosts, :through => :smart_pool_tags, :source => :host,
+                   :conditions => "smart_pool_tags.tagged_type =
'Host'"
+  has_many :tagged_vms,   :through => :smart_pool_tags, :source => :vm,
+                   :conditions => "smart_pool_tags.tagged_type =
'Vm'"
+
+
+  def create_for_user(user)
+    create_with_parent(DirectoryPool.get_or_create_user_root(user))
+  end
+
+  def add_item(item)
+    tag = SmartPoolTag.new(:smart_pool => self, :tagged => item)
+    tag.save!
+  end
+  def remove_item(item)
+    smart_pool_tags.find(:first, :conditions=> {
+                                  :tagged_type=>item.class.base_class.to_s,
+                                  :tagged_id=>item.id}).destroy
+  end
+
+end
diff --git a/wui/src/app/models/smart_pool_tag.rb
b/wui/src/app/models/smart_pool_tag.rb
new file mode 100644
index 0000000..e135ddf
--- /dev/null
+++ b/wui/src/app/models/smart_pool_tag.rb
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2008 Red Hat, Inc.
+# Written by Scott Seago <sseago at redhat.com>
+#
+# 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.
+
+class SmartPoolTag < ActiveRecord::Base
+  belongs_to :smart_pool
+  belongs_to :tagged,       :polymorphic => true
+  belongs_to :pool,         :class_name => "Pool",
+                            :foreign_key => "tagged_id"
+  belongs_to :storage_pool, :class_name => "StoragePool",
+                            :foreign_key => "tagged_id"
+  belongs_to :host,         :class_name => "Host",
+                            :foreign_key => "tagged_id"
+  belongs_to :vm,           :class_name => "Vm",
+                            :foreign_key => "tagged_id"
+
+  validates_uniqueness_of :smart_pool_id, :scope => [:tagged_id,
:tagged_type]
+
+  def tagged_type=(sType)
+    super(sType.to_s.classify.constantize.base_class.to_s)
+  end
+end
diff --git a/wui/src/app/models/storage_pool.rb
b/wui/src/app/models/storage_pool.rb
index 460b49d..bc98f8e 100644
--- a/wui/src/app/models/storage_pool.rb
+++ b/wui/src/app/models/storage_pool.rb
@@ -30,6 +30,9 @@ class StoragePool < ActiveRecord::Base
     end
   end

+  has_many :smart_pool_tags, :as => :tagged, :dependent => :destroy
+  has_many :smart_pools, :through => :smart_pool_tags
+
   validates_presence_of :ip_addr, :hardware_pool_id

   acts_as_xapian :texts => [ :ip_addr, :target, :export_path, :type ],
diff --git a/wui/src/app/models/vm.rb b/wui/src/app/models/vm.rb
index 80c7efb..cde28af 100644
--- a/wui/src/app/models/vm.rb
+++ b/wui/src/app/models/vm.rb
@@ -28,6 +28,10 @@ class Vm < ActiveRecord::Base
     end
   end
   has_and_belongs_to_many :storage_volumes
+
+  has_many :smart_pool_tags, :as => :tagged, :dependent => :destroy
+  has_many :smart_pools, :through => :smart_pool_tags
+
   validates_presence_of :uuid, :description, :num_vcpus_allocated,
                         :memory_allocated_in_mb, :memory_allocated,
:vnic_mac_addr

diff --git a/wui/src/app/views/hardware/show_hosts.rhtml
b/wui/src/app/views/hardware/show_hosts.rhtml
index f88c64b..31c575d 100644
--- a/wui/src/app/views/hardware/show_hosts.rhtml
+++ b/wui/src/app/views/hardware/show_hosts.rhtml
@@ -27,7 +27,7 @@
     hosts = get_selected_hosts()
     if (validate_selected(hosts, "host")) {
       $.post('<%= url_for :controller => "hardware",
:action => "move_hosts", :id => @pool %>',
-             { resource_ids: hosts.toString(), target_pool_id: <%=
Pool.root.id %> },
+             { resource_ids: hosts.toString(), target_pool_id: <%=
HardwarePool.get_default_pool.id %> },
               function(data,status){

$tabs.tabs("load",$tabs.data('selected.tabs'));
 		if (data.alert) {
diff --git a/wui/src/app/views/hardware/show_storage.rhtml
b/wui/src/app/views/hardware/show_storage.rhtml
index 02b5180..564b9ed 100644
--- a/wui/src/app/views/hardware/show_storage.rhtml
+++ b/wui/src/app/views/hardware/show_storage.rhtml
@@ -18,7 +18,7 @@
     storage = get_selected_storage()
     if (validate_selected(storage, "storage pool")) {
       $.post('<%= url_for :controller => "hardware",
:action => "move_storage", :id => @pool %>',
-             { resource_ids: storage.toString(), target_pool_id: <%=
Pool.root.id %> },
+             { resource_ids: storage.toString(), target_pool_id: <%=
HardwarePool.get_default_pool.id %> },
               function(data,status){

$tabs.tabs("load",$tabs.data('selected.tabs'));
 		if (data.alert) {
diff --git a/wui/src/app/views/layouts/_navigation_tabs.rhtml
b/wui/src/app/views/layouts/_navigation_tabs.rhtml
index 629ab93..4b0f18d 100644
--- a/wui/src/app/views/layouts/_navigation_tabs.rhtml
+++ b/wui/src/app/views/layouts/_navigation_tabs.rhtml
@@ -15,7 +15,7 @@
<%= link_to "User
Access", {:action => 'show_users', :id => @pool.id, :nolayout
=> :true}, :title => "content area" %>
<%= link_to "Tasks",
{:action => 'show_tasks', :id => @pool.id, :nolayout => :true},
:title => "content area" %>

-<% elsif controller.controller_name == "resources" and
@vm_resource_pool != nil %>
+<% elsif controller.controller_name == "resources" and @pool !=
nil %>

     $(document).ready(function(){
       $tabs = $("#resources_nav_tabs").tabs({
@@ -25,10 +25,10 @@
     });


-    <li id="nav_summary"
class="ui-tabs-selected"><%= link_to "Summary",
{:action => 'show', :id => @vm_resource_pool.id, :nolayout =>
:true}, :title => "content area" %>
- <%= link_to "Virtual
Machines", {:action => 'show_vms', :id =>
@vm_resource_pool.id, :nolayout => :true}, :title => "content
area" %>
- <%= link_to "User
Access", {:action => 'show_users', :id =>
@vm_resource_pool.id, :nolayout => :true}, :title => "content
area" %>
- <%= link_to "Tasks",
{:action => 'show_tasks', :id => @vm_resource_pool.id, :nolayout
=> :true}, :title => "content area" %>
+    <li id="nav_summary"
class="ui-tabs-selected"><%= link_to "Summary",
{:action => 'show', :id => @pool.id, :nolayout => :true},
:title => "content area" %>
+ <%= link_to "Virtual
Machines", {:action => 'show_vms', :id => @pool.id, :nolayout
=> :true}, :title => "content area" %>
+ <%= link_to "User
Access", {:action => 'show_users', :id => @pool.id, :nolayout
=> :true}, :title => "content area" %>
+ <%= link_to "Tasks",
{:action => 'show_tasks', :id => @pool.id, :nolayout => :true},
:title => "content area" %>

 <% elsif controller.controller_name == "search"  %>

diff --git a/wui/src/app/views/resources/edit.rhtml
b/wui/src/app/views/resources/edit.rhtml
index a101247..c6fd264 100644
--- a/wui/src/app/views/resources/edit.rhtml
+++ b/wui/src/app/views/resources/edit.rhtml
@@ -7,7 +7,7 @@

 <form method="POST" action="<%= url_for :action =>
'update' %>" id="vm_pool_form" >

-  <%= hidden_field_tag 'id', @vm_resource_pool.id %>
+  <%= hidden_field_tag 'id', @pool.id %>
   <%= render :partial => 'form' %>

 <%= popup_footer("$('#vm_pool_form').submit()", "Edit
Virtual Machine Pool") %>
@@ -26,7 +26,7 @@ $(function() {
             refresh_summary('vmpool_selection',
                             '<%= url_for :controller =>
"resources",
                                          :action =>
"quick_summary" %>',
-                            <%= @vm_resource_pool.id %>)
+                            <%= @pool.id %>)
           }
         }
     };
diff --git a/wui/src/app/views/resources/quick_summary.rhtml
b/wui/src/app/views/resources/quick_summary.rhtml
index 60d0df7..31c4033 100644
--- a/wui/src/app/views/resources/quick_summary.rhtml
+++ b/wui/src/app/views/resources/quick_summary.rhtml
@@ -1,21 +1,21 @@
 <%- content_for :title do -%>
-  <%=h @vm_resource_pool.name %> quota
+  <%=h @pool.name %> quota
 <%- end -%>
 <%- content_for :action_links do -%>
   <%if @is_hwpool_admin -%>
     <%= link_to image_tag("icon_edit.png") + " Edit",
-                          {:controller => 'resources', :action =>
'edit', :id => @vm_resource_pool},
+                          {:controller => 'resources', :action =>
'edit', :id => @pool},
                           :rel=>"facebox[.bolder]",
:class=>"selection_facebox" %>
-    <%if @vm_resource_pool.quota -%>
+    <%if @pool.quota -%>
       <%= link_to image_tag("icon_edit.png") + " Edit
Quota",
-                            {:controller => 'quota', :action =>
'edit', :id => @vm_resource_pool.quota},
+                            {:controller => 'quota', :action =>
'edit', :id => @pool.quota},
                             :rel=>"facebox[.bolder]",
:class=>"selection_facebox" %>
       <a href="#confirm_delete_quota"
rel="facebox[.bolder]">
         <%= image_tag "icon_x.png" %> Revert to Default Quota

     <% else -%>
       <%= link_to image_tag("icon_edit.png") + " Edit
Quota",
-                            {:controller => 'quota', :action =>
'new', :pool_id => @vm_resource_pool },
+                            {:controller => 'quota', :action =>
'new', :pool_id => @pool },
                             :rel=>"facebox[.bolder]",
:class=>"selection_facebox" %>
     <% end -%>
   <% end -%>
@@ -25,13 +25,13 @@
   function delete_vm_quota()
   {
     $(document).trigger('close.facebox');
-    $.post('<%= url_for :controller => "quota", :action
=> "destroy", :id => @vm_resource_pool.quota %>',
+    $.post('<%= url_for :controller => "quota", :action
=> "destroy", :id => @pool.quota %>',
            {x: 1},
             function(data,status){
               refresh_summary('vmpool_selection',
                           '<%= url_for :controller =>
"resources",
                                        :action => "quick_summary"
%>',
-                          <%= @vm_resource_pool.id %>);
+                          <%= @pool.id %>);
               if (data.alert) {
                 $.jGrowl(data.alert);
               }
@@ -39,8 +39,8 @@
   }


-    <div id="vmpool_selection_id"
style="display:none"><%= @vm_resource_pool.id %>
-    <% resources = @vm_resource_pool.full_resources %>
+    <div id="vmpool_selection_id"
style="display:none"><%= @pool.id %>
+    <% resources = @pool.full_resources %>


         <% for item in resources[:labels] %>
@@ -60,5 +60,5 @@



-    <%= render_component :controller=> 'graph', :action =>
'snapshot_graph', :id => @vm_resource_pool.id, :target =>
'resource' %>
+    <%= render_component :controller=> 'graph', :action =>
'snapshot_graph', :id => @pool.id, :target => 'resource'
%>

diff --git a/wui/src/app/views/resources/show.rhtml
b/wui/src/app/views/resources/show.rhtml
index 0db3a1b..789465b 100644
--- a/wui/src/app/views/resources/show.rhtml
+++ b/wui/src/app/views/resources/show.rhtml
@@ -1,18 +1,18 @@

- <%= image_tag
"icon_vmpool.png", :style=>"vertical-align:middle;"
%>&nbsp;<%= @vm_resource_pool.name
%>
+ <%= image_tag
"icon_vmpool.png", :style=>"vertical-align:middle;"
%> <%= @pool.name %>

<%= image_tag
"icon_smry_res.png", :style=>"vertical-align:middle;"
%> Resources

-      <%= render_component :controller=> 'graph', :action =>
'availability_graph', :id => @vm_resource_pool.id, :params => {
:target => 'vcpu' } %>
-      <%= render_component :controller=> 'graph', :action =>
'availability_graph', :id => @vm_resource_pool.id, :params => {
:target => 'vram' } %>
+      <%= render_component :controller=> 'graph', :action =>
'availability_graph', :id => @pool.id, :params => { :target =>
'vcpu' } %>
+      <%= render_component :controller=> 'graph', :action =>
'availability_graph', :id => @pool.id, :params => { :target =>
'vram' } %>



<%= image_tag
"icon_smry_his.png", :style=>"vertical-align:middle;"
%> History
-   <%= render_component :controller=> 'graph', :action =>
'history_graphs', :id => @vm_resource_pool.id, :params => {
:poolType => 'vm' } %>
+   <%= render_component :controller=> 'graph', :action =>
'history_graphs', :id => @pool.id, :params => { :poolType =>
'vm' } %>

<%= image_tag
"icon_smry_perf.png", :style=>"vertical-align:middle;"
%> Performance
-   <%= render_component :controller=> 'graph', :action =>
'snapshot_graph', :id => @vm_resource_pool.id, :params => {
:poolType => 'vm' } %>
+   <%= render_component :controller=> 'graph', :action =>
'snapshot_graph', :id => @pool.id, :params => { :poolType =>
'vm' } %>



@@ -23,6 +23,6 @@
          refresh_summary('vmpool_selection',
                          '<%= url_for :controller =>
"resources",
                                       :action => "quick_summary"
%>',
-                         <%= @vm_resource_pool.id %>)
+                         <%= @pool.id %>)


diff --git a/wui/src/app/views/resources/show_tasks.rhtml
b/wui/src/app/views/resources/show_tasks.rhtml
index 493264b..6c92779 100644
--- a/wui/src/app/views/resources/show_tasks.rhtml
+++ b/wui/src/app/views/resources/show_tasks.rhtml
@@ -24,7 +24,7 @@
   function apply_task_state_filter(task_state)
   {
     $tabs.tabs("url", $tabs.data("selected.tabs"),
-               "<%= url_for :action => 'show_tasks', :id
=> @vm_resource_pool.id,
+               "<%= url_for :action => 'show_tasks', :id
=> @pool.id,
                            :nolayout => :true %>" +
               "&task_state=" + task_state);
     $tabs.tabs("load", $tabs.data("selected.tabs"));
@@ -37,7 +37,7 @@
       <%= render :partial => "/task/grid", :locals => {
:table_id => "vm_tasks_grid",
                                                         :task_type => nil,
                                                         :task_state =>
@task_state,
-                                                        :pool =>
@vm_resource_pool,
+                                                        :pool => @pool,
                                                         :checkboxes =>
false,
                                                         :on_select =>
"vm_tasks_grid_select" } %>

diff --git a/wui/src/app/views/resources/show_users.rhtml
b/wui/src/app/views/resources/show_users.rhtml
index 7e2538d..f23018c 100644
--- a/wui/src/app/views/resources/show_users.rhtml
+++ b/wui/src/app/views/resources/show_users.rhtml
@@ -1,2 +1,2 @@
   <%= render :partial => "/user/show", :locals => {
:parent_controller => "resources",
-                                                    :pool =>
@vm_resource_pool } %>
+                                                    :pool => @pool } %>
diff --git a/wui/src/app/views/resources/show_vms.rhtml
b/wui/src/app/views/resources/show_vms.rhtml
index 45e0089..857f56b 100644
--- a/wui/src/app/views/resources/show_vms.rhtml
+++ b/wui/src/app/views/resources/show_vms.rhtml
@@ -1,6 +1,6 @@


- <a href="<%= url_for :controller => 'vm',
:action => 'new', :vm_resource_pool_id => @vm_resource_pool
%>" rel="facebox[.bolder]"><%= image_tag
"icon_addhost.png", :style => "vertical-align:middle;"
%> Add Virtual Machine
+ <a href="<%= url_for :controller => 'vm',
:action => 'new', :vm_resource_pool_id => @pool %>"
rel="facebox[.bolder]"><%= image_tag
"icon_addhost.png", :style => "vertical-align:middle;"
%> Add Virtual Machine

        <%= image_tag "icon_move.png", :style =>
"vertical-align:middle;" %>&nbsp;&nbsp;Actions
&nbsp;&nbsp;&nbsp;<%= image_tag
"icon_toolbar_arrow.gif", :style =>
"vertical-align:middle;" %>

@@ -28,7 +28,7 @@
   {
     vms = get_selected_vms()
     if (validate_selected(vms, "vm")) {
-      $.post('<%= url_for :controller => "vm", :action
=> "delete", :id => @vm_resource_pool %>',
+      $.post('<%= url_for :controller => "vm", :action
=> "delete", :id => @pool %>',
              { vm_ids: vms.toString() },
               function(data,status){

$tabs.tabs("load",$tabs.data('selected.tabs'));
@@ -47,7 +47,7 @@
     if (validate_selected(vms, "vm")) {
jQuery.facebox(' ');
       $('#vm_action_results').load('<%= url_for :controller
=> "resources",
-             :action => "vm_actions", :id => @vm_resource_pool
%>',
+             :action => "vm_actions", :id => @pool %>',
              { vm_ids: vms.toString(), vm_action: action });
     }
   }
@@ -66,10 +66,10 @@



-<% if @vm_resource_pool.vms.size != 0 %>
+<% if @pool.vms.size != 0 %>

        <%= render :partial => "/vm/grid", :locals => {
:table_id => "vms_grid",
-                                                       :pool =>
@vm_resource_pool,
+                                                       :pool => @pool,
                                                        :on_select =>
"vms_select",
                                                        :on_deselect =>
"load_widget_deselect",
                                                        :on_hover =>
"load_widget_hover",
@@ -88,7 +88,7 @@

No vms found in this pool.
             <%= image_tag "icon_addhost.png", :style =>
"vertical-align:middle;" %>&nbsp;&nbsp;
-            <a href="<%= url_for :controller => 'vm',
:action => 'new', :vm_resource_pool_id => @vm_resource_pool
%>" rel="facebox[.bolder]">Add first virtual machine to
resource pool
+            <a href="<%= url_for :controller => 'vm',
:action => 'new', :vm_resource_pool_id => @pool %>"
rel="facebox[.bolder]">Add first virtual machine to resource
pool



diff --git a/wui/src/db/migrate/017_add_smart_pools.rb
b/wui/src/db/migrate/017_add_smart_pools.rb
new file mode 100644
index 0000000..6d83c02
--- /dev/null
+++ b/wui/src/db/migrate/017_add_smart_pools.rb
@@ -0,0 +1,62 @@
+#
+# Copyright (C) 2008 Red Hat, Inc.
+# Written by Scott Seago <sseago at redhat.com>
+#
+# 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.
+
+class AddSmartPools < ActiveRecord::Migration
+  def self.up
+    create_table :smart_pool_tags  do |t|
+      t.integer :smart_pool_id, :null => false
+      t.integer :tagged_id,     :null => false
+      t.string :tagged_type,    :null => false
+    end
+    execute "alter table smart_pool_tags add constraint
+             fk_smart_pool_tags_pool_id
+             foreign key (smart_pool_id) references pools(id)"
+    execute "alter table smart_pool_tags add constraint
+             unique_smart_pool_tags
+             unique (smart_pool_id, tagged_id, tagged_type)"
+
+    begin
+      dir_root = DirectoryPool.get_directory_root
+      unless dir_root
+        Pool.transaction do
+          dir_root = DirectoryPool.create(:name=>DirectoryPool::ROOT_NAME)
+          hw_root = DirectoryPool.new(:name=>DirectoryPool::HARDWARE_ROOT)
+          hw_root.create_with_parent(dir_root)
+          smart_root = DirectoryPool.new(:name=>DirectoryPool::SMART_ROOT)
+          smart_root.create_with_parent(dir_root)
+          default_pool =
Pool.root(:conditions=>"type='HardwarePool'")
+          default_pool = HardwarePool.create( :name=>'default')
unless default_pool
+          default_pool.move_to_child_of(hw_root)
+          default_pool.permissions.each do |permission|
+            new_permission = Permission.new({:pool_id     => dir_root.id,
+                                             :uid         => permission.uid,
+                                             :user_role   =>
permission.user_role})
+            new_permission.save!
+          end
+        end
+      end
+    rescue
+      puts "Could not create DirectoryPool hierarchy..."
+    end
+  end
+
+  def self.down
+    drop_table :smart_pool_tags
+  end
+end
diff --git a/wui/src/dutils/active_record_env.rb
b/wui/src/dutils/active_record_env.rb
index 9b7b416..14593f7 100644
--- a/wui/src/dutils/active_record_env.rb
+++ b/wui/src/dutils/active_record_env.rb
@@ -58,6 +58,8 @@ require 'models/permission.rb'
 require 'models/quota.rb'

 require 'models/hardware_pool.rb'
+require 'models/directory_pool.rb'
+require 'models/smart_pool.rb'
 require 'models/host.rb'
 require 'models/cpu.rb'
 require 'models/nic.rb'
@@ -77,4 +79,6 @@ require 'models/nfs_storage_pool.rb'
 require 'models/storage_volume.rb'
 require 'models/iscsi_storage_volume.rb'
 require 'models/nfs_storage_volume.rb'
+require 'models/smart_pool.rb'
+require 'models/smart_pool_tag.rb'

diff --git a/wui/src/script/grant_admin_privileges
b/wui/src/script/grant_admin_privileges
index 82595cb..c263b9c 100755
--- a/wui/src/script/grant_admin_privileges
+++ b/wui/src/script/grant_admin_privileges
@@ -13,10 +13,10 @@ ActiveLdap::Base.establish_connection :base => base,
:host => host, :try_sasl =>

 if Account.exists?("uid=#{uid}")
   puts "Creating an admin account for #{uid}..."
-  $hwpool = HardwarePool.get_default_pool
+  $pool = DirectoryPool.get_directory_root
   permission = Permission.create(:user_role => Permission::ROLE_SUPER_ADMIN,
 				 :uid       => uid,
-				 :pool_id   => $hwpool.id)
+				 :pool_id   => $pool.id)
 else
   puts "Unable to verify user: uid=#{uid}"
 end
diff --git a/wui/src/vendor/plugins/betternestedset/lib/better_nested_set.rb
b/wui/src/vendor/plugins/betternestedset/lib/better_nested_set.rb
index cdcf0c6..e91ca93 100644
--- a/wui/src/vendor/plugins/betternestedset/lib/better_nested_set.rb
+++ b/wui/src/vendor/plugins/betternestedset/lib/better_nested_set.rb
@@ -81,13 +81,22 @@ module SymetrieCom
         # Returns the single root for the class (or just the first root, if
there are several).
         # Deprecation note: the original acts_as_nested_set allowed roots to
have parent_id = 0,
         # so we currently do the same. This silliness will not be tolerated in
future versions, however.
-        def root
-          acts_as_nested_set_options[:class].find(:first, :conditions =>
"(#{acts_as_nested_set_options[:parent_column]} IS NULL OR
#{acts_as_nested_set_options[:parent_column]} = 0)")
+        def root(find_opts={})
+          roots_internal(:first, find_opts)
         end

         # Returns the roots and/or virtual roots of all trees. See the
explanation of virtual roots in the README.
-        def roots
-          acts_as_nested_set_options[:class].find(:all, :conditions =>
"(#{acts_as_nested_set_options[:parent_column]} IS NULL OR
#{acts_as_nested_set_options[:parent_column]} = 0)", :order =>
"#{acts_as_nested_set_options[:left_column]}")
+        def roots(find_opts={})
+          roots_internal(:all, find_opts)
+        end
+        def roots_internal(all_or_one, find_opts={})
+          conditions = "(#{acts_as_nested_set_options[:parent_column]} IS
NULL"
+          conditions += " OR #{acts_as_nested_set_options[:parent_column]}
= 0)"
+          order_col = "#{acts_as_nested_set_options[:left_column]}"
+          opts = merge_incoming_opts({:conditions => conditions,
+                                      :order => order_col},
+                                     find_opts)
+          acts_as_nested_set_options[:class].find(all_or_one, opts)
         end

         # Checks the left/right indexes of all records,
@@ -128,6 +137,14 @@ module SymetrieCom
           items.map! {|e| "(#{acts_as_nested_set_options[:left_column]}
BETWEEN #{e[acts_as_nested_set_options[:left_column]]} AND
#{e[acts_as_nested_set_options[:right_column]]})" }
           "(#{items.join(' OR ')})"
         end
+
+        # accept incoming opts to allow filtering of results.
+        # So far only tested in limited use cases encountered in oVirt devel.
+        def merge_incoming_opts(set_opts, incoming_opts)
+          new_conditions = incoming_opts.delete(:conditions)
+          set_opts[:conditions] = "(#{set_opts[:conditions]}) AND
(#{new_conditions})" if new_conditions
+          set_opts.merge(incoming_opts)
+        end

       end

@@ -255,7 +272,7 @@ module SymetrieCom
           elsif new_record? || self[right_col_name] - self[left_col_name] == 1
             return [self]
           end
-          opts = merge_incoming_opts({:conditions =>
"#{scope_condition} #{exclude_str} AND (#{left_col_name} BETWEEN
#{self[left_col_name]} AND #{self[right_col_name]})",
+          opts = base_set_class.merge_incoming_opts({:conditions =>
"#{scope_condition} #{exclude_str} AND (#{left_col_name} BETWEEN
#{self[left_col_name]} AND #{self[right_col_name]})",
                                        :order => left_col_name},
                                      find_opts)
           base_set_class.find(:all, opts)
@@ -270,7 +287,7 @@ module SymetrieCom

         # Returns this record's immediate children.
         def children(find_opts={})
-          opts = merge_incoming_opts({:conditions =>
"#{scope_condition} AND #{parent_col_name} = #{self.id}",
+          opts = base_set_class.merge_incoming_opts({:conditions =>
"#{scope_condition} AND #{parent_col_name} = #{self.id}",
                                       :order => left_col_name},
                                      find_opts)
           base_set_class.find(:all, opts)
@@ -585,14 +602,6 @@ module SymetrieCom
             self.class.connection.quote(value, column)
           end

-          # accept incoming opts to allow filtering of results.
-          # So far only tested in limited use cases encountered in oVirt devel.
-          def merge_incoming_opts(set_opts, incoming_opts)
-            new_conditions = incoming_opts.delete(:conditions)
-            set_opts[:conditions] = "(#{set_opts[:conditions]}) AND
(#{new_conditions})" if new_conditions
-            set_opts.merge(incoming_opts)
-          end
-
       end
     end
   end
--
1.5.5.1