diff options
author | TatyanaGladysheva <tgladysheva@mirantis.com> | 2016-01-12 15:37:45 +0300 |
---|---|---|
committer | Sergei Chipiga <schipiga@mirantis.com> | 2016-04-20 11:14:02 +0300 |
commit | eda58de3f669abb0991bdaad791158e420615ccb (patch) | |
tree | c87331abcc4db9837bd78bd25970c076ad1e3886 | |
parent | 5d6003971f1c41abecd19c40b1384f9efa5210de (diff) |
Added test for check stacks creation and deletion functionality
Test checks that create/delete stacks actions are executed without
errors under admin user.
Stacks page object was defined similar to other pages.
Few other modifications I've made:
* horizon.conf - service_available section with new heat option
* stack template is added as separate file
Depends-On: I1f5dc1220aee39103289a579583095346cce0354
Implements blueprint: horizon-integration-tests-coverage
Change-Id: Ibc549f9ae4eac17d8e92d65afe1c5cee9be6e72e
Notes
Notes (review):
Code-Review+2: Timur Sufiev <tsufiev@mirantis.com>
Workflow+1: Timur Sufiev <tsufiev@mirantis.com>
Code-Review+1: Sergei Chipiga <schipiga@mirantis.com>
Verified+2: Jenkins
Submitted-by: Jenkins
Submitted-at: Wed, 20 Apr 2016 10:46:24 +0000
Reviewed-on: https://review.openstack.org/266368
Project: openstack/horizon
Branch: refs/heads/master
8 files changed, 197 insertions, 6 deletions
diff --git a/openstack_dashboard/test/integration_tests/config.py b/openstack_dashboard/test/integration_tests/config.py index c4acee3..c5cfc2f 100644 --- a/openstack_dashboard/test/integration_tests/config.py +++ b/openstack_dashboard/test/integration_tests/config.py | |||
@@ -70,6 +70,8 @@ NetworkGroup = [ | |||
70 | AvailableServiceGroup = [ | 70 | AvailableServiceGroup = [ |
71 | cfg.BoolOpt('neutron', | 71 | cfg.BoolOpt('neutron', |
72 | default=True), | 72 | default=True), |
73 | cfg.BoolOpt('heat', | ||
74 | default=True), | ||
73 | ] | 75 | ] |
74 | 76 | ||
75 | SeleniumGroup = [ | 77 | SeleniumGroup = [ |
diff --git a/openstack_dashboard/test/integration_tests/horizon.conf b/openstack_dashboard/test/integration_tests/horizon.conf index 2707381..a570280 100644 --- a/openstack_dashboard/test/integration_tests/horizon.conf +++ b/openstack_dashboard/test/integration_tests/horizon.conf | |||
@@ -67,6 +67,8 @@ tenant_network_cidr=10.100.0.0/16 | |||
67 | [service_available] | 67 | [service_available] |
68 | # Whether is Neutron expected to be available (boolean value) | 68 | # Whether is Neutron expected to be available (boolean value) |
69 | neutron=True | 69 | neutron=True |
70 | # Whether is Heat expected to be available (boolean value) | ||
71 | heat=True | ||
70 | 72 | ||
71 | [scenario] | 73 | [scenario] |
72 | # ssh username for image file (string value) | 74 | # ssh username for image file (string value) |
diff --git a/openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/keypairspage.py b/openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/keypairspage.py index 9c24358..2c3a93e 100644 --- a/openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/keypairspage.py +++ b/openstack_dashboard/test/integration_tests/pages/project/compute/access_and_security/keypairspage.py | |||
@@ -34,6 +34,11 @@ class KeypairsTable(tables.TableRegion): | |||
34 | delete_button.click() | 34 | delete_button.click() |
35 | return forms.BaseFormRegion(self.driver, self.conf) | 35 | return forms.BaseFormRegion(self.driver, self.conf) |
36 | 36 | ||
37 | @tables.bind_table_action('delete') | ||
38 | def delete_keypairs(self, delete_button): | ||
39 | delete_button.click() | ||
40 | return forms.BaseFormRegion(self.driver, self.conf) | ||
41 | |||
37 | 42 | ||
38 | class KeypairsPage(basepage.BaseNavigationPage): | 43 | class KeypairsPage(basepage.BaseNavigationPage): |
39 | 44 | ||
@@ -69,3 +74,9 @@ class KeypairsPage(basepage.BaseNavigationPage): | |||
69 | row = self._get_row_with_keypair_name(name) | 74 | row = self._get_row_with_keypair_name(name) |
70 | delete_keypair_form = self.keypairs_table.delete_keypair(row) | 75 | delete_keypair_form = self.keypairs_table.delete_keypair(row) |
71 | delete_keypair_form.submit() | 76 | delete_keypair_form.submit() |
77 | |||
78 | def delete_keypairs(self, name): | ||
79 | row = self._get_row_with_keypair_name(name) | ||
80 | row.mark() | ||
81 | delete_keypair_form = self.keypairs_table.delete_keypairs() | ||
82 | delete_keypair_form.submit() | ||
diff --git a/openstack_dashboard/test/integration_tests/pages/project/orchestration/stackspage.py b/openstack_dashboard/test/integration_tests/pages/project/orchestration/stackspage.py new file mode 100644 index 0000000..642a0c7 --- /dev/null +++ b/openstack_dashboard/test/integration_tests/pages/project/orchestration/stackspage.py | |||
@@ -0,0 +1,97 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
2 | # not use this file except in compliance with the License. You may obtain | ||
3 | # a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
10 | # License for the specific language governing permissions and limitations | ||
11 | # under the License. | ||
12 | |||
13 | from openstack_dashboard.test.integration_tests import config | ||
14 | from openstack_dashboard.test.integration_tests.pages import basepage | ||
15 | from openstack_dashboard.test.integration_tests.regions import forms | ||
16 | from openstack_dashboard.test.integration_tests.regions import tables | ||
17 | |||
18 | |||
19 | class StacksTable(tables.TableRegion): | ||
20 | name = "stacks" | ||
21 | SELECT_TEMPLATE_FORM_FIELDS = ("template_source", "template_upload", | ||
22 | "template_data", "template_url", | ||
23 | "environment_source", "environment_upload", | ||
24 | "environment_data") | ||
25 | LAUNCH_STACK_FORM_FIELDS = ("stack_name", "timeout_mins", | ||
26 | "enable_rollback", "password") | ||
27 | |||
28 | @tables.bind_table_action('launch') | ||
29 | def select_template(self, launch_button): | ||
30 | launch_button.click() | ||
31 | return forms.FormRegion( | ||
32 | self.driver, self.conf, | ||
33 | field_mappings=self.SELECT_TEMPLATE_FORM_FIELDS) | ||
34 | |||
35 | def launch_stack(self): | ||
36 | return forms.FormRegion(self.driver, self.conf, | ||
37 | field_mappings=self.LAUNCH_STACK_FORM_FIELDS) | ||
38 | |||
39 | @tables.bind_table_action('delete') | ||
40 | def delete_stack(self, delete_button): | ||
41 | delete_button.click() | ||
42 | return forms.BaseFormRegion(self.driver, self.conf) | ||
43 | |||
44 | |||
45 | class StacksPage(basepage.BaseNavigationPage): | ||
46 | DEFAULT_TEMPLATE_SOURCE = 'raw' | ||
47 | |||
48 | CONFIG = config.get_config() | ||
49 | DEFAULT_PASSWORD = CONFIG.identity.admin_password | ||
50 | STACKS_TABLE_NAME_COLUMN = 'name' | ||
51 | STACKS_TABLE_STATUS_COLUMN = 'stack_status' | ||
52 | |||
53 | def __init__(self, driver, conf): | ||
54 | super(StacksPage, self).__init__(driver, conf) | ||
55 | self._page_title = "Stacks" | ||
56 | |||
57 | @property | ||
58 | def stacks_table(self): | ||
59 | return StacksTable(self.driver, self.conf) | ||
60 | |||
61 | def _get_row_with_stack_name(self, name): | ||
62 | return self.stacks_table.get_row(self.STACKS_TABLE_NAME_COLUMN, name) | ||
63 | |||
64 | def create_stack(self, stack_name, template_data, | ||
65 | template_source=DEFAULT_TEMPLATE_SOURCE, | ||
66 | environment_source=None, | ||
67 | environment_upload=None, | ||
68 | timeout_mins=None, | ||
69 | enable_rollback=None, | ||
70 | password=DEFAULT_PASSWORD): | ||
71 | select_template_form = self.stacks_table.select_template() | ||
72 | select_template_form.template_source.value = template_source | ||
73 | select_template_form.template_data.text = template_data | ||
74 | select_template_form.submit() | ||
75 | launch_stack_form = self.stacks_table.launch_stack() | ||
76 | launch_stack_form.stack_name.text = stack_name | ||
77 | launch_stack_form.password.text = password | ||
78 | launch_stack_form.submit() | ||
79 | |||
80 | def delete_stack(self, name): | ||
81 | row = self._get_row_with_stack_name(name) | ||
82 | row.mark() | ||
83 | confirm_delete_stacks_form = self.stacks_table.delete_stack() | ||
84 | confirm_delete_stacks_form.submit() | ||
85 | |||
86 | def is_stack_present(self, name): | ||
87 | return bool(self._get_row_with_stack_name(name)) | ||
88 | |||
89 | def is_stack_create_complete(self, name): | ||
90 | row = self._get_row_with_stack_name(name) | ||
91 | return self.stacks_table.wait_cell_status( | ||
92 | lambda: row and row.cells[self.STACKS_TABLE_STATUS_COLUMN], | ||
93 | 'Create Complete') | ||
94 | |||
95 | def is_stack_deleted(self, name): | ||
96 | return self.stacks_table.is_row_deleted( | ||
97 | lambda: self._get_row_with_stack_name(name)) | ||
diff --git a/openstack_dashboard/test/integration_tests/tests/test-data/stack_template b/openstack_dashboard/test/integration_tests/tests/test-data/stack_template new file mode 100644 index 0000000..a86e585 --- /dev/null +++ b/openstack_dashboard/test/integration_tests/tests/test-data/stack_template | |||
@@ -0,0 +1,11 @@ | |||
1 | heat_template_version: 2013-05-23 | ||
2 | description: Simple template to deploy a single compute instance | ||
3 | resources: | ||
4 | my_instance: | ||
5 | type: OS::Nova::Server | ||
6 | properties: | ||
7 | key_name: {0} | ||
8 | image: {1} | ||
9 | flavor: m1.tiny | ||
10 | networks: | ||
11 | - network: {2} \ No newline at end of file | ||
diff --git a/openstack_dashboard/test/integration_tests/tests/test_stacks.py b/openstack_dashboard/test/integration_tests/tests/test_stacks.py new file mode 100644 index 0000000..b60476b --- /dev/null +++ b/openstack_dashboard/test/integration_tests/tests/test_stacks.py | |||
@@ -0,0 +1,70 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
2 | # not use this file except in compliance with the License. You may obtain | ||
3 | # a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
10 | # License for the specific language governing permissions and limitations | ||
11 | # under the License. | ||
12 | import os | ||
13 | |||
14 | from openstack_dashboard.test.integration_tests import decorators | ||
15 | from openstack_dashboard.test.integration_tests import helpers | ||
16 | from openstack_dashboard.test.integration_tests.regions import messages | ||
17 | |||
18 | |||
19 | class TestStacks(helpers.AdminTestCase): | ||
20 | KEYPAIR_NAME = 'keypair_for_stack' | ||
21 | STACKS_NAME = helpers.gen_random_resource_name('stack', timestamp=False) | ||
22 | STACK_TEMPLATE_PATH = os.path.join( | ||
23 | os.path.dirname(__file__), 'test-data/stack_template') | ||
24 | |||
25 | def setUp(self): | ||
26 | super(TestStacks, self).setUp() | ||
27 | keypair_page = self.home_pg.\ | ||
28 | go_to_compute_accessandsecurity_keypairspage() | ||
29 | keypair_page.create_keypair(self.KEYPAIR_NAME) | ||
30 | keypair_page = self.home_pg.\ | ||
31 | go_to_compute_accessandsecurity_keypairspage() | ||
32 | self.assertTrue(keypair_page.is_keypair_present(self.KEYPAIR_NAME)) | ||
33 | |||
34 | @decorators.services_required("heat") | ||
35 | def test_create_delete_stack(self): | ||
36 | """tests the stack creation and deletion functionality | ||
37 | * creates a new stack | ||
38 | * verifies the stack appears in the stacks table in Create Complete | ||
39 | state | ||
40 | * deletes the newly created stack | ||
41 | * verifies the stack does not appear in the table after deletion | ||
42 | """ | ||
43 | with open(self.STACK_TEMPLATE_PATH, 'r') as f: | ||
44 | template = f.read() | ||
45 | input_template = template.format(self.KEYPAIR_NAME, | ||
46 | self.CONFIG.image.images_list[0], | ||
47 | "public") | ||
48 | stacks_page = self.home_pg.go_to_orchestration_stackspage() | ||
49 | |||
50 | stacks_page.create_stack(self.STACKS_NAME, input_template) | ||
51 | self.assertTrue( | ||
52 | stacks_page.find_message_and_dismiss(messages.INFO)) | ||
53 | self.assertFalse( | ||
54 | stacks_page.find_message_and_dismiss(messages.ERROR)) | ||
55 | self.assertTrue(stacks_page.is_stack_present(self.STACKS_NAME)) | ||
56 | self.assertTrue(stacks_page.is_stack_create_complete(self.STACKS_NAME)) | ||
57 | |||
58 | stacks_page.delete_stack(self.STACKS_NAME) | ||
59 | self.assertTrue( | ||
60 | stacks_page.find_message_and_dismiss(messages.SUCCESS)) | ||
61 | self.assertFalse( | ||
62 | stacks_page.find_message_and_dismiss(messages.ERROR)) | ||
63 | self.assertTrue(stacks_page.is_stack_deleted(self.STACKS_NAME)) | ||
64 | |||
65 | def tearDown(self): | ||
66 | keypair_page = self.home_pg.\ | ||
67 | go_to_compute_accessandsecurity_keypairspage() | ||
68 | keypair_page.delete_keypairs(self.KEYPAIR_NAME) | ||
69 | keypair_page.find_message_and_dismiss(messages.SUCCESS) | ||
70 | super(TestStacks, self).tearDown() | ||
diff --git a/tools/gate/integration/devstack_exports.sh b/tools/gate/integration/devstack_exports.sh deleted file mode 100644 index ce5593e..0000000 --- a/tools/gate/integration/devstack_exports.sh +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | export PYTHONUNBUFFERED=true | ||
2 | export DEVSTACK_GATE_TIMEOUT=90 | ||
3 | export DEVSTACK_GATE_TEMPEST=0 | ||
4 | export DEVSTACK_GATE_EXERCISES=0 | ||
5 | export DEVSTACK_GATE_INSTALL_TESTONLY=1 | ||
6 | export DEVSTACK_GATE_NEUTRON=1 | ||
diff --git a/tools/gate/integration/devstack_gate_rc b/tools/gate/integration/devstack_gate_rc new file mode 100644 index 0000000..1579b49 --- /dev/null +++ b/tools/gate/integration/devstack_gate_rc | |||
@@ -0,0 +1,4 @@ | |||
1 | # This file contains various customized Devstack settings that Horizon uses at | ||
2 | # gate for integration tests job | ||
3 | |||
4 | export ENABLED_SERVICES=heat,h-eng,h-api,h-api-cfn,h-api-cw | ||