diff options
author | Matt Borland <matt.borland@hpe.com> | 2016-07-07 10:32:43 -0600 |
---|---|---|
committer | Matt Borland <matt.borland@hpe.com> | 2016-08-05 08:17:21 -0600 |
commit | 20bc6e1516f0cb5d4c9dbec464dc823cd8ed6b44 (patch) | |
tree | d6b053a8e6e70c80003d0a7531798fffa2511dad | |
parent | de1a23267aa5ad60702282917b53c1913c85e566 (diff) |
Make 'switch' between legacy and Angular Images
This patch follows on the example that the Containers set, providing
a 'switch' in the panel-enablement file that currently defaults to
'legacy' (Python-based Images panel) and allows for 'angular' (Angular-
based Images panel).
To be clear, this does NOT enable Angular Images. It's just setting the
stage to do so at some point, or to allow deployers/devs to easily switch
between the two.
A switch both for HORIZON_CONFIG and for integration tests is necessary
due to the way integration tests operate.
Co-Authored-By: Timur Sufiev <tsufiev@mirantis.com>
Change-Id: I12cd33552218ed1082d2d9a2ae8982639a217a6a
Partially-Implements: blueprint angularize-images-table
Notes
Notes (review):
Code-Review+2: Travis Tripp <travis.tripp@hpe.com>
Code-Review+1: Ying Zuo <yinzuo@cisco.com>
Code-Review+2: Rob Cresswell <robert.cresswell@outlook.com>
Workflow+1: Rob Cresswell <robert.cresswell@outlook.com>
Verified+2: Jenkins
Submitted-by: Jenkins
Submitted-at: Mon, 08 Aug 2016 13:11:25 +0000
Reviewed-on: https://review.openstack.org/339122
Project: openstack/horizon
Branch: refs/heads/master
24 files changed, 199 insertions, 147 deletions
diff --git a/doc/source/topics/settings.rst b/doc/source/topics/settings.rst index 9cbb635..460a403 100755 --- a/doc/source/topics/settings.rst +++ b/doc/source/topics/settings.rst | |||
@@ -180,6 +180,18 @@ A dictionary containing classes of exceptions which Horizon's centralized | |||
180 | exception handling should be aware of. Based on these exception categories, | 180 | exception handling should be aware of. Based on these exception categories, |
181 | Horizon will handle the exception and display a message to the user. | 181 | Horizon will handle the exception and display a message to the user. |
182 | 182 | ||
183 | ``images_panel`` | ||
184 | ----------- | ||
185 | |||
186 | .. versionadded:: 10.0.0(Newton) | ||
187 | |||
188 | Default: ``legacy`` | ||
189 | |||
190 | There are currently two panel types that may be specified: ``legacy`` and | ||
191 | ``angular``. ``legacy`` will display the Python-based (server-side) Images | ||
192 | panel and ``angular`` will display the Angular-based (client-side) Images | ||
193 | panel. | ||
194 | |||
183 | ``modal_backdrop`` | 195 | ``modal_backdrop`` |
184 | ------------------ | 196 | ------------------ |
185 | 197 | ||
diff --git a/openstack_dashboard/dashboards/admin/images/urls.py b/openstack_dashboard/dashboards/admin/images/urls.py index 2a907c9..d19b970 100644 --- a/openstack_dashboard/dashboards/admin/images/urls.py +++ b/openstack_dashboard/dashboards/admin/images/urls.py | |||
@@ -16,16 +16,24 @@ | |||
16 | # License for the specific language governing permissions and limitations | 16 | # License for the specific language governing permissions and limitations |
17 | # under the License. | 17 | # under the License. |
18 | 18 | ||
19 | from django.conf import settings | ||
19 | from django.conf.urls import url | 20 | from django.conf.urls import url |
20 | 21 | ||
21 | from openstack_dashboard.dashboards.admin.images import views | 22 | from openstack_dashboard.dashboards.admin.images import views |
22 | 23 | ||
23 | 24 | if settings.HORIZON_CONFIG['images_panel'] == 'angular': | |
24 | urlpatterns = [ | 25 | # New angular images |
25 | url(r'^$', views.IndexView.as_view(), name='index'), | 26 | urlpatterns = [ |
26 | url(r'^create/$', views.CreateView.as_view(), name='create'), | 27 | url(r'^$', views.AngularIndexView.as_view(), name='index'), |
27 | url(r'^(?P<image_id>[^/]+)/update/$', | 28 | url(r'^(?P<image_id>[^/]+)/detail/$', |
28 | views.UpdateView.as_view(), name='update'), | 29 | views.AngularIndexView.as_view(), name='detail'), |
29 | url(r'^(?P<image_id>[^/]+)/detail/$', | 30 | ] |
30 | views.DetailView.as_view(), name='detail') | 31 | else: |
31 | ] | 32 | urlpatterns = [ |
33 | url(r'^$', views.IndexView.as_view(), name='index'), | ||
34 | url(r'^create/$', views.CreateView.as_view(), name='create'), | ||
35 | url(r'^(?P<image_id>[^/]+)/update/$', | ||
36 | views.UpdateView.as_view(), name='update'), | ||
37 | url(r'^(?P<image_id>[^/]+)/detail/$', | ||
38 | views.DetailView.as_view(), name='detail') | ||
39 | ] | ||
diff --git a/openstack_dashboard/dashboards/admin/images/views.py b/openstack_dashboard/dashboards/admin/images/views.py index 35a0dfc..554e776 100644 --- a/openstack_dashboard/dashboards/admin/images/views.py +++ b/openstack_dashboard/dashboards/admin/images/views.py | |||
@@ -23,6 +23,7 @@ from oslo_utils import units | |||
23 | from django.core.urlresolvers import reverse | 23 | from django.core.urlresolvers import reverse |
24 | from django.core.urlresolvers import reverse_lazy | 24 | from django.core.urlresolvers import reverse_lazy |
25 | from django.utils.translation import ugettext_lazy as _ | 25 | from django.utils.translation import ugettext_lazy as _ |
26 | from django.views import generic | ||
26 | 27 | ||
27 | from horizon import exceptions | 28 | from horizon import exceptions |
28 | from horizon import messages | 29 | from horizon import messages |
@@ -40,6 +41,10 @@ from openstack_dashboard.dashboards.admin.images \ | |||
40 | LOG = logging.getLogger(__name__) | 41 | LOG = logging.getLogger(__name__) |
41 | 42 | ||
42 | 43 | ||
44 | class AngularIndexView(generic.TemplateView): | ||
45 | template_name = 'angular.html' | ||
46 | |||
47 | |||
43 | class IndexView(tables.DataTableView): | 48 | class IndexView(tables.DataTableView): |
44 | DEFAULT_FILTERS = {'is_public': None} | 49 | DEFAULT_FILTERS = {'is_public': None} |
45 | table_class = project_tables.AdminImagesTable | 50 | table_class = project_tables.AdminImagesTable |
diff --git a/openstack_dashboard/dashboards/project/images/images/urls.py b/openstack_dashboard/dashboards/project/images/images/urls.py index 7e63484b..8d01d1a 100644 --- a/openstack_dashboard/dashboards/project/images/images/urls.py +++ b/openstack_dashboard/dashboards/project/images/images/urls.py | |||
@@ -16,14 +16,23 @@ | |||
16 | # License for the specific language governing permissions and limitations | 16 | # License for the specific language governing permissions and limitations |
17 | # under the License. | 17 | # under the License. |
18 | 18 | ||
19 | from django.conf import settings | ||
19 | from django.conf.urls import url | 20 | from django.conf.urls import url |
20 | 21 | ||
21 | from openstack_dashboard.dashboards.project.images.images import views | 22 | from openstack_dashboard.dashboards.project.images.images import views |
23 | from openstack_dashboard.dashboards.project.images import views as imgviews | ||
22 | 24 | ||
23 | 25 | ||
24 | urlpatterns = [ | 26 | if settings.HORIZON_CONFIG['images_panel'] == 'angular': |
25 | url(r'^create/$', views.CreateView.as_view(), name='create'), | 27 | urlpatterns = [ |
26 | url(r'^(?P<image_id>[^/]+)/update/$', | 28 | url(r'^(?P<image_id>[^/]+)/$', imgviews.AngularIndexView.as_view(), |
27 | views.UpdateView.as_view(), name='update'), | 29 | name='detail'), |
28 | url(r'^(?P<image_id>[^/]+)/$', views.DetailView.as_view(), name='detail'), | 30 | ] |
29 | ] | 31 | else: |
32 | urlpatterns = [ | ||
33 | url(r'^create/$', views.CreateView.as_view(), name='create'), | ||
34 | url(r'^(?P<image_id>[^/]+)/update/$', | ||
35 | views.UpdateView.as_view(), name='update'), | ||
36 | url(r'^(?P<image_id>[^/]+)/$', views.DetailView.as_view(), | ||
37 | name='detail'), | ||
38 | ] | ||
diff --git a/openstack_dashboard/dashboards/project/images/urls.py b/openstack_dashboard/dashboards/project/images/urls.py index 71ded71..fd9e574 100644 --- a/openstack_dashboard/dashboards/project/images/urls.py +++ b/openstack_dashboard/dashboards/project/images/urls.py | |||
@@ -16,6 +16,7 @@ | |||
16 | # License for the specific language governing permissions and limitations | 16 | # License for the specific language governing permissions and limitations |
17 | # under the License. | 17 | # under the License. |
18 | 18 | ||
19 | from django.conf import settings | ||
19 | from django.conf.urls import include | 20 | from django.conf.urls import include |
20 | from django.conf.urls import url | 21 | from django.conf.urls import url |
21 | 22 | ||
@@ -26,8 +27,16 @@ from openstack_dashboard.dashboards.project.images.snapshots \ | |||
26 | from openstack_dashboard.dashboards.project.images import views | 27 | from openstack_dashboard.dashboards.project.images import views |
27 | 28 | ||
28 | 29 | ||
29 | urlpatterns = [ | 30 | if settings.HORIZON_CONFIG['images_panel'] == 'angular': |
30 | url(r'^$', views.IndexView.as_view(), name='index'), | 31 | # New angular images |
31 | url(r'', include(image_urls, namespace='images')), | 32 | urlpatterns = [ |
32 | url(r'', include(snapshot_urls, namespace='snapshots')), | 33 | url(r'^$', views.AngularIndexView.as_view(), name='index'), |
33 | ] | 34 | url(r'', include(image_urls, namespace='images')), |
35 | url(r'', include(snapshot_urls, namespace='snapshots')), | ||
36 | ] | ||
37 | else: | ||
38 | urlpatterns = [ | ||
39 | url(r'^$', views.IndexView.as_view(), name='index'), | ||
40 | url(r'', include(image_urls, namespace='images')), | ||
41 | url(r'', include(snapshot_urls, namespace='snapshots')), | ||
42 | ] | ||
diff --git a/openstack_dashboard/dashboards/project/images/views.py b/openstack_dashboard/dashboards/project/images/views.py index 621f6d9..7ed0764 100644 --- a/openstack_dashboard/dashboards/project/images/views.py +++ b/openstack_dashboard/dashboards/project/images/views.py | |||
@@ -22,6 +22,7 @@ Views for managing Images and Snapshots. | |||
22 | """ | 22 | """ |
23 | 23 | ||
24 | from django.utils.translation import ugettext_lazy as _ | 24 | from django.utils.translation import ugettext_lazy as _ |
25 | from django.views import generic | ||
25 | 26 | ||
26 | from horizon import exceptions | 27 | from horizon import exceptions |
27 | from horizon import messages | 28 | from horizon import messages |
@@ -34,6 +35,10 @@ from openstack_dashboard.dashboards.project.images.images \ | |||
34 | import tables as images_tables | 35 | import tables as images_tables |
35 | 36 | ||
36 | 37 | ||
38 | class AngularIndexView(generic.TemplateView): | ||
39 | template_name = 'angular.html' | ||
40 | |||
41 | |||
37 | class IndexView(tables.DataTableView): | 42 | class IndexView(tables.DataTableView): |
38 | table_class = images_tables.ImagesTable | 43 | table_class = images_tables.ImagesTable |
39 | template_name = 'project/images/index.html' | 44 | template_name = 'project/images/index.html' |
diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py index b22c862..ca089fa 100644 --- a/openstack_dashboard/dashboards/project/instances/tests.py +++ b/openstack_dashboard/dashboards/project/instances/tests.py | |||
@@ -1276,11 +1276,6 @@ class InstanceTests(helpers.TestCase): | |||
1276 | server.id, | 1276 | server.id, |
1277 | "snapshot1").AndReturn(self.snapshots.first()) | 1277 | "snapshot1").AndReturn(self.snapshots.first()) |
1278 | 1278 | ||
1279 | api.glance.image_list_detailed(IsA(http.HttpRequest), | ||
1280 | marker=None, | ||
1281 | paginate=True) \ | ||
1282 | .AndReturn([[], False, False]) | ||
1283 | |||
1284 | self.mox.ReplayAll() | 1279 | self.mox.ReplayAll() |
1285 | 1280 | ||
1286 | formData = {'instance_id': server.id, | 1281 | formData = {'instance_id': server.id, |
diff --git a/openstack_dashboard/dashboards/project/ngimages/__init__.py b/openstack_dashboard/dashboards/project/ngimages/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/openstack_dashboard/dashboards/project/ngimages/__init__.py +++ /dev/null | |||
diff --git a/openstack_dashboard/dashboards/project/ngimages/panel.py b/openstack_dashboard/dashboards/project/ngimages/panel.py deleted file mode 100644 index 93e1833..0000000 --- a/openstack_dashboard/dashboards/project/ngimages/panel.py +++ /dev/null | |||
@@ -1,23 +0,0 @@ | |||
1 | # (c) Copyright 2015 Hewlett-Packard Development Company, L.P. | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
4 | # not use this file except in compliance with the License. You may obtain | ||
5 | # a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
12 | # License for the specific language governing permissions and limitations | ||
13 | # under the License. | ||
14 | |||
15 | from django.utils.translation import ugettext_lazy as _ | ||
16 | |||
17 | import horizon | ||
18 | |||
19 | |||
20 | class NGImages(horizon.Panel): | ||
21 | name = _("Images") | ||
22 | slug = 'ngimages' | ||
23 | permissions = ('openstack.services.image',) | ||
diff --git a/openstack_dashboard/dashboards/project/ngimages/urls.py b/openstack_dashboard/dashboards/project/ngimages/urls.py deleted file mode 100644 index 82ed696..0000000 --- a/openstack_dashboard/dashboards/project/ngimages/urls.py +++ /dev/null | |||
@@ -1,22 +0,0 @@ | |||
1 | # (c) Copyright 2015 Hewlett-Packard Development Company, L.P. | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
4 | # not use this file except in compliance with the License. You may obtain | ||
5 | # a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
12 | # License for the specific language governing permissions and limitations | ||
13 | # under the License. | ||
14 | |||
15 | from django.conf.urls import url | ||
16 | |||
17 | from openstack_dashboard.dashboards.project.ngimages import views | ||
18 | |||
19 | |||
20 | urlpatterns = [ | ||
21 | url('', views.IndexView.as_view(), name='index'), | ||
22 | ] | ||
diff --git a/openstack_dashboard/dashboards/project/ngimages/views.py b/openstack_dashboard/dashboards/project/ngimages/views.py deleted file mode 100644 index e072aea..0000000 --- a/openstack_dashboard/dashboards/project/ngimages/views.py +++ /dev/null | |||
@@ -1,19 +0,0 @@ | |||
1 | # (c) Copyright 2015 Hewlett-Packard Development Company, L.P. | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
4 | # not use this file except in compliance with the License. You may obtain | ||
5 | # a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
12 | # License for the specific language governing permissions and limitations | ||
13 | # under the License. | ||
14 | |||
15 | from django.views import generic | ||
16 | |||
17 | |||
18 | class IndexView(generic.TemplateView): | ||
19 | template_name = 'angular.html' | ||
diff --git a/openstack_dashboard/enabled/_1050_project_images_panel.py b/openstack_dashboard/enabled/_1050_project_images_panel.py index 5033577..88e1a82 100644 --- a/openstack_dashboard/enabled/_1050_project_images_panel.py +++ b/openstack_dashboard/enabled/_1050_project_images_panel.py | |||
@@ -1,3 +1,17 @@ | |||
1 | # (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
4 | # not use this file except in compliance with the License. You may obtain | ||
5 | # a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
12 | # License for the specific language governing permissions and limitations | ||
13 | # under the License. | ||
14 | |||
1 | # The slug of the panel to be added to HORIZON_CONFIG. Required. | 15 | # The slug of the panel to be added to HORIZON_CONFIG. Required. |
2 | PANEL = 'images' | 16 | PANEL = 'images' |
3 | # The slug of the dashboard the PANEL associated with. Required. | 17 | # The slug of the dashboard the PANEL associated with. Required. |
diff --git a/openstack_dashboard/enabled/_1051_project_ng_images_panel.py b/openstack_dashboard/enabled/_1051_project_ng_images_panel.py deleted file mode 100644 index a92ec15..0000000 --- a/openstack_dashboard/enabled/_1051_project_ng_images_panel.py +++ /dev/null | |||
@@ -1,30 +0,0 @@ | |||
1 | # (c) Copyright 2015 Hewlett-Packard Development Company, L.P. | ||
2 | # | ||
3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
4 | # not use this file except in compliance with the License. You may obtain | ||
5 | # a copy of the License at | ||
6 | # | ||
7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | # | ||
9 | # Unless required by applicable law or agreed to in writing, software | ||
10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
12 | # License for the specific language governing permissions and limitations | ||
13 | # under the License. | ||
14 | |||
15 | # The slug of the dashboard the PANEL associated with. Required. | ||
16 | PANEL_DASHBOARD = 'project' | ||
17 | |||
18 | # The slug of the panel group the PANEL is associated with. | ||
19 | # If you want the panel to show up without a panel group, | ||
20 | # use the panel group "default". | ||
21 | PANEL_GROUP = 'compute' | ||
22 | |||
23 | # The slug of the panel to be added to HORIZON_CONFIG. Required. | ||
24 | PANEL = 'ngimages' | ||
25 | |||
26 | # If set to True, this settings file will not be added to the settings. | ||
27 | DISABLED = True | ||
28 | |||
29 | # Python panel class of the PANEL to be added. | ||
30 | ADD_PANEL = 'openstack_dashboard.dashboards.project.ngimages.panel.NGImages' | ||
diff --git a/openstack_dashboard/settings.py b/openstack_dashboard/settings.py index 682e5f8..64dbab2 100644 --- a/openstack_dashboard/settings.py +++ b/openstack_dashboard/settings.py | |||
@@ -79,6 +79,7 @@ HORIZON_CONFIG = { | |||
79 | 'js_spec_files': [], | 79 | 'js_spec_files': [], |
80 | 'external_templates': [], | 80 | 'external_templates': [], |
81 | 'plugins': [], | 81 | 'plugins': [], |
82 | 'images_panel': 'legacy', | ||
82 | 'integration_tests_support': INTEGRATION_TESTS_SUPPORT | 83 | 'integration_tests_support': INTEGRATION_TESTS_SUPPORT |
83 | } | 84 | } |
84 | 85 | ||
diff --git a/openstack_dashboard/static/app/core/images/images.module.js b/openstack_dashboard/static/app/core/images/images.module.js index 113809d..2aea051 100644 --- a/openstack_dashboard/static/app/core/images/images.module.js +++ b/openstack_dashboard/static/app/core/images/images.module.js | |||
@@ -295,9 +295,25 @@ | |||
295 | var path = $windowProvider.$get().STATIC_URL + 'app/core/images/'; | 295 | var path = $windowProvider.$get().STATIC_URL + 'app/core/images/'; |
296 | $provide.constant('horizon.app.core.images.basePath', path); | 296 | $provide.constant('horizon.app.core.images.basePath', path); |
297 | 297 | ||
298 | $routeProvider.when('/project/ngimages/', { | 298 | $routeProvider.when('/project/images/:id/', { |
299 | redirectTo: goToAngularDetails | ||
300 | }); | ||
301 | |||
302 | $routeProvider.when('/admin/images/:id/detail/', { | ||
303 | redirectTo: goToAngularDetails | ||
304 | }); | ||
305 | |||
306 | $routeProvider.when('/project/images/', { | ||
299 | templateUrl: path + 'panel.html' | 307 | templateUrl: path + 'panel.html' |
300 | }); | 308 | }); |
309 | |||
310 | $routeProvider.when('/admin/images/', { | ||
311 | templateUrl: path + 'panel.html' | ||
312 | }); | ||
313 | |||
314 | function goToAngularDetails(params) { | ||
315 | return 'project/ngdetails/OS::Glance::Image/' + params.id; | ||
316 | } | ||
301 | } | 317 | } |
302 | 318 | ||
303 | })(); | 319 | })(); |
diff --git a/openstack_dashboard/static/app/core/images/details/drawer.controller.js b/openstack_dashboard/static/app/core/images/summary.controller.js index 05b2fc1..05b2fc1 100644 --- a/openstack_dashboard/static/app/core/images/details/drawer.controller.js +++ b/openstack_dashboard/static/app/core/images/summary.controller.js | |||
diff --git a/openstack_dashboard/static/app/core/images/details/drawer.controller.spec.js b/openstack_dashboard/static/app/core/images/summary.controller.spec.js index 38b765f..38b765f 100644 --- a/openstack_dashboard/static/app/core/images/details/drawer.controller.spec.js +++ b/openstack_dashboard/static/app/core/images/summary.controller.spec.js | |||
diff --git a/openstack_dashboard/test/integration_tests/config.py b/openstack_dashboard/test/integration_tests/config.py index bd95cde..2bcb6ab 100644 --- a/openstack_dashboard/test/integration_tests/config.py +++ b/openstack_dashboard/test/integration_tests/config.py | |||
@@ -57,6 +57,9 @@ IdentityGroup = [ | |||
57 | ] | 57 | ] |
58 | 58 | ||
59 | ImageGroup = [ | 59 | ImageGroup = [ |
60 | cfg.StrOpt('panel_type', | ||
61 | default='legacy', | ||
62 | help='type/version of images panel'), | ||
60 | cfg.StrOpt('http_image', | 63 | cfg.StrOpt('http_image', |
61 | default='http://download.cirros-cloud.net/0.3.1/' | 64 | default='http://download.cirros-cloud.net/0.3.1/' |
62 | 'cirros-0.3.1-x86_64-uec.tar.gz', | 65 | 'cirros-0.3.1-x86_64-uec.tar.gz', |
diff --git a/openstack_dashboard/test/integration_tests/decorators.py b/openstack_dashboard/test/integration_tests/decorators.py index 14c1a16..a787aeb 100644 --- a/openstack_dashboard/test/integration_tests/decorators.py +++ b/openstack_dashboard/test/integration_tests/decorators.py | |||
@@ -58,6 +58,18 @@ NOT_TEST_OBJECT_ERROR_MSG = "Decorator can be applied only on test" \ | |||
58 | " classes and test methods." | 58 | " classes and test methods." |
59 | 59 | ||
60 | 60 | ||
61 | def _get_skip_method(obj): | ||
62 | """Make sure that we can decorate both methods and classes.""" | ||
63 | if inspect.isclass(obj): | ||
64 | if not _is_test_cls(obj): | ||
65 | raise ValueError(NOT_TEST_OBJECT_ERROR_MSG) | ||
66 | return _mark_class_skipped | ||
67 | else: | ||
68 | if not _is_test_method_name(obj.__name__): | ||
69 | raise ValueError(NOT_TEST_OBJECT_ERROR_MSG) | ||
70 | return _mark_method_skipped | ||
71 | |||
72 | |||
61 | def services_required(*req_services): | 73 | def services_required(*req_services): |
62 | """Decorator for marking test's service requirements, | 74 | """Decorator for marking test's service requirements, |
63 | if requirements are not met in the configuration file | 75 | if requirements are not met in the configuration file |
@@ -85,15 +97,7 @@ def services_required(*req_services): | |||
85 | . | 97 | . |
86 | """ | 98 | """ |
87 | def actual_decoration(obj): | 99 | def actual_decoration(obj): |
88 | # make sure that we can decorate method and classes as well | 100 | skip_method = _get_skip_method(obj) |
89 | if inspect.isclass(obj): | ||
90 | if not _is_test_cls(obj): | ||
91 | raise ValueError(NOT_TEST_OBJECT_ERROR_MSG) | ||
92 | skip_method = _mark_class_skipped | ||
93 | else: | ||
94 | if not _is_test_method_name(obj.__name__): | ||
95 | raise ValueError(NOT_TEST_OBJECT_ERROR_MSG) | ||
96 | skip_method = _mark_method_skipped | ||
97 | # get available services from configuration | 101 | # get available services from configuration |
98 | avail_services = config.get_config().service_available | 102 | avail_services = config.get_config().service_available |
99 | for req_service in req_services: | 103 | for req_service in req_services: |
@@ -105,6 +109,32 @@ def services_required(*req_services): | |||
105 | return actual_decoration | 109 | return actual_decoration |
106 | 110 | ||
107 | 111 | ||
112 | def _parse_compound_config_option_value(option_name): | ||
113 | """Parses the value of a given config option where option's section name is | ||
114 | separated from option name by '.'. | ||
115 | """ | ||
116 | name_parts = option_name.split('.') | ||
117 | name_parts.reverse() | ||
118 | option = config.get_config() | ||
119 | while name_parts: | ||
120 | option = getattr(option, name_parts.pop()) | ||
121 | return option | ||
122 | |||
123 | |||
124 | def config_option_required(option_key, required_value, message=None): | ||
125 | if message is None: | ||
126 | message = "%s option equal to '%s' is required for this test to work" \ | ||
127 | " properly." % (option_key, required_value) | ||
128 | |||
129 | def actual_decoration(obj): | ||
130 | skip_method = _get_skip_method(obj) | ||
131 | option_value = _parse_compound_config_option_value(option_key) | ||
132 | if option_value != required_value: | ||
133 | obj = skip_method(obj, message) | ||
134 | return obj | ||
135 | return actual_decoration | ||
136 | |||
137 | |||
108 | def skip_because(**kwargs): | 138 | def skip_because(**kwargs): |
109 | """Decorator for skipping tests hitting known bugs | 139 | """Decorator for skipping tests hitting known bugs |
110 | 140 | ||
@@ -120,14 +150,7 @@ def skip_because(**kwargs): | |||
120 | . | 150 | . |
121 | """ | 151 | """ |
122 | def actual_decoration(obj): | 152 | def actual_decoration(obj): |
123 | if inspect.isclass(obj): | 153 | skip_method = _get_skip_method(obj) |
124 | if not _is_test_cls(obj): | ||
125 | raise ValueError(NOT_TEST_OBJECT_ERROR_MSG) | ||
126 | skip_method = _mark_class_skipped | ||
127 | else: | ||
128 | if not _is_test_method_name(obj.__name__): | ||
129 | raise ValueError(NOT_TEST_OBJECT_ERROR_MSG) | ||
130 | skip_method = _mark_method_skipped | ||
131 | bugs = kwargs.get("bugs") | 154 | bugs = kwargs.get("bugs") |
132 | if bugs and isinstance(bugs, collections.Iterable): | 155 | if bugs and isinstance(bugs, collections.Iterable): |
133 | for bug in bugs: | 156 | for bug in bugs: |
diff --git a/openstack_dashboard/test/integration_tests/horizon.conf b/openstack_dashboard/test/integration_tests/horizon.conf index 31ccc70..b225dff 100644 --- a/openstack_dashboard/test/integration_tests/horizon.conf +++ b/openstack_dashboard/test/integration_tests/horizon.conf | |||
@@ -35,6 +35,7 @@ maximize_browser=yes | |||
35 | 35 | ||
36 | [image] | 36 | [image] |
37 | # http accessible image (string value) | 37 | # http accessible image (string value) |
38 | panel_type=legacy | ||
38 | http_image=http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-uec.tar.gz | 39 | http_image=http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-uec.tar.gz |
39 | images_list=cirros-0.3.4-x86_64-uec, | 40 | images_list=cirros-0.3.4-x86_64-uec, |
40 | cirros-0.3.4-x86_64-uec-kernel, | 41 | cirros-0.3.4-x86_64-uec-kernel, |
diff --git a/openstack_dashboard/test/integration_tests/pages/project/compute/imagespage.py b/openstack_dashboard/test/integration_tests/pages/project/compute/imagespage.py index 42117d4..2bb7b83 100644 --- a/openstack_dashboard/test/integration_tests/pages/project/compute/imagespage.py +++ b/openstack_dashboard/test/integration_tests/pages/project/compute/imagespage.py | |||
@@ -9,6 +9,8 @@ | |||
9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
10 | # License for the specific language governing permissions and limitations | 10 | # License for the specific language governing permissions and limitations |
11 | # under the License. | 11 | # under the License. |
12 | from selenium.webdriver.common import by | ||
13 | |||
12 | from openstack_dashboard.test.integration_tests.pages import basepage | 14 | from openstack_dashboard.test.integration_tests.pages import basepage |
13 | from openstack_dashboard.test.integration_tests.regions import forms | 15 | from openstack_dashboard.test.integration_tests.regions import forms |
14 | from openstack_dashboard.test.integration_tests.regions import tables | 16 | from openstack_dashboard.test.integration_tests.regions import tables |
@@ -250,3 +252,12 @@ class ImagesPage(basepage.BaseNavigationPage): | |||
250 | launch_instance.count.value = instance_count | 252 | launch_instance.count.value = instance_count |
251 | launch_instance.submit() | 253 | launch_instance.submit() |
252 | return InstancesPage(self.driver, self.conf) | 254 | return InstancesPage(self.driver, self.conf) |
255 | |||
256 | |||
257 | class ImagesPageNG(ImagesPage): | ||
258 | _resource_page_header_locator = (by.By.CSS_SELECTOR, | ||
259 | 'hz-resource-panel hz-page-header h1') | ||
260 | |||
261 | @property | ||
262 | def header(self): | ||
263 | return self._get_element(*self._resource_page_header_locator) | ||
diff --git a/openstack_dashboard/test/integration_tests/tests/test_images.py b/openstack_dashboard/test/integration_tests/tests/test_images.py index ec264d4..9e40ca2 100644 --- a/openstack_dashboard/test/integration_tests/tests/test_images.py +++ b/openstack_dashboard/test/integration_tests/tests/test_images.py | |||
@@ -15,14 +15,39 @@ from openstack_dashboard.test.integration_tests import helpers | |||
15 | from openstack_dashboard.test.integration_tests.regions import messages | 15 | from openstack_dashboard.test.integration_tests.regions import messages |
16 | 16 | ||
17 | 17 | ||
18 | class TestImagesBasic(helpers.TestCase): | 18 | @decorators.config_option_required('image.panel_type', 'legacy', |
19 | """Login as demo user""" | 19 | message="Angular Panels not tested") |
20 | IMAGE_NAME = helpers.gen_random_resource_name("image") | 20 | class TestImagesLegacy(helpers.TestCase): |
21 | |||
22 | @property | 21 | @property |
23 | def images_page(self): | 22 | def images_page(self): |
24 | return self.home_pg.go_to_compute_imagespage() | 23 | return self.home_pg.go_to_compute_imagespage() |
25 | 24 | ||
25 | |||
26 | @decorators.config_option_required('image.panel_type', 'angular', | ||
27 | message="Legacy Panels not tested") | ||
28 | class TestImagesAngular(helpers.TestCase): | ||
29 | @property | ||
30 | def images_page(self): | ||
31 | # FIXME(tsufiev): had to return angularized version of Images Page | ||
32 | # object with the horrendous hack below because it's not so easy to | ||
33 | # wire into the Navigation machinery and tell it to return an '*NG' | ||
34 | # version of ImagesPage class if one adds '_ng' suffix to | ||
35 | # 'go_to_compute_imagespage()' method. Yet that's how it should work | ||
36 | # (or rewrite Navigation module completely). | ||
37 | from openstack_dashboard.test.integration_tests.pages.project.\ | ||
38 | compute.imagespage import ImagesPageNG | ||
39 | self.home_pg.go_to_compute_imagespage() | ||
40 | return ImagesPageNG(self.driver, self.CONFIG) | ||
41 | |||
42 | def test_basic_image_browse(self): | ||
43 | images_page = self.images_page | ||
44 | self.assertEqual(images_page.header.text, 'Images') | ||
45 | |||
46 | |||
47 | class TestImagesBasic(TestImagesLegacy): | ||
48 | """Login as demo user""" | ||
49 | IMAGE_NAME = helpers.gen_random_resource_name("image") | ||
50 | |||
26 | def image_create(self, local_file=None): | 51 | def image_create(self, local_file=None): |
27 | images_page = self.images_page | 52 | images_page = self.images_page |
28 | if local_file: | 53 | if local_file: |
@@ -227,14 +252,10 @@ class TestImagesBasic(helpers.TestCase): | |||
227 | self.image_delete(new_image_name) | 252 | self.image_delete(new_image_name) |
228 | 253 | ||
229 | 254 | ||
230 | class TestImagesAdvanced(helpers.TestCase): | 255 | class TestImagesAdvanced(TestImagesLegacy): |
231 | """Login as demo user""" | 256 | """Login as demo user""" |
232 | IMAGE_NAME = helpers.gen_random_resource_name("image") | 257 | IMAGE_NAME = helpers.gen_random_resource_name("image") |
233 | 258 | ||
234 | @property | ||
235 | def images_page(self): | ||
236 | return self.home_pg.go_to_compute_imagespage() | ||
237 | |||
238 | def test_create_volume_from_image(self): | 259 | def test_create_volume_from_image(self): |
239 | """This test case checks create volume from image functionality: | 260 | """This test case checks create volume from image functionality: |
240 | Steps: | 261 | Steps: |
@@ -293,7 +314,7 @@ class TestImagesAdvanced(helpers.TestCase): | |||
293 | self.assertTrue(instances_page.is_instance_deleted(target_instance)) | 314 | self.assertTrue(instances_page.is_instance_deleted(target_instance)) |
294 | 315 | ||
295 | 316 | ||
296 | class TestImagesAdmin(helpers.AdminTestCase, TestImagesBasic): | 317 | class TestImagesAdmin(helpers.AdminTestCase, TestImagesLegacy): |
297 | """Login as admin user""" | 318 | """Login as admin user""" |
298 | IMAGE_NAME = helpers.gen_random_resource_name("image") | 319 | IMAGE_NAME = helpers.gen_random_resource_name("image") |
299 | 320 | ||
diff --git a/openstack_dashboard/test/settings.py b/openstack_dashboard/test/settings.py index fdd0a4a..cecabc2 100644 --- a/openstack_dashboard/test/settings.py +++ b/openstack_dashboard/test/settings.py | |||
@@ -96,6 +96,7 @@ HORIZON_CONFIG = { | |||
96 | 'unauthorized': exceptions.UNAUTHORIZED}, | 96 | 'unauthorized': exceptions.UNAUTHORIZED}, |
97 | 'angular_modules': [], | 97 | 'angular_modules': [], |
98 | 'js_files': [], | 98 | 'js_files': [], |
99 | 'images_panel': 'legacy', | ||
99 | } | 100 | } |
100 | 101 | ||
101 | # Load the pluggable dashboard settings | 102 | # Load the pluggable dashboard settings |
diff --git a/releasenotes/notes/image-panel-switch-38e9d3716451f9e3.yaml b/releasenotes/notes/image-panel-switch-38e9d3716451f9e3.yaml new file mode 100644 index 0000000..a52a50e --- /dev/null +++ b/releasenotes/notes/image-panel-switch-38e9d3716451f9e3.yaml | |||
@@ -0,0 +1,12 @@ | |||
1 | --- | ||
2 | prelude: > | ||
3 | The Image panel now may be configured to use | ||
4 | either the legacy or Angular code. | ||
5 | features: | ||
6 | - HORIZON_CONFIG now allows for a key 'images_panel' to be | ||
7 | specified as 'legacy' or 'angular' indicating the type of | ||
8 | panel to use. | ||
9 | - Integration tests for Image features may also be toggled | ||
10 | in openstack_dashboard/test/integration_tests/horizon.conf | ||
11 | using the 'panel_type' feature, either set to 'legacy' or | ||
12 | 'angular' to match the enabled panel type. | ||