[Ovirt-devel] [PATCH server] REBASE Cloud UI V1 (readonly).

Jason Guiditta jguiditt at redhat.com
Tue May 19 14:26:01 UTC 2009


This patch creates the shell for much of what the cloud ui will
become.  It is focused on layout (lightly fedora themed), accessibility,
and 'bookmarkability'.  This layout uses no javascript (that will be
used to enhance interactivity in a future patch).  It also introduces
the idea of using a simple table rather than the more conplex js/json
we use in the admin side.  Plan here is to gradually enhance this as
needed to make more interactive.

CSS was kept separate from admin CSS to facilitate theming by
users/deployments.

Controllers are designed (even though large parts are stubbed) to be
'skinny' and leverage as much as possible the new service api.  There
are a few readonly AR calls that in near future could easily be moved to
service layer to be even simpler calls.  They are kept separate from admin
things so they can be as simple as possible as well a making it conceivable
that one could deploy the cloud part of the app without also deplying admin
(perhaps one wants to deply that elsewhere).  For now this would have to be
done manually, but perhaps in future we can setup the service layer/ar classes
to be a separate rpm that two different wuis (admin/cloud) could call into,
enhancing modularity.

Signed-off-by: Jason Guiditta <jguiditt at redhat.com>
---
 src/app/controllers/cloud/cloud_controller.rb      |   35 ++++
 src/app/controllers/cloud/dashboard_controller.rb  |   24 +++
 src/app/controllers/cloud/instance_controller.rb   |   95 +++++++++++
 src/app/helpers/cloud/cloud_helper.rb              |   21 +++
 src/app/helpers/cloud/dashboard_helper.rb          |   21 +++
 src/app/helpers/cloud/instance_helper.rb           |   38 +++++
 src/app/views/cloud/dashboard/index.rhtml          |    2 +
 src/app/views/cloud/instance/_list.rhtml           |   34 ++++
 src/app/views/cloud/instance/_show.rhtml           |   47 ++++++
 src/app/views/cloud/instance/create.rhtml          |    2 +
 src/app/views/cloud/instance/destroy.rhtml         |    2 +
 src/app/views/cloud/instance/edit.rhtml            |    2 +
 src/app/views/cloud/instance/index.rhtml           |   23 +++
 src/app/views/cloud/instance/new.rhtml             |    2 +
 src/app/views/cloud/instance/show.rhtml            |    5 +
 src/app/views/cloud/instance/update.rhtml          |    2 +
 src/app/views/layouts/cloud/cloud.rhtml            |   55 +++++++
 src/public/images/icon_failed_11px.png             |  Bin 0 -> 374 bytes
 src/public/images/icon_queued_11px.png             |  Bin 0 -> 429 bytes
 src/public/images/icon_startup_11px.png            |  Bin 0 -> 458 bytes
 src/public/images/icon_unknown_11px.png            |  Bin 0 -> 421 bytes
 src/public/images/icon_warning_11px.png            |  Bin 0 -> 343 bytes
 src/public/stylesheets/cloud/layout.css            |  166 ++++++++++++++++++++
 src/test/functional/cloud/cloud_controller_test.rb |    8 +
 .../functional/cloud/dashboard_controller_test.rb  |    8 +
 .../functional/cloud/instance_controller_test.rb   |   23 +++
 26 files changed, 615 insertions(+), 0 deletions(-)
 create mode 100644 src/app/controllers/cloud/cloud_controller.rb
 create mode 100644 src/app/controllers/cloud/dashboard_controller.rb
 create mode 100644 src/app/controllers/cloud/instance_controller.rb
 create mode 100644 src/app/helpers/cloud/cloud_helper.rb
 create mode 100644 src/app/helpers/cloud/dashboard_helper.rb
 create mode 100644 src/app/helpers/cloud/instance_helper.rb
 create mode 100644 src/app/views/cloud/dashboard/index.rhtml
 create mode 100644 src/app/views/cloud/instance/_list.rhtml
 create mode 100644 src/app/views/cloud/instance/_show.rhtml
 create mode 100644 src/app/views/cloud/instance/create.rhtml
 create mode 100644 src/app/views/cloud/instance/destroy.rhtml
 create mode 100644 src/app/views/cloud/instance/edit.rhtml
 create mode 100644 src/app/views/cloud/instance/index.rhtml
 create mode 100644 src/app/views/cloud/instance/new.rhtml
 create mode 100644 src/app/views/cloud/instance/show.rhtml
 create mode 100644 src/app/views/cloud/instance/update.rhtml
 create mode 100644 src/app/views/layouts/cloud/cloud.rhtml
 create mode 100644 src/public/images/icon_failed_11px.png
 create mode 100644 src/public/images/icon_queued_11px.png
 create mode 100644 src/public/images/icon_startup_11px.png
 create mode 100644 src/public/images/icon_unknown_11px.png
 create mode 100644 src/public/images/icon_warning_11px.png
 create mode 100644 src/public/stylesheets/cloud/layout.css
 create mode 100644 src/test/functional/cloud/cloud_controller_test.rb
 create mode 100644 src/test/functional/cloud/dashboard_controller_test.rb
 create mode 100644 src/test/functional/cloud/instance_controller_test.rb

