Basic starter scenario for testing the dashboard

Add tests to check that the homepage is working, and that it's
possible to log in and see the overview page.

Fixes bug 1204408

Change-Id: Ifd62e833c85b78c39052f1365a4e1e00680713ef
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index a73e8a0..8b9bb9c 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -318,6 +318,13 @@
 # any key, which will generate a keypair for each test class
 #keypair_name = heat_key
 
+[dashboard]
+# URL where to find the dashboard home page
+dashboard_url = 'http://localhost/'
+
+# URL where to submit the login form
+login_url = 'http://localhost/auth/login/'
+
 [scenario]
 # Directory containing image files
 img_dir = /opt/stack/new/devstack/files/images/cirros-0.3.1-x86_64-uec
@@ -357,3 +364,5 @@
 nova = True
 # Whether or not Heat is expected to be available
 heat = false
+# Whether or not horizon is expected to be available
+horizon = True
diff --git a/tempest/config.py b/tempest/config.py
index d9de205..c9b82ca 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -410,6 +410,26 @@
     for opt in OrchestrationGroup:
         conf.register_opt(opt, group='orchestration')
 
+
+dashboard_group = cfg.OptGroup(name="dashboard",
+                               title="Dashboard options")
+
+DashboardGroup = [
+    cfg.StrOpt('dashboard_url',
+               default='http://localhost/',
+               help="Where the dashboard can be found"),
+    cfg.StrOpt('login_url',
+               default='http://localhost/auth/login/',
+               help="Login page for the dashboard"),
+]
+
+
+def register_dashboard_opts(conf):
+    conf.register_group(scenario_group)
+    for opt in DashboardGroup:
+        conf.register_opt(opt, group='dashboard')
+
+
 boto_group = cfg.OptGroup(name='boto',
                           title='EC2/S3 options')
 BotoConfig = [
@@ -554,6 +574,9 @@
     cfg.BoolOpt('heat',
                 default=False,
                 help="Whether or not Heat is expected to be available"),
+    cfg.BoolOpt('horizon',
+                default=True,
+                help="Whether or not Horizon is expected to be available"),
 ]
 
 
@@ -609,6 +632,7 @@
         register_volume_opts(cfg.CONF)
         register_object_storage_opts(cfg.CONF)
         register_orchestration_opts(cfg.CONF)
+        register_dashboard_opts(cfg.CONF)
         register_boto_opts(cfg.CONF)
         register_compute_admin_opts(cfg.CONF)
         register_stress_opts(cfg.CONF)
@@ -622,6 +646,7 @@
         self.volume = cfg.CONF.volume
         self.object_storage = cfg.CONF['object-storage']
         self.orchestration = cfg.CONF.orchestration
+        self.dashboard = cfg.CONF.dashboard
         self.boto = cfg.CONF.boto
         self.compute_admin = cfg.CONF['compute-admin']
         self.stress = cfg.CONF.stress
diff --git a/tempest/scenario/test_dashboard_basic_ops.py b/tempest/scenario/test_dashboard_basic_ops.py
new file mode 100644
index 0000000..9a45572
--- /dev/null
+++ b/tempest/scenario/test_dashboard_basic_ops.py
@@ -0,0 +1,72 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import urllib
+import urllib2
+
+from lxml import html
+
+from tempest.scenario import manager
+
+
+class TestDashboardBasicOps(manager.OfficialClientTest):
+
+    """
+    This is a basic scenario test:
+    * checks that the login page is available
+    * logs in as a regular user
+    * checks that the user home page loads without error
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestDashboardBasicOps, cls).setUpClass()
+
+        if not cls.config.service_available.horizon:
+            raise cls.skipException("Horizon support is required")
+
+    def check_login_page(self):
+        response = urllib2.urlopen(self.config.dashboard.dashboard_url)
+        self.assertIn("<h3>Log In</h3>", response.read())
+
+    def user_login(self):
+        self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor())
+        response = self.opener.open(self.config.dashboard.dashboard_url).read()
+
+        # Grab the CSRF token and default region
+        csrf_token = html.fromstring(response).xpath(
+            '//input[@name="csrfmiddlewaretoken"]/@value')[0]
+        region = html.fromstring(response).xpath(
+            '//input[@name="region"]/@value')[0]
+
+        # Prepare login form request
+        req = urllib2.Request(self.config.dashboard.login_url)
+        req.add_header('Content-type', 'application/x-www-form-urlencoded')
+        req.add_header('Referer', self.config.dashboard.dashboard_url)
+        params = {'username': self.config.identity.username,
+                  'password': self.config.identity.password,
+                  'region': region,
+                  'csrfmiddlewaretoken': csrf_token}
+        self.opener.open(req, urllib.urlencode(params))
+
+    def check_home_page(self):
+        response = self.opener.open(self.config.dashboard.dashboard_url)
+        self.assertIn('Overview', response.read())
+
+    def test_basic_scenario(self):
+        self.check_login_page()
+        self.user_login()
+        self.check_home_page()