From eac3e7032a48fb9f03f56af0cc2e2085d55af0d0 Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 28 Nov 2017 21:12:02 +0900 Subject: [PATCH] Drop Heat related code from horizon Orchestration tab in the admin info panel needs a discussion. It seems not to be covered by heat-dashboard yet. blueprint heat-dashboard-split-out Change-Id: I56e6edb1f2ac72e2f42d0e9f3291308e67f24cad --- doc/source/admin/manage-services.rst | 5 - doc/source/configuration/customizing.rst | 1 - doc/source/configuration/settings.rst | 24 - doc/source/contributor/quickstart.rst | 2 +- doc/source/contributor/ref/local_conf.rst | 3 - doc/source/install/from-source.rst | 1 - doc/source/user/index.rst | 1 - doc/source/user/log-in.rst | 17 - doc/source/user/stacks.rst | 149 --- openstack_dashboard/api/__init__.py | 2 - openstack_dashboard/api/heat.py | 265 ----- openstack_dashboard/api/rest/__init__.py | 2 - openstack_dashboard/api/rest/heat.py | 51 - openstack_dashboard/conf/heat_policy.json | 92 -- .../dashboards/admin/dashboard.py | 3 +- .../dashboards/admin/info/panel.py | 3 +- .../dashboards/admin/info/tables.py | 42 - .../dashboards/admin/info/tabs.py | 27 +- .../dashboards/admin/info/tests.py | 17 +- .../dashboards/project/stacks/__init__.py | 0 .../dashboards/project/stacks/api.py | 83 -- .../dashboards/project/stacks/forms.py | 488 -------- .../dashboards/project/stacks/mappings.py | 350 ------ .../dashboards/project/stacks/panel.py | 21 - .../project/stacks/resource_types/__init__.py | 0 .../project/stacks/resource_types/panel.py | 23 - .../project/stacks/resource_types/tables.py | 36 - .../project/stacks/resource_types/tabs.py | 32 - .../stacks.resource_types/_details.html | 15 - .../project/stacks/resource_types/tests.py | 52 - .../project/stacks/resource_types/urls.py | 22 - .../project/stacks/resource_types/views.py | 78 -- .../dashboards/project/stacks/sro.py | 44 - .../dashboards/project/stacks/tables.py | 413 ------- .../dashboards/project/stacks/tabs.py | 173 --- .../stacks/template_versions/__init__.py | 0 .../project/stacks/template_versions/panel.py | 23 - .../stacks/template_versions/tables.py | 52 - .../project/stacks/template_versions/tabs.py | 51 - .../stacks.template_versions/_details.html | 3 - .../stacks.template_versions/index.html | 7 - .../project/stacks/template_versions/tests.py | 79 -- .../project/stacks/template_versions/urls.py | 24 - .../project/stacks/template_versions/views.py | 61 - .../templates/stacks/_change_template.html | 7 - .../stacks/templates/stacks/_create.html | 6 - .../templates/stacks/_detail_events.html | 3 - .../templates/stacks/_detail_overview.html | 55 - .../templates/stacks/_detail_resources.html | 3 - .../templates/stacks/_detail_topology.html | 9 - .../stacks/templates/stacks/_preview.html | 6 - .../templates/stacks/_preview_details.html | 58 - .../templates/stacks/_preview_template.html | 7 - .../templates/stacks/_resource_info.html | 10 - .../templates/stacks/_resource_overview.html | 38 - .../templates/stacks/_select_template.html | 7 - .../stacks/templates/stacks/_stack_info.html | 14 - .../templates/stacks/_stack_template.html | 5 - .../stacks/templates/stacks/_update.html | 6 - .../templates/stacks/change_template.html | 7 - .../stacks/templates/stacks/create.html | 7 - .../stacks/templates/stacks/preview.html | 7 - .../templates/stacks/preview_details.html | 7 - .../templates/stacks/preview_template.html | 7 - .../templates/stacks/select_template.html | 7 - .../stacks/templates/stacks/update.html | 7 - .../dashboards/project/stacks/tests.py | 1003 ----------------- .../dashboards/project/stacks/urls.py | 38 - .../dashboards/project/stacks/views.py | 358 ------ .../_1610_orchestration_panel_group.py | 8 - .../enabled/_1620_project_stacks_panel.py | 9 - .../_1630_project_resource_types_panel.py | 10 - .../_1640_project_template_versions_panel.py | 10 - openstack_dashboard/exceptions.py | 5 - .../local/local_settings.py.example | 6 - openstack_dashboard/settings.py | 1 - .../openstack-service-api/heat.service.js | 80 -- .../heat.service.spec.js | 80 -- .../test/api_tests/heat_rest_tests.py | 69 -- .../test/api_tests/heat_tests.py | 358 ------ openstack_dashboard/test/helpers.py | 11 - .../test/integration_tests/config.py | 2 - .../test/integration_tests/horizon.conf | 2 - .../integration_tests/pages/navigation.py | 7 - .../pages/project/orchestration/__init__.py | 0 .../pages/project/orchestration/stackspage.py | 99 -- .../integration_tests/tests/test_stacks.py | 73 -- .../test/test_data/exceptions.py | 4 - .../test/test_data/heat_data.py | 617 ---------- .../test/test_data/keystone_data.py | 8 - openstack_dashboard/test/test_data/utils.py | 2 - .../heat-panel-splitout-b609b157aa4bf29b.yaml | 11 + requirements.txt | 1 - tools/gate/integration/devstack_gate_rc | 2 +- 94 files changed, 17 insertions(+), 5977 deletions(-) delete mode 100644 doc/source/user/stacks.rst delete mode 100644 openstack_dashboard/api/heat.py delete mode 100644 openstack_dashboard/api/rest/heat.py delete mode 100644 openstack_dashboard/conf/heat_policy.json delete mode 100644 openstack_dashboard/dashboards/project/stacks/__init__.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/api.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/forms.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/mappings.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/panel.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/resource_types/__init__.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/resource_types/panel.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/resource_types/tables.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/resource_types/tabs.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/resource_types/templates/stacks.resource_types/_details.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/resource_types/tests.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/resource_types/urls.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/resource_types/views.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/sro.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/tables.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/tabs.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/template_versions/__init__.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/template_versions/panel.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/template_versions/tables.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/template_versions/tabs.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/template_versions/templates/stacks.template_versions/_details.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/template_versions/templates/stacks.template_versions/index.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/template_versions/tests.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/template_versions/urls.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/template_versions/views.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_change_template.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_create.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_events.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_overview.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_resources.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_topology.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_template.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_resource_info.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_resource_overview.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_select_template.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_stack_info.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_stack_template.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/_update.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/change_template.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/create.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/preview.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/preview_details.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/preview_template.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/select_template.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/templates/stacks/update.html delete mode 100644 openstack_dashboard/dashboards/project/stacks/tests.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/urls.py delete mode 100644 openstack_dashboard/dashboards/project/stacks/views.py delete mode 100644 openstack_dashboard/enabled/_1610_orchestration_panel_group.py delete mode 100644 openstack_dashboard/enabled/_1620_project_stacks_panel.py delete mode 100644 openstack_dashboard/enabled/_1630_project_resource_types_panel.py delete mode 100644 openstack_dashboard/enabled/_1640_project_template_versions_panel.py delete mode 100644 openstack_dashboard/static/app/core/openstack-service-api/heat.service.js delete mode 100644 openstack_dashboard/static/app/core/openstack-service-api/heat.service.spec.js delete mode 100644 openstack_dashboard/test/api_tests/heat_rest_tests.py delete mode 100644 openstack_dashboard/test/api_tests/heat_tests.py delete mode 100644 openstack_dashboard/test/integration_tests/pages/project/orchestration/__init__.py delete mode 100644 openstack_dashboard/test/integration_tests/pages/project/orchestration/stackspage.py delete mode 100644 openstack_dashboard/test/integration_tests/tests/test_stacks.py delete mode 100644 openstack_dashboard/test/test_data/heat_data.py create mode 100644 releasenotes/notes/heat-panel-splitout-b609b157aa4bf29b.yaml diff --git a/doc/source/admin/manage-services.rst b/doc/source/admin/manage-services.rst index 7afaae7f9d..d611911210 100644 --- a/doc/source/admin/manage-services.rst +++ b/doc/source/admin/manage-services.rst @@ -30,8 +30,3 @@ As an administrative user, you can view information for OpenStack services. * :guilabel:`Network Agents`: Displays the network agents active within the cluster, such as L3 and DHCP agents, and the status of each agent. - - * :guilabel:`Orchestration Services`: - Displays information specific to the Orchestration service. Name, - engine id, host and topic are listed for each service, as well as its - activation status. diff --git a/doc/source/configuration/customizing.rst b/doc/source/configuration/customizing.rst index 89ff91624e..bff71f3866 100644 --- a/doc/source/configuration/customizing.rst +++ b/doc/source/configuration/customizing.rst @@ -116,7 +116,6 @@ You can also override existing methods with your own versions:: NO = lambda *x: False - tabs.HeatServiceTab.allowed = NO tables.AssociateIP.allowed = NO tables.SimpleAssociateIP.allowed = NO tables.SimpleDisassociateIP.allowed = NO diff --git a/doc/source/configuration/settings.rst b/doc/source/configuration/settings.rst index 7ef538f3df..b8de5bbaba 100644 --- a/doc/source/configuration/settings.rst +++ b/doc/source/configuration/settings.rst @@ -782,7 +782,6 @@ Default: 'compute': 'nova_policy.json', 'volume': 'cinder_policy.json', 'image': 'glance_policy.json', - 'orchestration': 'heat_policy.json', 'network': 'neutron_policy.json', } @@ -1103,29 +1102,6 @@ Default: Used to customize features related to the image service, such as the list of supported image formats. -Heat ----- - -OPENSTACK_HEAT_STACK -~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 9.0.0(Mitaka) - -Default: - -.. code-block:: python - - { - 'enable_user_pass': True - } - -A dictionary of settings to use with heat stacks. Currently, the only setting -available is "enable_user_pass", which can be used to disable the password -field while launching the stack. Currently HEAT API needs user password to -perform all the heat operations because in HEAT API trusts is not enabled by -default. So, this setting can be set as "False" in-case HEAT uses trusts by -default otherwise it needs to be set as "True". - Keystone -------- diff --git a/doc/source/contributor/quickstart.rst b/doc/source/contributor/quickstart.rst index b464a9e03f..e903139ecc 100644 --- a/doc/source/contributor/quickstart.rst +++ b/doc/source/contributor/quickstart.rst @@ -83,7 +83,7 @@ To start the Horizon development server use the command below .. note:: - The default port for runserver is 8000 which is already consumed by + The default port for runserver is 8000 which might be already consumed by heat-api-cfn in DevStack. If running in DevStack ``tox -e runserver -- localhost:9000`` will start the test server at ``http://localhost:9000`` diff --git a/doc/source/contributor/ref/local_conf.rst b/doc/source/contributor/ref/local_conf.rst index 813a51f5bf..7066712b4f 100644 --- a/doc/source/contributor/ref/local_conf.rst +++ b/doc/source/contributor/ref/local_conf.rst @@ -69,9 +69,6 @@ see https://docs.openstack.org/devstack/latest/ SWIFT_REPLICAS=1 SWIFT_DATA_DIR=$DEST/data/swift - # Enable Heat - enable_plugin heat https://git.openstack.org/openstack/heat - # Enable Neutron enable_plugin neutron https://git.openstack.org/openstack/neutron diff --git a/doc/source/install/from-source.rst b/doc/source/install/from-source.rst index d34492c0ee..c0ca1e1f45 100644 --- a/doc/source/install/from-source.rst +++ b/doc/source/install/from-source.rst @@ -22,7 +22,6 @@ System Requirements * `cinder `_: Block Storage * `glance `_: Image Management - * `heat `_: Orchestration * `neutron `_: Networking * `nova `_: Compute * `swift `_: Object Storage diff --git a/doc/source/user/index.rst b/doc/source/user/index.rst index cde57d3cc2..0112c3ef5f 100644 --- a/doc/source/user/index.rst +++ b/doc/source/user/index.rst @@ -18,7 +18,6 @@ sizes of server instances. manage-containers.rst manage-volumes.rst manage-shares.rst - stacks.rst databases.rst manage-lbaasv2.rst browser_support diff --git a/doc/source/user/log-in.rst b/doc/source/user/log-in.rst index 0f80d6e24a..25b560fc7e 100644 --- a/doc/source/user/log-in.rst +++ b/doc/source/user/log-in.rst @@ -50,11 +50,6 @@ The dashboard is generally installed on the controller node. (:ref:`dashboard-admin-tab`) and :guilabel:`Identity` tab (:ref:`dashboard-identity-tab`) are displayed. -.. note:: - - Some tabs, such as :guilabel:`Orchestration` and :guilabel:`Firewalls`, - only appear on the dashboard if they are properly configured. - .. _dashboard-project-tab: OpenStack dashboard — Project tab @@ -143,15 +138,6 @@ Network tab * :guilabel:`Firewall Rules`: Add and manage firewall rules. -Orchestration tab ------------------ - -* :guilabel:`Stacks`: Use the REST API to orchestrate multiple composite - cloud applications. - -* :guilabel:`Resource Types`: Show a list of all the supported resource - types for HOT templates. - Object Store tab ---------------- @@ -234,9 +220,6 @@ System tab * :guilabel:`Network Agents`: View the network agents. - * :guilabel:`Orchestration Services`: View a list of all Orchestration - services. - * :guilabel:`Shares`: Use the following tabs to complete these tasks: * :guilabel:`Shares`: View, create, manage, and delete shares. diff --git a/doc/source/user/stacks.rst b/doc/source/user/stacks.rst deleted file mode 100644 index 7767976b96..0000000000 --- a/doc/source/user/stacks.rst +++ /dev/null @@ -1,149 +0,0 @@ -======================== -Launch and manage stacks -======================== - -OpenStack Orchestration is a service that you can use to -orchestrate multiple composite cloud applications. This -service supports the use of both the Amazon Web Services (AWS) -CloudFormation template format through both a Query API that -is compatible with CloudFormation and the native OpenStack -Heat Orchestration Template (HOT) format through a REST API. - -These flexible template languages enable application -developers to describe and automate the deployment of -infrastructure, services, and applications. The templates -enable creation of most OpenStack resource types, such as -instances, floating IP addresses, volumes, security groups, -and users. Once created, the resources are referred to as -stacks. - -The template languages are described in the `Template Guide -`_. - -Launch a stack -~~~~~~~~~~~~~~ - -#. Log in to the dashboard. -#. Select the appropriate project from the drop down menu at the top left. -#. On the :guilabel:`Project` tab, open the :guilabel:`Orchestration` tab and - click :guilabel:`Stacks` category. -#. Click :guilabel:`Launch Stack`. -#. In the :guilabel:`Select Template` dialog box, specify the - following values: - - +---------------------------------------+-------------------------------+ - | :guilabel:`Template Source` | Choose the source of the | - | | template from the list. | - +---------------------------------------+-------------------------------+ - | :guilabel:`Template URL/File/Data` | Depending on the source that | - | | you select, enter the URL, | - | | browse to the file location, | - | | or directly include the | - | | template. | - +---------------------------------------+-------------------------------+ - | :guilabel:`Environment Source` | Choose the source of the | - | | environment from the list. | - | | The environment files contain | - | | additional settings for the | - | | stack. | - +---------------------------------------+-------------------------------+ - | :guilabel:`Environment File/Data` | Depending on the source that | - | | you select, browse to the | - | | file location, directly | - | | include the environment | - +---------------------------------------+-------------------------------+ - -#. Click :guilabel:`Next`. -#. In the :guilabel:`Launch Stack` dialog box, specify the - following values: - - +---------------------------------+---------------------------------+ - | :guilabel:`Stack Name` | Enter a name to identify | - | | the stack. | - +---------------------------------+---------------------------------+ - | :guilabel:`Creation Timeout` | Specify the number of minutes | - | :guilabel:`(minutes)` | that can elapse before the | - | | launch of the stack times out. | - +---------------------------------+---------------------------------+ - | :guilabel:`Rollback On Failure` | Select this check box if you | - | | want the service to roll back | - | | changes if the stack fails to | - | | launch. | - +---------------------------------+---------------------------------+ - | :guilabel:`Password for user` | Specify the password that | - | :guilabel:`"demo"` | the default user uses when the | - | | stack is created. | - +---------------------------------+---------------------------------+ - | :guilabel:`DBUsername` | Specify the name of the | - | | database user. | - +---------------------------------+---------------------------------+ - | :guilabel:`LinuxDistribution` | Specify the Linux distribution | - | | that is used in the stack. | - +---------------------------------+---------------------------------+ - | :guilabel:`DBRootPassword` | Specify the root password for | - | | the database. | - +---------------------------------+---------------------------------+ - | :guilabel:`KeyName` | Specify the name of the key pair| - | | to use to log in to the stack. | - +---------------------------------+---------------------------------+ - | :guilabel:`DBName` | Specify the name of the | - | | database. | - +---------------------------------+---------------------------------+ - | :guilabel:`DBPassword` | Specify the password of the | - | | database. | - +---------------------------------+---------------------------------+ - | :guilabel:`InstanceType` | Specify the flavor for the | - | | instance. | - +---------------------------------+---------------------------------+ - -#. Click :guilabel:`Launch` to create a stack. The :guilabel:`Stacks` - tab shows the stack. - -After the stack is created, click on the stack name to see the -following details: - -Topology - The topology of the stack. - -Overview - The parameters and details of the stack. - -Resources - The resources used by the stack. - -Events - The events related to the stack. - -Template - The template for the stack. - -Manage a stack -~~~~~~~~~~~~~~ - -#. Log in to the dashboard. -#. Select the appropriate project from the drop down menu at the top left. -#. On the :guilabel:`Project` tab, open the :guilabel:`Orchestration` tab and - click :guilabel:`Stacks` category. -#. Select the stack that you want to update. -#. Click :guilabel:`Change Stack Template`. -#. In the :guilabel:`Select Template` dialog box, select the - new template source or environment source. -#. Click :guilabel:`Next`. - - The :guilabel:`Update Stack Parameters` window appears. -#. Enter new values for any parameters that you want to update. -#. Click :guilabel:`Update`. - -Delete a stack -~~~~~~~~~~~~~~ - -When you delete a stack, you cannot undo this action. - -#. Log in to the dashboard. -#. Select the appropriate project from the drop down menu at the top left. -#. On the :guilabel:`Project` tab, open the :guilabel:`Orchestration` tab and - click :guilabel:`Stacks` category. -#. Select the stack that you want to delete. -#. Click :guilabel:`Delete Stack`. -#. In the confirmation dialog box, click :guilabel:`Delete Stack` - to confirm the deletion. diff --git a/openstack_dashboard/api/__init__.py b/openstack_dashboard/api/__init__.py index 6262470038..9483b39414 100644 --- a/openstack_dashboard/api/__init__.py +++ b/openstack_dashboard/api/__init__.py @@ -34,7 +34,6 @@ Keystone/Nova/Glance/Swift et. al. from openstack_dashboard.api import base from openstack_dashboard.api import cinder from openstack_dashboard.api import glance -from openstack_dashboard.api import heat from openstack_dashboard.api import keystone from openstack_dashboard.api import network from openstack_dashboard.api import neutron @@ -46,7 +45,6 @@ __all__ = [ "base", "cinder", "glance", - "heat", "keystone", "network", "neutron", diff --git a/openstack_dashboard/api/heat.py b/openstack_dashboard/api/heat.py deleted file mode 100644 index c31c61dad9..0000000000 --- a/openstack_dashboard/api/heat.py +++ /dev/null @@ -1,265 +0,0 @@ -# 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 contextlib - -from django.conf import settings -from heatclient import client as heat_client -from heatclient.common import template_format -from heatclient.common import template_utils -from heatclient.common import utils as heat_utils -from oslo_serialization import jsonutils -import six -from six.moves.urllib import request - -from horizon import exceptions -from horizon.utils import functions as utils -from horizon.utils.memoized import memoized -from openstack_dashboard.api import base -from openstack_dashboard.contrib.developer.profiler import api as profiler - - -def format_parameters(params): - parameters = {} - for count, p in enumerate(params, 1): - parameters['Parameters.member.%d.ParameterKey' % count] = p - parameters['Parameters.member.%d.ParameterValue' % count] = params[p] - return parameters - - -@memoized -def heatclient(request, password=None): - api_version = "1" - insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) - cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None) - endpoint = base.url_for(request, 'orchestration') - kwargs = { - 'token': request.user.token.id, - 'insecure': insecure, - 'ca_file': cacert, - 'username': request.user.username, - 'password': password - # 'timeout': args.timeout, - # 'ca_file': args.ca_file, - # 'cert_file': args.cert_file, - # 'key_file': args.key_file, - } - client = heat_client.Client(api_version, endpoint, **kwargs) - client.format_parameters = format_parameters - return client - - -@profiler.trace -def stacks_list(request, marker=None, sort_dir='desc', sort_key='created_at', - paginate=False, filters=None): - limit = getattr(settings, 'API_RESULT_LIMIT', 1000) - page_size = utils.get_page_size(request) - - if paginate: - request_size = page_size + 1 - else: - request_size = limit - - kwargs = {'sort_dir': sort_dir, 'sort_key': sort_key} - if marker: - kwargs['marker'] = marker - - if filters: - kwargs.update(filters) - if 'status' in kwargs: - kwargs['status'] = kwargs['status'].replace(' ', '_').upper() - - stacks_iter = heatclient(request).stacks.list(limit=request_size, - **kwargs) - - has_prev_data = False - has_more_data = False - stacks = list(stacks_iter) - - if paginate: - if len(stacks) > page_size: - stacks.pop() - has_more_data = True - if marker is not None: - has_prev_data = True - elif sort_dir == 'asc' and marker is not None: - has_more_data = True - elif marker is not None: - has_prev_data = True - return (stacks, has_more_data, has_prev_data) - - -def _ignore_if(key, value): - if key != 'get_file' and key != 'type': - return True - if not isinstance(value, six.string_types): - return True - if (key == 'type' and - not value.endswith(('.yaml', '.template'))): - return True - return False - - -@profiler.trace -def get_template_files(template_data=None, template_url=None): - if template_data: - tpl = template_data - elif template_url: - with contextlib.closing(request.urlopen(template_url)) as u: - tpl = u.read() - else: - return {}, None - if not tpl: - return {}, None - if isinstance(tpl, six.binary_type): - tpl = tpl.decode('utf-8') - template = template_format.parse(tpl) - files = {} - _get_file_contents(template, files) - return files, template - - -def _get_file_contents(from_data, files): - if not isinstance(from_data, (dict, list)): - return - if isinstance(from_data, dict): - recurse_data = from_data.values() - for key, value in from_data.items(): - if _ignore_if(key, value): - continue - if not value.startswith(('http://', 'https://')): - raise exceptions.GetFileError(value, 'get_file') - if value not in files: - file_content = heat_utils.read_url_content(value) - if template_utils.is_template(file_content): - template = get_template_files(template_url=value)[1] - file_content = jsonutils.dumps(template) - files[value] = file_content - else: - recurse_data = from_data - for value in recurse_data: - _get_file_contents(value, files) - - -@profiler.trace -def stack_delete(request, stack_id): - return heatclient(request).stacks.delete(stack_id) - - -@profiler.trace -def stack_get(request, stack_id): - return heatclient(request).stacks.get(stack_id) - - -@profiler.trace -def template_get(request, stack_id): - return heatclient(request).stacks.template(stack_id) - - -@profiler.trace -def stack_create(request, password=None, **kwargs): - return heatclient(request, password).stacks.create(**kwargs) - - -@profiler.trace -def stack_preview(request, password=None, **kwargs): - return heatclient(request, password).stacks.preview(**kwargs) - - -@profiler.trace -def stack_update(request, stack_id, password=None, **kwargs): - return heatclient(request, password).stacks.update(stack_id, **kwargs) - - -@profiler.trace -def snapshot_create(request, stack_id): - return heatclient(request).stacks.snapshot(stack_id) - - -@profiler.trace -def snapshot_list(request, stack_id): - return heatclient(request).stacks.snapshot_list(stack_id) - - -@profiler.trace -def snapshot_show(request, stack_id, snapshot_id): - return heatclient(request).stacks.snapshot_show(stack_id, snapshot_id) - - -@profiler.trace -def snapshot_delete(request, stack_id, snapshot_id): - return heatclient(request).stacks.snapshot_delete(stack_id, snapshot_id) - - -@profiler.trace -def events_list(request, stack_name): - return heatclient(request).events.list(stack_name) - - -@profiler.trace -def resources_list(request, stack_name): - return heatclient(request).resources.list(stack_name) - - -@profiler.trace -def resource_get(request, stack_id, resource_name): - return heatclient(request).resources.get(stack_id, resource_name) - - -@profiler.trace -def resource_metadata_get(request, stack_id, resource_name): - return heatclient(request).resources.metadata(stack_id, resource_name) - - -@profiler.trace -def template_validate(request, **kwargs): - return heatclient(request).stacks.validate(**kwargs) - - -@profiler.trace -def action_check(request, stack_id): - return heatclient(request).actions.check(stack_id) - - -@profiler.trace -def action_suspend(request, stack_id): - return heatclient(request).actions.suspend(stack_id) - - -@profiler.trace -def action_resume(request, stack_id): - return heatclient(request).actions.resume(stack_id) - - -@profiler.trace -def resource_types_list(request, filters=None): - return heatclient(request).resource_types.list(filters=filters) - - -@profiler.trace -def resource_type_get(request, resource_type): - return heatclient(request).resource_types.get(resource_type) - - -@profiler.trace -def service_list(request): - return heatclient(request).services.list() - - -@profiler.trace -def template_version_list(request): - return heatclient(request).template_versions.list() - - -@profiler.trace -def template_function_list(request, template_version): - return heatclient(request).template_versions.get(template_version) diff --git a/openstack_dashboard/api/rest/__init__.py b/openstack_dashboard/api/rest/__init__.py index 925cbb098f..11df2d5294 100644 --- a/openstack_dashboard/api/rest/__init__.py +++ b/openstack_dashboard/api/rest/__init__.py @@ -24,7 +24,6 @@ in https://wiki.openstack.org/wiki/APIChangeGuidelines. from openstack_dashboard.api.rest import cinder from openstack_dashboard.api.rest import config from openstack_dashboard.api.rest import glance -from openstack_dashboard.api.rest import heat from openstack_dashboard.api.rest import keystone from openstack_dashboard.api.rest import network from openstack_dashboard.api.rest import neutron @@ -37,7 +36,6 @@ __all__ = [ 'cinder', 'config', 'glance', - 'heat', 'keystone', 'network', 'neutron', diff --git a/openstack_dashboard/api/rest/heat.py b/openstack_dashboard/api/rest/heat.py deleted file mode 100644 index df8752f1f8..0000000000 --- a/openstack_dashboard/api/rest/heat.py +++ /dev/null @@ -1,51 +0,0 @@ -# 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. -"""API for the heat service.""" - -from django.views import generic - -from openstack_dashboard import api -from openstack_dashboard.api.rest import urls -from openstack_dashboard.api.rest import utils as rest_utils - - -@urls.register -class Validate(generic.View): - """API for validating a template""" - url_regex = r'heat/validate/$' - - @rest_utils.ajax(data_required=True) - def post(self, request): - """Validate a template - - The following parameters may be passed in the POST - application/json object. The parameters are: - request: - - :param template_url: The template to validate - """ - return api.heat.template_validate(request, **(request.DATA)) - - -@urls.register -class Services(generic.View): - """API for heat services.""" - url_regex = r'heat/services/$' - - @rest_utils.ajax() - def get(self, request): - """Get a list of heat services.""" - if api.base.is_service_enabled(request, 'orchestration'): - result = api.heat.service_list(request) - return {'items': [u.to_dict() for u in result]} - else: - raise rest_utils.AjaxError(501, '') diff --git a/openstack_dashboard/conf/heat_policy.json b/openstack_dashboard/conf/heat_policy.json deleted file mode 100644 index b40b1eef2e..0000000000 --- a/openstack_dashboard/conf/heat_policy.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "context_is_admin": "role:admin", - "deny_stack_user": "not role:heat_stack_user", - "deny_everybody": "!", - - "cloudformation:ListStacks": "rule:deny_stack_user", - "cloudformation:CreateStack": "rule:deny_stack_user", - "cloudformation:DescribeStacks": "rule:deny_stack_user", - "cloudformation:DeleteStack": "rule:deny_stack_user", - "cloudformation:UpdateStack": "rule:deny_stack_user", - "cloudformation:CancelUpdateStack": "rule:deny_stack_user", - "cloudformation:DescribeStackEvents": "rule:deny_stack_user", - "cloudformation:ValidateTemplate": "rule:deny_stack_user", - "cloudformation:GetTemplate": "rule:deny_stack_user", - "cloudformation:EstimateTemplateCost": "rule:deny_stack_user", - "cloudformation:DescribeStackResource": "", - "cloudformation:DescribeStackResources": "rule:deny_stack_user", - "cloudformation:ListStackResources": "rule:deny_stack_user", - - "cloudwatch:DeleteAlarms": "rule:deny_stack_user", - "cloudwatch:DescribeAlarmHistory": "rule:deny_stack_user", - "cloudwatch:DescribeAlarms": "rule:deny_stack_user", - "cloudwatch:DescribeAlarmsForMetric": "rule:deny_stack_user", - "cloudwatch:DisableAlarmActions": "rule:deny_stack_user", - "cloudwatch:EnableAlarmActions": "rule:deny_stack_user", - "cloudwatch:GetMetricStatistics": "rule:deny_stack_user", - "cloudwatch:ListMetrics": "rule:deny_stack_user", - "cloudwatch:PutMetricAlarm": "rule:deny_stack_user", - "cloudwatch:PutMetricData": "", - "cloudwatch:SetAlarmState": "rule:deny_stack_user", - - "actions:action": "rule:deny_stack_user", - "build_info:build_info": "rule:deny_stack_user", - "events:index": "rule:deny_stack_user", - "events:show": "rule:deny_stack_user", - "resource:index": "rule:deny_stack_user", - "resource:metadata": "", - "resource:signal": "", - "resource:mark_unhealthy": "rule:deny_stack_user", - "resource:show": "rule:deny_stack_user", - "stacks:abandon": "rule:deny_stack_user", - "stacks:create": "rule:deny_stack_user", - "stacks:delete": "rule:deny_stack_user", - "stacks:detail": "rule:deny_stack_user", - "stacks:export": "rule:deny_stack_user", - "stacks:generate_template": "rule:deny_stack_user", - "stacks:global_index": "rule:deny_everybody", - "stacks:index": "rule:deny_stack_user", - "stacks:list_resource_types": "rule:deny_stack_user", - "stacks:list_template_versions": "rule:deny_stack_user", - "stacks:list_template_functions": "rule:deny_stack_user", - "stacks:lookup": "", - "stacks:preview": "rule:deny_stack_user", - "stacks:resource_schema": "rule:deny_stack_user", - "stacks:show": "rule:deny_stack_user", - "stacks:template": "rule:deny_stack_user", - "stacks:environment": "rule:deny_stack_user", - "stacks:update": "rule:deny_stack_user", - "stacks:update_patch": "rule:deny_stack_user", - "stacks:preview_update": "rule:deny_stack_user", - "stacks:preview_update_patch": "rule:deny_stack_user", - "stacks:validate_template": "rule:deny_stack_user", - "stacks:snapshot": "rule:deny_stack_user", - "stacks:show_snapshot": "rule:deny_stack_user", - "stacks:delete_snapshot": "rule:deny_stack_user", - "stacks:list_snapshots": "rule:deny_stack_user", - "stacks:restore_snapshot": "rule:deny_stack_user", - "stacks:list_outputs": "rule:deny_stack_user", - "stacks:show_output": "rule:deny_stack_user", - - "software_configs:global_index": "rule:deny_everybody", - "software_configs:index": "rule:deny_stack_user", - "software_configs:create": "rule:deny_stack_user", - "software_configs:show": "rule:deny_stack_user", - "software_configs:delete": "rule:deny_stack_user", - "software_deployments:index": "rule:deny_stack_user", - "software_deployments:create": "rule:deny_stack_user", - "software_deployments:show": "rule:deny_stack_user", - "software_deployments:update": "rule:deny_stack_user", - "software_deployments:delete": "rule:deny_stack_user", - "software_deployments:metadata": "", - - "service:index": "rule:context_is_admin", - - "resource_types:OS::Nova::Flavor": "rule:context_is_admin", - "resource_types:OS::Cinder::EncryptedVolumeType": "rule:context_is_admin", - "resource_types:OS::Cinder::VolumeType": "rule:context_is_admin", - "resource_types:OS::Manila::ShareType": "rule:context_is_admin", - "resource_types:OS::Neutron::QoSPolicy": "rule:context_is_admin", - "resource_types:OS::Neutron::QoSBandwidthLimitRule": "rule:context_is_admin", - "resource_types:OS::Nova::HostAggregate": "rule:context_is_admin" -} diff --git a/openstack_dashboard/dashboards/admin/dashboard.py b/openstack_dashboard/dashboards/admin/dashboard.py index a1eb5feb2f..f526d957b7 100644 --- a/openstack_dashboard/dashboards/admin/dashboard.py +++ b/openstack_dashboard/dashboards/admin/dashboard.py @@ -28,8 +28,7 @@ class Admin(horizon.Dashboard): ('image', 'context_is_admin'), ('volume', 'context_is_admin'), ('compute', 'context_is_admin'), - ('network', 'context_is_admin'), - ('orchestration', 'context_is_admin'),) + ('network', 'context_is_admin'),) else: permissions = (tuple(utils.get_admin_permissions()),) diff --git a/openstack_dashboard/dashboards/admin/info/panel.py b/openstack_dashboard/dashboards/admin/info/panel.py index 8979032d5c..85a9642449 100644 --- a/openstack_dashboard/dashboards/admin/info/panel.py +++ b/openstack_dashboard/dashboards/admin/info/panel.py @@ -26,5 +26,4 @@ class Info(horizon.Panel): slug = 'info' policy_rules = (("compute", "context_is_admin"), ("volume", "context_is_admin"), - ("network", "context_is_admin"), - ("orchestration", "context_is_admin"),) + ("network", "context_is_admin"),) diff --git a/openstack_dashboard/dashboards/admin/info/tables.py b/openstack_dashboard/dashboards/admin/info/tables.py index 44f0758ebc..40894f0e0c 100644 --- a/openstack_dashboard/dashboards/admin/info/tables.py +++ b/openstack_dashboard/dashboards/admin/info/tables.py @@ -236,45 +236,3 @@ class NetworkAgentsTable(tables.DataTable): table_actions = (NetworkAgentsFilterAction, ) row_actions = (NetworkL3AgentRoutersLinkAction, ) multi_select = False - - -class HeatServiceFilterAction(tables.FilterAction): - filter_field = 'type' - - def filter(self, table, services, filter_string): - q = filter_string.lower() - - def comp(service): - attr = getattr(service, self.filter_field, '') - if attr is not None and q in attr.lower(): - return True - return False - - return filter(comp, services) - - -class HeatServiceTable(tables.DataTable): - hostname = tables.Column('hostname', verbose_name=_('Hostname')) - binary = tables.Column("binary", verbose_name=_('Name')) - engine_id = tables.Column('engine_id', verbose_name=_('Engine Id')) - host = tables.Column('host', verbose_name=_('Host')) - topic = tables.Column('topic', verbose_name=_('Topic')) - # For consistent with other tables in system info, set column name to - # 'state' - state = tables.Column('status', verbose_name=_('State'), - display_choices=SERVICE_STATE_DISPLAY_CHOICES) - updated_at = tables.Column('updated_at', - verbose_name=pgettext_lazy( - 'Time since the last update', - u'Last Updated'), - filters=(utils_filters.parse_isotime, - filters.timesince)) - - def get_object_id(self, obj): - return "%s" % obj.engine_id - - class Meta(object): - name = "heat_services" - verbose_name = _("Orchestration Services") - table_actions = (HeatServiceFilterAction,) - multi_select = False diff --git a/openstack_dashboard/dashboards/admin/info/tabs.py b/openstack_dashboard/dashboards/admin/info/tabs.py index cdda728073..5cd265fd1f 100644 --- a/openstack_dashboard/dashboards/admin/info/tabs.py +++ b/openstack_dashboard/dashboards/admin/info/tabs.py @@ -18,7 +18,6 @@ from horizon import exceptions from horizon import tabs from openstack_dashboard.api import base from openstack_dashboard.api import cinder -from openstack_dashboard.api import heat from openstack_dashboard.api import neutron from openstack_dashboard.api import nova from openstack_dashboard.dashboards.admin.info import constants @@ -118,32 +117,8 @@ class NetworkAgentsTab(tabs.TableTab): return agents -class HeatServiceTab(tabs.TableTab): - table_classes = (tables.HeatServiceTable,) - name = tables.HeatServiceTable.Meta.verbose_name - slug = tables.HeatServiceTable.Meta.name - template_name = constants.INFO_DETAIL_TEMPLATE_NAME - - def allowed(self, request): - try: - return base.is_service_enabled(request, 'orchestration') - except Exception: - exceptions.handle(request, _('Orchestration service is disabled.')) - return False - - def get_heat_services_data(self): - try: - services = heat.service_list(self.tab_group.request) - except Exception: - msg = _('Unable to get Orchestration service list.') - exceptions.check_message(["Connection", "refused"], msg) - exceptions.handle(self.request, msg) - services = [] - return services - - class SystemInfoTabs(tabs.TabGroup): slug = "system_info" tabs = (ServicesTab, NovaServicesTab, CinderServicesTab, - NetworkAgentsTab, HeatServiceTab) + NetworkAgentsTab) sticky = True diff --git a/openstack_dashboard/dashboards/admin/info/tests.py b/openstack_dashboard/dashboards/admin/info/tests.py index c7405d3ead..9cbe86594c 100644 --- a/openstack_dashboard/dashboards/admin/info/tests.py +++ b/openstack_dashboard/dashboards/admin/info/tests.py @@ -29,7 +29,7 @@ class SystemInfoViewTests(test.BaseAdminViewTests): api.nova: ('service_list',), api.neutron: ('agent_list', 'is_extension_supported'), api.cinder: ('service_list',), - api.heat: ('service_list',)}) + }) def _test_base_index(self): api.base.is_service_enabled(IsA(http.HttpRequest), IgnoreArg()) \ .MultipleTimes().AndReturn(True) @@ -49,10 +49,6 @@ class SystemInfoViewTests(test.BaseAdminViewTests): api.cinder.service_list(IsA(http.HttpRequest)).\ AndReturn(cinder_services) - heat_services = self.heat_services.list() - api.heat.service_list(IsA(http.HttpRequest)).\ - AndReturn(heat_services) - self.mox.ReplayAll() res = self.client.get(INDEX_URL) @@ -88,14 +84,3 @@ class SystemInfoViewTests(test.BaseAdminViewTests): ) self.mox.VerifyAll() - - def test_heat_index(self): - res = self._test_base_index() - heat_services_tab = res.context['tab_group'].\ - get_tab('heat_services') - self.assertQuerysetEqual( - heat_services_tab._tables['heat_services'].data, - [service.__repr__() for service in self.heat_services.list()] - ) - - self.mox.VerifyAll() diff --git a/openstack_dashboard/dashboards/project/stacks/__init__.py b/openstack_dashboard/dashboards/project/stacks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openstack_dashboard/dashboards/project/stacks/api.py b/openstack_dashboard/dashboards/project/stacks/api.py deleted file mode 100644 index ce5affd812..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/api.py +++ /dev/null @@ -1,83 +0,0 @@ -# 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 json - -from openstack_dashboard.api import heat - -from openstack_dashboard.dashboards.project.stacks import mappings -from openstack_dashboard.dashboards.project.stacks import sro - - -class Stack(object): - pass - - -def d3_data(request, stack_id=''): - try: - stack = heat.stack_get(request, stack_id) - except Exception: - stack = Stack() - stack.id = stack_id - stack.stack_name = request.session.get('stack_name', '') - stack.stack_status = 'DELETE_COMPLETE' - stack.stack_status_reason = 'DELETE_COMPLETE' - - try: - resources = heat.resources_list(request, stack.stack_name) - except Exception: - resources = [] - - d3_data = {"nodes": [], "stack": {}} - if stack: - stack_image = mappings.get_resource_image(stack.stack_status, 'stack') - stack_node = { - 'stack_id': stack.id, - 'name': stack.stack_name, - 'status': stack.stack_status, - 'image': stack_image, - 'image_size': 60, - 'image_x': -30, - 'image_y': -30, - 'text_x': 40, - 'text_y': ".35em", - 'in_progress': (stack.status == 'IN_PROGRESS'), - 'info_box': sro.stack_info(stack, stack_image) - } - d3_data['stack'] = stack_node - - if resources: - for resource in resources: - resource_image = mappings.get_resource_image( - resource.resource_status, - resource.resource_type) - resource_status = mappings.get_resource_status( - resource.resource_status) - if resource_status in ('IN_PROGRESS', 'INIT'): - in_progress = True - else: - in_progress = False - resource_node = { - 'name': resource.resource_name, - 'status': resource.resource_status, - 'image': resource_image, - 'required_by': resource.required_by, - 'image_size': 50, - 'image_x': -25, - 'image_y': -25, - 'text_x': 35, - 'text_y': ".35em", - 'in_progress': in_progress, - 'info_box': sro.resource_info(resource) - } - d3_data['nodes'].append(resource_node) - return json.dumps(d3_data) diff --git a/openstack_dashboard/dashboards/project/stacks/forms.py b/openstack_dashboard/dashboards/project/stacks/forms.py deleted file mode 100644 index e8b970299f..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/forms.py +++ /dev/null @@ -1,488 +0,0 @@ -# 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 json -import logging - -import django -from django.conf import settings -from django.utils import html -from django.utils.translation import ugettext_lazy as _ -from django.views.decorators.debug import sensitive_variables - -from oslo_utils import strutils -import six - -from horizon import exceptions -from horizon import forms -from horizon import messages - -from openstack_dashboard import api -from openstack_dashboard.dashboards.project.images \ - import utils as image_utils -from openstack_dashboard.dashboards.project.instances \ - import utils as instance_utils - - -LOG = logging.getLogger(__name__) - - -def create_upload_form_attributes(prefix, input_type, name): - """Creates attribute dicts for the switchable upload form - - :type prefix: str - :param prefix: prefix (environment, template) of field - :type input_type: str - :param input_type: field type (file, raw, url) - :type name: str - :param name: translated text label to display to user - :rtype: dict - :return: an attribute set to pass to form build - """ - attributes = {'class': 'switched', 'data-switch-on': prefix + 'source'} - attributes['data-' + prefix + 'source-' + input_type] = name - return attributes - - -class TemplateForm(forms.SelfHandlingForm): - - class Meta(object): - name = _('Select Template') - help_text = _('Select a template to launch a stack.') - - # TODO(jomara) - update URL choice for template & environment files - # w/ client side download when applicable - base_choices = [('file', _('File')), - ('raw', _('Direct Input'))] - url_choice = [('url', _('URL'))] - attributes = {'class': 'switchable', 'data-slug': 'templatesource'} - template_source = forms.ChoiceField(label=_('Template Source'), - choices=base_choices + url_choice, - widget=forms.ThemableSelectWidget( - attrs=attributes)) - - attributes = create_upload_form_attributes( - 'template', - 'file', - _('Template File')) - template_upload = forms.FileField( - label=_('Template File'), - help_text=_('A local template to upload.'), - widget=forms.FileInput(attrs=attributes), - required=False) - - attributes = create_upload_form_attributes( - 'template', - 'url', - _('Template URL')) - template_url = forms.URLField( - label=_('Template URL'), - help_text=_('An external (HTTP) URL to load the template from.'), - widget=forms.TextInput(attrs=attributes), - required=False) - - attributes = create_upload_form_attributes( - 'template', - 'raw', - _('Template Data')) - template_data = forms.CharField( - label=_('Template Data'), - help_text=_('The raw contents of the template.'), - widget=forms.widgets.Textarea(attrs=attributes), - required=False) - - attributes = {'data-slug': 'envsource', 'class': 'switchable'} - environment_source = forms.ChoiceField( - label=_('Environment Source'), - choices=base_choices, - widget=forms.ThemableSelectWidget(attrs=attributes), - required=False) - - attributes = create_upload_form_attributes( - 'env', - 'file', - _('Environment File')) - environment_upload = forms.FileField( - label=_('Environment File'), - help_text=_('A local environment to upload.'), - widget=forms.FileInput(attrs=attributes), - required=False) - - attributes = create_upload_form_attributes( - 'env', - 'raw', - _('Environment Data')) - environment_data = forms.CharField( - label=_('Environment Data'), - help_text=_('The raw contents of the environment file.'), - widget=forms.widgets.Textarea(attrs=attributes), - required=False) - - if django.VERSION >= (1, 9): - # Note(Itxaka): On django>=1.9 Charfield has an strip option that - # we need to set to False as to not hit - # https://bugs.launchpad.net/python-heatclient/+bug/1546166 - environment_data.strip = False - template_data.strip = False - - def __init__(self, *args, **kwargs): - self.next_view = kwargs.pop('next_view') - super(TemplateForm, self).__init__(*args, **kwargs) - - def clean(self): - cleaned = super(TemplateForm, self).clean() - - files = self.request.FILES - self.clean_uploaded_files('template', _('template'), cleaned, files) - self.clean_uploaded_files('environment', _('environment'), cleaned, - files) - - # Validate the template and get back the params. - kwargs = {} - if cleaned['environment_data']: - kwargs['environment'] = cleaned['environment_data'] - try: - files, tpl =\ - api.heat.get_template_files(cleaned.get('template_data'), - cleaned.get('template_url')) - kwargs['files'] = files - kwargs['template'] = tpl - validated = api.heat.template_validate(self.request, **kwargs) - cleaned['template_validate'] = validated - cleaned['template_validate']['files'] = files - cleaned['template_validate']['template'] = tpl - except Exception as e: - raise forms.ValidationError(six.text_type(e)) - - return cleaned - - def clean_uploaded_files(self, prefix, field_label, cleaned, files): - """Cleans Template & Environment data from form upload. - - Does some of the crunchy bits for processing uploads vs raw - data depending on what the user specified. Identical process - for environment data & template data. - - :type prefix: str - :param prefix: prefix (environment, template) of field - :type field_label: str - :param field_label: translated prefix str for messages - :type input_type: dict - :param prefix: existing cleaned fields from form - :rtype: dict - :return: cleaned dict including environment & template data - """ - - upload_str = prefix + "_upload" - data_str = prefix + "_data" - url = cleaned.get(prefix + '_url') - data = cleaned.get(prefix + '_data') - - has_upload = upload_str in files - # Uploaded file handler - if has_upload and not url: - log_template_name = files[upload_str].name - LOG.info('got upload %s', log_template_name) - - tpl = files[upload_str].read() - if tpl.startswith('{'): - try: - json.loads(tpl) - except Exception as e: - msg = _('There was a problem parsing the' - ' %(prefix)s: %(error)s') - msg = msg % {'prefix': prefix, 'error': six.text_type(e)} - raise forms.ValidationError(msg) - cleaned[data_str] = tpl - - # URL handler - elif url and (has_upload or data): - msg = _('Please specify a %s using only one source method.') - msg = msg % field_label - raise forms.ValidationError(msg) - - elif prefix == 'template': - # Check for raw template input - blank environment allowed - if not url and not data: - msg = _('You must specify a template via one of the ' - 'available sources.') - raise forms.ValidationError(msg) - - def create_kwargs(self, data): - kwargs = {'parameters': data['template_validate'], - 'environment_data': data['environment_data']} - if data.get('stack_id'): - kwargs['stack_id'] = data['stack_id'] - return kwargs - - def handle(self, request, data): - kwargs = self.create_kwargs(data) - # NOTE (gabriel): This is a bit of a hack, essentially rewriting this - # request so that we can chain it as an input to the next view... - # but hey, it totally works. - request.method = 'GET' - - return self.next_view.as_view()(request, **kwargs) - - -class ChangeTemplateForm(TemplateForm): - class Meta(object): - name = _('Edit Template') - help_text = _('Select a new template to re-launch a stack.') - stack_id = forms.CharField(label=_('Stack ID'), - widget=forms.widgets.HiddenInput) - stack_name = forms.CharField(label=_('Stack Name'), - widget=forms.TextInput(attrs={'readonly': - 'readonly'})) - - -class PreviewTemplateForm(TemplateForm): - class Meta(object): - name = _('Preview Template') - help_text = _('Select a new template to preview a stack.') - - -class CreateStackForm(forms.SelfHandlingForm): - - param_prefix = '__param_' - - class Meta(object): - name = _('Create Stack') - - environment_data = forms.CharField( - widget=forms.widgets.HiddenInput, - required=False) - if django.VERSION >= (1, 9): - # Note(Itxaka): On django>=1.9 Charfield has an strip option that - # we need to set to False as to not hit - # https://bugs.launchpad.net/python-heatclient/+bug/1546166 - environment_data.strip = False - - parameters = forms.CharField( - widget=forms.widgets.HiddenInput) - stack_name = forms.RegexField( - max_length=255, - label=_('Stack Name'), - help_text=_('Name of the stack to create.'), - regex=r"^[a-zA-Z][a-zA-Z0-9_.-]*$", - error_messages={'invalid': - _('Name must start with a letter and may ' - 'only contain letters, numbers, underscores, ' - 'periods and hyphens.')}) - timeout_mins = forms.IntegerField( - initial=60, - label=_('Creation Timeout (minutes)'), - help_text=_('Stack creation timeout in minutes.')) - enable_rollback = forms.BooleanField( - label=_('Rollback On Failure'), - help_text=_('Enable rollback on create/update failure.'), - required=False) - - def __init__(self, *args, **kwargs): - parameters = kwargs.pop('parameters') - # special case: load template data from API, not passed in params - if kwargs.get('validate_me'): - parameters = kwargs.pop('validate_me') - super(CreateStackForm, self).__init__(*args, **kwargs) - - if self._stack_password_enabled(): - self.fields['password'] = forms.CharField( - label=_('Password for user "%s"') % self.request.user.username, - help_text=_('This is required for operations to be performed ' - 'throughout the lifecycle of the stack'), - widget=forms.PasswordInput()) - - self._build_parameter_fields(parameters) - - def _stack_password_enabled(self): - stack_settings = getattr(settings, 'OPENSTACK_HEAT_STACK', {}) - return stack_settings.get('enable_user_pass', True) - - def _build_parameter_fields(self, template_validate): - self.help_text = template_validate['Description'] - - params = template_validate.get('Parameters', {}) - if template_validate.get('ParameterGroups'): - params_in_order = [] - for group in template_validate['ParameterGroups']: - for param in group.get('parameters', []): - if param in params: - params_in_order.append((param, params[param])) - else: - # no parameter groups, simply sorted to make the order fixed - params_in_order = sorted(params.items()) - for param_key, param in params_in_order: - field = None - field_key = self.param_prefix + param_key - initial = param.get('Value', - param.get('DefaultValue', - param.get('Default'))) - field_args = { - 'initial': initial, - 'label': param.get('Label', param_key), - 'help_text': html.escape(param.get('Description', '')), - 'required': initial is None, - } - - param_type = param.get('Type', None) - hidden = strutils.bool_from_string(param.get('NoEcho', 'false')) - if 'CustomConstraint' in param: - choices = self._populate_custom_choices( - param['CustomConstraint']) - field_args['choices'] = choices - field = forms.ChoiceField(**field_args) - - elif 'AllowedValues' in param: - choices = map(lambda x: (x, x), param['AllowedValues']) - field_args['choices'] = choices - field = forms.ChoiceField(**field_args) - - elif param_type == 'Json' and 'Default' in param: - field_args['initial'] = json.dumps(param['Default']) - field = forms.CharField(**field_args) - - elif param_type in ('CommaDelimitedList', 'String', 'Json'): - if 'MinLength' in param: - field_args['min_length'] = int(param['MinLength']) - field_args['required'] = field_args['min_length'] > 0 - if 'MaxLength' in param: - field_args['max_length'] = int(param['MaxLength']) - if hidden: - field_args['widget'] = forms.PasswordInput( - render_value=True) - field = forms.CharField(**field_args) - - elif param_type == 'Number': - if 'MinValue' in param: - field_args['min_value'] = int(param['MinValue']) - if 'MaxValue' in param: - field_args['max_value'] = int(param['MaxValue']) - field = forms.IntegerField(**field_args) - - elif param_type == 'Boolean': - field_args['required'] = False - field = forms.BooleanField(**field_args) - - if field: - self.fields[field_key] = field - - @sensitive_variables('password') - def handle(self, request, data): - prefix_length = len(self.param_prefix) - params_list = [(k[prefix_length:], v) for (k, v) in data.items() - if k.startswith(self.param_prefix)] - fields = { - 'stack_name': data.get('stack_name'), - 'timeout_mins': data.get('timeout_mins'), - 'disable_rollback': not(data.get('enable_rollback')), - 'parameters': dict(params_list), - 'files': json.loads(data.get('parameters')).get('files'), - 'template': json.loads(data.get('parameters')).get('template') - } - if data.get('password'): - fields['password'] = data.get('password') - - if data.get('environment_data'): - fields['environment'] = data.get('environment_data') - - try: - api.heat.stack_create(self.request, **fields) - messages.info(request, _("Stack creation started.")) - return True - except Exception: - exceptions.handle(request) - - def _populate_custom_choices(self, custom_type): - if custom_type == 'neutron.network': - return instance_utils.network_field_data(self.request, True) - if custom_type == 'nova.keypair': - return instance_utils.keypair_field_data(self.request, True) - if custom_type == 'glance.image': - return image_utils.image_field_data(self.request, True) - if custom_type == 'nova.flavor': - return instance_utils.flavor_field_data(self.request, True) - return [] - - -class EditStackForm(CreateStackForm): - - class Meta(object): - name = _('Update Stack Parameters') - - stack_id = forms.CharField( - label=_('Stack ID'), - widget=forms.widgets.HiddenInput) - stack_name = forms.CharField( - label=_('Stack Name'), - widget=forms.TextInput(attrs={'readonly': 'readonly'})) - - @sensitive_variables('password') - def handle(self, request, data): - prefix_length = len(self.param_prefix) - params_list = [(k[prefix_length:], v) for (k, v) in data.items() - if k.startswith(self.param_prefix)] - - stack_id = data.get('stack_id') - fields = { - 'stack_name': data.get('stack_name'), - 'timeout_mins': data.get('timeout_mins'), - 'disable_rollback': not(data.get('enable_rollback')), - 'parameters': dict(params_list), - 'files': json.loads(data.get('parameters')).get('files'), - 'template': json.loads(data.get('parameters')).get('template') - } - if data.get('password'): - fields['password'] = data.get('password') - - if data.get('environment_data'): - fields['environment'] = data.get('environment_data') - - try: - api.heat.stack_update(self.request, stack_id=stack_id, **fields) - messages.info(request, _("Stack update started.")) - return True - except Exception: - exceptions.handle(request) - - -class PreviewStackForm(CreateStackForm): - - class Meta(object): - name = _('Preview Stack Parameters') - - def __init__(self, *args, **kwargs): - self.next_view = kwargs.pop('next_view') - super(CreateStackForm, self).__init__(*args, **kwargs) - - def handle(self, request, data): - prefix_length = len(self.param_prefix) - params_list = [(k[prefix_length:], v) for (k, v) in data.items() - if k.startswith(self.param_prefix)] - fields = { - 'stack_name': data.get('stack_name'), - 'timeout_mins': data.get('timeout_mins'), - 'disable_rollback': not(data.get('enable_rollback')), - 'parameters': dict(params_list), - 'files': json.loads(data.get('parameters')).get('files'), - 'template': json.loads(data.get('parameters')).get('template') - } - - if data.get('environment_data'): - fields['environment'] = data.get('environment_data') - - try: - stack_preview = api.heat.stack_preview(self.request, **fields) - request.method = 'GET' - return self.next_view.as_view()(request, - stack_preview=stack_preview) - except Exception: - exceptions.handle(request) diff --git a/openstack_dashboard/dashboards/project/stacks/mappings.py b/openstack_dashboard/dashboards/project/stacks/mappings.py deleted file mode 100644 index eeab18cffa..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/mappings.py +++ /dev/null @@ -1,350 +0,0 @@ -# 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 json -import logging - -from django.conf import settings -from django.core.urlresolvers import reverse -from django.template.defaultfilters import register -from django.utils import html -from django.utils import safestring -import six -import six.moves.urllib.parse as urlparse - -from openstack_dashboard.api import swift - -LOG = logging.getLogger(__name__) - - -resource_urls = { - "AWS::AutoScaling::AutoScalingGroup": { - 'link': 'horizon:project:stacks:detail'}, - "AWS::CloudFormation::Stack": { - 'link': 'horizon:project:stacks:detail'}, - "AWS::EC2::Instance": { - 'link': 'horizon:project:instances:detail'}, - "AWS::EC2::InternetGateway": { - 'link': 'horizon:project:networks:ports:detail'}, - "AWS::EC2::NetworkInterface": { - 'link': 'horizon:project:networks:ports:detail'}, - "AWS::EC2::RouteTable": { - 'link': 'horizon:project:routers:detail'}, - "AWS::EC2::SecurityGroup": { - 'link': 'horizon:project:security_groups:index'}, - "AWS::EC2::Subnet": { - 'link': 'horizon:project:networks:subnets:detail'}, - "AWS::EC2::Volume": { - 'link': 'horizon:project:volumes:detail'}, - "AWS::EC2::VPC": { - 'link': 'horizon:project:networks:detail'}, - "AWS::S3::Bucket": { - 'link': 'horizon:project:containers:index'}, - "OS::Cinder::Volume": { - 'link': 'horizon:project:volumes:detail'}, - "OS::Heat::AccessPolicy": { - 'link': 'horizon:project:stacks:detail'}, - "OS::Heat::AutoScalingGroup": { - 'link': 'horizon:project:stacks:detail'}, - "OS::Heat::CloudConfig": { - 'link': 'horizon:project:stacks:detail'}, - "OS::Neutron::Firewall": { - 'link': 'horizon:project:firewalls:firewalldetails'}, - "OS::Neutron::FirewallPolicy": { - 'link': 'horizon:project:firewalls:policydetails'}, - "OS::Neutron::FirewallRule": { - 'link': 'horizon:project:firewalls:ruledetails'}, - "OS::Heat::HARestarter": { - 'link': 'horizon:project:stacks:detail'}, - "OS::Heat::InstanceGroup": { - 'link': 'horizon:project:stacks:detail'}, - "OS::Heat::MultipartMime": { - 'link': 'horizon:project:stacks:detail'}, - "OS::Heat::ResourceGroup": { - 'link': 'horizon:project:stacks:detail'}, - "OS::Heat::SoftwareConfig": { - 'link': 'horizon:project:stacks:detail'}, - "OS::Heat::StructuredConfig": { - 'link': 'horizon:project:stacks:detail'}, - "OS::Heat::StructuredDeployment": { - 'link': 'horizon:project:stacks:detail'}, - "OS::Heat::Stack": { - 'link': 'horizon:project:stacks:detail'}, - "OS::Heat::WaitCondition": { - 'link': 'horizon:project:stacks:detail'}, - "OS::Heat::WaitConditionHandle": { - 'link': 'horizon:project:stacks:detail'}, - "OS::Neutron::IKEPolicy": { - 'link': 'horizon:project:vpn:ikepolicydetails'}, - "OS::Neutron::IPsecPolicy": { - 'link': 'horizon:project:vpn:ipsecpolicydetails'}, - "OS::Neutron::IPsecSiteConnection": { - 'link': 'horizon:project:vpn:ipsecsiteconnectiondetails'}, - "OS::Neutron::Net": { - 'link': 'horizon:project:networks:detail'}, - "OS::Neutron::Port": { - 'link': 'horizon:project:networks:ports:detail'}, - "OS::Neutron::Router": { - 'link': 'horizon:project:routers:detail'}, - "OS::Neutron::Subnet": { - 'link': 'horizon:project:networks:subnets:detail'}, - "OS::Neutron::VPNService": { - 'link': 'horizon:project:vpn:vpnservicedetails'}, - "OS::Nova::KeyPair": { - 'link': 'horizon:project:key_pairs:index'}, - "OS::Nova::Server": { - 'link': 'horizon:project:instances:detail'}, - "OS::Swift::Container": { - 'link': 'horizon:project:containers:index', - 'format_pattern': '%s' + swift.FOLDER_DELIMITER}, -} - - -def resource_to_url(resource): - if (not resource or - not resource.physical_resource_id or - not hasattr(resource, 'resource_type')): - return None - - mapping = resource_urls.get(resource.resource_type, {}) - try: - if 'link' not in mapping: - return None - format_pattern = mapping.get('format_pattern') or '%s' - rid = format_pattern % resource.physical_resource_id - url = reverse(mapping['link'], args=(rid,)) - except Exception as e: - LOG.exception(e) - return None - return url - - -@register.filter -def stack_output(output): - if not output: - return u'' - if isinstance(output, six.string_types): - parts = urlparse.urlsplit(output) - if parts.netloc and parts.scheme in ('http', 'https'): - url = html.escape(output) - safe_link = u'%s' % (url, url) - return safestring.mark_safe(safe_link) - if isinstance(output, dict) or isinstance(output, list): - output = json.dumps(output, indent=2) - return safestring.mark_safe(u'
%s
' % html.escape(output)) - -static_url = getattr(settings, "STATIC_URL", "/static/") -resource_images = { - 'LB_FAILED': static_url + 'dashboard/img/lb-red.svg', - 'LB_DELETE': static_url + 'dashboard/img/lb-red.svg', - 'LB_IN_PROGRESS': static_url + 'dashboard/img/lb-gray.gif', - 'LB_INIT': static_url + 'dashboard/img/lb-gray.svg', - 'LB_COMPLETE': static_url + 'dashboard/img/lb-green.svg', - 'DB_FAILED': static_url + 'dashboard/img/db-red.svg', - 'DB_DELETE': static_url + 'dashboard/img/db-red.svg', - 'DB_IN_PROGRESS': static_url + 'dashboard/img/db-gray.gif', - 'DB_INIT': static_url + 'dashboard/img/db-gray.svg', - 'DB_COMPLETE': static_url + 'dashboard/img/db-green.svg', - 'STACK_FAILED': static_url + 'dashboard/img/stack-red.svg', - 'STACK_DELETE': static_url + 'dashboard/img/stack-red.svg', - 'STACK_IN_PROGRESS': static_url + 'dashboard/img/stack-gray.gif', - 'STACK_INIT': static_url + 'dashboard/img/stack-gray.svg', - 'STACK_COMPLETE': static_url + 'dashboard/img/stack-green.svg', - 'SERVER_FAILED': static_url + 'dashboard/img/server-red.svg', - 'SERVER_DELETE': static_url + 'dashboard/img/server-red.svg', - 'SERVER_IN_PROGRESS': static_url + 'dashboard/img/server-gray.gif', - 'SERVER_INIT': static_url + 'dashboard/img/server-gray.svg', - 'SERVER_COMPLETE': static_url + 'dashboard/img/server-green.svg', - 'ALARM_FAILED': static_url + 'dashboard/img/alarm-red.svg', - 'ALARM_DELETE': static_url + 'dashboard/img/alarm-red.svg', - 'ALARM_IN_PROGRESS': static_url + 'dashboard/img/alarm-gray.gif', - 'ALARM_INIT': static_url + 'dashboard/img/alarm-gray.svg', - 'ALARM_COMPLETE': static_url + 'dashboard/img/alarm-green.svg', - 'VOLUME_FAILED': static_url + 'dashboard/img/volume-red.svg', - 'VOLUME_DELETE': static_url + 'dashboard/img/volume-red.svg', - 'VOLUME_IN_PROGRESS': static_url + 'dashboard/img/volume-gray.gif', - 'VOLUME_INIT': static_url + 'dashboard/img/volume-gray.svg', - 'VOLUME_COMPLETE': static_url + 'dashboard/img/volume-green.svg', - 'IMAGE_FAILED': static_url + 'dashboard/img/image-red.svg', - 'IMAGE_DELETE': static_url + 'dashboard/img/image-red.svg', - 'IMAGE_IN_PROGRESS': static_url + 'dashboard/img/image-gray.gif', - 'IMAGE_INIT': static_url + 'dashboard/img/image-gray.svg', - 'IMAGE_COMPLETE': static_url + 'dashboard/img/image-green.svg', - 'WAIT_FAILED': static_url + 'dashboard/img/wait-red.svg', - 'WAIT_DELETE': static_url + 'dashboard/img/wait-red.svg', - 'WAIT_IN_PROGRESS': static_url + 'dashboard/img/wait-gray.gif', - 'WAIT_INIT': static_url + 'dashboard/img/wait-gray.svg', - 'WAIT_COMPLETE': static_url + 'dashboard/img/wait-green.svg', - 'FIREWALL_FAILED': static_url + 'dashboard/img/firewall-red.svg', - 'FIREWALL_DELETE': static_url + 'dashboard/img/firewall-red.svg', - 'FIREWALL_IN_PROGRESS': static_url + 'dashboard/img/firewall-gray.gif', - 'FIREWALL_INIT': static_url + 'dashboard/img/firewall-gray.svg', - 'FIREWALL_COMPLETE': static_url + 'dashboard/img/firewall-green.svg', - 'FLOATINGIP_FAILED': static_url + 'dashboard/img/floatingip-red.svg', - 'FLOATINGIP_DELETE': static_url + 'dashboard/img/floatingip-red.svg', - 'FLOATINGIP_IN_PROGRESS': static_url + 'dashboard/img/floatingip-gray.gif', - 'FLOATINGIP_INIT': static_url + 'dashboard/img/floatingip-gray.svg', - 'FLOATINGIP_COMPLETE': static_url + 'dashboard/img/floatingip-green.svg', - 'ROUTER_FAILED': static_url + 'dashboard/img/router-red.svg', - 'ROUTER_DELETE': static_url + 'dashboard/img/router-red.svg', - 'ROUTER_IN_PROGRESS': static_url + 'dashboard/img/router-gray.gif', - 'ROUTER_INIT': static_url + 'dashboard/img/router-gray.svg', - 'ROUTER_COMPLETE': static_url + 'dashboard/img/router-green.svg', - 'POLICY_FAILED': static_url + 'dashboard/img/policy-red.svg', - 'POLICY_DELETE': static_url + 'dashboard/img/policy-red.svg', - 'POLICY_IN_PROGRESS': static_url + 'dashboard/img/policy-gray.gif', - 'POLICY_INIT': static_url + 'dashboard/img/policy-gray.svg', - 'POLICY_COMPLETE': static_url + 'dashboard/img/policy-green.svg', - 'CONFIG_FAILED': static_url + 'dashboard/img/config-red.svg', - 'CONFIG_DELETE': static_url + 'dashboard/img/config-red.svg', - 'CONFIG_IN_PROGRESS': static_url + 'dashboard/img/config-gray.gif', - 'CONFIG_INIT': static_url + 'dashboard/img/config-gray.svg', - 'CONFIG_COMPLETE': static_url + 'dashboard/img/config-green.svg', - 'NETWORK_FAILED': static_url + 'dashboard/img/network-red.svg', - 'NETWORK_DELETE': static_url + 'dashboard/img/network-red.svg', - 'NETWORK_IN_PROGRESS': static_url + 'dashboard/img/network-gray.gif', - 'NETWORK_INIT': static_url + 'dashboard/img/network-gray.svg', - 'NETWORK_COMPLETE': static_url + 'dashboard/img/network-green.svg', - 'PORT_FAILED': static_url + 'dashboard/img/port-red.svg', - 'PORT_DELETE': static_url + 'dashboard/img/port-red.svg', - 'PORT_IN_PROGRESS': static_url + 'dashboard/img/port-gray.gif', - 'PORT_INIT': static_url + 'dashboard/img/port-gray.svg', - 'PORT_COMPLETE': static_url + 'dashboard/img/port-green.svg', - 'SECURITYGROUP_FAILED': static_url + 'dashboard/img/securitygroup-red.svg', - 'SECURITYGROUP_DELETE': static_url + 'dashboard/img/securitygroup-red.svg', - 'SECURITYGROUP_IN_PROGRESS': - static_url + 'dashboard/img/securitygroup-gray.gif', - 'SECURITYGROUP_INIT': static_url + 'dashboard/img/securitygroup-gray.svg', - 'SECURITYGROUP_COMPLETE': - static_url + 'dashboard/img/securitygroup-green.svg', - 'VPN_FAILED': static_url + 'dashboard/img/vpn-red.svg', - 'VPN_DELETE': static_url + 'dashboard/img/vpn-red.svg', - 'VPN_IN_PROGRESS': static_url + 'dashboard/img/vpn-gray.gif', - 'VPN_INIT': static_url + 'dashboard/img/vpn-gray.svg', - 'VPN_COMPLETE': static_url + 'dashboard/img/vpn-green.svg', - 'FLAVOR_FAILED': static_url + 'dashboard/img/flavor-red.svg', - 'FLAVOR_DELETE': static_url + 'dashboard/img/flavor-red.svg', - 'FLAVOR_IN_PROGRESS': static_url + 'dashboard/img/flavor-gray.gif', - 'FLAVOR_INIT': static_url + 'dashboard/img/flavor-gray.svg', - 'FLAVOR_COMPLETE': static_url + 'dashboard/img/flavor-green.svg', - 'KEYPAIR_FAILED': static_url + 'dashboard/img/keypair-red.svg', - 'KEYPAIR_DELETE': static_url + 'dashboard/img/keypair-red.svg', - 'KEYPAIR_IN_PROGRESS': static_url + 'dashboard/img/keypair-gray.gif', - 'KEYPAIR_INIT': static_url + 'dashboard/img/keypair-gray.svg', - 'KEYPAIR_COMPLETE': static_url + 'dashboard/img/keypair-green.svg', - 'UNKNOWN_FAILED': static_url + 'dashboard/img/unknown-red.svg', - 'UNKNOWN_DELETE': static_url + 'dashboard/img/unknown-red.svg', - 'UNKNOWN_IN_PROGRESS': static_url + 'dashboard/img/unknown-gray.gif', - 'UNKNOWN_INIT': static_url + 'dashboard/img/unknown-gray.svg', - 'UNKNOWN_COMPLETE': static_url + 'dashboard/img/unknown-green.svg', -} - - -resource_types = { - # LB - 'LoadBalance': 'LB', - 'HealthMonitor': 'LB', - 'PoolMember': 'LB', - 'Pool': 'LB', - # DB - 'DBInstance': 'DB', - 'Database': 'DB', - # SERVER - 'Instance': 'SERVER', - 'Server': 'SERVER', - # ALARM - 'Alarm': 'ALARM', - 'CombinationAlarm': 'ALARM', - 'CWLiteAlarm': 'ALARM', - # VOLUME - 'Volume': 'VOLUME', - 'VolumeAttachment': 'VOLUME', - # STACK - 'stack': 'STACK', - 'AutoScalingGroup': 'STACK', - 'InstanceGroup': 'STACK', - 'ServerGroup': 'STACK', - 'ResourceGroup': 'STACK', - # IMAGE - 'Image': 'IMAGE', - # WAIT - 'WaitCondition': 'WAIT', - 'WaitConditionHandle': 'WAIT', - 'UpdateWaitConditionHandle': 'WAIT', - # FIREWALL - 'Firewall': 'FIREWALL', - 'FirewallPolicy': 'FIREWALL', - 'FirewallRule': 'FIREWALL', - # FLOATINGIP - 'FloatingIP': 'FLOATINGIP', - 'FloatingIPAssociation': 'FLOATINGIP', - # ROUTER - 'Router': 'ROUTER', - 'RouterGateway': 'ROUTER', - 'RouterInterface': 'ROUTER', - # POLICY - 'ScalingPolicy': 'POLICY', - # CONFIG - 'CloudConfig': 'CONFIG', - 'MultipartMime': 'CONFIG', - 'SoftwareConfig': 'CONFIG', - 'SoftwareDeployment': 'CONFIG', - 'StructuredConfig': 'CONFIG', - 'StructuredDeployment': 'CONFIG', - # NETWORK - 'Net': 'NETWORK', - 'Subnet': 'NETWORK', - 'NetworkGateway': 'NETWORK', - 'ProviderNet': 'NETWORK', - # PORT - 'Port': 'PORT', - # SECURITYGROUP - 'SecurityGroup': 'SECURITYGROUP', - # VPN - 'VPNService': 'VPN', - # FLAVOR - 'Flavor': 'FLAVOR', - # KEYPAIR - 'KeyPair': 'KEYPAIR', -} - - -def get_resource_type(type): - for key, value in resource_types.items(): - if key in type: - return value - - return 'UNKNOWN' - - -def get_resource_status(status): - if ('IN_PROGRESS' in status): - return 'IN_PROGRESS' - elif ('FAILED' in status): - return 'FAILED' - elif ('DELETE' in status): - return 'DELETE' - elif ('INIT' in status): - return 'INIT' - else: - return 'COMPLETE' - - -def get_resource_image(status, type): - """Sets the image url and in_progress action sw based on status.""" - resource_type = get_resource_type(type) - resource_status = get_resource_status(status) - resource_state = resource_type + "_" + resource_status - - for key in resource_images: - if key == resource_state: - return resource_images.get(key) diff --git a/openstack_dashboard/dashboards/project/stacks/panel.py b/openstack_dashboard/dashboards/project/stacks/panel.py deleted file mode 100644 index f9e8800381..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/panel.py +++ /dev/null @@ -1,21 +0,0 @@ -# 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. - -from django.utils.translation import ugettext_lazy as _ - -import horizon - - -class Stacks(horizon.Panel): - name = _("Stacks") - slug = "stacks" - permissions = ('openstack.services.orchestration',) diff --git a/openstack_dashboard/dashboards/project/stacks/resource_types/__init__.py b/openstack_dashboard/dashboards/project/stacks/resource_types/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openstack_dashboard/dashboards/project/stacks/resource_types/panel.py b/openstack_dashboard/dashboards/project/stacks/resource_types/panel.py deleted file mode 100644 index b1f37443fc..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/panel.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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. - -from django.utils.translation import ugettext_lazy as _ - -import horizon - - -class ResourceTypes(horizon.Panel): - name = _("Resource Types") - slug = "stacks.resource_types" - permissions = ('openstack.services.orchestration',) - policy_rules = (("orchestration", "stacks:list_resource_types"),) diff --git a/openstack_dashboard/dashboards/project/stacks/resource_types/tables.py b/openstack_dashboard/dashboards/project/stacks/resource_types/tables.py deleted file mode 100644 index 66e7702ab1..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/tables.py +++ /dev/null @@ -1,36 +0,0 @@ -# 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. - -from django.utils.translation import ugettext_lazy as _ - -from horizon import tables - - -class ResourceTypesFilterAction(tables.FilterAction): - filter_type = 'server' - filter_choices = (('name', _('Type ='), True, _("Case sensitive")),) - - -class ResourceTypesTable(tables.DataTable): - name = tables.Column("resource_type", - verbose_name=_("Type"), - link="horizon:project:stacks.resource_types:details",) - - def get_object_id(self, resource): - return resource.resource_type - - class Meta(object): - name = "resource_types" - verbose_name = _("Resource Types") - table_actions = (ResourceTypesFilterAction,) - multi_select = False diff --git a/openstack_dashboard/dashboards/project/stacks/resource_types/tabs.py b/openstack_dashboard/dashboards/project/stacks/resource_types/tabs.py deleted file mode 100644 index b0d5467e30..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/tabs.py +++ /dev/null @@ -1,32 +0,0 @@ -# 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. - -from django.utils.translation import ugettext_lazy as _ - -from horizon import tabs - - -class ResourceTypeOverviewTab(tabs.Tab): - name = _("Overview") - slug = "resource_type_overview" - template_name = "project/stacks.resource_types/_details.html" - - def get_context_data(self, request): - return {"r_type": self.tab_group.kwargs['rt'], - "r_type_attributes": self.tab_group.kwargs['rt_attributes'], - "r_type_properties": self.tab_group.kwargs['rt_properties']} - - -class ResourceTypeDetailsTabs(tabs.TabGroup): - slug = "resource_type_details" - tabs = (ResourceTypeOverviewTab,) diff --git a/openstack_dashboard/dashboards/project/stacks/resource_types/templates/stacks.resource_types/_details.html b/openstack_dashboard/dashboards/project/stacks/resource_types/templates/stacks.resource_types/_details.html deleted file mode 100644 index 704787e13b..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/templates/stacks.resource_types/_details.html +++ /dev/null @@ -1,15 +0,0 @@ -{% load i18n %} - -
-
-
{{ r_type }}
-
- -