diff --git a/src/app/controllers/cloud/cloud_controller.rb b/src/app/controllers/cloud/cloud_controller.rb
new file mode 100644
index 0000000..154d15d
--- /dev/null
+++ b/src/app/controllers/cloud/cloud_controller.rb
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2009 Red Hat, Inc.
+# Written by Jason Guiditta <jguiditt 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 Cloud::CloudController < ApplicationController
+  include VmService
+
+  layout 'cloud/cloud'
+
+  before_filter :set_vars
+
+  protected
+
+
+  # NOTE: This probably will/should be moved to use set_perms in
+  # ApplicationService once that is ready to go.
+  def set_vars
+    @user = get_login_user
+  end
+end
diff --git a/src/app/controllers/cloud/dashboard_controller.rb b/src/app/controllers/cloud/dashboard_controller.rb
new file mode 100644
index 0000000..8fd0f02
--- /dev/null
+++ b/src/app/controllers/cloud/dashboard_controller.rb
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2009 Red Hat, Inc.
+# Written by Jason Guiditta <jguiditt 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 Cloud::DashboardController < Cloud::CloudController
+  def index
+  end
+
+end
diff --git a/src/app/controllers/cloud/instance_controller.rb b/src/app/controllers/cloud/instance_controller.rb
new file mode 100644
index 0000000..a4d0fd8
--- /dev/null
+++ b/src/app/controllers/cloud/instance_controller.rb
@@ -0,0 +1,95 @@
+#
+# Copyright (C) 2009 Red Hat, Inc.
+# Written by Jason Guiditta <jguiditt 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 Cloud::InstanceController < Cloud::CloudController
+
+  before_filter :handle_form
+
+  def index
+    list
+  end
+
+  def list
+    page = find_in_params_or_default(:page, 1)
+    order = find_in_params_or_default(:sort,"vms.id")
+    @vms = Vm.paged_with_perms(@user,
+                            Privilege::VIEW,
+                            # NOTE: maybe this ^^ part could be taken care of behind the scenes?
+                            # Also, needs to be changed to a cloud priv
+                            page, order)
+    @actions = VmTask.get_vm_actions
+    show
+  end
+
+  def show
+    ids = params[:ids]
+    task_page = find_in_params_or_default(:task_page, 1)
+    task_order = find_in_params_or_default(:task_order, "tasks.id")
+    @vm_details = Vm.find(ids) if ids
+    if @vm_details
+      @tasks = VmTask.paginate(:conditions => ["task_target_id in (:ids)",{:ids => ids}],
+                             :per_page => 5, :page => task_page, :order => task_order)
+    end
+  end
+
+  # TODO: implement
+  def create
+  end
+
+  # TODO: implement
+  def update
+  end
+
+  # TODO: implement
+  def destroy
+  end
+
+  # TODO: implement
+  def new
+  end
+
+  # TODO: implement
+  def edit
+  end
+
+  private
+
+# Pass in the symbol for the param key you want, and an optional default value
+  def find_in_params_or_default(key, default=nil)
+    return params[key] && params[key] != "" ? params[key] : default
+  end
+
+# This redirects the user to a get url if they are just trying to view details for one or more
+# instances.
+# TODO: if the user is trying to submit an acton on selected instances, call the service
+# layer and display the :flash (might still want to do :get redirect to keep pagination/sorting
+# correct.
+  def handle_form
+    case params[:submit_for_list]
+      when "Show Selected"
+        params.delete(:submit_for_list)
+        redirect_to :action => "index", :params => params
+        return
+#    Do this if we have submitted an action on one or more vms.
+#    svc_vm_action(params[:ids], params[:vm_action], params[:action_args])
+    end
+  end
+
+end
diff --git a/src/app/helpers/cloud/cloud_helper.rb b/src/app/helpers/cloud/cloud_helper.rb
new file mode 100644
index 0000000..9ec71da
--- /dev/null
+++ b/src/app/helpers/cloud/cloud_helper.rb
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2009 Red Hat, Inc.
+# Written by Jason Guiditta <jguiditt 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.
+
+module Cloud::CloudHelper
+end
diff --git a/src/app/helpers/cloud/dashboard_helper.rb b/src/app/helpers/cloud/dashboard_helper.rb
new file mode 100644
index 0000000..713f3a9
--- /dev/null
+++ b/src/app/helpers/cloud/dashboard_helper.rb
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2009 Red Hat, Inc.
+# Written by Jason Guiditta <jguiditt 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.
+
+module Cloud::DashboardHelper
+end
diff --git a/src/app/helpers/cloud/instance_helper.rb b/src/app/helpers/cloud/instance_helper.rb
new file mode 100644
index 0000000..2db0529
--- /dev/null
+++ b/src/app/helpers/cloud/instance_helper.rb
@@ -0,0 +1,38 @@
+#
+# Copyright (C) 2009 Red Hat, Inc.
+# Written by Jason Guiditta <jguiditt 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.
+
+module Cloud::InstanceHelper
+#  TODO: add checking to make sure this is a symbol, possibly as simple as adding a
+#   to_sym, just not sure what happens if it is already a symbol
+  def sort_td_class_helper(param, key = :sort)
+    result = 'sortup' if params[key] == param
+    result = 'sortdown' if params[key] == param + " DESC"
+    return result
+  end
+
+  # pass in an optional symbol for the key you want to use
+  # TODO: add checking to make sure this is a symbol, possibly as simple as adding a
+  # to_sym, just not sure what happens if it is already a symbol
+  def sort_link_helper(text, param, key = :sort)
+    param += " DESC" if params[key] == param
+    param_list = params.clone
+    param_list[key] == param ? param_list[key] = param += " DESC": param_list[key] = param
+    link_to(text, :action => 'index', :params => param_list)
+  end
+end
diff --git a/src/app/views/cloud/dashboard/index.rhtml b/src/app/views/cloud/dashboard/index.rhtml
new file mode 100644
index 0000000..de94fa4
--- /dev/null
+++ b/src/app/views/cloud/dashboard/index.rhtml
@@ -0,0 +1,2 @@
+<h1>Cloud::Dashboard#index</h1>
+<p>Find me in app/views/cloud/dashboard/index.html.erb</p>
diff --git a/src/app/views/cloud/instance/_list.rhtml b/src/app/views/cloud/instance/_list.rhtml
new file mode 100644
index 0000000..1fa46de
--- /dev/null
+++ b/src/app/views/cloud/instance/_list.rhtml
@@ -0,0 +1,34 @@
+<%=  hidden_field_tag 'page', params[:page] %>
+<%=  hidden_field_tag 'sort', params[:sort] %>
+<table>
+  <thead>
+    <th scope="col"><div> </div></th>
+    <th scope="col">
+      <div class="sortable <%= sort_td_class_helper "description" %>">
+        <%= sort_link_helper "Instance", "description" %>
+      </div>
+    </th>
+    <th scope="col" class="sortable <%= sort_td_class_helper "provisioning" %>">
+      <div><%= sort_link_helper "Profile", "provisioning" %></div>
+    </th>
+    <th scope="col" class="sortable <%= sort_td_class_helper "state" %>">
+      <div><%= sort_link_helper "State", "state" %></div>
+    </th>
+    <th scope="col"><div>IP Address</div></th>
+    <th scope="col"><div>Load</div></th>
+  </thead>
+  <tbody>
+    <%  @vms.each { |vm| %>
+      <% checked = (params[:ids] != nil && params[:ids].include?(vm.id.to_s)) ? 'checked' : nil %>
+      <tr class="<%= cycle("even", "odd") -%><%if checked%> selected<%end%>" >
+        <td class="center"><div><%= check_box_tag "ids[]", "#{vm.id}", checked -%></div></td>
+        <td><div><%= vm.description %></div></td>
+        <td><div><%= vm.provisioning %></div></td> <!-- TODO: possibly add default output value for this? -->
+        <td><div class="state-container instance-<%= vm.state %>"><%= vm.state.capitalize %></div></td>
+        <td><div>N/A</div></td>
+        <td><div>N/A</div></td>
+      </tr>
+    <%  } %>
+  </tbody>
+</table>
+<%= will_paginate @vms, :sort => params[:sort] %>
\ No newline at end of file
diff --git a/src/app/views/cloud/instance/_show.rhtml b/src/app/views/cloud/instance/_show.rhtml
new file mode 100644
index 0000000..001286e
--- /dev/null
+++ b/src/app/views/cloud/instance/_show.rhtml
@@ -0,0 +1,47 @@
+  <div>
+    <div id="detail_header">
+      <%= submit_tag 'Show Selected', :id => 'submit_for_list', :name => 'submit_for_list' %>
+      </form>
+      <% if @vm_details %>
+        <h3>
+        <% if @vm_details.size == 1 %>
+          <%=@vm_details[0].description%>
+        <% else %>
+          <%= @vm_details.size %> instances selected
+        <% end %>
+        </h3>
+      <% end %>
+    </div>
+<% if @vm_details %>
+    <div id="graph">Placeholder for graph widget</div>
+    <table>
+      <thead>
+        <th scope="col">
+          <div class="sortable <%= sort_td_class_helper "action", :task_order %>">
+            <%= sort_link_helper "Task", "action", :task_order %>
+          </div>
+        </th>
+        <th scope="col" class="sortable <%= sort_td_class_helper "state", :task_order %>">
+          <div><%= sort_link_helper "State", "state", :task_order %></div>
+        </th>
+        <th scope="col" class="sortable <%= sort_td_class_helper "vms.description", :task_order %>">
+          <div><%= sort_link_helper "Instance", "vms.description", :task_order %></div>
+        </th>
+        <th scope="col" class="sortable <%= sort_td_class_helper "time_started", :task_order %>">
+          <div><%= sort_link_helper "Started", "time_started", :task_order %></div>
+        </th>
+      </thead>
+      <tbody>
+        <%@tasks.each { |task| %>
+          <tr class="<%= cycle("even", "odd") -%>">
+            <td><div><%= task.action %></div></td>
+            <td><div class="state-container task-<%= task.state %>"><%= task.state.capitalize %></div></td>
+            <td><div><%= task.vm.description %></div></td>
+            <td><div><%= task.created_at %> </div></td>
+          </tr>
+        <%  } %>
+      </tbody>
+    </table>
+    <%= will_paginate @tasks, :param_name => 'task_page' %>
+<% end %>
+  </div>
\ No newline at end of file
diff --git a/src/app/views/cloud/instance/create.rhtml b/src/app/views/cloud/instance/create.rhtml
new file mode 100644
index 0000000..b608264
--- /dev/null
+++ b/src/app/views/cloud/instance/create.rhtml
@@ -0,0 +1,2 @@
+<h1>Cloud::Instance#create</h1>
+<p>Find me in app/views/cloud/instance/create.html.erb</p>
diff --git a/src/app/views/cloud/instance/destroy.rhtml b/src/app/views/cloud/instance/destroy.rhtml
new file mode 100644
index 0000000..8322a5a
--- /dev/null
+++ b/src/app/views/cloud/instance/destroy.rhtml
@@ -0,0 +1,2 @@
+<h1>Cloud::Instance#destroy</h1>
+<p>Find me in app/views/cloud/instance/destroy.html.erb</p>
diff --git a/src/app/views/cloud/instance/edit.rhtml b/src/app/views/cloud/instance/edit.rhtml
new file mode 100644
index 0000000..b7517a5
--- /dev/null
+++ b/src/app/views/cloud/instance/edit.rhtml
@@ -0,0 +1,2 @@
+<h1>Cloud::Instance#edit</h1>
+<p>Find me in app/views/cloud/instance/edit.html.erb</p>
diff --git a/src/app/views/cloud/instance/index.rhtml b/src/app/views/cloud/instance/index.rhtml
new file mode 100644
index 0000000..218bd8c
--- /dev/null
+++ b/src/app/views/cloud/instance/index.rhtml
@@ -0,0 +1,23 @@
+<div id="toolbar_nav">
+  <!-- TODO: Make each li a submit button with same styling as current li.
+       Handlng of this will be implemented in InstanceController::handle_form
+  -->
+  <ul>
+    <li>New Instance</li>
+    <li>
+      Actions
+      <ul>
+        <% @actions.each {|action| %>
+          <li><%= image_tag action[2]%><%= action[0] %></li>
+        <% } %>
+      </ul>
+    </li>
+  </ul>
+</div>
+<form  action="<%= url_for({:action => 'index'})%>" method="post"> <%# This form tag is terminated in _show.rhtml %>
+  <div id="list-view">
+      <%= render :partial => 'list' %>
+  </div>
+  <div id="detail-view">
+      <%= render :partial => 'show' %>
+  </div>
\ No newline at end of file
diff --git a/src/app/views/cloud/instance/new.rhtml b/src/app/views/cloud/instance/new.rhtml
new file mode 100644
index 0000000..6f91d27
--- /dev/null
+++ b/src/app/views/cloud/instance/new.rhtml
@@ -0,0 +1,2 @@
+<h1>Cloud::Instance#new</h1>
+<p>Find me in app/views/cloud/instance/new.html.erb</p>
diff --git a/src/app/views/cloud/instance/show.rhtml b/src/app/views/cloud/instance/show.rhtml
new file mode 100644
index 0000000..8e44308
--- /dev/null
+++ b/src/app/views/cloud/instance/show.rhtml
@@ -0,0 +1,5 @@
+<!-- TODO: Get new mockup from jeremy perry of what else
+     gets displayed if we are in the 'drilldown view'
+-->
+
+<%= render :partial => 'show' %>
\ No newline at end of file
diff --git a/src/app/views/cloud/instance/update.rhtml b/src/app/views/cloud/instance/update.rhtml
new file mode 100644
index 0000000..6c88fb5
--- /dev/null
+++ b/src/app/views/cloud/instance/update.rhtml
@@ -0,0 +1,2 @@
+<h1>Cloud::Instance#update</h1>
+<p>Find me in app/views/cloud/instance/update.html.erb</p>
diff --git a/src/app/views/layouts/cloud/cloud.rhtml b/src/app/views/layouts/cloud/cloud.rhtml
new file mode 100644
index 0000000..b891bf2
--- /dev/null
+++ b/src/app/views/layouts/cloud/cloud.rhtml
@@ -0,0 +1,55 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<head>
+  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+  <title><%= yield :title -%></title>
+  <link rel="stylesheet" type="text/css" href="http://fedoraproject.org/static/css/fedora.css" />
+  <%= stylesheet_link_tag 'cloud/layout' %>
+    <%= yield :scripts -%>
+  </head>
+
+  <body>
+    <div id="head">
+      <!-- Header stuff! -->
+      <div id="login-box">
+        Hi, <%= @user %>
+        | <%= link_to 'Log out', { :controller => "/login", :action => "logout"}%>
+         <%= link_to image_tag("Help_16.png"),
+                {:controller => 'help', :action => @help_section, :anchor => @anchor},
+                :id=>"help-link", :popup => true, :title => "Help" %>
+      </div>
+      <div id="nav">
+        <ul class="toolbar">
+          <li>
+            <%=
+                link_to_unless(controller.controller_name == 'dashboard', "Dashboard", { :controller => 'cloud/dashboard' }) do |name|
+                  link_to(name, { :controller => 'cloud/dashboard'}, :class => 'current')
+                end
+            %>
+          </li>
+          <li>
+            <%=
+                link_to_unless(controller.controller_name == 'instance', "Instances", { :controller => 'cloud/instance' }) do |name|
+                  link_to(name, { :controller => 'cloud/instance'}, :class => 'current')
+                end
+            %>
+          </li>
+          <!-- Enable this once we have a profile view.
+          <li>
+            <%=
+                link_to_unless(controller.controller_name == 'profile', "Profiles", { :controller => 'cloud/profile' }) do |name|
+                  link_to(name, { :controller => 'cloud/profile'}, :class => 'current')
+                end
+            %>
+          </li>-->
+        </ul>
+      </div>
+    </div>
+    <div id="content">
+      <%= yield %>
+    </div>
+  </body>
+</html>
diff --git a/src/public/images/icon_failed_11px.png b/src/public/images/icon_failed_11px.png
new file mode 100644
index 0000000000000000000000000000000000000000..a8022652ab0172e5ce91eb0a4429cce496d92a6f
GIT binary patch
literal 374
zcmV-+0g3*JP)<h;3K|Lk000e1NJLTq000XB000XJ1^@s6sjPP<0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz7)eAyRCwBSQM*dSP!K({1}nF)3>Fp=
zI|~u*L=eQS@&SThk?-&~_5)HyE5TyHMJ((L3wB#r*k|sY at g%!(qu{_WGiM&>&Rkhs
zo&#_QBy)JdAO|EBU`fAZ;?5nl2x~sBE{NfpTH$}0Q13?;V-}-T77cN&`wx$uU11Ss
zv7*hA5Ul8WNz;i-XJUvOY$XMYHDCGii<kHyYIB|^sv+HgTw*;rXuuFBW10ljcI(vp
ze(KBd(T^{EEw3};<g#fLp$X7V6(_H6=rB%iT1*uhw3MgpAY<?QkaG9*3}5s4-}4=E
z!B8_lAz#%Of6iw8JNtL=&QfpPj(6bZ3p1aZ**1S`&&|djv1CllZ_NFf7T*F404n*b
UH}$I_cK`qY07*qoM6N<$f}Hi8umAu6

literal 0
HcmV?d00001

diff --git a/src/public/images/icon_queued_11px.png b/src/public/images/icon_queued_11px.png
new file mode 100644
index 0000000000000000000000000000000000000000..bd99c71acb11fb0308d26760110e66d470317cc8
GIT binary patch
literal 429
zcmV;e0aE^nP)<h;3K|Lk000e1NJLTq000XB000XJ1^@s6sjPP<0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzPf0{URCwB4Q9WzIP!zpStpyhyoW(j6
z)5W>P2<8W<jazU{aCPq7rTqg5u7z%bQ^i3M`v*G3+5b=~B=6pyr%fxpaNfx~?{ITJ
zl!%at at p#+ at xqt&$Kn$#aSr~@z#*;IL?*}8zda5-~G;?b>bc+mzHi0$`e1Bwo`Tc(1
z7U5 at MX@W_Zsn_e#X0yS!n0u8)Zszm(M=niMH%Zebm@<?~C4xq^T8;YsKD5NVyM-i4
z9QqfPN`>wR19BXPJkO(Yxol*|n5{pX0_2$4wrvW6;O8$1DIv?4W6Yza&N45TONye1
zz77YvC=@a|&Nmv3Yjl1KP=>d~f|OE}Lk8Br`fL8GUjM|n?$CMFh!_`L7K>!&&!=gC
z^-i-qDI<2f-4V)tK)KDV?z7kdkL&gNMP(c8*qt`IIBigrC5mE2vcmIhyWJi)_$|Ny
X31G at EBP7mw00000NkvXXu0mjfll#5W

literal 0
HcmV?d00001

diff --git a/src/public/images/icon_startup_11px.png b/src/public/images/icon_startup_11px.png
new file mode 100644
index 0000000000000000000000000000000000000000..1b8769fc7af754de05c442a63fed404b5e744e29
GIT binary patch
literal 458
zcmV;*0X6=KP)<h;3K|Lk000e1NJLTq000XB000XJ1^@s6sjPP<0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzY)M2xRCwAnQM*b5Q4l?IlL!$+3t#vs
zG_dSyVW9++Xc0 at FEDAybR(^nWswg)8fS{cu+Nix2K}2i>3oX`0BKSZ{1uJ*&_3TYt
zILw at L&kVCOvT>S3^UD!FktaMNAZo-JaUB~}(^w=9h)rT_-~VliW0M~=-=VZdPHQO{
z19!>Wf;Nq6V+59Kap@^B=4ci_(lm~2!ReYo6p1xcvwV59%-k0Eib-D6Fy8Oux?W|c
zM&~Hxp!FlG(-R}F!U$eS(%qK9&GRWujo%0(g`4_`)uT|z#Zydzn0$UeAv>^&xNz+-
zZO6zCu2|i6ju0f=#gxOjCp%huF)=s~krY0@>X;rbVkT3--RlMJ-!5UaI!?HAsUL1&
z>f!yWv!`tcv(7pS6Wi!(cX0P|j_Pe02J%SHt}N`O*-eVvSc$4D60hz5PD})kFtWo~
zNouVqnTkN3DfnD;o{<-nfDbjU`%K~sGWio=0FQs4hHXF@$N&HU07*qoM6N<$f;C{m
AI{*Lx

literal 0
HcmV?d00001

diff --git a/src/public/images/icon_unknown_11px.png b/src/public/images/icon_unknown_11px.png
new file mode 100644
index 0000000000000000000000000000000000000000..1e7fda4dc5793502e90cfabff60ea32eb35abaae
GIT binary patch
literal 421
zcmV;W0b2fvP)<h;3K|Lk000e1NJLTq000XB000XJ1^@s6sjPP<0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzM at d9MRCwAnQL$>mP!v74<U52~hXjOx
zvy)p#Gl)aK!a{{+E>x+b{Q_q{AyWnkK_N>V9qrb3?0fO{yk`uY3%U24^X at xO*6UBS
z-;hX%5fKqJkr7)WcZA?_xd5t95Tt;FQ2vT9F+^igc_i<R*aX%#LpD<Z!cM1y at p$Zy
z+3)wLstP~fzo9}?nw{>fH3<cwwbm2Mvc$(~g~4Eee!maS(O4T=(<F4ZBXL$Ni}!`w
z0^RPtcUM&ynz%Xpqreyw-N>%=dJmXPCMb#mhr<ExL?;qR<eY2D;+uRronp7!dE)u}
z<?QL<hjn28Os0ZZ+WgYj)UR?4<l1X}vcNQkIr?})H&`wgo-luzdr|9rq<9<fra3oa
zj at S5RvuE#$)|K3&%bA<xBsX-BjfkE&;0p?0P~D|#u90z<E!F-7{{<KTM8={uf|Zq&
P00000NkvXXu0mjf6ZgH1

literal 0
HcmV?d00001

diff --git a/src/public/images/icon_warning_11px.png b/src/public/images/icon_warning_11px.png
new file mode 100644
index 0000000000000000000000000000000000000000..44700c87cc1440335f186fbda793c633924ef6de
GIT binary patch
literal 343
zcmV-d0jU0oP)<h;3K|Lk000e1NJLTq000XB000XJ1^@s6sjPP<0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy`AI}URCwBqkikmBKomvqdvqpNQ>v53
zl~xzVojd6__z{BN&_y7Afqt7yx1#7yG|9$7n^0zalWdaOf)^gcefMyg8G61$Pw|Yh
z#k+e5&*}dA$vt*6#)hf_>B$W@=VsP}<7>)P2osC|HG=VeC%np$9$qqdW^t5o<F~?-
zE6Vgfj2^aL at 5u~d{QeJIl}N{@VwbIVD_}h3X5WLOgfkULs}`sOd++yA(zcWTy6~8a
z_<~JvKyaNqdRHxg{MD8}UoPTkDy^D~QWDi at Cb(S#a at v}toiLExbM!W+VsggvFv2d?
pONac!mV<Q-^Z8=&51C&91^^~AOG{24q`m+E002ovPDHLkV1lujmu&z5

literal 0
HcmV?d00001

diff --git a/src/public/stylesheets/cloud/layout.css b/src/public/stylesheets/cloud/layout.css
new file mode 100644
index 0000000..60b5c91
--- /dev/null
+++ b/src/public/stylesheets/cloud/layout.css
@@ -0,0 +1,166 @@
+/* ------ General Layout ------ */
+
+.toolbar {text-align:left;}
+
+.current, .toolbar a:hover {
+  border-bottom: 5px solid #5599B8;
+}
+
+.toolbar a {
+  text-decoration: none;
+}
+
+#nav {height:1em;padding-top:1ex;}
+
+#nav a {display: inline-block;}
+
+#head {height:50px;}
+
+#login-box {
+  text-align:right;
+  /*
+    TODO: put this back in with a real logo when we have one.
+        background: url('../../images/logo_cumulus.png') left top no-repeat;
+  */
+}
+
+#content {margin-left:18px;}
+
+#content table{width:100%;}
+
+#content table tr.odd {background:#eef2f2;}
+
+#content table tr:hover {background: #729FCF; color: #FFFFFF;}
+
+#content table th, #content table td {
+  border:1px solid #AAAAAA;
+  padding:0;
+  text-align:left;
+}
+
+#content table th {background-color: #F0F0F0;}
+
+#content table div {
+  padding: 0.4ex;
+}
+
+#content table th a {
+  display: block;
+  text-decoration: none;
+}
+
+.sortup {
+  background: #F0F0F0 url('../../images/Sort_up_11.png') top center no-repeat !important;
+}
+
+.sortdown {
+  background: #F0F0F0 url('../../images/Sort_down_11.png') top center no-repeat !important;
+}
+
+.sortable:hover {background-color: #DDDDDD !important;}
+
+.selected {background: #337ACC !important; color: #FFFFFF;}
+
+.center {text-align:center;}
+
+.pagination {padding-top: 1ex;}
+
+#list-view {}
+
+#detail-view {
+  border-top: 3px solid #AAAAAA;
+  margin-top: 2ex;
+}
+
+#graph {
+  border: 1px solid #AAAAAA;
+  margin-bottom: 1.5ex;
+  height: 15ex;
+}
+
+#submit_for_list {
+  float: right;
+  background: none;
+  border: 0;
+  color: #337ACC;
+  cursor: pointer;
+}
+
+#detail_header {
+  background-color: #F0F0F0;
+  height: 10ex;
+  margin-bottom: 1.5ex;
+}
+
+/* ----- Backgrounds for states ----- */
+.state-container {
+  margin-left:5px;
+  text-indent:10px;
+}
+
+.task-canceled {background:url(../../images/icon-canceled-11px.png) left center no-repeat;}
+.task-failed {background:url(../../images/icon-failed-11px.png) left center no-repeat;}
+.task-finished {background:url(../../images/icon-finished-11px.png) left center no-repeat;}
+.task-paused {background:url(../../images/icon-paused-11px.png) left center no-repeat;}
+.task-queued {background:url(../../images/icon-queued-11px.png) left center no-repeat;}
+.task-running {background:url(../../images/icon-running-11px.png) left center no-repeat;}
+.instance-unreachable {background:url(../../images/icon_unknown_11px.png) left center no-repeat;}
+.instance-stopped {background:url(../../images/icon_stop_11px.png) left center no-repeat;}
+.instance-suspended {background:url(../../images/icon_suspend_11px.png) left center no-repeat;}
+.instance-saved {background:url(../../images/icon_save_11px.png) left center no-repeat;}
+.instance-create_failed {background:url(../../images/icon_failed_11px.png) left center no-repeat;}
+.instance-invalid {background:url(../../images/icon_warning_11px.png) left center no-repeat;}
+.instance-pending {background:url(../../images/icon_queued_11px.png) left center no-repeat;}
+.instance-running {background:url(../../images/icon_start_11px.png) left center no-repeat;}
+.instance-creating {background:url(../../images/icon_startup_11px.png) left center no-repeat;}
+
+
+/*  ----- Toolbar Navigation --------  */
+#toolbar_nav {
+  background: #F0F0F0;
+}
+
+#toolbar_nav img {
+  margin: 0;
+}
+
+#toolbar_nav ul{
+  list-style: none;
+}
+
+#toolbar_nav li {
+  display: inline-block;
+  position: relative;
+  color: #666666;
+  padding: 0 1ex;
+}
+#toolbar_nav li:hover {
+  background: #337ACC;
+  color: #FFFFFF;
+  cursor: pointer;
+}
+
+#toolbar_nav li.current { /* This is not really needed right now, but will be useful when we add filters. */
+  background: #4B95B8;
+}
+
+#toolbar_nav li ul {
+  display: none;
+  position: absolute;
+  top: 1em;
+  left: 0;
+  background: #F0F0F0;
+}
+
+#toolbar_nav li ul li {
+  display: block;
+  white-space: nowrap;
+  padding-right: 1ex;
+}
+
+#toolbar_nav li > ul {
+  top: auto;
+  left: auto;
+}
+
+#toolbar_nav li:hover ul {display: block;}
\ No newline at end of file
diff --git a/src/test/functional/cloud/cloud_controller_test.rb b/src/test/functional/cloud/cloud_controller_test.rb
new file mode 100644
index 0000000..864400c
--- /dev/null
+++ b/src/test/functional/cloud/cloud_controller_test.rb
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class Cloud::CloudControllerTest < ActionController::TestCase
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/src/test/functional/cloud/dashboard_controller_test.rb b/src/test/functional/cloud/dashboard_controller_test.rb
new file mode 100644
index 0000000..3ddb6ca
--- /dev/null
+++ b/src/test/functional/cloud/dashboard_controller_test.rb
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class Cloud::DashboardControllerTest < ActionController::TestCase
+  # Replace this with your real tests.
+  def test_truth
+    assert true
+  end
+end
diff --git a/src/test/functional/cloud/instance_controller_test.rb b/src/test/functional/cloud/instance_controller_test.rb
new file mode 100644
index 0000000..213d2e0
--- /dev/null
+++ b/src/test/functional/cloud/instance_controller_test.rb
@@ -0,0 +1,23 @@
+require 'test_helper'
+
+class Cloud::InstanceControllerTest < ActionController::TestCase
+  fixtures :vms, :tasks
+  def setup
+    @controller = Cloud::InstanceController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  def test_should_show_index
+    get :index
+    assert_response :success
+    assert_template 'index'
+    assert_not_nil assigns(:vms)
+    assert_not_nil assigns(:user)
+  end
+
+  def test_should_redirect_if_request_for_selected
+    post(:index,{:submit_for_list => 'Show Selected'})
+    assert_response :redirect
+  end
+end
-- 
1.6.0.6




More information about the ovirt-devel mailing list