{% trans "Attributes" %}

-
{{ r_type_attributes }}
-  
- -

{% trans "Properties" %}

-
{{ r_type_properties }}
-  
-
diff --git a/openstack_dashboard/dashboards/project/stacks/resource_types/tests.py b/openstack_dashboard/dashboards/project/stacks/resource_types/tests.py deleted file mode 100644 index 4c73644beb..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/tests.py +++ /dev/null @@ -1,52 +0,0 @@ -# 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. - -from django.core.urlresolvers import reverse -from django import http - -from mox3.mox import IsA - -from openstack_dashboard import api -from openstack_dashboard.test import helpers as test - - -class ResourceTypesTests(test.TestCase): - - @test.create_stubs({api.heat: ('resource_types_list',)}) - def test_index(self): - filters = {} - api.heat.resource_types_list( - IsA(http.HttpRequest), filters=filters).AndReturn( - self.resource_types.list()) - self.mox.ReplayAll() - - res = self.client.get( - reverse('horizon:project:stacks.resource_types:index')) - self.assertTemplateUsed( - res, 'horizon/common/_data_table_view.html') - self.assertContains(res, 'AWS::CloudFormation::Stack') - - @test.create_stubs({api.heat: ('resource_type_get',)}) - def test_detail_view(self): - rt = self.api_resource_types.first() - - api.heat.resource_type_get( - IsA(http.HttpRequest), rt['resource_type']).AndReturn(rt) - self.mox.ReplayAll() - - url = reverse('horizon:project:stacks.resource_types:details', - args=[rt['resource_type']]) - res = self.client.get(url) - - self.assertTemplateUsed(res, 'horizon/common/_detail.html') - self.assertNoMessages() diff --git a/openstack_dashboard/dashboards/project/stacks/resource_types/urls.py b/openstack_dashboard/dashboards/project/stacks/resource_types/urls.py deleted file mode 100644 index 8ab8cb0bda..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/urls.py +++ /dev/null @@ -1,22 +0,0 @@ -# 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. - -from django.conf.urls import url - -from openstack_dashboard.dashboards.project.stacks.resource_types import views - -urlpatterns = [ - url(r'^$', views.ResourceTypesView.as_view(), name='index'), - url(r'^(?P[^/]+)/$', - views.DetailView.as_view(), name='details'), -] diff --git a/openstack_dashboard/dashboards/project/stacks/resource_types/views.py b/openstack_dashboard/dashboards/project/stacks/resource_types/views.py deleted file mode 100644 index 8afcdf830d..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/views.py +++ /dev/null @@ -1,78 +0,0 @@ -# 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 yaml - -from django.core.urlresolvers import reverse -from django.utils.translation import ugettext_lazy as _ - -from horizon import exceptions -from horizon import tables -from horizon import tabs - -from openstack_dashboard import api -import openstack_dashboard.dashboards.project.stacks.resource_types.tables \ - as project_tables -import openstack_dashboard.dashboards.project.stacks.resource_types.tabs \ - as project_tabs - - -class ResourceTypesView(tables.DataTableView): - table_class = project_tables.ResourceTypesTable - page_title = _("Resource Types") - - def get_data(self): - try: - filters = self.get_filters() - if 'name' in filters: - filters['name'] = '.*' + filters['name'] - r_types = sorted(api.heat.resource_types_list(self.request, - filters=filters), - key=lambda resource: resource.resource_type) - except Exception: - r_types = [] - msg = _('Unable to retrieve stack resource types.') - exceptions.handle(self.request, msg) - return r_types - - -class DetailView(tabs.TabView): - tab_group_class = project_tabs.ResourceTypeDetailsTabs - template_name = 'horizon/common/_detail.html' - page_title = "{{ resource_type }}" - - def get_resource_type(self, request, **kwargs): - try: - resource_type_overview = api.heat.resource_type_get( - request, - kwargs['resource_type']) - return resource_type_overview - except Exception: - msg = _('Unable to retrieve resource type details.') - exceptions.handle(request, msg, redirect=self.get_redirect_url()) - - def get_tabs(self, request, **kwargs): - resource_type_overview = self.get_resource_type(request, **kwargs) - r_type = resource_type_overview['resource_type'] - r_type_attributes = resource_type_overview['attributes'] - r_type_properties = resource_type_overview['properties'] - return self.tab_group_class( - request, - rt=r_type, - rt_attributes=yaml.safe_dump(r_type_attributes, indent=2), - rt_properties=yaml.safe_dump(r_type_properties, indent=2), - **kwargs) - - @staticmethod - def get_redirect_url(): - return reverse('horizon:project:stacks.resources:index') diff --git a/openstack_dashboard/dashboards/project/stacks/sro.py b/openstack_dashboard/dashboards/project/stacks/sro.py deleted file mode 100644 index 4250e7ee9e..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/sro.py +++ /dev/null @@ -1,44 +0,0 @@ -# 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. - -from django.template.defaultfilters import title -from django.template.loader import render_to_string - -from horizon.utils import filters - - -def stack_info(stack, stack_image): - stack.stack_status_desc = title( - filters.replace_underscores(stack.stack_status)) - if stack.stack_status_reason: - stack.stack_status_reason = title( - filters.replace_underscores(stack.stack_status_reason) - ) - context = {} - context['stack'] = stack - context['stack_image'] = stack_image - return render_to_string('project/stacks/_stack_info.html', - context) - - -def resource_info(resource): - resource.resource_status_desc = title( - filters.replace_underscores(resource.resource_status) - ) - if resource.resource_status_reason: - resource.resource_status_reason = title( - filters.replace_underscores(resource.resource_status_reason) - ) - context = {} - context['resource'] = resource - return render_to_string('project/stacks/_resource_info.html', - context) diff --git a/openstack_dashboard/dashboards/project/stacks/tables.py b/openstack_dashboard/dashboards/project/stacks/tables.py deleted file mode 100644 index 96391ab5c5..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/tables.py +++ /dev/null @@ -1,413 +0,0 @@ -# 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. - -from django.core import urlresolvers -from django.http import Http404 -from django.template.defaultfilters import title -from django.utils.translation import pgettext_lazy -from django.utils.translation import ugettext_lazy as _ -from django.utils.translation import ungettext_lazy -from heatclient import exc - -from horizon import exceptions -from horizon import messages -from horizon import tables -from horizon.utils import filters - -from openstack_dashboard import api -from openstack_dashboard.dashboards.project.stacks import mappings - - -class LaunchStack(tables.LinkAction): - name = "launch" - verbose_name = _("Launch Stack") - url = "horizon:project:stacks:select_template" - classes = ("ajax-modal",) - icon = "plus" - policy_rules = (("orchestration", "stacks:validate_template"), - ("orchestration", "stacks:create"),) - - -class PreviewStack(tables.LinkAction): - name = "preview" - verbose_name = _("Preview Stack") - url = "horizon:project:stacks:preview_template" - classes = ("ajax-modal",) - icon = "eye" - policy_rules = (("orchestration", "stacks:validate_template"), - ("orchestration", "stacks:preview"),) - - -class CheckStack(tables.BatchAction): - name = "check" - verbose_name = _("Check Stack") - policy_rules = (("orchestration", "actions:action"),) - icon = "check-square" - - @staticmethod - def action_present(count): - return ungettext_lazy( - u"Check Stack", - u"Check Stacks", - count - ) - - @staticmethod - def action_past(count): - return ungettext_lazy( - u"Checked Stack", - u"Checked Stacks", - count - ) - - def action(self, request, stack_id): - api.heat.action_check(request, stack_id) - - -class SuspendStack(tables.BatchAction): - name = "suspend" - verbose_name = _("Suspend Stack") - policy_rules = (("orchestration", "actions:action"),) - icon = "pause" - - @staticmethod - def action_present(count): - return ungettext_lazy( - u"Suspend Stack", - u"Suspend Stacks", - count - ) - - @staticmethod - def action_past(count): - return ungettext_lazy( - u"Suspended Stack", - u"Suspended Stacks", - count - ) - - def action(self, request, stack_id): - try: - api.heat.action_suspend(request, stack_id) - except Exception: - msg = _('Failed to suspend stack.') - exceptions.handle(request, msg) - - -class ResumeStack(tables.BatchAction): - name = "resume" - verbose_name = _("Resume Stack") - policy_rules = (("orchestration", "actions:action"),) - icon = "play" - - @staticmethod - def action_present(count): - return ungettext_lazy( - u"Resume Stack", - u"Resume Stacks", - count - ) - - @staticmethod - def action_past(count): - return ungettext_lazy( - u"Resumed Stack", - u"Resumed Stacks", - count - ) - - def action(self, request, stack_id): - try: - api.heat.action_resume(request, stack_id) - except Exception: - msg = _('Failed to resume stack.') - exceptions.handle(request, msg) - - -class ChangeStackTemplate(tables.LinkAction): - name = "edit" - verbose_name = _("Change Stack Template") - url = "horizon:project:stacks:change_template" - classes = ("ajax-modal",) - icon = "pencil" - - def get_link_url(self, stack): - return urlresolvers.reverse(self.url, args=[stack.id]) - - -class DeleteStack(tables.DeleteAction): - @staticmethod - def action_present(count): - return ungettext_lazy( - u"Delete Stack", - u"Delete Stacks", - count - ) - - @staticmethod - def action_past(count): - return ungettext_lazy( - u"Deleted Stack", - u"Deleted Stacks", - count - ) - - policy_rules = (("orchestration", "stacks:delete"),) - - def delete(self, request, stack_id): - try: - api.heat.stack_delete(request, stack_id) - except Exception: - msg = _('Failed to delete stack.') - exceptions.handle(request, msg) - - def allowed(self, request, stack): - if stack is not None: - return stack.stack_status != 'DELETE_COMPLETE' - return True - - -class StacksUpdateRow(tables.Row): - ajax = True - - def can_be_selected(self, datum): - return datum.stack_status != 'DELETE_COMPLETE' - - def get_data(self, request, stack_id): - try: - stack = api.heat.stack_get(request, stack_id) - if stack.stack_status == 'DELETE_COMPLETE': - # returning 404 to the ajax call removes the - # row from the table on the ui - raise Http404 - return stack - except Http404: - raise - except Exception as e: - messages.error(request, e) - raise - - -class StacksFilterAction(tables.FilterAction): - filter_type = 'server' - filter_choices = (('name', _('Stack Name ='), True, _('Case-sensitive')), - ('id', _('Stack ID ='), True), - ('status', _('Status ='), True)) - - -class StacksTable(tables.DataTable): - STATUS_CHOICES = ( - ("Complete", True), - ("Failed", False), - ) - STACK_STATUS_DISPLAY_CHOICES = ( - ("init_in_progress", pgettext_lazy("current status of stack", - u"Init In Progress")), - ("init_complete", pgettext_lazy("current status of stack", - u"Init Complete")), - ("init_failed", pgettext_lazy("current status of stack", - u"Init Failed")), - ("create_in_progress", pgettext_lazy("current status of stack", - u"Create In Progress")), - ("create_complete", pgettext_lazy("current status of stack", - u"Create Complete")), - ("create_failed", pgettext_lazy("current status of stack", - u"Create Failed")), - ("delete_in_progress", pgettext_lazy("current status of stack", - u"Delete In Progress")), - ("delete_complete", pgettext_lazy("current status of stack", - u"Delete Complete")), - ("delete_failed", pgettext_lazy("current status of stack", - u"Delete Failed")), - ("update_in_progress", pgettext_lazy("current status of stack", - u"Update In Progress")), - ("update_complete", pgettext_lazy("current status of stack", - u"Update Complete")), - ("update_failed", pgettext_lazy("current status of stack", - u"Update Failed")), - ("rollback_in_progress", pgettext_lazy("current status of stack", - u"Rollback In Progress")), - ("rollback_complete", pgettext_lazy("current status of stack", - u"Rollback Complete")), - ("rollback_failed", pgettext_lazy("current status of stack", - u"Rollback Failed")), - ("suspend_in_progress", pgettext_lazy("current status of stack", - u"Suspend In Progress")), - ("suspend_complete", pgettext_lazy("current status of stack", - u"Suspend Complete")), - ("suspend_failed", pgettext_lazy("current status of stack", - u"Suspend Failed")), - ("resume_in_progress", pgettext_lazy("current status of stack", - u"Resume In Progress")), - ("resume_complete", pgettext_lazy("current status of stack", - u"Resume Complete")), - ("resume_failed", pgettext_lazy("current status of stack", - u"Resume Failed")), - ("adopt_in_progress", pgettext_lazy("current status of stack", - u"Adopt In Progress")), - ("adopt_complete", pgettext_lazy("current status of stack", - u"Adopt Complete")), - ("adopt_failed", pgettext_lazy("current status of stack", - u"Adopt Failed")), - ("snapshot_in_progress", pgettext_lazy("current status of stack", - u"Snapshot In Progress")), - ("snapshot_complete", pgettext_lazy("current status of stack", - u"Snapshot Complete")), - ("snapshot_failed", pgettext_lazy("current status of stack", - u"Snapshot Failed")), - ("check_in_progress", pgettext_lazy("current status of stack", - u"Check In Progress")), - ("check_complete", pgettext_lazy("current status of stack", - u"Check Complete")), - ("check_failed", pgettext_lazy("current status of stack", - u"Check Failed")), - ) - name = tables.Column("stack_name", - verbose_name=_("Stack Name"), - link="horizon:project:stacks:detail",) - created = tables.Column("creation_time", - verbose_name=_("Created"), - filters=(filters.parse_isotime, - filters.timesince_sortable), - attrs={'data-type': 'timesince'}) - updated = tables.Column("updated_time", - verbose_name=_("Updated"), - filters=(filters.parse_isotime, - filters.timesince_or_never)) - status = tables.Column("status", - hidden=True, - status=True, - status_choices=STATUS_CHOICES) - - stack_status = tables.Column("stack_status", - verbose_name=_("Status"), - display_choices=STACK_STATUS_DISPLAY_CHOICES) - - def get_object_display(self, stack): - return stack.stack_name - - class Meta(object): - name = "stacks" - verbose_name = _("Stacks") - pagination_param = 'stack_marker' - status_columns = ["status", ] - row_class = StacksUpdateRow - table_actions_menu = (CheckStack, - SuspendStack, - ResumeStack,) - table_actions = (LaunchStack, - PreviewStack, - DeleteStack, - StacksFilterAction,) - row_actions = (CheckStack, - SuspendStack, - ResumeStack, - ChangeStackTemplate, - DeleteStack,) - - -def get_resource_url(obj): - if obj.physical_resource_id == obj.stack_id: - return None - return urlresolvers.reverse('horizon:project:stacks:resource', - args=(obj.stack_id, obj.resource_name)) - - -class EventsTable(tables.DataTable): - - logical_resource = tables.Column('resource_name', - verbose_name=_("Stack Resource"), - link=get_resource_url) - physical_resource = tables.Column('physical_resource_id', - verbose_name=_("Resource")) - timestamp = tables.Column('event_time', - verbose_name=_("Time Since Event"), - filters=(filters.parse_isotime, - filters.timesince_or_never)) - status = tables.Column("resource_status", - filters=(title, filters.replace_underscores), - verbose_name=_("Status"),) - - statusreason = tables.Column("resource_status_reason", - verbose_name=_("Status Reason"),) - - class Meta(object): - name = "events" - verbose_name = _("Stack Events") - - -class ResourcesUpdateRow(tables.Row): - ajax = True - - def get_data(self, request, resource_name): - try: - stack = self.table.stack - stack_identifier = '%s/%s' % (stack.stack_name, stack.id) - return api.heat.resource_get( - request, stack_identifier, resource_name) - except exc.HTTPNotFound: - # returning 404 to the ajax call removes the - # row from the table on the ui - raise Http404 - except Exception as e: - messages.error(request, e) - - -class ResourcesTable(tables.DataTable): - class StatusColumn(tables.Column): - def get_raw_data(self, datum): - return datum.resource_status.partition("_")[2] - - STATUS_CHOICES = ( - ("Complete", True), - ("Failed", False), - ) - STATUS_DISPLAY_CHOICES = StacksTable.STACK_STATUS_DISPLAY_CHOICES - - logical_resource = tables.Column('resource_name', - verbose_name=_("Stack Resource"), - link=get_resource_url) - physical_resource = tables.Column('physical_resource_id', - verbose_name=_("Resource"), - link=mappings.resource_to_url) - resource_type = tables.Column("resource_type", - verbose_name=_("Stack Resource Type"),) - updated_time = tables.Column('updated_time', - verbose_name=_("Date Updated"), - filters=(filters.parse_isotime, - filters.timesince_or_never)) - status = tables.Column("resource_status", - verbose_name=_("Status"), - display_choices=STATUS_DISPLAY_CHOICES) - - statusreason = tables.Column("resource_status_reason", - verbose_name=_("Status Reason"),) - - status_hidden = StatusColumn("status", - hidden=True, - status=True, - status_choices=STATUS_CHOICES) - - def __init__(self, request, data=None, - needs_form_wrapper=None, **kwargs): - super(ResourcesTable, self).__init__( - request, data, needs_form_wrapper, **kwargs) - self.stack = kwargs['stack'] - - def get_object_id(self, datum): - return datum.resource_name - - class Meta(object): - name = "resources" - verbose_name = _("Stack Resources") - status_columns = ["status_hidden", ] - row_class = ResourcesUpdateRow diff --git a/openstack_dashboard/dashboards/project/stacks/tabs.py b/openstack_dashboard/dashboards/project/stacks/tabs.py deleted file mode 100644 index 1e6c92be1f..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/tabs.py +++ /dev/null @@ -1,173 +0,0 @@ -# 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 logging - -from django.utils.translation import ugettext_lazy as _ - -from horizon import messages -from horizon import tabs -from openstack_dashboard import api -from openstack_dashboard import policy - -from openstack_dashboard.dashboards.project.stacks \ - import api as project_api -from openstack_dashboard.dashboards.project.stacks import mappings -from openstack_dashboard.dashboards.project.stacks \ - import tables as project_tables - - -LOG = logging.getLogger(__name__) - - -class StackTopologyTab(tabs.Tab): - name = _("Topology") - slug = "topology" - template_name = "project/stacks/_detail_topology.html" - preload = False - - def allowed(self, request): - return policy.check( - (("orchestration", "stacks:template"), - ("orchestration", "stacks:lookup"), - ("orchestration", "stacks:show"), - ("orchestration", "resource:index"),), - request) - - def get_context_data(self, request): - context = {} - stack = self.tab_group.kwargs['stack'] - context['stack_id'] = stack.id - context['d3_data'] = project_api.d3_data(request, stack_id=stack.id) - return context - - -class StackOverviewTab(tabs.Tab): - name = _("Overview") - slug = "overview" - template_name = "project/stacks/_detail_overview.html" - - def allowed(self, request): - return policy.check( - (("orchestration", "stacks:template"), - ("orchestration", "stacks:lookup"), - ("orchestration", "stacks:show"),), - request) - - def get_context_data(self, request): - return {"stack": self.tab_group.kwargs['stack']} - - -class ResourceOverviewTab(tabs.Tab): - name = _("Overview") - slug = "resource_overview" - template_name = "project/stacks/_resource_overview.html" - - def get_context_data(self, request): - resource = self.tab_group.kwargs['resource'] - resource_url = mappings.resource_to_url(resource) - return { - "resource": resource, - "resource_url": resource_url, - "metadata": self.tab_group.kwargs['metadata']} - - -class StackEventsTab(tabs.Tab): - name = _("Events") - slug = "events" - template_name = "project/stacks/_detail_events.html" - preload = False - - def allowed(self, request): - return policy.check( - (("orchestration", "stacks:template"), - ("orchestration", "stacks:lookup"), - ("orchestration", "stacks:show"), - ("orchestration", "events:index"),), - request) - - def get_context_data(self, request): - stack = self.tab_group.kwargs['stack'] - try: - stack_identifier = '%s/%s' % (stack.stack_name, stack.id) - events = api.heat.events_list(self.request, stack_identifier) - LOG.debug('got events %s', events) - # The stack id is needed to generate the resource URL. - for event in events: - event.stack_id = stack.id - except Exception: - events = [] - messages.error(request, _( - 'Unable to get events for stack "%s".') % stack.stack_name) - return {"stack": stack, - "table": project_tables.EventsTable(request, data=events), } - - -class StackResourcesTab(tabs.Tab): - name = _("Resources") - slug = "resources" - template_name = "project/stacks/_detail_resources.html" - preload = False - - def allowed(self, request): - return policy.check( - (("orchestration", "stacks:template"), - ("orchestration", "stacks:lookup"), - ("orchestration", "stacks:show"), - ("orchestration", "resource:index"),), - request) - - def get_context_data(self, request): - stack = self.tab_group.kwargs['stack'] - try: - stack_identifier = '%s/%s' % (stack.stack_name, stack.id) - resources = api.heat.resources_list(self.request, stack_identifier) - LOG.debug('got resources %s', resources) - # The stack id is needed to generate the resource URL. - for r in resources: - r.stack_id = stack.id - except Exception: - resources = [] - messages.error(request, _( - 'Unable to get resources for stack "%s".') % stack.stack_name) - return {"stack": stack, - "table": project_tables.ResourcesTable( - request, data=resources, stack=stack), } - - -class StackTemplateTab(tabs.Tab): - name = _("Template") - slug = "stack_template" - template_name = "project/stacks/_stack_template.html" - - def allowed(self, request): - return policy.check( - (("orchestration", "stacks:template"), - ("orchestration", "stacks:lookup"), - ("orchestration", "stacks:show"),), - request) - - def get_context_data(self, request): - return {"stack_template": self.tab_group.kwargs['stack_template']} - - -class StackDetailTabs(tabs.TabGroup): - slug = "stack_details" - tabs = (StackTopologyTab, StackOverviewTab, StackResourcesTab, - StackEventsTab, StackTemplateTab) - sticky = True - - -class ResourceDetailTabs(tabs.TabGroup): - slug = "resource_details" - tabs = (ResourceOverviewTab,) - sticky = True diff --git a/openstack_dashboard/dashboards/project/stacks/template_versions/__init__.py b/openstack_dashboard/dashboards/project/stacks/template_versions/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openstack_dashboard/dashboards/project/stacks/template_versions/panel.py b/openstack_dashboard/dashboards/project/stacks/template_versions/panel.py deleted file mode 100644 index 3492d59a27..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/panel.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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. - -from django.utils.translation import ugettext_lazy as _ - -import horizon - - -class TemplateVersions(horizon.Panel): - name = _("Template Versions") - slug = "stacks.template_versions" - permissions = ('openstack.services.orchestration',) - policy_rules = (("orchestration", "stacks:list_template_versions"),) diff --git a/openstack_dashboard/dashboards/project/stacks/template_versions/tables.py b/openstack_dashboard/dashboards/project/stacks/template_versions/tables.py deleted file mode 100644 index 8c63d14cb2..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/tables.py +++ /dev/null @@ -1,52 +0,0 @@ -# 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. - -from django.template import defaultfilters as filters -from django.utils.translation import ugettext_lazy as _ - -from horizon import tables - - -class TemplateVersionsTable(tables.DataTable): - version = tables.Column( - "version", - verbose_name=_("Version"), - link="horizon:project:stacks.template_versions:details",) - type = tables.Column( - "type", - verbose_name=_("Type"), - filters=(filters.upper,)) - - def get_object_id(self, template_versions): - return template_versions.version - - class Meta(object): - name = "template_versions" - table_actions = (tables.FilterAction,) - verbose_name = _("Template Versions") - table_actions = (tables.FilterAction,) - multi_select = False - - -class TemplateFunctionsTable(tables.DataTable): - functions = tables.Column('functions', verbose_name=_("Function")) - description = tables.Column('description', verbose_name=_("Description")) - - def get_object_id(self, template_functions): - return template_functions.functions - - class Meta(object): - name = "template_functions" - verbose_name = _("Template Functions") - table_actions = (tables.FilterAction,) - multi_select = False diff --git a/openstack_dashboard/dashboards/project/stacks/template_versions/tabs.py b/openstack_dashboard/dashboards/project/stacks/template_versions/tabs.py deleted file mode 100644 index 9cb90968c3..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/tabs.py +++ /dev/null @@ -1,51 +0,0 @@ -# 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. - -from django.utils.translation import ugettext_lazy as _ - -from horizon import messages -from horizon import tabs -from openstack_dashboard import api -from openstack_dashboard import policy - -from openstack_dashboard.dashboards.project.stacks.template_versions \ - import tables as project_tables - - -class TemplateFunctionsTab(tabs.Tab): - name = _("Template Functions") - slug = "template_functions" - template_name = "project/stacks.template_versions/_details.html" - preload = False - - def allowed(self, request): - return policy.check( - (("orchestration", "stacks:list_template_functions"),), - request) - - def get_context_data(self, request): - template_version = self.tab_group.kwargs['template_version'] - try: - template_functions = api.heat.template_function_list( - self.request, template_version) - except Exception: - template_functions = [] - messages.error(request, _('Unable to get functions for template ' - 'version "%s".') % template_version) - return {"table": project_tables.TemplateFunctionsTable( - request, data=template_functions), } - - -class TemplateVersionDetailsTabs(tabs.TabGroup): - slug = "template_version_details" - tabs = (TemplateFunctionsTab,) diff --git a/openstack_dashboard/dashboards/project/stacks/template_versions/templates/stacks.template_versions/_details.html b/openstack_dashboard/dashboards/project/stacks/template_versions/templates/stacks.template_versions/_details.html deleted file mode 100644 index 9976f88dd9..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/templates/stacks.template_versions/_details.html +++ /dev/null @@ -1,3 +0,0 @@ -{% load i18n %} - -{{ table.render }} diff --git a/openstack_dashboard/dashboards/project/stacks/template_versions/templates/stacks.template_versions/index.html b/openstack_dashboard/dashboards/project/stacks/template_versions/templates/stacks.template_versions/index.html deleted file mode 100644 index 0a39d4a5e8..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/templates/stacks.template_versions/index.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Template Versions" %}{% endblock %} - -{% block main %} - {{ table.render }} -{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/template_versions/tests.py b/openstack_dashboard/dashboards/project/stacks/template_versions/tests.py deleted file mode 100644 index fcace15f47..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/tests.py +++ /dev/null @@ -1,79 +0,0 @@ -# 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. - -from django.core.urlresolvers import reverse -from django import http - -from mox3.mox import IsA - -from openstack_dashboard import api -from openstack_dashboard.test import helpers as test - - -class TemplateVersionsTests(test.TestCase): - INDEX_URL = reverse('horizon:project:stacks.template_versions:index') - - @test.create_stubs({api.heat: ('template_version_list',)}) - def test_index(self): - api.heat.template_version_list( - IsA(http.HttpRequest)).AndReturn(self.template_versions.list()) - self.mox.ReplayAll() - - res = self.client.get(self.INDEX_URL) - self.assertTemplateUsed( - res, 'project/stacks.template_versions/index.html') - self.assertContains(res, 'HeatTemplateFormatVersion.2012-12-12') - - @test.create_stubs({api.heat: ('template_version_list',)}) - def test_index_exception(self): - api.heat.template_version_list( - IsA(http.HttpRequest)).AndRaise(self.exceptions.heat) - self.mox.ReplayAll() - - res = self.client.get(self.INDEX_URL) - self.assertTemplateUsed( - res, 'project/stacks.template_versions/index.html') - self.assertEqual(len(res.context['table'].data), 0) - self.assertMessageCount(res, error=1) - - @test.create_stubs({api.heat: ('template_function_list',)}) - def test_detail_view(self): - t_version = self.template_versions.first().version - t_functions = self.template_functions.list() - - api.heat.template_function_list( - IsA(http.HttpRequest), t_version).AndReturn(t_functions) - self.mox.ReplayAll() - - url = reverse('horizon:project:stacks.template_versions:details', - args=[t_version]) - res = self.client.get(url) - - self.assertTemplateUsed(res, 'horizon/common/_detail.html') - self.assertNoMessages() - - @test.create_stubs({api.heat: ('template_function_list',)}) - def test_detail_view_with_exception(self): - t_version = self.template_versions.first().version - - api.heat.template_function_list( - IsA(http.HttpRequest), t_version).AndRaise(self.exceptions.heat) - self.mox.ReplayAll() - - url = reverse('horizon:project:stacks.template_versions:details', - args=[t_version]) - res = self.client.get(url) - - self.assertTemplateUsed(res, 'horizon/common/_detail.html') - self.assertEqual(len(res.context['table'].data), 0) - self.assertMessageCount(res, error=1) diff --git a/openstack_dashboard/dashboards/project/stacks/template_versions/urls.py b/openstack_dashboard/dashboards/project/stacks/template_versions/urls.py deleted file mode 100644 index 5e2bbc2e58..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/urls.py +++ /dev/null @@ -1,24 +0,0 @@ -# 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. - -from django.conf.urls import url - -from openstack_dashboard.dashboards.project.stacks.template_versions \ - import views - - -urlpatterns = [ - url(r'^$', views.TemplateVersionsView.as_view(), name='index'), - url(r'^(?P[^/]+)/$', - views.DetailView.as_view(), name='details'), -] diff --git a/openstack_dashboard/dashboards/project/stacks/template_versions/views.py b/openstack_dashboard/dashboards/project/stacks/template_versions/views.py deleted file mode 100644 index 22eabd77fa..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/views.py +++ /dev/null @@ -1,61 +0,0 @@ -# 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. - -from django.core.urlresolvers import reverse -from django.utils.translation import ugettext_lazy as _ - -from horizon import exceptions -from horizon import tables -from horizon import tabs - -from openstack_dashboard import api -import openstack_dashboard.dashboards.project.stacks.template_versions.tables \ - as project_tables -import openstack_dashboard.dashboards.project.stacks.template_versions.tabs \ - as project_tabs - - -class TemplateVersionsView(tables.DataTableView): - table_class = project_tables.TemplateVersionsTable - template_name = 'project/stacks.template_versions/index.html' - page_title = _("Template Versions") - - def get_data(self): - try: - template_versions = sorted( - api.heat.template_version_list(self.request), - key=lambda template_version: template_version.version) - except Exception: - template_versions = [] - msg = _('Unable to retrieve template versions.') - exceptions.handle(self.request, msg) - return template_versions - - -class DetailView(tabs.TabView): - tab_group_class = project_tabs.TemplateVersionDetailsTabs - template_name = 'horizon/common/_detail.html' - page_title = "{{ template_version }}" - - def get_template_version(self, request, **kwargs): - try: - template_functions = api.heat.template_function_list( - request, kwargs['template_version']) - return template_functions - except Exception: - msg = _('Unable to retrieve template functions.') - exceptions.handle(request, msg, redirect=self.get_redirect_url()) - - @staticmethod - def get_redirect_url(): - return reverse('horizon:project:stacks.template_versions:index') diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_change_template.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_change_template.html deleted file mode 100644 index bd31f700fd..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_change_template.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "horizon/common/_modal_form.html" %} -{% load i18n %} -{% block form_attrs %}enctype="multipart/form-data"{% endblock %} -{% block modal-body-right %} -

{% trans "Description:" %}

-

{% trans "Use one of the available template source options to specify the template to be used in creating this stack." %}

-{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_create.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_create.html deleted file mode 100644 index 19a3f1b217..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_create.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "horizon/common/_modal_form.html" %} -{% load i18n %} -{% block modal-body-right %} -

{% trans "Description:" %}

-

{% trans "Create a new stack with the provided values." %}

-{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_events.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_events.html deleted file mode 100644 index 9976f88dd9..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_events.html +++ /dev/null @@ -1,3 +0,0 @@ -{% load i18n %} - -{{ table.render }} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_overview.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_overview.html deleted file mode 100644 index 56534bba3e..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_overview.html +++ /dev/null @@ -1,55 +0,0 @@ -{% load i18n sizeformat %} - -
-
-
{% trans "Name" %}
-
{{ stack.stack_name }}
-
{% trans "ID" %}
-
{{ stack.id }}
-
{% trans "Description" %}
-
{{ stack.description }}
-
- -

{% trans "Status" %}

-
-
-
{% trans "Created" %}
-
{{ stack.creation_time|parse_isotime|timesince_or_never }}
-
{% trans "Last Updated" %}
-
{{ stack.updated_time|parse_isotime|timesince_or_never }}
-
{% trans "Status" %}
-
- {% blocktrans with stack_status_title=stack.stack_status|title stack_status_reason=stack.stack_status_reason %}{{ stack_status_title }}: {{ stack_status_reason }}{% endblocktrans %} -
-
- -

{% trans "Outputs" %}

-
-
- {% for output in stack.outputs %} -
{{ output.output_key }}
-
{{ output.description }}
-
- {{ output.output_value|stack_output }} -
- {% endfor %} -
- -

{% trans "Stack Parameters" %}

-
-
- {% for key, value in stack.parameters.items %} -
{{ key }}
-
{{ value }}
- {% endfor %} -
- -

{% trans "Launch Parameters" %}

-
-
-
{% trans "Timeout" %}
-
{{ stack.timeout_mins }} {% trans "Minutes" %}
-
{% trans "Rollback" %}
-
{% if stack.disable_rollback %}{% trans "Disabled" %}{% else %}{% trans "Enabled" %}{% endif %}
-
-
diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_resources.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_resources.html deleted file mode 100644 index 9976f88dd9..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_resources.html +++ /dev/null @@ -1,3 +0,0 @@ -{% load i18n %} - -{{ table.render }} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_topology.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_topology.html deleted file mode 100644 index d906ceadd9..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_topology.html +++ /dev/null @@ -1,9 +0,0 @@ -{% load i18n sizeformat %} - -
-
-
-
-
-
-
\ No newline at end of file diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview.html deleted file mode 100644 index 2402478753..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "horizon/common/_modal_form.html" %} -{% load i18n %} -{% block modal-body-right %} -

{% trans "Description:" %}

-

{% trans "Preview a new stack with the provided values." %}

-{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html deleted file mode 100644 index 12d131a488..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html +++ /dev/null @@ -1,58 +0,0 @@ -{% extends "horizon/common/_modal.html" %} -{% load i18n %} - -{% block modal-header %}{% trans "Stack Preview" %}{% endblock %} - -{% block modal-body %} -
-
-
- {% for key, value in stack_preview.items %} - {% if key != 'parameters' and key != 'resources' and key != 'links' %} -
{{ key }}
-
{{ value }}
- {% endif %} - {% endfor %} -
- - {% if stack_preview.parameters %} -
{% trans "Parameters" %}
-
-
- {% for key, value in stack_preview.parameters.items %} -
{{ key }}
-
{{ value }}
- {% endfor %} -
- {% endif %} - - {% if stack_preview.links %} -
{% trans "Links" %}
-
- {% for link in stack_preview.links %} -
-
{{ link.rel }}
-
{{ link.href }}
-
- {% endfor %} - {% endif %} - - {% if stack_preview.resources %} -
{% trans "Resources" %}
- {% for resource in stack_preview.resources %} -
-
- {% for key, value in resource.items %} -
{{ key }}
-
{{ value }}
- {% endfor %} -
- {% endfor %} - {% endif %} -
-
-{% endblock %} - -{% block modal-footer %} - {% trans "Close" %} -{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_template.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_template.html deleted file mode 100644 index 76f14b0c7f..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_template.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "horizon/common/_modal_form.html" %} -{% load i18n %} -{% block form_attrs %}enctype="multipart/form-data"{% endblock %} -{% block modal-body-right %} -

{% trans "Description:" %}

-

{% trans "Use one of the available template source options to specify the template to be used in previewing this stack." %}

-{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_resource_info.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_resource_info.html deleted file mode 100644 index 989c0127bb..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_resource_info.html +++ /dev/null @@ -1,10 +0,0 @@ -

{{ resource.resource_name }}

- -{% if resource.resource_status == 'CREATE_FAILED' %} -

{{ resource.resource_status_desc }}

-

{{ resource.resource_status_reason }}

-{% else %} -

{{ resource.resource_status_desc }}

-{% endif %} - -

{{ resource.resource_type }}

\ No newline at end of file diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_resource_overview.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_resource_overview.html deleted file mode 100644 index ef1ca25e5d..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_resource_overview.html +++ /dev/null @@ -1,38 +0,0 @@ -{% load i18n sizeformat %} - -
-
-
{% trans "Stack Resource ID" %}
-
{{ resource.resource_name }}
-
{% trans "Resource ID" %}
-
- {% if resource_url %} - - {{ resource.physical_resource_id }} - - {% else %} - {{ resource.physical_resource_id }} - {% endif %} -
-
{% trans "Stack Resource Type" %}
-
{{ resource.resource_type }}
-
{% trans "Description" %}
-
{{ resource.description }}
-
- -

{% trans "Status" %}

-
-
-
{% trans "Last Updated" %}
-
{{ resource.updated_time|parse_isotime|timesince_or_never }}
-
{% trans "Status" %}
-
- {% blocktrans with resource_status=resource.resource_status|title|replace_underscores resource_status_reason=resource.resource_status_reason %}{{ resource_status }}: {{ resource_status_reason }}{% endblocktrans %} -
-
- -

{% trans "Resource Metadata" %}

-
-
{{ metadata }}
-  
-
diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_select_template.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_select_template.html deleted file mode 100644 index bd31f700fd..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_select_template.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "horizon/common/_modal_form.html" %} -{% load i18n %} -{% block form_attrs %}enctype="multipart/form-data"{% endblock %} -{% block modal-body-right %} -

{% trans "Description:" %}

-

{% trans "Use one of the available template source options to specify the template to be used in creating this stack." %}

-{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_stack_info.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_stack_info.html deleted file mode 100644 index 6e657cb3b3..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_stack_info.html +++ /dev/null @@ -1,14 +0,0 @@ - -
-

{{ stack.stack_name }}

-

{{ stack.stack_status_desc }}

-
-
-{% if stack.stack_status == 'CREATE_FAILED' %} -

{{ stack.stack_status_reason }}

-{% endif %} -{% for output in stack.outputs %} - {% if output.output_key == 'WebsiteURL' %} - {{ output.description }} - {% endif %} -{% endfor %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_stack_template.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_stack_template.html deleted file mode 100644 index 17eed2085c..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_stack_template.html +++ /dev/null @@ -1,5 +0,0 @@ -{% load i18n sizeformat %} - -
-
{{ stack_template }}
-
diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_update.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/_update.html deleted file mode 100644 index f3b63377c3..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_update.html +++ /dev/null @@ -1,6 +0,0 @@ -{% extends "horizon/common/_modal_form.html" %} -{% load i18n %} -{% block modal-body-right %} -

{% trans "Description:" %}

-

{% trans "Update a stack with the provided values. Please note that any encrypted parameters, such as passwords, will be reset to default if you do not change them here." %}

-{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/change_template.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/change_template.html deleted file mode 100644 index 8bf6bfdf4f..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/change_template.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Change Template" %}{% endblock %} - -{% block main %} - {% include 'project/stacks/_change_template.html' %} -{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/create.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/create.html deleted file mode 100644 index 7881a96a4a..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/create.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Launch Stack" %}{% endblock %} - -{% block main %} - {% include 'project/stacks/_create.html' %} -{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/preview.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/preview.html deleted file mode 100644 index ded74a40c2..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/preview.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Preview Stack" %}{% endblock %} - -{% block main %} - {% include 'project/stacks/_preview.html' %} -{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/preview_details.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/preview_details.html deleted file mode 100644 index 8d86df5405..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/preview_details.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Preview Stack Details" %}{% endblock %} - -{% block main %} - {% include 'project/stacks/_preview_details.html' %} -{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/preview_template.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/preview_template.html deleted file mode 100644 index e20931cf7f..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/preview_template.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Preview Template" %}{% endblock %} - -{% block main %} - {% include 'project/stacks/_preview_template.html' %} -{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/select_template.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/select_template.html deleted file mode 100644 index efe0b444f3..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/select_template.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Select Template" %}{% endblock %} - -{% block main %} - {% include 'project/stacks/_select_template.html' %} -{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/templates/stacks/update.html b/openstack_dashboard/dashboards/project/stacks/templates/stacks/update.html deleted file mode 100644 index 61b9766fad..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/update.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Update Stack Parameters" %}{% endblock %} - -{% block main %} - {% include 'project/stacks/_update.html' %} -{% endblock %} diff --git a/openstack_dashboard/dashboards/project/stacks/tests.py b/openstack_dashboard/dashboards/project/stacks/tests.py deleted file mode 100644 index d646a51d15..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/tests.py +++ /dev/null @@ -1,1003 +0,0 @@ -# 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 json -import re - -import django -from django.conf import settings -from django.core import exceptions -from django.core.urlresolvers import reverse -from django import http -from django.test.utils import override_settings -from django.utils import html -from heatclient.common import template_format as hc_format -from mox3.mox import IsA -import six - -from openstack_dashboard import api -from openstack_dashboard.dashboards.project.stacks import api as project_api -from openstack_dashboard.dashboards.project.stacks import forms -from openstack_dashboard.dashboards.project.stacks import mappings -from openstack_dashboard.dashboards.project.stacks import tables -from openstack_dashboard.test import helpers as test - - -INDEX_TEMPLATE = 'horizon/common/_data_table_view.html' -INDEX_URL = reverse('horizon:project:stacks:index') -DETAIL_URL = 'horizon:project:stacks:detail' - - -class MockResource(object): - def __init__(self, resource_type, physical_resource_id): - self.resource_type = resource_type - self.physical_resource_id = physical_resource_id - - -class MappingsTests(test.TestCase): - - def test_mappings(self): - - def assertMappingUrl(url, resource_type, physical_resource_id): - mock = MockResource(resource_type, physical_resource_id) - mock_url = mappings.resource_to_url(mock) - self.assertEqual(url, mock_url) - - assertMappingUrl( - '/project/networks/subnets/aaa/detail', - 'OS::Neutron::Subnet', - 'aaa') - assertMappingUrl( - None, - 'OS::Neutron::Subnet', - None) - assertMappingUrl( - None, - None, - None) - assertMappingUrl( - None, - 'AWS::AutoScaling::LaunchConfiguration', - 'aaa') - assertMappingUrl( - '/project/instances/aaa/', - 'AWS::EC2::Instance', - 'aaa') - assertMappingUrl( - '/project/containers/container/aaa/', - 'OS::Swift::Container', - 'aaa') - assertMappingUrl( - None, - 'Foo::Bar::Baz', - 'aaa') - assertMappingUrl( - '/project/instances/aaa/', - 'OS::Nova::Server', - 'aaa') - assertMappingUrl( - '/project/stacks/stack/aaa/', - 'OS::Heat::ResourceGroup', - 'aaa') - - def test_stack_output(self): - self.assertEqual(u'
foo
', mappings.stack_output('foo')) - self.assertEqual(u'', mappings.stack_output(None)) - - outputs = ['one', 'two', 'three'] - # On Python 3, the pretty JSON output doesn't add space before newline - if six.PY3: - expected_text = """[\n "one",\n "two",\n "three"\n]""" - else: - expected_text = """[\n "one", \n "two", \n "three"\n]""" - - self.assertEqual(u'
%s
' % html.escape(expected_text), - mappings.stack_output(outputs)) - - outputs = {'foo': 'bar'} - expected_text = """{\n "foo": "bar"\n}""" - self.assertEqual(u'
%s
' % html.escape(expected_text), - mappings.stack_output(outputs)) - - self.assertEqual( - u'' - 'http://www.example.com/foo', - mappings.stack_output('http://www.example.com/foo')) - - -class StackTests(test.TestCase): - - @override_settings(API_RESULT_PAGE_SIZE=2) - @test.create_stubs({api.heat: ('stacks_list',)}) - def test_index_paginated(self): - stacks = self.stacks.list()[:5] - filters = {} - api.heat.stacks_list(IsA(http.HttpRequest), - marker=None, - paginate=True, - sort_dir='desc', - filters=filters) \ - .AndReturn([stacks, True, True]) - api.heat.stacks_list(IsA(http.HttpRequest), - marker=None, - paginate=True, - sort_dir='desc', - filters=filters) \ - .AndReturn([stacks[:2], True, True]) - api.heat.stacks_list(IsA(http.HttpRequest), - marker=stacks[2].id, - paginate=True, - sort_dir='desc', - filters=filters) \ - .AndReturn([stacks[2:4], True, True]) - api.heat.stacks_list(IsA(http.HttpRequest), - marker=stacks[4].id, - paginate=True, - sort_dir='desc', - filters=filters) \ - .AndReturn([stacks[4:], True, True]) - self.mox.ReplayAll() - - url = reverse('horizon:project:stacks:index') - res = self.client.get(url) - # get all - self.assertEqual(len(res.context['stacks_table'].data), - len(stacks)) - self.assertTemplateUsed(res, INDEX_TEMPLATE) - - res = self.client.get(url) - # get first page with 2 items - self.assertEqual(len(res.context['stacks_table'].data), - settings.API_RESULT_PAGE_SIZE) - - url = "%s?%s=%s" % (reverse('horizon:project:stacks:index'), - tables.StacksTable._meta.pagination_param, - stacks[2].id) - res = self.client.get(url) - # get second page (items 2-4) - self.assertEqual(len(res.context['stacks_table'].data), - settings.API_RESULT_PAGE_SIZE) - - url = "%s?%s=%s" % (reverse('horizon:project:stacks:index'), - tables.StacksTable._meta.pagination_param, - stacks[4].id) - res = self.client.get(url) - # get third page (item 5) - self.assertEqual(len(res.context['stacks_table'].data), - 1) - - @override_settings(API_RESULT_PAGE_SIZE=2) - @test.create_stubs({api.heat: ('stacks_list',)}) - def test_index_prev_paginated(self): - stacks = self.stacks.list()[:3] - filters = {} - api.heat.stacks_list(IsA(http.HttpRequest), - marker=None, - paginate=True, - sort_dir='desc', - filters=filters) \ - .AndReturn([stacks, True, False]) - api.heat.stacks_list(IsA(http.HttpRequest), - marker=None, - paginate=True, - sort_dir='desc', - filters=filters) \ - .AndReturn([stacks[:2], True, True]) - api.heat.stacks_list(IsA(http.HttpRequest), - marker=stacks[2].id, - paginate=True, - sort_dir='desc', - filters=filters) \ - .AndReturn([stacks[2:], True, True]) - api.heat.stacks_list(IsA(http.HttpRequest), - marker=stacks[2].id, - paginate=True, - sort_dir='asc', - filters=filters) \ - .AndReturn([stacks[:2], True, True]) - self.mox.ReplayAll() - - url = reverse('horizon:project:stacks:index') - res = self.client.get(url) - # get all - self.assertEqual(len(res.context['stacks_table'].data), - len(stacks)) - self.assertTemplateUsed(res, INDEX_TEMPLATE) - - res = self.client.get(url) - # get first page with 2 items - self.assertEqual(len(res.context['stacks_table'].data), - settings.API_RESULT_PAGE_SIZE) - - url = "%s?%s=%s" % (reverse('horizon:project:stacks:index'), - tables.StacksTable._meta.pagination_param, - stacks[2].id) - res = self.client.get(url) - # get second page (item 3) - self.assertEqual(len(res.context['stacks_table'].data), 1) - - url = "%s?%s=%s" % (reverse('horizon:project:stacks:index'), - tables.StacksTable._meta.prev_pagination_param, - stacks[2].id) - res = self.client.get(url) - # prev back to get first page with 2 pages - self.assertEqual(len(res.context['stacks_table'].data), - settings.API_RESULT_PAGE_SIZE) - - @test.create_stubs({api.heat: ('stack_create', 'template_validate'), - api.neutron: ('network_list_for_tenant', )}) - def test_launch_stack(self): - template = self.stack_templates.first() - stack = self.stacks.first() - - api.heat.template_validate(IsA(http.HttpRequest), - files={}, - template=hc_format.parse(template.data)) \ - .AndReturn(json.loads(template.validate)) - - api.heat.stack_create(IsA(http.HttpRequest), - stack_name=stack.stack_name, - timeout_mins=60, - disable_rollback=True, - template=None, - parameters=IsA(dict), - password='password', - files=None) - api.neutron.network_list_for_tenant(IsA(http.HttpRequest), - self.tenant.id) \ - .AndReturn(self.networks.list()) - api.neutron.network_list_for_tenant(IsA(http.HttpRequest), - self.tenant.id) \ - .AndReturn(self.networks.list()) - - self.mox.ReplayAll() - - url = reverse('horizon:project:stacks:select_template') - res = self.client.get(url) - self.assertTemplateUsed(res, 'project/stacks/select_template.html') - - form_data = {'template_source': 'raw', - 'template_data': template.data, - 'method': forms.TemplateForm.__name__} - res = self.client.post(url, form_data) - self.assertTemplateUsed(res, 'project/stacks/create.html') - - url = reverse('horizon:project:stacks:launch') - form_data = {'template_source': 'raw', - 'template_data': template.data, - 'password': 'password', - 'parameters': template.validate, - 'stack_name': stack.stack_name, - "timeout_mins": 60, - "disable_rollback": True, - "__param_DBUsername": "admin", - "__param_LinuxDistribution": "F17", - "__param_InstanceType": "m1.small", - "__param_KeyName": "test", - "__param_DBPassword": "admin", - "__param_DBRootPassword": "admin", - "__param_DBName": "wordpress", - "__param_Network": self.networks.list()[0]['id'], - 'method': forms.CreateStackForm.__name__} - res = self.client.post(url, form_data) - self.assertRedirectsNoFollow(res, INDEX_URL) - - @test.create_stubs({api.heat: ('stack_create', 'template_validate'), - api.neutron: ('network_list_for_tenant', )}) - def test_launch_stack_with_environment(self): - template = self.stack_templates.first() - environment = self.stack_environments.first() - stack = self.stacks.first() - - api.heat.template_validate(IsA(http.HttpRequest), - files={}, - template=hc_format.parse(template.data), - environment=environment.data) \ - .AndReturn(json.loads(template.validate)) - - api.heat.stack_create(IsA(http.HttpRequest), - stack_name=stack.stack_name, - timeout_mins=60, - disable_rollback=True, - template=None, - environment=environment.data, - parameters=IsA(dict), - password='password', - files=None) - api.neutron.network_list_for_tenant(IsA(http.HttpRequest), - self.tenant.id) \ - .AndReturn(self.networks.list()) - api.neutron.network_list_for_tenant(IsA(http.HttpRequest), - self.tenant.id) \ - .AndReturn(self.networks.list()) - - self.mox.ReplayAll() - - url = reverse('horizon:project:stacks:select_template') - res = self.client.get(url) - self.assertTemplateUsed(res, 'project/stacks/select_template.html') - - form_data = {'template_source': 'raw', - 'template_data': template.data, - 'environment_source': 'raw', - 'environment_data': environment.data, - 'method': forms.TemplateForm.__name__} - res = self.client.post(url, form_data) - self.assertTemplateUsed(res, 'project/stacks/create.html') - - url = reverse('horizon:project:stacks:launch') - form_data = {'template_source': 'raw', - 'template_data': template.data, - 'environment_source': 'raw', - 'environment_data': environment.data, - 'password': 'password', - 'parameters': template.validate, - 'stack_name': stack.stack_name, - "timeout_mins": 60, - "disable_rollback": True, - "__param_DBUsername": "admin", - "__param_LinuxDistribution": "F17", - "__param_InstanceType": "m1.small", - "__param_KeyName": "test", - "__param_DBPassword": "admin", - "__param_DBRootPassword": "admin", - "__param_DBName": "wordpress", - "__param_Network": self.networks.list()[0]['id'], - 'method': forms.CreateStackForm.__name__} - res = self.client.post(url, form_data) - self.assertRedirectsNoFollow(res, INDEX_URL) - - @test.create_stubs({api.heat: ('template_validate',)}) - def test_launch_stack_with_hidden_parameters(self): - template = { - 'data': ('heat_template_version: 2013-05-23\n' - 'parameters:\n' - ' public_string:\n' - ' type: string\n' - ' secret_string:\n' - ' type: string\n' - ' hidden: true\n'), - 'validate': { - 'Description': 'No description', - 'Parameters': { - 'public_string': { - 'Label': 'public_string', - 'Description': '', - 'Type': 'String', - 'NoEcho': 'false' - }, - 'secret_string': { - 'Label': 'secret_string', - 'Description': '', - 'Type': 'String', - 'NoEcho': 'true' - } - } - } - } - api.heat.template_validate(IsA(http.HttpRequest), - files={}, - template=hc_format.parse(template['data'])) \ - .AndReturn(template['validate']) - - self.mox.ReplayAll() - - url = reverse('horizon:project:stacks:select_template') - res = self.client.get(url) - self.assertTemplateUsed(res, 'project/stacks/select_template.html') - - form_data = {'template_source': 'raw', - 'template_data': template['data'], - 'method': forms.TemplateForm.__name__} - res = self.client.post(url, form_data) - self.assertTemplateUsed(res, 'project/stacks/create.html') - - # ensure the fields were rendered correctly - if django.VERSION >= (1, 10): - pattern = ('') - secret = ('') - else: - pattern = ('') - secret = ('') - - self.assertContains(res, pattern, html=True) - self.assertContains(res, secret, html=True) - - @test.create_stubs({api.heat: ('template_validate',)}) - def test_launch_stack_with_parameter_group(self): - template = { - 'data': ('heat_template_version: 2013-05-23\n' - 'parameters:\n' - ' last_param:\n' - ' type: string\n' - ' first_param:\n' - ' type: string\n' - ' middle_param:\n' - ' type: string\n' - 'parameter_groups:\n' - '- parameters:\n' - ' - first_param\n' - ' - middle_param\n' - ' - last_param\n'), - 'validate': { - 'Description': 'No description', - 'Parameters': { - 'last_param': { - 'Label': 'last_param', - 'Description': '', - 'Type': 'String', - 'NoEcho': 'false' - }, - 'first_param': { - 'Label': 'first_param', - 'Description': '', - 'Type': 'String', - 'NoEcho': 'false' - }, - 'middle_param': { - 'Label': 'middle_param', - 'Description': '', - 'Type': 'String', - 'NoEcho': 'true' - } - }, - 'ParameterGroups': [ - { - 'parameters': [ - 'first_param', - 'middle_param', - 'last_param' - ] - } - ] - } - } - api.heat.template_validate(IsA(http.HttpRequest), - files={}, - template=hc_format.parse(template['data'])) \ - .AndReturn(template['validate']) - - self.mox.ReplayAll() - - url = reverse('horizon:project:stacks:select_template') - res = self.client.get(url) - self.assertTemplateUsed(res, 'project/stacks/select_template.html') - - form_data = {'template_source': 'raw', - 'template_data': template['data'], - 'method': forms.TemplateForm.__name__} - res = self.client.post(url, form_data) - self.assertTemplateUsed(res, 'project/stacks/create.html') - - # ensure the fields were rendered in the correct order - regex = re.compile('^.*>first_param<.*>middle_param<.*>last_param<.*$', - flags=re.DOTALL) - self.assertRegexpMatches(res.content.decode('utf-8'), regex) - - @test.create_stubs({api.heat: ('stack_create', 'template_validate')}) - def test_launch_stack_parameter_types(self): - template = { - 'data': ('heat_template_version: 2013-05-23\n' - 'parameters:\n' - ' param1:\n' - ' type: string\n' - ' param2:\n' - ' type: number\n' - ' param3:\n' - ' type: json\n' - ' param4:\n' - ' type: comma_delimited_list\n' - ' param5:\n' - ' type: boolean\n'), - 'validate': { - "Description": "No description", - "Parameters": { - "param1": { - "Type": "String", - "NoEcho": "false", - "Description": "", - "Label": "param1" - }, - "param2": { - "Type": "Number", - "NoEcho": "false", - "Description": "", - "Label": "param2" - }, - "param3": { - "Type": "Json", - "NoEcho": "false", - "Description": "", - "Label": "param3" - }, - "param4": { - "Type": "CommaDelimitedList", - "NoEcho": "false", - "Description": "", - "Label": "param4" - }, - "param5": { - "Type": "Boolean", - "NoEcho": "false", - "Description": "", - "Label": "param5" - } - } - } - } - stack = self.stacks.first() - - api.heat.template_validate(IsA(http.HttpRequest), - files={}, - template=hc_format.parse(template['data'])) \ - .AndReturn(template['validate']) - - api.heat.stack_create(IsA(http.HttpRequest), - stack_name=stack.stack_name, - timeout_mins=60, - disable_rollback=True, - template=hc_format.parse(template['data']), - parameters={'param1': 'some string', - 'param2': 42, - 'param3': '{"key": "value"}', - 'param4': 'a,b,c', - 'param5': True}, - password='password', - files={}) - - self.mox.ReplayAll() - - url = reverse('horizon:project:stacks:select_template') - res = self.client.get(url) - self.assertTemplateUsed(res, 'project/stacks/select_template.html') - - form_data = {'template_source': 'raw', - 'template_data': template['data'], - 'method': forms.TemplateForm.__name__} - res = self.client.post(url, form_data) - self.assertTemplateUsed(res, 'project/stacks/create.html') - - # ensure the fields were rendered correctly - if django.VERSION >= (1, 10): - input_str = ('') - else: - input_str = ('') - - self.assertContains(res, input_str.format(1, 'text'), html=True) - # the custom number spinner produces an input element - # that doesn't match the input_strs above - # validate with id alone - self.assertContains(res, 'id="id___param_param2"') - self.assertContains(res, input_str.format(3, 'text'), html=True) - self.assertContains(res, input_str.format(4, 'text'), html=True) - self.assertContains( - res, - '', - html=True) - - # post some sample data and make sure it validates - url = reverse('horizon:project:stacks:launch') - form_data = {'template_source': 'raw', - 'template_data': template['data'], - 'password': 'password', - 'parameters': json.dumps(template['validate']), - 'stack_name': stack.stack_name, - "timeout_mins": 60, - "disable_rollback": True, - "__param_param1": "some string", - "__param_param2": 42, - "__param_param3": '{"key": "value"}', - "__param_param4": "a,b,c", - "__param_param5": True, - 'method': forms.CreateStackForm.__name__} - res = self.client.post(url, form_data) - self.assertRedirectsNoFollow(res, INDEX_URL) - - @test.create_stubs({api.heat: ('stack_update', 'stack_get', 'template_get', - 'template_validate'), - api.neutron: ('network_list_for_tenant', )}) - def test_edit_stack_template(self): - template = self.stack_templates.first() - stack = self.stacks.first() - - # GET to template form - api.heat.stack_get(IsA(http.HttpRequest), - stack.id).AndReturn(stack) - # POST template form, validation - api.heat.template_validate(IsA(http.HttpRequest), - files={}, - template=hc_format.parse(template.data)) \ - .AndReturn(json.loads(template.validate)) - - # GET to edit form - api.heat.stack_get(IsA(http.HttpRequest), - stack.id).AndReturn(stack) - api.heat.template_get(IsA(http.HttpRequest), - stack.id) \ - .AndReturn(json.loads(template.validate)) - - # POST to edit form - api.heat.stack_get(IsA(http.HttpRequest), - stack.id).AndReturn(stack) - - fields = { - 'stack_name': stack.stack_name, - 'disable_rollback': True, - 'timeout_mins': 61, - 'password': 'password', - 'template': None, - 'parameters': IsA(dict), - 'files': None - } - api.heat.stack_update(IsA(http.HttpRequest), - stack_id=stack.id, - **fields) - api.neutron.network_list_for_tenant(IsA(http.HttpRequest), - self.tenant.id) \ - .AndReturn(self.networks.list()) - - self.mox.ReplayAll() - - url = reverse('horizon:project:stacks:change_template', - args=[stack.id]) - res = self.client.get(url) - self.assertTemplateUsed(res, 'project/stacks/change_template.html') - - form_data = {'template_source': 'raw', - 'template_data': template.data, - 'method': forms.ChangeTemplateForm.__name__} - res = self.client.post(url, form_data) - - url = reverse('horizon:project:stacks:edit_stack', - args=[stack.id, ]) - form_data = {'template_source': 'raw', - 'template_data': template.data, - 'password': 'password', - 'parameters': template.validate, - 'stack_name': stack.stack_name, - 'stack_id': stack.id, - "timeout_mins": 61, - "disable_rollback": True, - "__param_DBUsername": "admin", - "__param_LinuxDistribution": "F17", - "__param_InstanceType": "m1.small", - "__param_KeyName": "test", - "__param_DBPassword": "admin", - "__param_DBRootPassword": "admin", - "__param_DBName": "wordpress", - "__param_Network": self.networks.list()[0]['id'], - 'method': forms.EditStackForm.__name__} - res = self.client.post(url, form_data) - self.assertRedirectsNoFollow(res, INDEX_URL) - - def test_launch_stack_form_invalid_name_digit(self): - self._test_launch_stack_invalid_name('2_StartWithDigit') - - def test_launch_stack_form_invalid_name_underscore(self): - self._test_launch_stack_invalid_name('_StartWithUnderscore') - - def test_launch_stack_form_invalid_name_point(self): - self._test_launch_stack_invalid_name('.StartWithPoint') - - @test.create_stubs({api.neutron: ('network_list_for_tenant', )}) - def _test_launch_stack_invalid_name(self, name): - api.neutron.network_list_for_tenant(IsA(http.HttpRequest), - self.tenant.id) \ - .AndReturn(self.networks.list()) - self.mox.ReplayAll() - - template = self.stack_templates.first() - url = reverse('horizon:project:stacks:launch') - form_data = {'template_source': 'raw', - 'template_data': template.data, - 'password': 'password', - 'parameters': template.validate, - 'stack_name': name, - "timeout_mins": 60, - "disable_rollback": True, - "__param_DBUsername": "admin", - "__param_LinuxDistribution": "F17", - "__param_InstanceType": "m1.small", - "__param_KeyName": "test", - "__param_DBPassword": "admin", - "__param_DBRootPassword": "admin", - "__param_DBName": "wordpress", - "__param_Network": self.networks.list()[0]['id'], - 'method': forms.CreateStackForm.__name__} - - res = self.client.post(url, form_data) - error = ('Name must start with a letter and may only contain letters, ' - 'numbers, underscores, periods and hyphens.') - - self.assertFormErrors(res, 1) - self.assertFormError(res, "form", 'stack_name', error) - - def _test_stack_action(self, action): - stack = self.stacks.first() - filters = {} - api.heat.stacks_list(IsA(http.HttpRequest), - marker=None, - paginate=True, - sort_dir='desc', - filters=filters) \ - .AndReturn([self.stacks.list(), True, True]) - - getattr(api.heat, 'action_%s' % action)(IsA(http.HttpRequest), - stack.id).AndReturn(stack) - - self.mox.ReplayAll() - - form_data = {"action": "stacks__%s__%s" % (action, stack.id)} - res = self.client.post(INDEX_URL, form_data) - - self.assertNoFormErrors(res) - self.assertRedirectsNoFollow(res, INDEX_URL) - - @test.create_stubs({api.heat: ('stacks_list', 'action_check',)}) - def test_check_stack(self): - self._test_stack_action('check') - - @test.create_stubs({api.heat: ('stacks_list', 'action_suspend',)}) - def test_suspend_stack(self): - self._test_stack_action('suspend') - - @test.create_stubs({api.heat: ('stacks_list', 'action_resume',)}) - def test_resume_stack(self): - self._test_stack_action('resume') - - @test.create_stubs({api.heat: ('stack_preview', 'template_validate')}) - def test_preview_stack(self): - template = self.stack_templates.first() - stack = self.stacks.first() - - api.heat.template_validate(IsA(http.HttpRequest), - files={}, - template=hc_format.parse(template.data)) \ - .AndReturn(json.loads(template.validate)) - - api.heat.stack_preview(IsA(http.HttpRequest), - stack_name=stack.stack_name, - timeout_mins=60, - disable_rollback=True, - template=None, - parameters=IsA(dict), - files=None).AndReturn(stack) - - self.mox.ReplayAll() - - url = reverse('horizon:project:stacks:preview_template') - res = self.client.get(url) - self.assertTemplateUsed(res, 'project/stacks/preview_template.html') - - form_data = {'template_source': 'raw', - 'template_data': template.data, - 'method': forms.PreviewTemplateForm.__name__} - res = self.client.post(url, form_data) - self.assertTemplateUsed(res, 'project/stacks/preview.html') - - url = reverse('horizon:project:stacks:preview') - form_data = {'template_source': 'raw', - 'template_data': template.data, - 'parameters': template.validate, - 'stack_name': stack.stack_name, - "timeout_mins": 60, - "disable_rollback": True, - "__param_DBUsername": "admin", - "__param_LinuxDistribution": "F17", - "__param_InstanceType": "m1.small", - "__param_KeyName": "test", - "__param_DBPassword": "admin", - "__param_DBRootPassword": "admin", - "__param_DBName": "wordpress", - 'method': forms.PreviewStackForm.__name__} - res = self.client.post(url, form_data) - self.assertTemplateUsed(res, 'project/stacks/preview_details.html') - self.assertEqual(res.context['stack_preview']['stack_name'], - stack.stack_name) - - @test.create_stubs({api.heat: ('stack_get', 'template_get', - 'resources_list')}) - def test_detail_stack_topology(self): - stack = self.stacks.first() - template = self.stack_templates.first() - api.heat.stack_get(IsA(http.HttpRequest), stack.id) \ - .MultipleTimes().AndReturn(stack) - api.heat.template_get(IsA(http.HttpRequest), stack.id) \ - .AndReturn(json.loads(template.validate)) - api.heat.resources_list(IsA(http.HttpRequest), stack.stack_name) \ - .AndReturn([]) - self.mox.ReplayAll() - - url = '?'.join([reverse(DETAIL_URL, args=[stack.id]), - '='.join(['tab', 'stack_details__stack_topology'])]) - res = self.client.get(url) - tab = res.context['tab_group'].get_tab('topology') - d3_data = tab.data['d3_data'] - self.assertEqual(tab.template_name, - 'project/stacks/_detail_topology.html') - # status is CREATE_COMPLETE, so we expect the topology to display it - self.assertIn('info_box', d3_data) - self.assertIn('stack-green.svg', d3_data) - self.assertIn('Create Complete', d3_data) - - @test.create_stubs({api.heat: ('stack_get', 'template_get'), - project_api: ('d3_data',)}) - def test_detail_stack_overview(self): - stack = self.stacks.first() - template = self.stack_templates.first() - api.heat.stack_get(IsA(http.HttpRequest), stack.id) \ - .MultipleTimes().AndReturn(stack) - api.heat.template_get(IsA(http.HttpRequest), stack.id) \ - .AndReturn(json.loads(template.validate)) - project_api.d3_data(IsA(http.HttpRequest), stack_id=stack.id) \ - .AndReturn(json.dumps({"nodes": [], "stack": {}})) - self.mox.ReplayAll() - - url = '?'.join([reverse(DETAIL_URL, args=[stack.id]), - '='.join(['tab', 'stack_details__stack_overview'])]) - res = self.client.get(url) - tab = res.context['tab_group'].get_tab('overview') - overview_data = tab.data['stack'] - self.assertEqual(tab.template_name, - 'project/stacks/_detail_overview.html') - self.assertEqual(stack.stack_name, overview_data.stack_name) - - @test.create_stubs({api.heat: ('stack_get', 'template_get'), - project_api: ('d3_data',)}) - def test_detail_stack_resources(self): - stack = self.stacks.first() - template = self.stack_templates.first() - api.heat.stack_get(IsA(http.HttpRequest), stack.id) \ - .MultipleTimes().AndReturn(stack) - api.heat.template_get(IsA(http.HttpRequest), stack.id) \ - .AndReturn(json.loads(template.validate)) - project_api.d3_data(IsA(http.HttpRequest), stack_id=stack.id) \ - .AndReturn(json.dumps({"nodes": [], "stack": {}})) - self.mox.ReplayAll() - - url = '?'.join([reverse(DETAIL_URL, args=[stack.id]), - '='.join(['tab', 'stack_details__resource_overview'])]) - res = self.client.get(url) - tab = res.context['tab_group'].get_tab('resources') - self.assertEqual(tab.template_name, - 'project/stacks/_detail_resources.html') - - @test.create_stubs({api.heat: ('stack_get', 'template_get')}) - def test_detail_stack_template(self): - stack = self.stacks.first() - template = self.stack_templates.first() - api.heat.stack_get(IsA(http.HttpRequest), stack.id) \ - .AndReturn(stack) - api.heat.template_get(IsA(http.HttpRequest), stack.id) \ - .AndReturn(json.loads(template.validate)) - self.mox.ReplayAll() - - url = '?'.join([reverse(DETAIL_URL, args=[stack.id]), - '='.join(['tab', 'stack_details__stack_template'])]) - res = self.client.get(url) - tab = res.context['tab_group'].get_tab('stack_template') - template_data = tab.data['stack_template'] - self.assertEqual(tab.template_name, - 'project/stacks/_stack_template.html') - self.assertIn(json.loads(template.validate)['Description'], - template_data) - - @test.create_stubs({api.heat: ('resource_get', 'resource_metadata_get')}) - def test_resource_view(self): - stack = self.stacks.first() - resource = self.heat_resources.first() - metadata = {} - api.heat.resource_get( - IsA(http.HttpRequest), stack.id, resource.resource_name) \ - .AndReturn(resource) - api.heat.resource_metadata_get( - IsA(http.HttpRequest), stack.id, resource.resource_name) \ - .AndReturn(metadata) - self.mox.ReplayAll() - - url = reverse('horizon:project:stacks:resource', - args=[stack.id, resource.resource_name]) - res = self.client.get(url) - self.assertTemplateUsed(res, 'horizon/common/_detail.html') - self.assertTemplateUsed(res, 'project/stacks/_resource_overview.html') - self.assertEqual(res.context['resource'].logical_resource_id, - resource.logical_resource_id) - - -class TemplateFormTests(test.TestCase): - - class SimpleFile(object): - def __init__(self, name, data): - self.name = name - self.data = data - - def read(self): - return self.data - - def test_create_upload_form_attributes(self): - attrs = forms.create_upload_form_attributes( - 'env', 'url', 'Environment') - self.assertEqual(attrs['data-envsource-url'], 'Environment') - - def test_clean_file_upload_form_url(self): - kwargs = {'next_view': 'Launch Stack'} - t = forms.TemplateForm({}, **kwargs) - precleaned = { - 'template_url': 'http://templateurl.com', - } - t.clean_uploaded_files('template', 'template', precleaned, {}) - - self.assertEqual(precleaned['template_url'], 'http://templateurl.com') - - def test_clean_file_upload_form_multiple(self): - kwargs = {'next_view': 'Launch Stack'} - t = forms.TemplateForm({}, **kwargs) - precleaned = { - 'template_url': 'http://templateurl.com', - 'template_data': 'http://templateurl.com', - } - self.assertRaises( - exceptions.ValidationError, - t.clean_uploaded_files, - 'template', - 'template', - precleaned, - {}) - - def test_clean_file_upload_form_invalid_json(self): - kwargs = {'next_view': 'Launch Stack'} - t = forms.TemplateForm({}, **kwargs) - precleaned = { - 'template_data': 'http://templateurl.com', - } - json_str = '{notvalidjson::::::json/////json' - files = {'template_upload': - self.SimpleFile('template_name', json_str)} - - self.assertRaises( - exceptions.ValidationError, - t.clean_uploaded_files, - 'template', - 'template', - precleaned, - files) - - def test_clean_file_upload_form_valid_data(self): - kwargs = {'next_view': 'Launch Stack'} - t = forms.TemplateForm({}, **kwargs) - precleaned = { - 'template_data': 'http://templateurl.com', - } - - json_str = '{"isvalid":"json"}' - files = {'template_upload': - self.SimpleFile('template_name', json_str)} - - t.clean_uploaded_files('template', 'template', precleaned, files) - self.assertEqual( - json_str, - precleaned['template_data']) diff --git a/openstack_dashboard/dashboards/project/stacks/urls.py b/openstack_dashboard/dashboards/project/stacks/urls.py deleted file mode 100644 index c63a18fe62..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/urls.py +++ /dev/null @@ -1,38 +0,0 @@ -# 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. - -from django.conf.urls import url - -from openstack_dashboard.dashboards.project.stacks import views - -urlpatterns = [ - url(r'^$', views.IndexView.as_view(), name='index'), - url(r'^select_template$', - views.SelectTemplateView.as_view(), - name='select_template'), - url(r'^launch$', views.CreateStackView.as_view(), name='launch'), - url(r'^preview_template$', - views.PreviewTemplateView.as_view(), name='preview_template'), - url(r'^preview$', views.PreviewStackView.as_view(), name='preview'), - url(r'^preview_details$', - views.PreviewStackDetailsView.as_view(), name='preview_details'), - url(r'^stack/(?P[^/]+)/$', - views.DetailView.as_view(), name='detail'), - url(r'^(?P[^/]+)/change_template$', - views.ChangeTemplateView.as_view(), name='change_template'), - url(r'^(?P[^/]+)/edit_stack$', - views.EditStackView.as_view(), name='edit_stack'), - url(r'^stack/(?P[^/]+)/(?P[^/]+)/$', - views.ResourceView.as_view(), name='resource'), - url(r'^get_d3_data/(?P[^/]+)/$', - views.JSONView.as_view(), name='d3_data'), -] diff --git a/openstack_dashboard/dashboards/project/stacks/views.py b/openstack_dashboard/dashboards/project/stacks/views.py deleted file mode 100644 index f5d47ec560..0000000000 --- a/openstack_dashboard/dashboards/project/stacks/views.py +++ /dev/null @@ -1,358 +0,0 @@ -# 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 json -from operator import attrgetter - -import yaml - -from django.core.urlresolvers import reverse -from django.core.urlresolvers import reverse_lazy -from django.http import HttpResponse -from django.utils.translation import ugettext_lazy as _ -import django.views.generic - -from horizon import exceptions -from horizon import forms -from horizon import tables -from horizon import tabs -from horizon.utils import memoized -from horizon import views -from openstack_dashboard import api -from openstack_dashboard.dashboards.project.stacks \ - import api as project_api -from openstack_dashboard.dashboards.project.stacks \ - import forms as project_forms -from openstack_dashboard.dashboards.project.stacks \ - import tables as project_tables -from openstack_dashboard.dashboards.project.stacks \ - import tabs as project_tabs - - -class IndexView(tables.DataTableView): - table_class = project_tables.StacksTable - page_title = _("Stacks") - - def __init__(self, *args, **kwargs): - super(IndexView, self).__init__(*args, **kwargs) - self._more = None - - def has_prev_data(self, table): - return self._prev - - def has_more_data(self, table): - return self._more - - def get_data(self): - stacks = [] - filters = self.get_filters() - prev_marker = self.request.GET.get( - project_tables.StacksTable._meta.prev_pagination_param) - if prev_marker is not None: - sort_dir = 'asc' - marker = prev_marker - else: - sort_dir = 'desc' - marker = self.request.GET.get( - project_tables.StacksTable._meta.pagination_param) - try: - stacks, self._more, self._prev = api.heat.stacks_list( - self.request, - marker=marker, - paginate=True, - sort_dir=sort_dir, - filters=filters) - if prev_marker is not None: - stacks = sorted(stacks, key=attrgetter('creation_time'), - reverse=True) - except Exception: - self._prev = False - self._more = False - msg = _('Unable to retrieve stack list.') - exceptions.handle(self.request, msg) - return stacks - - -class SelectTemplateView(forms.ModalFormView): - template_name = 'project/stacks/select_template.html' - form_id = "select_template" - form_class = project_forms.TemplateForm - submit_label = _("Next") - submit_url = reverse_lazy("horizon:project:stacks:select_template") - success_url = reverse_lazy('horizon:project:stacks:launch') - page_title = _("Select Template") - - def get_initial(self): - initial = {} - for name in [ - 'template_url', - 'template_source', - 'template_data', - 'environment_source', - 'environment_data' - ]: - tmp = self.request.GET.get(name) - if tmp: - initial[name] = tmp - return initial - - def get_form_kwargs(self): - kwargs = super(SelectTemplateView, self).get_form_kwargs() - kwargs['next_view'] = CreateStackView - return kwargs - - -class ChangeTemplateView(forms.ModalFormView): - template_name = 'project/stacks/change_template.html' - form_id = "change_template" - form_class = project_forms.ChangeTemplateForm - submit_label = _("Next") - submit_url = "horizon:project:stacks:change_template" - cancel_url = reverse_lazy('horizon:project:stacks:index') - success_url = reverse_lazy('horizon:project:stacks:edit_stack') - page_title = _("Change Template") - - def get_context_data(self, **kwargs): - context = super(ChangeTemplateView, self).get_context_data(**kwargs) - args = (self.get_object().id,) - context['submit_url'] = reverse(self.submit_url, args=args) - return context - - @memoized.memoized_method - def get_object(self): - stack_id = self.kwargs['stack_id'] - try: - self._stack = api.heat.stack_get(self.request, stack_id) - except Exception: - msg = _("Unable to retrieve stack.") - redirect = reverse('horizon:project:stacks:index') - exceptions.handle(self.request, msg, redirect=redirect) - return self._stack - - def get_initial(self): - stack = self.get_object() - return {'stack_id': stack.id, - 'stack_name': stack.stack_name - } - - def get_form_kwargs(self): - kwargs = super(ChangeTemplateView, self).get_form_kwargs() - kwargs['next_view'] = EditStackView - return kwargs - - -class PreviewTemplateView(forms.ModalFormView): - template_name = 'project/stacks/preview_template.html' - form_id = "preview_template" - form_class = project_forms.PreviewTemplateForm - submit_label = _("Next") - submit_url = reverse_lazy('horizon:project:stacks:preview_template') - success_url = reverse_lazy('horizon:project:stacks:preview') - page_title = _("Preview Template") - - def get_form_kwargs(self): - kwargs = super(PreviewTemplateView, self).get_form_kwargs() - kwargs['next_view'] = PreviewStackView - return kwargs - - -class CreateStackView(forms.ModalFormView): - template_name = 'project/stacks/create.html' - form_id = "launch_stack" - form_class = project_forms.CreateStackForm - submit_label = _("Launch") - submit_url = reverse_lazy("horizon:project:stacks:launch") - success_url = reverse_lazy('horizon:project:stacks:index') - page_title = _("Launch Stack") - - def get_initial(self): - initial = {} - if 'environment_data' in self.kwargs: - initial['environment_data'] = self.kwargs['environment_data'] - if 'parameters' in self.kwargs: - initial['parameters'] = json.dumps(self.kwargs['parameters']) - return initial - - def get_form_kwargs(self): - kwargs = super(CreateStackView, self).get_form_kwargs() - if 'parameters' in self.kwargs: - kwargs['parameters'] = self.kwargs['parameters'] - else: - data = json.loads(self.request.POST['parameters']) - kwargs['parameters'] = data - return kwargs - - -# edit stack parameters, coming from template selector -class EditStackView(CreateStackView): - template_name = 'project/stacks/update.html' - form_id = "update_stack" - form_class = project_forms.EditStackForm - submit_label = _("Update") - submit_url = "horizon:project:stacks:edit_stack" - success_url = reverse_lazy('horizon:project:stacks:index') - page_title = _("Update Stack") - - def get_initial(self): - initial = super(EditStackView, self).get_initial() - - initial['stack'] = self.get_object()['stack'] - if initial['stack']: - initial['stack_id'] = initial['stack'].id - initial['stack_name'] = initial['stack'].stack_name - - return initial - - def get_context_data(self, **kwargs): - context = super(EditStackView, self).get_context_data(**kwargs) - args = (self.get_object()['stack'].id,) - context['submit_url'] = reverse(self.submit_url, args=args) - return context - - @memoized.memoized_method - def get_object(self): - stack_id = self.kwargs['stack_id'] - try: - stack = {} - stack['stack'] = api.heat.stack_get(self.request, stack_id) - stack['template'] = api.heat.template_get(self.request, stack_id) - self._stack = stack - except Exception: - msg = _("Unable to retrieve stack.") - redirect = reverse('horizon:project:stacks:index') - exceptions.handle(self.request, msg, redirect=redirect) - return self._stack - - -class PreviewStackView(CreateStackView): - template_name = 'project/stacks/preview.html' - form_id = "preview_stack" - form_class = project_forms.PreviewStackForm - submit_label = _("Preview") - submit_url = reverse_lazy('horizon:project:stacks:preview') - success_url = reverse_lazy('horizon:project:stacks:index') - page_title = _("Preview Stack") - - def get_form_kwargs(self): - kwargs = super(CreateStackView, self).get_form_kwargs() - kwargs['next_view'] = PreviewStackDetailsView - return kwargs - - -class PreviewStackDetailsView(forms.ModalFormMixin, views.HorizonTemplateView): - template_name = 'project/stacks/preview_details.html' - page_title = _("Preview Stack Details") - - def get_context_data(self, **kwargs): - context = super( - PreviewStackDetailsView, self).get_context_data(**kwargs) - context['stack_preview'] = self.kwargs['stack_preview'].to_dict() - return context - - -class DetailView(tabs.TabView): - tab_group_class = project_tabs.StackDetailTabs - template_name = 'horizon/common/_detail.html' - page_title = "{{ stack.stack_name|default:stack.id }}" - - def get_context_data(self, **kwargs): - context = super(DetailView, self).get_context_data(**kwargs) - stack = self.get_data(self.request, **kwargs) - table = project_tables.StacksTable(self.request) - context["stack"] = stack - context["url"] = self.get_redirect_url() - context["actions"] = table.render_row_actions(stack) - return context - - @memoized.memoized_method - def get_data(self, request, **kwargs): - stack_id = kwargs['stack_id'] - try: - stack = api.heat.stack_get(request, stack_id) - request.session['stack_id'] = stack.id - request.session['stack_name'] = stack.stack_name - return stack - except Exception: - msg = _("Unable to retrieve stack.") - exceptions.handle(request, msg, redirect=self.get_redirect_url()) - - @memoized.memoized_method - def get_template(self, request, **kwargs): - try: - stack_template = api.heat.template_get( - request, - kwargs['stack_id']) - return yaml.safe_dump(stack_template, indent=2) - except Exception: - msg = _("Unable to retrieve stack template.") - exceptions.handle(request, msg, redirect=self.get_redirect_url()) - - def get_tabs(self, request, **kwargs): - stack = self.get_data(request, **kwargs) - stack_template = self.get_template(request, **kwargs) - return self.tab_group_class( - request, stack=stack, stack_template=stack_template, **kwargs) - - @staticmethod - def get_redirect_url(): - return reverse('horizon:project:stacks:index') - - -class ResourceView(tabs.TabView): - tab_group_class = project_tabs.ResourceDetailTabs - template_name = 'horizon/common/_detail.html' - page_title = "{{ resource.resource_name|"\ - "default:resource.logical_resource_id }}" - - def get_context_data(self, **kwargs): - context = super(ResourceView, self).get_context_data(**kwargs) - context["resource"] = self.get_data(self.request, **kwargs) - context["metadata"] = self.get_metadata(self.request, **kwargs) - return context - - @memoized.memoized_method - def get_data(self, request, **kwargs): - try: - resource = api.heat.resource_get( - request, - kwargs['stack_id'], - kwargs['resource_name']) - return resource - except Exception: - msg = _("Unable to retrieve resource.") - redirect = reverse('horizon:project:stacks:index') - exceptions.handle(request, msg, redirect=redirect) - - @memoized.memoized_method - def get_metadata(self, request, **kwargs): - try: - metadata = api.heat.resource_metadata_get( - request, - kwargs['stack_id'], - kwargs['resource_name']) - return json.dumps(metadata, indent=2) - except Exception: - msg = _("Unable to retrieve metadata.") - redirect = reverse('horizon:project:stacks:index') - exceptions.handle(request, msg, redirect=redirect) - - def get_tabs(self, request, **kwargs): - resource = self.get_data(request, **kwargs) - metadata = self.get_metadata(request, **kwargs) - return self.tab_group_class( - request, resource=resource, metadata=metadata, **kwargs) - - -class JSONView(django.views.generic.View): - def get(self, request, stack_id=''): - return HttpResponse(project_api.d3_data(request, stack_id=stack_id), - content_type="application/json") diff --git a/openstack_dashboard/enabled/_1610_orchestration_panel_group.py b/openstack_dashboard/enabled/_1610_orchestration_panel_group.py deleted file mode 100644 index 986879ca81..0000000000 --- a/openstack_dashboard/enabled/_1610_orchestration_panel_group.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.utils.translation import ugettext_lazy as _ - -# The slug of the panel group to be added to HORIZON_CONFIG. Required. -PANEL_GROUP = 'orchestration' -# The display name of the PANEL_GROUP. Required. -PANEL_GROUP_NAME = _('Orchestration') -# The slug of the dashboard the PANEL_GROUP associated with. Required. -PANEL_GROUP_DASHBOARD = 'project' diff --git a/openstack_dashboard/enabled/_1620_project_stacks_panel.py b/openstack_dashboard/enabled/_1620_project_stacks_panel.py deleted file mode 100644 index b044fcca9d..0000000000 --- a/openstack_dashboard/enabled/_1620_project_stacks_panel.py +++ /dev/null @@ -1,9 +0,0 @@ -# The slug of the panel to be added to HORIZON_CONFIG. Required. -PANEL = 'stacks' -# The slug of the dashboard the PANEL associated with. Required. -PANEL_DASHBOARD = 'project' -# The slug of the panel group the PANEL is associated with. -PANEL_GROUP = 'orchestration' - -# Python panel class of the PANEL to be added. -ADD_PANEL = 'openstack_dashboard.dashboards.project.stacks.panel.Stacks' diff --git a/openstack_dashboard/enabled/_1630_project_resource_types_panel.py b/openstack_dashboard/enabled/_1630_project_resource_types_panel.py deleted file mode 100644 index 8b048924f5..0000000000 --- a/openstack_dashboard/enabled/_1630_project_resource_types_panel.py +++ /dev/null @@ -1,10 +0,0 @@ -# The slug of the panel to be added to HORIZON_CONFIG. Required. -PANEL = 'stacks.resource_types' -# The slug of the dashboard the PANEL associated with. Required. -PANEL_DASHBOARD = 'project' -# The slug of the panel group the PANEL is associated with. -PANEL_GROUP = 'orchestration' - -# Python panel class of the PANEL to be added. -ADD_PANEL = ('openstack_dashboard.dashboards.project.' - 'stacks.resource_types.panel.ResourceTypes') diff --git a/openstack_dashboard/enabled/_1640_project_template_versions_panel.py b/openstack_dashboard/enabled/_1640_project_template_versions_panel.py deleted file mode 100644 index f31e040418..0000000000 --- a/openstack_dashboard/enabled/_1640_project_template_versions_panel.py +++ /dev/null @@ -1,10 +0,0 @@ -# The slug of the panel to be added to HORIZON_CONFIG. Required. -PANEL = 'stacks.template_versions' -# The slug of the dashboard the PANEL associated with. Required. -PANEL_DASHBOARD = 'project' -# The slug of the panel group the PANEL is associated with. -PANEL_GROUP = 'orchestration' - -# Python panel class of the PANEL to be added. -ADD_PANEL = ('openstack_dashboard.dashboards.project.' - 'stacks.template_versions.panel.TemplateVersions') diff --git a/openstack_dashboard/exceptions.py b/openstack_dashboard/exceptions.py index 520214d75c..e77e12b3e8 100644 --- a/openstack_dashboard/exceptions.py +++ b/openstack_dashboard/exceptions.py @@ -18,7 +18,6 @@ from cinderclient import exceptions as cinderclient from glanceclient.common import exceptions as glanceclient -from heatclient import exc as heatclient from keystoneclient import exceptions as keystoneclient from neutronclient.common import exceptions as neutronclient from novaclient import exceptions as novaclient @@ -32,7 +31,6 @@ UNAUTHORIZED = ( novaclient.Unauthorized, glanceclient.Unauthorized, neutronclient.Unauthorized, - heatclient.HTTPUnauthorized, ) @@ -42,7 +40,6 @@ NOT_FOUND = ( novaclient.NotFound, glanceclient.NotFound, neutronclient.NotFound, - heatclient.HTTPNotFound, ) @@ -62,7 +59,5 @@ RECOVERABLE = ( neutronclient.Forbidden, neutronclient.NeutronClientException, swiftclient.ClientException, - heatclient.HTTPForbidden, - heatclient.HTTPException, requests.RequestException, ) diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example index a4e34b9d54..36e698fea8 100644 --- a/openstack_dashboard/local/local_settings.py.example +++ b/openstack_dashboard/local/local_settings.py.example @@ -502,7 +502,6 @@ TIME_ZONE = "UTC" # 'compute': 'nova_policy.json', # 'volume': 'cinder_policy.json', # 'image': 'glance_policy.json', -# 'orchestration': 'heat_policy.json', # 'network': 'neutron_policy.json', #} @@ -608,11 +607,6 @@ LOGGING = { 'level': 'DEBUG', 'propagate': False, }, - 'heatclient': { - 'handlers': ['console'], - 'level': 'DEBUG', - 'propagate': False, - }, 'swiftclient': { 'handlers': ['console'], 'level': 'DEBUG', diff --git a/openstack_dashboard/settings.py b/openstack_dashboard/settings.py index ba82951045..6027ea0aad 100644 --- a/openstack_dashboard/settings.py +++ b/openstack_dashboard/settings.py @@ -259,7 +259,6 @@ POLICY_FILES = { 'compute': 'nova_policy.json', 'volume': 'cinder_policy.json', 'image': 'glance_policy.json', - 'orchestration': 'heat_policy.json', 'network': 'neutron_policy.json', } diff --git a/openstack_dashboard/static/app/core/openstack-service-api/heat.service.js b/openstack_dashboard/static/app/core/openstack-service-api/heat.service.js deleted file mode 100644 index 78e60b046a..0000000000 --- a/openstack_dashboard/static/app/core/openstack-service-api/heat.service.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. - * - * 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. - */ -(function () { - 'use strict'; - - angular - .module('horizon.app.core.openstack-service-api') - .factory('horizon.app.core.openstack-service-api.heat', heatAPI); - - heatAPI.$inject = [ - 'horizon.framework.util.http.service', - 'horizon.framework.widgets.toast.service' - ]; - - /** - * @ngdoc service - * @name heatAPI - * @param {Object} apiService - * @param {Object} toastService - * @description Provides direct pass through to Heat with NO abstraction. - * @returns {Object} The service - */ - function heatAPI(apiService, toastService) { - var service = { - validate: validate, - getServices: getServices - }; - - return service; - - /** - * @name validate - * @description - * Validate a template. - * - * @param {string} params - * - template_url - * Specifies the template to validate. - * - * @param {boolean} suppressError - * If passed in, this will not show the default error handling - * (horizon alert). - * @returns {Object} The result of the API call - */ - function validate(params, suppressError) { - var promise = apiService.post('/api/heat/validate/', params); - return suppressError ? promise : promise.error(function() { - toastService.add('error', gettext('Unable to validate the template.')); - }); - } - - /** - * @name getServices - * @description Get the list of heat services. - * - * @returns {Object} The listing result is an object with property "services." Each item is - * a service. - */ - function getServices() { - return apiService.get('/api/heat/services/') - .error(function () { - toastService.add('error', gettext('Unable to retrieve the heat services.')); - }); - } - } - -}()); diff --git a/openstack_dashboard/static/app/core/openstack-service-api/heat.service.spec.js b/openstack_dashboard/static/app/core/openstack-service-api/heat.service.spec.js deleted file mode 100644 index 0cdac559f5..0000000000 --- a/openstack_dashboard/static/app/core/openstack-service-api/heat.service.spec.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * (c) Copyright 2015 Hewlett-Packard Development Company, L.P. - * - * 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. - */ - -(function() { - 'use strict'; - - describe('Heat API', function() { - var testCall, service; - var apiService = {}; - var toastService = {}; - - beforeEach( - module('horizon.mock.openstack-service-api', - function($provide, initServices) { - testCall = initServices($provide, apiService, toastService); - }) - ); - - beforeEach(module('horizon.app.core.openstack-service-api')); - - beforeEach(inject(['horizon.app.core.openstack-service-api.heat', function(heatAPI) { - service = heatAPI; - }])); - - it('defines the service', function() { - expect(service).toBeDefined(); - }); - - var tests = [ - { - 'func': 'validate', - 'method': 'post', - 'path': '/api/heat/validate/', - 'data': { - 'template_url':'http://localhost/test.template' - }, - 'error': 'Unable to validate the template.', - 'testInput': [ - { - 'template_url':'http://localhost/test.template' - } - ] - }, - { - 'func': 'getServices', - 'method': 'get', - 'path': '/api/heat/services/', - 'error': 'Unable to retrieve the heat services.' - } - ]; - - // Iterate through the defined tests and apply as Jasmine specs. - angular.forEach(tests, function(params) { - it('defines the ' + params.func + ' call properly', function() { - var callParams = [apiService, service, toastService, params]; - testCall.apply(this, callParams); - }); - }); - - it('suppresses the error for template validation as instructed by the param', function() { - spyOn(apiService, 'post').and.returnValue("promise"); - expect(service.validate("whatever", true)).toBe("promise"); - }); - - }); - -})(); diff --git a/openstack_dashboard/test/api_tests/heat_rest_tests.py b/openstack_dashboard/test/api_tests/heat_rest_tests.py deleted file mode 100644 index ef5cc0f7c6..0000000000 --- a/openstack_dashboard/test/api_tests/heat_rest_tests.py +++ /dev/null @@ -1,69 +0,0 @@ -# (c) Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# 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 json - -import mock - -from openstack_dashboard import api -from openstack_dashboard.api.rest import heat -from openstack_dashboard.test import helpers as test - - -class ValidateRestTestCase(test.TestCase): - @mock.patch.object(heat.api, 'heat') - def test_validate_post(self, hc): - body = '''{"template_url":"http://localhost/template.yaml"}''' - request = self.mock_rest_request(body=body) - hc.template_validate.return_value = ({'Description': 'foo'}) - response = heat.Validate().post(request) - self.assertStatusCode(response, 200) - self.assertEqual(response.json, {"Description": "foo"}) - kwargs = json.loads(body) - hc.template_validate.assert_called_once_with(request, **kwargs) - - -class HeatRestTestCase(test.TestCase): - # - # Services - # - - @test.create_stubs({api.base: ('is_service_enabled',)}) - @mock.patch.object(heat.api, 'heat') - def test_services_get(self, hc): - request = self.mock_rest_request(GET={}) - - api.base.is_service_enabled(request, 'orchestration').AndReturn(True) - - hc.service_list.return_value = [ - mock.Mock(**{'to_dict.return_value': {'id': '1'}}), - mock.Mock(**{'to_dict.return_value': {'id': '2'}}) - ] - self.mox.ReplayAll() - - response = heat.Services().get(request) - self.assertStatusCode(response, 200) - self.assertEqual(response.content.decode('utf-8'), - '{"items": [{"id": "1"}, {"id": "2"}]}') - hc.service_list.assert_called_once_with(request) - - @test.create_stubs({api.base: ('is_service_enabled',)}) - def test_services_get_disabled(self): - request = self.mock_rest_request(GET={}) - - api.base.is_service_enabled(request, 'orchestration').AndReturn(False) - - self.mox.ReplayAll() - - response = heat.Services().get(request) - self.assertStatusCode(response, 501) diff --git a/openstack_dashboard/test/api_tests/heat_tests.py b/openstack_dashboard/test/api_tests/heat_tests.py deleted file mode 100644 index d241277771..0000000000 --- a/openstack_dashboard/test/api_tests/heat_tests.py +++ /dev/null @@ -1,358 +0,0 @@ -# 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 six - -from django.conf import settings -from django.test.utils import override_settings - -from horizon import exceptions -from openstack_dashboard import api -from openstack_dashboard.test import helpers as test - - -class HeatApiTests(test.APITestCase): - def test_stack_list(self): - api_stacks = self.stacks.list() - limit = getattr(settings, 'API_RESULT_LIMIT', 1000) - - heatclient = self.stub_heatclient() - heatclient.stacks = self.mox.CreateMockAnything() - heatclient.stacks.list(limit=limit, - sort_dir='desc', - sort_key='created_at',) \ - .AndReturn(iter(api_stacks)) - self.mox.ReplayAll() - stacks, has_more, has_prev = api.heat.stacks_list(self.request) - self.assertItemsEqual(stacks, api_stacks) - self.assertFalse(has_more) - self.assertFalse(has_prev) - - @override_settings(API_RESULT_PAGE_SIZE=2) - def test_stack_list_sort_options(self): - # Verify that sort_dir and sort_key work - api_stacks = self.stacks.list() - limit = getattr(settings, 'API_RESULT_LIMIT', 1000) - sort_dir = 'asc' - sort_key = 'size' - - heatclient = self.stub_heatclient() - heatclient.stacks = self.mox.CreateMockAnything() - heatclient.stacks.list(limit=limit, - sort_dir=sort_dir, - sort_key=sort_key,) \ - .AndReturn(iter(api_stacks)) - self.mox.ReplayAll() - - stacks, has_more, has_prev = api.heat.stacks_list(self.request, - sort_dir=sort_dir, - sort_key=sort_key) - self.assertItemsEqual(stacks, api_stacks) - self.assertFalse(has_more) - self.assertFalse(has_prev) - - @override_settings(API_RESULT_PAGE_SIZE=20) - def test_stack_list_pagination_less_page_size(self): - api_stacks = self.stacks.list() - page_size = settings.API_RESULT_PAGE_SIZE - sort_dir = 'desc' - sort_key = 'created_at' - - heatclient = self.stub_heatclient() - heatclient.stacks = self.mox.CreateMockAnything() - heatclient.stacks.list(limit=page_size + 1, - sort_dir=sort_dir, - sort_key=sort_key,) \ - .AndReturn(iter(api_stacks)) - self.mox.ReplayAll() - - stacks, has_more, has_prev = api.heat.stacks_list(self.request, - sort_dir=sort_dir, - sort_key=sort_key, - paginate=True) - expected_stacks = api_stacks[:page_size] - self.assertItemsEqual(stacks, expected_stacks) - self.assertFalse(has_more) - self.assertFalse(has_prev) - - @override_settings(API_RESULT_PAGE_SIZE=10) - def test_stack_list_pagination_equal_page_size(self): - api_stacks = self.stacks.list() - page_size = settings.API_RESULT_PAGE_SIZE - sort_dir = 'desc' - sort_key = 'created_at' - - heatclient = self.stub_heatclient() - heatclient.stacks = self.mox.CreateMockAnything() - heatclient.stacks.list(limit=page_size + 1, - sort_dir=sort_dir, - sort_key=sort_key,) \ - .AndReturn(iter(api_stacks)) - self.mox.ReplayAll() - - stacks, has_more, has_prev = api.heat.stacks_list(self.request, - sort_dir=sort_dir, - sort_key=sort_key, - paginate=True) - expected_stacks = api_stacks[:page_size] - self.assertItemsEqual(stacks, expected_stacks) - self.assertFalse(has_more) - self.assertFalse(has_prev) - - @override_settings(API_RESULT_PAGE_SIZE=2) - def test_stack_list_pagination_marker(self): - page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 20) - sort_dir = 'desc' - sort_key = 'created_at' - marker = 'nonsense' - - api_stacks = self.stacks.list() - - heatclient = self.stub_heatclient() - heatclient.stacks = self.mox.CreateMockAnything() - heatclient.stacks.list(limit=page_size + 1, - marker=marker, - sort_dir=sort_dir, - sort_key=sort_key,) \ - .AndReturn(iter(api_stacks[:page_size + 1])) - self.mox.ReplayAll() - - stacks, has_more, has_prev = api.heat.stacks_list(self.request, - marker=marker, - paginate=True, - sort_dir=sort_dir, - sort_key=sort_key,) - - self.assertEqual(len(stacks), page_size) - self.assertItemsEqual(stacks, api_stacks[:page_size]) - self.assertTrue(has_more) - self.assertTrue(has_prev) - - @override_settings(API_RESULT_PAGE_SIZE=2) - def test_stack_list_pagination_marker_prev(self): - page_size = getattr(settings, 'API_RESULT_PAGE_SIZE', 20) - sort_dir = 'asc' - sort_key = 'created_at' - marker = 'nonsense' - - api_stacks = self.stacks.list() - - heatclient = self.stub_heatclient() - heatclient.stacks = self.mox.CreateMockAnything() - heatclient.stacks.list(limit=page_size + 1, - marker=marker, - sort_dir=sort_dir, - sort_key=sort_key,) \ - .AndReturn(iter(api_stacks[:page_size + 1])) - self.mox.ReplayAll() - - stacks, has_more, has_prev = api.heat.stacks_list(self.request, - marker=marker, - paginate=True, - sort_dir=sort_dir, - sort_key=sort_key,) - - self.assertEqual(len(stacks), page_size) - self.assertItemsEqual(stacks, api_stacks[:page_size]) - self.assertTrue(has_more) - self.assertTrue(has_prev) - - def test_template_get(self): - api_stacks = self.stacks.list() - stack_id = api_stacks[0].id - mock_data_template = self.stack_templates.list()[0] - - heatclient = self.stub_heatclient() - heatclient.stacks = self.mox.CreateMockAnything() - heatclient.stacks.template(stack_id).AndReturn(mock_data_template) - self.mox.ReplayAll() - - template = api.heat.template_get(self.request, stack_id) - self.assertEqual(mock_data_template.data, template.data) - - def test_stack_create(self): - api_stacks = self.stacks.list() - stack = api_stacks[0] - - heatclient = self.stub_heatclient() - heatclient.stacks = self.mox.CreateMockAnything() - form_data = {'timeout_mins': 600} - password = 'secret' - heatclient.stacks.create(**form_data).AndReturn(stack) - self.mox.ReplayAll() - - returned_stack = api.heat.stack_create(self.request, - password, - **form_data) - from heatclient.v1 import stacks - self.assertIsInstance(returned_stack, stacks.Stack) - - def test_stack_update(self): - api_stacks = self.stacks.list() - stack = api_stacks[0] - stack_id = stack.id - - heatclient = self.stub_heatclient() - heatclient.stacks = self.mox.CreateMockAnything() - form_data = {'timeout_mins': 600} - password = 'secret' - heatclient.stacks.update(stack_id, **form_data).AndReturn(stack) - self.mox.ReplayAll() - - returned_stack = api.heat.stack_update(self.request, - stack_id, - password, - **form_data) - from heatclient.v1 import stacks - self.assertIsInstance(returned_stack, stacks.Stack) - - def test_snapshot_create(self): - stack_id = self.stacks.first().id - snapshot_create = self.stack_snapshot_create.list()[0] - - heatclient = self.stub_heatclient() - heatclient.stacks = self.mox.CreateMockAnything() - heatclient.stacks.snapshot(stack_id).AndReturn(snapshot_create) - self.mox.ReplayAll() - - returned_snapshot_create_info = api.heat.snapshot_create(self.request, - stack_id) - - self.assertEqual(returned_snapshot_create_info, snapshot_create) - - def test_snapshot_list(self): - stack_id = self.stacks.first().id - snapshot_list = self.stack_snapshot.list() - - heatclient = self.stub_heatclient() - heatclient.stacks = self.mox.CreateMockAnything() - heatclient.stacks.snapshot_list(stack_id).AndReturn(snapshot_list) - self.mox.ReplayAll() - - returned_snapshots = api.heat.snapshot_list(self.request, stack_id) - - self.assertItemsEqual(returned_snapshots, snapshot_list) - - def test_get_template_files_with_template_data(self): - tmpl = ''' - # comment - - heat_template_version: 2013-05-23 - resources: - server1: - type: OS::Nova::Server - properties: - flavor: m1.medium - image: cirros - ''' - expected_files = {} - files = api.heat.get_template_files(template_data=tmpl)[0] - self.assertEqual(files, expected_files) - - def test_get_template_files(self): - tmpl = ''' - # comment - - heat_template_version: 2013-05-23 - resources: - server1: - type: OS::Nova::Server - properties: - flavor: m1.medium - image: cirros - user_data_format: RAW - user_data: - get_file: http://test.example/example - ''' - expected_files = {u'http://test.example/example': b'echo "test"'} - url = 'http://test.example/example' - data = b'echo "test"' - self.mox.StubOutWithMock(six.moves.urllib.request, 'urlopen') - six.moves.urllib.request.urlopen(url).AndReturn( - six.BytesIO(data)) - self.mox.ReplayAll() - files = api.heat.get_template_files(template_data=tmpl)[0] - self.assertEqual(files, expected_files) - - def test_get_template_files_with_template_url(self): - url = 'https://test.example/example.yaml' - data = b''' - # comment - - heat_template_version: 2013-05-23 - resources: - server1: - type: OS::Nova::Server - properties: - flavor: m1.medium - image: cirros - user_data_format: RAW - user_data: - get_file: http://test.example/example - ''' - url2 = 'http://test.example/example' - data2 = b'echo "test"' - expected_files = {'http://test.example/example': b'echo "test"'} - self.mox.StubOutWithMock(six.moves.urllib.request, 'urlopen') - six.moves.urllib.request.urlopen(url).AndReturn( - six.BytesIO(data)) - six.moves.urllib.request.urlopen(url2).AndReturn( - six.BytesIO(data2)) - self.mox.ReplayAll() - files = api.heat.get_template_files(template_url=url)[0] - self.assertEqual(files, expected_files) - - def test_get_template_files_invalid(self): - tmpl = ''' - # comment - - heat_template_version: 2013-05-23 - resources: - server1: - type: OS::Nova::Server - properties: - flavor: m1.medium - image: cirros - user_data_format: RAW - user_data: - get_file: file:///example - ''' - try: - api.heat.get_template_files(template_data=tmpl)[0] - except exceptions.GetFileError: - self.assertRaises(exceptions.GetFileError) - - def test_template_version_list(self): - api_template_versions = self.template_versions.list() - - heatclient = self.stub_heatclient() - heatclient.template_versions = self.mox.CreateMockAnything() - heatclient.template_versions.list().AndReturn(api_template_versions) - self.mox.ReplayAll() - - template_versions = api.heat.template_version_list(self.request) - - self.assertItemsEqual(template_versions, api_template_versions) - - def test_template_function_list(self): - template_version = self.template_versions.first().version - api_template_functions = self.template_functions.list() - - heatclient = self.stub_heatclient() - heatclient.template_versions = self.mox.CreateMockAnything() - heatclient.template_versions.get( - template_version).AndReturn(api_template_functions) - self.mox.ReplayAll() - - template_functions = api.heat.template_function_list( - self.request, template_version) - - self.assertItemsEqual(template_functions, api_template_functions) diff --git a/openstack_dashboard/test/helpers.py b/openstack_dashboard/test/helpers.py index 1b8fe87614..8970c3a1be 100644 --- a/openstack_dashboard/test/helpers.py +++ b/openstack_dashboard/test/helpers.py @@ -37,7 +37,6 @@ from django.utils import http from cinderclient import client as cinder_client import glanceclient -from heatclient import client as heat_client from keystoneclient.v2_0 import client as keystone_client import mock from mox3 import mox @@ -433,7 +432,6 @@ class APITestCase(TestCase): self._original_novaclient = api.nova.novaclient self._original_neutronclient = api.neutron.neutronclient self._original_cinderclient = api.cinder.cinderclient - self._original_heatclient = api.heat.heatclient # Replace the clients with our stubs. api.glance.glanceclient = fake_glanceclient @@ -441,8 +439,6 @@ class APITestCase(TestCase): api.nova.novaclient = fake_novaclient api.neutron.neutronclient = lambda request: self.stub_neutronclient() api.cinder.cinderclient = lambda request: self.stub_cinderclient() - api.heat.heatclient = (lambda request, password=None: - self.stub_heatclient()) def tearDown(self): super(APITestCase, self).tearDown() @@ -451,7 +447,6 @@ class APITestCase(TestCase): api.keystone.keystoneclient = self._original_keystoneclient api.neutron.neutronclient = self._original_neutronclient api.cinder.cinderclient = self._original_cinderclient - api.heat.heatclient = self._original_heatclient def stub_novaclient(self): if not hasattr(self, "novaclient"): @@ -518,12 +513,6 @@ class APITestCase(TestCase): expected_calls -= 1 return self.swiftclient - def stub_heatclient(self): - if not hasattr(self, "heatclient"): - self.mox.StubOutWithMock(heat_client, 'Client') - self.heatclient = self.mox.CreateMock(heat_client.Client) - return self.heatclient - class APIMockTestCase(APITestCase): def stub_cinderclient(self): diff --git a/openstack_dashboard/test/integration_tests/config.py b/openstack_dashboard/test/integration_tests/config.py index 668bc1d321..43cb7aab49 100644 --- a/openstack_dashboard/test/integration_tests/config.py +++ b/openstack_dashboard/test/integration_tests/config.py @@ -80,8 +80,6 @@ NetworkGroup = [ AvailableServiceGroup = [ cfg.BoolOpt('neutron', default=True), - cfg.BoolOpt('heat', - default=True), ] SeleniumGroup = [ diff --git a/openstack_dashboard/test/integration_tests/horizon.conf b/openstack_dashboard/test/integration_tests/horizon.conf index a83249956a..9f5de9fcf1 100644 --- a/openstack_dashboard/test/integration_tests/horizon.conf +++ b/openstack_dashboard/test/integration_tests/horizon.conf @@ -77,8 +77,6 @@ tenant_network_cidr=10.100.0.0/16 [service_available] # Whether is Neutron expected to be available (boolean value) neutron=True -# Whether is Heat expected to be available (boolean value) -heat=True [scenario] # ssh username for image file (string value) diff --git a/openstack_dashboard/test/integration_tests/pages/navigation.py b/openstack_dashboard/test/integration_tests/pages/navigation.py index d348a4f7ad..9791782bda 100644 --- a/openstack_dashboard/test/integration_tests/pages/navigation.py +++ b/openstack_dashboard/test/integration_tests/pages/navigation.py @@ -106,13 +106,6 @@ class Navigation(object): "Containers", ) }, - "Orchestration": - { - ITEMS: - ( - "Stacks", - ) - } }, "Admin": { diff --git a/openstack_dashboard/test/integration_tests/pages/project/orchestration/__init__.py b/openstack_dashboard/test/integration_tests/pages/project/orchestration/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/openstack_dashboard/test/integration_tests/pages/project/orchestration/stackspage.py b/openstack_dashboard/test/integration_tests/pages/project/orchestration/stackspage.py deleted file mode 100644 index 3b83d03644..0000000000 --- a/openstack_dashboard/test/integration_tests/pages/project/orchestration/stackspage.py +++ /dev/null @@ -1,99 +0,0 @@ -# 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. - -from openstack_dashboard.test.integration_tests import config -from openstack_dashboard.test.integration_tests.pages import basepage -from openstack_dashboard.test.integration_tests.regions import forms -from openstack_dashboard.test.integration_tests.regions import tables - - -class StacksTable(tables.TableRegion): - name = "stacks" - SELECT_TEMPLATE_FORM_FIELDS = ("template_source", "template_upload", - "template_data", "template_url", - "environment_source", "environment_upload", - "environment_data") - LAUNCH_STACK_FORM_FIELDS = ("stack_name", "timeout_mins", - "enable_rollback", "password") - - @tables.bind_table_action('launch') - def select_template(self, launch_button): - launch_button.click() - return forms.FormRegion( - self.driver, self.conf, - field_mappings=self.SELECT_TEMPLATE_FORM_FIELDS) - - def launch_stack(self): - return forms.FormRegion(self.driver, self.conf, - field_mappings=self.LAUNCH_STACK_FORM_FIELDS) - - @tables.bind_table_action('delete') - def delete_stack(self, delete_button): - delete_button.click() - return forms.BaseFormRegion(self.driver, self.conf) - - -class StacksPage(basepage.BaseNavigationPage): - DEFAULT_TEMPLATE_SOURCE = 'raw' - - CONFIG = config.get_config() - DEFAULT_PASSWORD = CONFIG.identity.admin_password - STACKS_TABLE_NAME_COLUMN = 'name' - STACKS_TABLE_STATUS_COLUMN = 'stack_status' - - def __init__(self, driver, conf): - super(StacksPage, self).__init__(driver, conf) - self._page_title = "Stacks" - - @property - def stacks_table(self): - return StacksTable(self.driver, self.conf) - - def _get_row_with_stack_name(self, name): - return self.stacks_table.get_row(self.STACKS_TABLE_NAME_COLUMN, name) - - def create_stack(self, stack_name, template_data, - template_source=DEFAULT_TEMPLATE_SOURCE, - environment_source=None, - environment_upload=None, - timeout_mins=None, - enable_rollback=None, - password=DEFAULT_PASSWORD): - select_template_form = self.stacks_table.select_template() - select_template_form.template_source.value = template_source - select_template_form.template_data.text = template_data - select_template_form.submit() - launch_stack_form = self.stacks_table.launch_stack() - launch_stack_form.stack_name.text = stack_name - launch_stack_form.password.text = password - launch_stack_form.submit() - - def delete_stack(self, name): - row = self._get_row_with_stack_name(name) - row.mark() - confirm_delete_stacks_form = self.stacks_table.delete_stack() - confirm_delete_stacks_form.submit() - - def is_stack_present(self, name): - return bool(self._get_row_with_stack_name(name)) - - def is_stack_create_complete(self, name): - def cell_getter(): - row = self._get_row_with_stack_name(name) - return row and row.cells[self.STACKS_TABLE_STATUS_COLUMN] - - return bool(self.stacks_table.wait_cell_status(cell_getter, - 'Create Complete')) - - def is_stack_deleted(self, name): - return self.stacks_table.is_row_deleted( - lambda: self._get_row_with_stack_name(name)) diff --git a/openstack_dashboard/test/integration_tests/tests/test_stacks.py b/openstack_dashboard/test/integration_tests/tests/test_stacks.py deleted file mode 100644 index 7c9ff95ba0..0000000000 --- a/openstack_dashboard/test/integration_tests/tests/test_stacks.py +++ /dev/null @@ -1,73 +0,0 @@ -# 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 os - -from openstack_dashboard.test.integration_tests import decorators -from openstack_dashboard.test.integration_tests import helpers -from openstack_dashboard.test.integration_tests.regions import messages - - -class TestStacks(helpers.AdminTestCase): - KEYPAIR_NAME = 'keypair_for_stack' - STACKS_NAME = helpers.gen_random_resource_name('stack', timestamp=False) - STACK_TEMPLATE_PATH = os.path.join( - os.path.dirname(__file__), 'test-data/stack_template') - - def setUp(self): - super(TestStacks, self).setUp() - keypair_page = self.home_pg.\ - go_to_compute_accessandsecurity_keypairspage() - keypair_page.create_keypair(self.KEYPAIR_NAME) - keypair_page = self.home_pg.\ - go_to_compute_accessandsecurity_keypairspage() - self.assertTrue(keypair_page.is_keypair_present(self.KEYPAIR_NAME)) - - def cleanup(): - keypair_page = self.home_pg.\ - go_to_compute_accessandsecurity_keypairspage() - keypair_page.delete_keypairs(self.KEYPAIR_NAME) - keypair_page.find_message_and_dismiss(messages.SUCCESS) - - self.addCleanup(cleanup) - - @decorators.skip_because(bugs=['1584057']) - @decorators.services_required("heat") - def test_create_delete_stack(self): - """tests the stack creation and deletion functionality - - * creates a new stack - * verifies the stack appears in the stacks table in Create Complete - state - * deletes the newly created stack - * verifies the stack does not appear in the table after deletion - """ - with open(self.STACK_TEMPLATE_PATH, 'r') as f: - template = f.read() - input_template = template.format(self.KEYPAIR_NAME, - self.CONFIG.image.images_list[0], - "public") - stacks_page = self.home_pg.go_to_orchestration_stackspage() - - stacks_page.create_stack(self.STACKS_NAME, input_template) - self.assertTrue( - stacks_page.find_message_and_dismiss(messages.INFO)) - self.assertFalse( - stacks_page.find_message_and_dismiss(messages.ERROR)) - self.assertTrue(stacks_page.is_stack_present(self.STACKS_NAME)) - self.assertTrue(stacks_page.is_stack_create_complete(self.STACKS_NAME)) - - stacks_page.delete_stack(self.STACKS_NAME) - self.assertTrue( - stacks_page.find_message_and_dismiss(messages.SUCCESS)) - self.assertFalse( - stacks_page.find_message_and_dismiss(messages.ERROR)) - self.assertTrue(stacks_page.is_stack_deleted(self.STACKS_NAME)) diff --git a/openstack_dashboard/test/test_data/exceptions.py b/openstack_dashboard/test/test_data/exceptions.py index 8f6e90bf10..9364d6379c 100644 --- a/openstack_dashboard/test/test_data/exceptions.py +++ b/openstack_dashboard/test/test_data/exceptions.py @@ -14,7 +14,6 @@ from cinderclient import exceptions as cinder_exceptions import glanceclient.exc as glance_exceptions -import heatclient.exc as heat_exceptions from keystoneclient import exceptions as keystone_exceptions from neutronclient.common import exceptions as neutron_exceptions from novaclient import exceptions as nova_exceptions @@ -81,6 +80,3 @@ def data(TEST): cinder_exception = cinder_exceptions.BadRequest TEST.exceptions.cinder = create_stubbed_exception(cinder_exception) - - heat_exception = heat_exceptions.HTTPException - TEST.exceptions.heat = create_stubbed_exception(heat_exception) diff --git a/openstack_dashboard/test/test_data/heat_data.py b/openstack_dashboard/test/test_data/heat_data.py deleted file mode 100644 index 61ea1b91d1..0000000000 --- a/openstack_dashboard/test/test_data/heat_data.py +++ /dev/null @@ -1,617 +0,0 @@ -# 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. - -from heatclient.v1 import resource_types -from heatclient.v1 import resources -from heatclient.v1 import services -from heatclient.v1 import stacks -from heatclient.v1 import template_versions - -from openstack_dashboard.test.test_data import utils - -# A slightly hacked up copy of a sample cloudformation template for testing. -TEMPLATE = """ -{ -"AWSTemplateFormatVersion": "2010-09-09", -"Description": "AWS CloudFormation Sample Template.", -"Parameters": { -"KeyName": { -"Description": "Name of an EC2 Key Pair to enable SSH access to the instances", -"Type": "String" -}, -"InstanceType": { -"Description": "WebServer EC2 instance type", -"Type": "String", -"Default": "m1.small", -"AllowedValues": [ -"m1.tiny", -"m1.small", -"m1.medium", -"m1.large", -"m1.xlarge" -], -"ConstraintDescription": "must be a valid EC2 instance type." -}, -"DBName": { -"Default": "wordpress", -"Description": "The WordPress database name", -"Type": "String", -"MinLength": "1", -"MaxLength": "64", -"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*", -"ConstraintDescription": "must begin with a letter and..." -}, -"DBUsername": { -"Default": "admin", -"NoEcho": "true", -"Description": "The WordPress database admin account username", -"Type": "String", -"MinLength": "1", -"MaxLength": "16", -"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*", -"ConstraintDescription": "must begin with a letter and..." -}, -"DBPassword": { -"Default": "admin", -"NoEcho": "true", -"Description": "The WordPress database admin account password", -"Type": "String", -"MinLength": "1", -"MaxLength": "41", -"AllowedPattern": "[a-zA-Z0-9]*", -"ConstraintDescription": "must contain only alphanumeric characters." -}, -"DBRootPassword": { -"Default": "admin", -"NoEcho": "true", -"Description": "Root password for MySQL", -"Type": "String", -"MinLength": "1", -"MaxLength": "41", -"AllowedPattern": "[a-zA-Z0-9]*", -"ConstraintDescription": "must contain only alphanumeric characters." -}, -"LinuxDistribution": { -"Default": "F17", -"Description": "Distribution of choice", -"Type": "String", -"AllowedValues": [ -"F18", -"F17", -"U10", -"RHEL-6.1", -"RHEL-6.2", -"RHEL-6.3" -] -}, -"Network": { -"Type": "String", -"CustomConstraint": "neutron.network" -} -}, -"Mappings": { -"AWSInstanceType2Arch": { -"m1.tiny": { -"Arch": "32" -}, -"m1.small": { -"Arch": "64" -}, -"m1.medium": { -"Arch": "64" -}, -"m1.large": { -"Arch": "64" -}, -"m1.xlarge": { -"Arch": "64" -} -}, -"DistroArch2AMI": { -"F18": { -"32": "F18-i386-cfntools", -"64": "F18-x86_64-cfntools" -}, -"F17": { -"32": "F17-i386-cfntools", -"64": "F17-x86_64-cfntools" -}, -"U10": { -"32": "U10-i386-cfntools", -"64": "U10-x86_64-cfntools" -}, -"RHEL-6.1": { -"32": "rhel61-i386-cfntools", -"64": "rhel61-x86_64-cfntools" -}, -"RHEL-6.2": { -"32": "rhel62-i386-cfntools", -"64": "rhel62-x86_64-cfntools" -}, -"RHEL-6.3": { -"32": "rhel63-i386-cfntools", -"64": "rhel63-x86_64-cfntools" -} -} -}, -"Resources": { -"WikiDatabase": { -"Type": "AWS::EC2::Instance", -"Metadata": { -"AWS::CloudFormation::Init": { -"config": { -"packages": { -"yum": { -"mysql": [], -"mysql-server": [], -"httpd": [], -"wordpress": [] -} -}, -"services": { -"systemd": { -"mysqld": { -"enabled": "true", -"ensureRunning": "true" -}, -"httpd": { -"enabled": "true", -"ensureRunning": "true" -} -} -} -} -} -}, -"Properties": { -"ImageId": { -"Fn::FindInMap": [ -"DistroArch2AMI", -{ -"Ref": "LinuxDistribution" -}, -{ -"Fn::FindInMap": [ -"AWSInstanceType2Arch", -{ -"Ref": "InstanceType" -}, -"Arch" -] -} -] -}, -"InstanceType": { -"Ref": "InstanceType" -}, -"KeyName": { -"Ref": "KeyName" -}, -"UserData": { -"Fn::Base64": { -"Fn::Join": [ -"", -[ -"#!/bin/bash -v\\n", -"/opt/aws/bin/cfn-init\\n" -] -] -} -} -} -} -}, -"Outputs": { -"WebsiteURL": { -"Value": { -"Fn::Join": [ -"", -[ -"http://", -{ -"Fn::GetAtt": [ -"WikiDatabase", -"PublicIp" -] -}, -"/wordpress" -] -] -}, -"Description": "URL for Wordpress wiki" -} -} -} -""" - -VALIDATE = """ -{ -"Description": "AWS CloudFormation Sample Template.", -"Parameters": { -"DBUsername": { -"Type": "String", -"Description": "The WordPress database admin account username", -"Default": "admin", -"MinLength": "1", -"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*", -"NoEcho": "true", -"MaxLength": "16", -"ConstraintDescription": "must begin with a letter and..." -}, -"LinuxDistribution": { -"Default": "F17", -"Type": "String", -"Description": "Distribution of choice", -"AllowedValues": [ -"F18", -"F17", -"U10", -"RHEL-6.1", -"RHEL-6.2", -"RHEL-6.3" -] -}, -"DBRootPassword": { -"Type": "String", -"Description": "Root password for MySQL", -"Default": "admin", -"MinLength": "1", -"AllowedPattern": "[a-zA-Z0-9]*", -"NoEcho": "true", -"MaxLength": "41", -"ConstraintDescription": "must contain only alphanumeric characters." -}, -"KeyName": { -"Type": "String", -"Description": "Name of an EC2 Key Pair to enable SSH access to the instances" -}, -"DBName": { -"Type": "String", -"Description": "The WordPress database name", -"Default": "wordpress", -"MinLength": "1", -"AllowedPattern": "[a-zA-Z][a-zA-Z0-9]*", -"MaxLength": "64", -"ConstraintDescription": "must begin with a letter and..." -}, -"DBPassword": { -"Type": "String", -"Description": "The WordPress database admin account password", -"Default": "admin", -"MinLength": "1", -"AllowedPattern": "[a-zA-Z0-9]*", -"NoEcho": "true", -"MaxLength": "41", -"ConstraintDescription": "must contain only alphanumeric characters." -}, -"InstanceType": { -"Default": "m1.small", -"Type": "String", -"ConstraintDescription": "must be a valid EC2 instance type.", -"Description": "WebServer EC2 instance type", -"AllowedValues": [ -"m1.tiny", -"m1.small", -"m1.medium", -"m1.large", -"m1.xlarge" -] -}, -"Network": { -"Type": "String", -"CustomConstraint": "neutron.network" -} -} -} -""" - -ENVIRONMENT = """ -parameters: - InstanceType: m1.xsmall - db_password: verybadpass - KeyName: heat_key -""" - -SNAPSHOT_CREATE = """ -{ - "status": "IN_PROGRESS", - "name": "None", - "data": "None", - "creation_time": "2016-02-19T07:25:23.494152", - "status_reason": "None", - "id": "8af90c07-b788-44ee-a8ab-5990197f5e32" -} -""" - - -class Environment(object): - def __init__(self, data): - self.data = data - - -class Template(object): - def __init__(self, data, validate): - self.data = data - self.validate = validate - - -class Snapshot(object): - def __init__(self, data): - self.data = data - - -def data(TEST): - TEST.stacks = utils.TestDataContainer() - TEST.stack_templates = utils.TestDataContainer() - TEST.stack_environments = utils.TestDataContainer() - TEST.stack_snapshot_create = utils.TestDataContainer() - TEST.stack_snapshot = utils.TestDataContainer() - TEST.resource_types = utils.TestDataContainer() - TEST.heat_resources = utils.TestDataContainer() - TEST.heat_services = utils.TestDataContainer() - TEST.template_versions = utils.TestDataContainer() - TEST.template_functions = utils.TestDataContainer() - - # Services - service_1 = services.Service(services.ServiceManager(None), { - "status": "up", - "binary": "heat-engine", - "report_interval": 60, - "engine_id": "2f7b5a9b-c50b-4b01-8248-f89f5fb338d1", - "created_at": "2015-02-06T03:23:32.000000", - "hostname": "mrkanag", - "updated_at": "2015-02-20T09:49:52.000000", - "topic": "engine", - "host": "engine-1", - "deleted_at": None, - "id": "1efd7015-5016-4caa-b5c8-12438af7b100" - }) - - service_2 = services.Service(services.ServiceManager(None), { - "status": "up", - "binary": "heat-engine", - "report_interval": 60, - "engine_id": "2f7b5a9b-c50b-4b01-8248-f89f5fb338d2", - "created_at": "2015-02-06T03:23:32.000000", - "hostname": "mrkanag", - "updated_at": "2015-02-20T09:49:52.000000", - "topic": "engine", - "host": "engine-2", - "deleted_at": None, - "id": "1efd7015-5016-4caa-b5c8-12438af7b100" - }) - - TEST.heat_services.add(service_1) - TEST.heat_services.add(service_2) - - # Data return by heatclient. - TEST.api_resource_types = utils.TestDataContainer() - - for i in range(10): - stack_data = { - "description": "No description", - "links": [{ - "href": "http://192.168.1.70:8004/v1/" - "051c727ee67040d6a7b7812708485a97/" - "stacks/stack-test{0}/" - "05b4f39f-ea96-4d91-910c-e758c078a089{0}".format(i), - "rel": "self" - }], - "parameters": { - 'DBUsername': '******', - 'InstanceType': 'm1.small', - 'AWS::StackId': ( - 'arn:openstack:heat::2ce287:stacks/teststack/88553ec'), - 'DBRootPassword': '******', - 'AWS::StackName': "teststack{0}".format(i), - 'DBPassword': '******', - 'AWS::Region': 'ap-southeast-1', - 'DBName': u'wordpress' - }, - "stack_status_reason": "Stack successfully created", - "stack_name": "stack-test{0}".format(i), - "creation_time": "2013-04-22T00:11:39Z", - "updated_time": "2013-04-22T00:11:39Z", - "stack_status": "CREATE_COMPLETE", - "id": "05b4f39f-ea96-4d91-910c-e758c078a089{0}".format(i) - } - stack = stacks.Stack(stacks.StackManager(None), stack_data) - TEST.stacks.add(stack) - - for i in range(10): - snapshot_data = { - "status": "COMPLETE", - "name": 'null', - "data": { - "files": {}, - "status": "COMPLETE", - "name": "zhao3", - "tags": ["a", " 123", " b", " 456"], - "stack_user_project_id": "3cba4460875444049a2a7cc5420ccddb", - "environment": { - "encrypted_param_names": [], - "parameter_defaults": {}, - "event_sinks": [], - "parameters": {}, - "resource_registry": { - "resources": {} - } - }, - "template": { - "heat_template_version": "2013-05-23", - "description": - "HOT template for Test.", - "resources": { - "private_subnet": { - "type": "OS::Neutron::Subnet", - "properties": { - "network_id": {"get_resource": "private_net"}, - "cidr": "172.16.2.0/24", - "gateway_ip": "172.16.2.1" - } - }, - "private_net": { - "type": "OS::Neutron::Net", - "properties": {"name": "private-net"} - } - } - }, - "action": "SNAPSHOT", - "project_id": "1acd0026829f4d28bb2eff912d7aad0d", - "id": "70650725-bdbd-419f-b53f-5707767bfe0e", - "resources": { - "private_subnet": { - "status": "COMPLETE", - "name": "private_subnet", - "resource_data": {}, - "resource_id": "9c7211b3-31c7-41f6-b92a-442ad3f71ef0", - "action": "SNAPSHOT", - "type": "OS::Neutron::Subnet", - "metadata": {} - }, - "private_net": { - "status": "COMPLETE", - "name": "private_net", - "resource_data": {}, - "resource_id": "ff4fd287-31b2-4d00-bc96-c409bc1db027", - "action": "SNAPSHOT", - "type": "OS::Neutron::Net", - "metadata": {} - } - } - }, - - "creation_time": "2016-02-21T04:02:54", - "status_reason": "Stack SNAPSHOT completed successfully", - "id": "01558a3b-ba05-4427-bbb4-1e4ab71cfca{0}".format(i) - } - TEST.stack_snapshot.add(snapshot_data) - - TEST.stack_templates.add(Template(TEMPLATE, VALIDATE)) - TEST.stack_environments.add(Environment(ENVIRONMENT)) - TEST.stack_snapshot_create.add(Snapshot(SNAPSHOT_CREATE)) - - # Resource types list - r_type_1 = { - "resource_type": "AWS::CloudFormation::Stack", - "attributes": {}, - "properties": { - "Parameters": { - "description": - "The set of parameters passed to this nested stack.", - "immutable": False, - "required": False, - "type": "map", - "update_allowed": True}, - "TemplateURL": { - "description": "The URL of a template that specifies" - " the stack to be created as a resource.", - "immutable": False, - "required": True, - "type": "string", - "update_allowed": True}, - "TimeoutInMinutes": { - "description": "The length of time, in minutes," - " to wait for the nested stack creation.", - "immutable": False, - "required": False, - "type": "number", - "update_allowed": True} - } - } - - r_type_2 = { - "resource_type": "OS::Heat::CloudConfig", - "attributes": { - "config": { - "description": "The config value of the software config."} - }, - "properties": { - "cloud_config": { - "description": "Map representing the cloud-config data" - " structure which will be formatted as YAML.", - "immutable": False, - "required": False, - "type": "map", - "update_allowed": False} - } - } - - r_types_list = [r_type_1, r_type_2] - - for rt in r_types_list: - r_type = resource_types.ResourceType( - resource_types.ResourceTypeManager(None), rt['resource_type']) - TEST.resource_types.add(r_type) - TEST.api_resource_types.add(rt) - - # Resources - resource_1 = resources.Resource(resources.ResourceManager(None), { - "logical_resource_id": "my_resource", - "physical_resource_id": "7b5e29b1-c94d-402d-b69c-df9ac6dfc0ce", - "resource_name": "my_resource", - "links": [ - { - "href": "http://192.168.1.70:8004/v1/" - "051c727ee67040d6a7b7812708485a97/" - "stacks/%s/%s/resources/my_resource" % - (TEST.stacks.first().stack_name, - TEST.stacks.first().id), - "rel": "self" - }, - { - "href": "http://192.168.1.70:8004/v1/" - "051c727ee67040d6a7b7812708485a97/" - "stacks/%s/%s" % - (TEST.stacks.first().stack_name, - TEST.stacks.first().id), - "rel": "stack" - } - ], - "attributes": { - "metadata": {} - } - }) - - TEST.heat_resources.add(resource_1) - - # Template versions - template_version_1 = template_versions.TemplateVersion( - template_versions.TemplateVersionManager(None), { - "version": "HeatTemplateFormatVersion.2012-12-12", - "type": "cfn" - }) - - template_version_2 = template_versions.TemplateVersion( - template_versions.TemplateVersionManager(None), { - "version": "heat_template_version.2013-05-23", - "type": "hot" - }) - - TEST.template_versions.add(template_version_1) - TEST.template_versions.add(template_version_2) - - # Template functions - template_function_1 = template_versions.TemplateVersion( - template_versions.TemplateVersionManager(None), { - "functions": "Fn::GetAZs", - "description": "A function for retrieving the availability zones." - }) - - template_function_2 = template_versions.TemplateVersion( - template_versions.TemplateVersionManager(None), { - "functions": "Fn::Join", - "description": "A function for joining strings." - }) - - TEST.template_functions.add(template_function_1) - TEST.template_functions.add(template_function_2) diff --git a/openstack_dashboard/test/test_data/keystone_data.py b/openstack_dashboard/test/test_data/keystone_data.py index 2a37f20996..a43d56565d 100644 --- a/openstack_dashboard/test/test_data/keystone_data.py +++ b/openstack_dashboard/test/test_data/keystone_data.py @@ -103,14 +103,6 @@ SERVICE_CATALOG = [ "adminURL": "http://admin.nova.example.com:8773/services/Admin", "publicURL": "http://public.nova.example.com:8773/services/Cloud", "internalURL": "http://int.nova.example.com:8773/services/Cloud"}]}, - {"type": "orchestration", - "name": "Heat", - "endpoints_links": [], - "endpoints": [ - {"region": "RegionOne", - "adminURL": "http://admin.heat.example.com:8004/v1", - "publicURL": "http://public.heat.example.com:8004/v1", - "internalURL": "http://int.heat.example.com:8004/v1"}]} ] diff --git a/openstack_dashboard/test/test_data/utils.py b/openstack_dashboard/test/test_data/utils.py index 4337bf0c0b..79c2f84f4b 100644 --- a/openstack_dashboard/test/test_data/utils.py +++ b/openstack_dashboard/test/test_data/utils.py @@ -17,7 +17,6 @@ def load_test_data(load_onto=None): from openstack_dashboard.test.test_data import cinder_data from openstack_dashboard.test.test_data import exceptions from openstack_dashboard.test.test_data import glance_data - from openstack_dashboard.test.test_data import heat_data from openstack_dashboard.test.test_data import keystone_data from openstack_dashboard.test.test_data import neutron_data from openstack_dashboard.test.test_data import nova_data @@ -32,7 +31,6 @@ def load_test_data(load_onto=None): cinder_data.data, neutron_data.data, swift_data.data, - heat_data.data, ) if load_onto: for data_func in loaders: diff --git a/releasenotes/notes/heat-panel-splitout-b609b157aa4bf29b.yaml b/releasenotes/notes/heat-panel-splitout-b609b157aa4bf29b.yaml new file mode 100644 index 0000000000..69a8ad3fc7 --- /dev/null +++ b/releasenotes/notes/heat-panel-splitout-b609b157aa4bf29b.yaml @@ -0,0 +1,11 @@ +--- +upgrade: + - | + Heat dashboard is now split out into a separate project + ``heat-dashboard``. All new features and maintenances are + provided from the new project from now on. The new project provides + all features available in Horizon in the past release. + To continue to use heat dashboard, install ``heat-dashboard`` + and set up the horizon plugin configuration file in ``enabled`` directory. + For more information, see ``heat-dashboard`` documentation + https://docs.openstack.org/heat-dashboard/latest/. diff --git a/requirements.txt b/requirements.txt index f6c26ac45d..31cacf34b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -30,7 +30,6 @@ pymongo!=3.1,>=3.0.2 # Apache-2.0 pyScss!=1.3.5,>=1.3.4 # MIT License python-cinderclient>=3.2.0 # Apache-2.0 python-glanceclient>=2.8.0 # Apache-2.0 -python-heatclient>=1.10.0 # Apache-2.0 python-keystoneclient>=3.8.0 # Apache-2.0 python-neutronclient>=6.3.0 # Apache-2.0 python-novaclient>=9.1.0 # Apache-2.0 diff --git a/tools/gate/integration/devstack_gate_rc b/tools/gate/integration/devstack_gate_rc index 1579b49f69..83eab88d03 100644 --- a/tools/gate/integration/devstack_gate_rc +++ b/tools/gate/integration/devstack_gate_rc @@ -1,4 +1,4 @@ # This file contains various customized Devstack settings that Horizon uses at # gate for integration tests job -export ENABLED_SERVICES=heat,h-eng,h-api,h-api-cfn,h-api-cw +