diff options
94 files changed, 17 insertions, 5977 deletions
diff --git a/doc/source/admin/manage-services.rst b/doc/source/admin/manage-services.rst index 7afaae7..d611911 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. | |||
30 | * :guilabel:`Network Agents`: | 30 | * :guilabel:`Network Agents`: |
31 | Displays the network agents active within the cluster, such as L3 and | 31 | Displays the network agents active within the cluster, such as L3 and |
32 | DHCP agents, and the status of each agent. | 32 | DHCP agents, and the status of each agent. |
33 | |||
34 | * :guilabel:`Orchestration Services`: | ||
35 | Displays information specific to the Orchestration service. Name, | ||
36 | engine id, host and topic are listed for each service, as well as its | ||
37 | activation status. | ||
diff --git a/doc/source/configuration/customizing.rst b/doc/source/configuration/customizing.rst index 89ff916..bff71f3 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:: | |||
116 | 116 | ||
117 | NO = lambda *x: False | 117 | NO = lambda *x: False |
118 | 118 | ||
119 | tabs.HeatServiceTab.allowed = NO | ||
120 | tables.AssociateIP.allowed = NO | 119 | tables.AssociateIP.allowed = NO |
121 | tables.SimpleAssociateIP.allowed = NO | 120 | tables.SimpleAssociateIP.allowed = NO |
122 | tables.SimpleDisassociateIP.allowed = NO | 121 | tables.SimpleDisassociateIP.allowed = NO |
diff --git a/doc/source/configuration/settings.rst b/doc/source/configuration/settings.rst index e01f30e..791cc11 100644 --- a/doc/source/configuration/settings.rst +++ b/doc/source/configuration/settings.rst | |||
@@ -804,7 +804,6 @@ Default: | |||
804 | 'compute': 'nova_policy.json', | 804 | 'compute': 'nova_policy.json', |
805 | 'volume': 'cinder_policy.json', | 805 | 'volume': 'cinder_policy.json', |
806 | 'image': 'glance_policy.json', | 806 | 'image': 'glance_policy.json', |
807 | 'orchestration': 'heat_policy.json', | ||
808 | 'network': 'neutron_policy.json', | 807 | 'network': 'neutron_policy.json', |
809 | } | 808 | } |
810 | 809 | ||
@@ -1125,29 +1124,6 @@ Default: | |||
1125 | Used to customize features related to the image service, such as the list of | 1124 | Used to customize features related to the image service, such as the list of |
1126 | supported image formats. | 1125 | supported image formats. |
1127 | 1126 | ||
1128 | Heat | ||
1129 | ---- | ||
1130 | |||
1131 | OPENSTACK_HEAT_STACK | ||
1132 | ~~~~~~~~~~~~~~~~~~~~ | ||
1133 | |||
1134 | .. versionadded:: 9.0.0(Mitaka) | ||
1135 | |||
1136 | Default: | ||
1137 | |||
1138 | .. code-block:: python | ||
1139 | |||
1140 | { | ||
1141 | 'enable_user_pass': True | ||
1142 | } | ||
1143 | |||
1144 | A dictionary of settings to use with heat stacks. Currently, the only setting | ||
1145 | available is "enable_user_pass", which can be used to disable the password | ||
1146 | field while launching the stack. Currently HEAT API needs user password to | ||
1147 | perform all the heat operations because in HEAT API trusts is not enabled by | ||
1148 | default. So, this setting can be set as "False" in-case HEAT uses trusts by | ||
1149 | default otherwise it needs to be set as "True". | ||
1150 | |||
1151 | Keystone | 1127 | Keystone |
1152 | -------- | 1128 | -------- |
1153 | 1129 | ||
diff --git a/doc/source/contributor/quickstart.rst b/doc/source/contributor/quickstart.rst index b464a9e..e903139 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 | |||
83 | 83 | ||
84 | .. note:: | 84 | .. note:: |
85 | 85 | ||
86 | The default port for runserver is 8000 which is already consumed by | 86 | The default port for runserver is 8000 which might be already consumed by |
87 | heat-api-cfn in DevStack. If running in DevStack | 87 | heat-api-cfn in DevStack. If running in DevStack |
88 | ``tox -e runserver -- localhost:9000`` will start the test server at | 88 | ``tox -e runserver -- localhost:9000`` will start the test server at |
89 | ``http://localhost:9000`` | 89 | ``http://localhost:9000`` |
diff --git a/doc/source/contributor/ref/local_conf.rst b/doc/source/contributor/ref/local_conf.rst index 813a51f..7066712 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/ | |||
69 | SWIFT_REPLICAS=1 | 69 | SWIFT_REPLICAS=1 |
70 | SWIFT_DATA_DIR=$DEST/data/swift | 70 | SWIFT_DATA_DIR=$DEST/data/swift |
71 | 71 | ||
72 | # Enable Heat | ||
73 | enable_plugin heat https://git.openstack.org/openstack/heat | ||
74 | |||
75 | # Enable Neutron | 72 | # Enable Neutron |
76 | enable_plugin neutron https://git.openstack.org/openstack/neutron | 73 | enable_plugin neutron https://git.openstack.org/openstack/neutron |
77 | 74 | ||
diff --git a/doc/source/install/from-source.rst b/doc/source/install/from-source.rst index d34492c..c0ca1e1 100644 --- a/doc/source/install/from-source.rst +++ b/doc/source/install/from-source.rst | |||
@@ -22,7 +22,6 @@ System Requirements | |||
22 | 22 | ||
23 | * `cinder <https://docs.openstack.org/cinder/latest/>`_: Block Storage | 23 | * `cinder <https://docs.openstack.org/cinder/latest/>`_: Block Storage |
24 | * `glance <https://docs.openstack.org/glance/latest/>`_: Image Management | 24 | * `glance <https://docs.openstack.org/glance/latest/>`_: Image Management |
25 | * `heat <https://docs.openstack.org/heat/latest/>`_: Orchestration | ||
26 | * `neutron <https://docs.openstack.org/neutron/latest/>`_: Networking | 25 | * `neutron <https://docs.openstack.org/neutron/latest/>`_: Networking |
27 | * `nova <https://docs.openstack.org/nova/latest/>`_: Compute | 26 | * `nova <https://docs.openstack.org/nova/latest/>`_: Compute |
28 | * `swift <https://docs.openstack.org/swift/latest/>`_: Object Storage | 27 | * `swift <https://docs.openstack.org/swift/latest/>`_: Object Storage |
diff --git a/doc/source/user/index.rst b/doc/source/user/index.rst index cde57d3..0112c3e 100644 --- a/doc/source/user/index.rst +++ b/doc/source/user/index.rst | |||
@@ -18,7 +18,6 @@ sizes of server instances. | |||
18 | manage-containers.rst | 18 | manage-containers.rst |
19 | manage-volumes.rst | 19 | manage-volumes.rst |
20 | manage-shares.rst | 20 | manage-shares.rst |
21 | stacks.rst | ||
22 | databases.rst | 21 | databases.rst |
23 | manage-lbaasv2.rst | 22 | manage-lbaasv2.rst |
24 | browser_support | 23 | browser_support |
diff --git a/doc/source/user/log-in.rst b/doc/source/user/log-in.rst index 0f80d6e..25b560f 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. | |||
50 | (:ref:`dashboard-admin-tab`) and :guilabel:`Identity` tab | 50 | (:ref:`dashboard-admin-tab`) and :guilabel:`Identity` tab |
51 | (:ref:`dashboard-identity-tab`) are displayed. | 51 | (:ref:`dashboard-identity-tab`) are displayed. |
52 | 52 | ||
53 | .. note:: | ||
54 | |||
55 | Some tabs, such as :guilabel:`Orchestration` and :guilabel:`Firewalls`, | ||
56 | only appear on the dashboard if they are properly configured. | ||
57 | |||
58 | .. _dashboard-project-tab: | 53 | .. _dashboard-project-tab: |
59 | 54 | ||
60 | OpenStack dashboard — Project tab | 55 | OpenStack dashboard — Project tab |
@@ -143,15 +138,6 @@ Network tab | |||
143 | 138 | ||
144 | * :guilabel:`Firewall Rules`: Add and manage firewall rules. | 139 | * :guilabel:`Firewall Rules`: Add and manage firewall rules. |
145 | 140 | ||
146 | Orchestration tab | ||
147 | ----------------- | ||
148 | |||
149 | * :guilabel:`Stacks`: Use the REST API to orchestrate multiple composite | ||
150 | cloud applications. | ||
151 | |||
152 | * :guilabel:`Resource Types`: Show a list of all the supported resource | ||
153 | types for HOT templates. | ||
154 | |||
155 | Object Store tab | 141 | Object Store tab |
156 | ---------------- | 142 | ---------------- |
157 | 143 | ||
@@ -234,9 +220,6 @@ System tab | |||
234 | 220 | ||
235 | * :guilabel:`Network Agents`: View the network agents. | 221 | * :guilabel:`Network Agents`: View the network agents. |
236 | 222 | ||
237 | * :guilabel:`Orchestration Services`: View a list of all Orchestration | ||
238 | services. | ||
239 | |||
240 | * :guilabel:`Shares`: Use the following tabs to complete these tasks: | 223 | * :guilabel:`Shares`: Use the following tabs to complete these tasks: |
241 | 224 | ||
242 | * :guilabel:`Shares`: View, create, manage, and delete shares. | 225 | * :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 7767976..0000000 --- a/doc/source/user/stacks.rst +++ /dev/null | |||
@@ -1,149 +0,0 @@ | |||
1 | ======================== | ||
2 | Launch and manage stacks | ||
3 | ======================== | ||
4 | |||
5 | OpenStack Orchestration is a service that you can use to | ||
6 | orchestrate multiple composite cloud applications. This | ||
7 | service supports the use of both the Amazon Web Services (AWS) | ||
8 | CloudFormation template format through both a Query API that | ||
9 | is compatible with CloudFormation and the native OpenStack | ||
10 | Heat Orchestration Template (HOT) format through a REST API. | ||
11 | |||
12 | These flexible template languages enable application | ||
13 | developers to describe and automate the deployment of | ||
14 | infrastructure, services, and applications. The templates | ||
15 | enable creation of most OpenStack resource types, such as | ||
16 | instances, floating IP addresses, volumes, security groups, | ||
17 | and users. Once created, the resources are referred to as | ||
18 | stacks. | ||
19 | |||
20 | The template languages are described in the `Template Guide | ||
21 | <https://docs.openstack.org/heat/latest/template_guide/>`_. | ||
22 | |||
23 | Launch a stack | ||
24 | ~~~~~~~~~~~~~~ | ||
25 | |||
26 | #. Log in to the dashboard. | ||
27 | #. Select the appropriate project from the drop down menu at the top left. | ||
28 | #. On the :guilabel:`Project` tab, open the :guilabel:`Orchestration` tab and | ||
29 | click :guilabel:`Stacks` category. | ||
30 | #. Click :guilabel:`Launch Stack`. | ||
31 | #. In the :guilabel:`Select Template` dialog box, specify the | ||
32 | following values: | ||
33 | |||
34 | +---------------------------------------+-------------------------------+ | ||
35 | | :guilabel:`Template Source` | Choose the source of the | | ||
36 | | | template from the list. | | ||
37 | +---------------------------------------+-------------------------------+ | ||
38 | | :guilabel:`Template URL/File/Data` | Depending on the source that | | ||
39 | | | you select, enter the URL, | | ||
40 | | | browse to the file location, | | ||
41 | | | or directly include the | | ||
42 | | | template. | | ||
43 | +---------------------------------------+-------------------------------+ | ||
44 | | :guilabel:`Environment Source` | Choose the source of the | | ||
45 | | | environment from the list. | | ||
46 | | | The environment files contain | | ||
47 | | | additional settings for the | | ||
48 | | | stack. | | ||
49 | +---------------------------------------+-------------------------------+ | ||
50 | | :guilabel:`Environment File/Data` | Depending on the source that | | ||
51 | | | you select, browse to the | | ||
52 | | | file location, directly | | ||
53 | | | include the environment | | ||
54 | +---------------------------------------+-------------------------------+ | ||
55 | |||
56 | #. Click :guilabel:`Next`. | ||
57 | #. In the :guilabel:`Launch Stack` dialog box, specify the | ||
58 | following values: | ||
59 | |||
60 | +---------------------------------+---------------------------------+ | ||
61 | | :guilabel:`Stack Name` | Enter a name to identify | | ||
62 | | | the stack. | | ||
63 | +---------------------------------+---------------------------------+ | ||
64 | | :guilabel:`Creation Timeout` | Specify the number of minutes | | ||
65 | | :guilabel:`(minutes)` | that can elapse before the | | ||
66 | | | launch of the stack times out. | | ||
67 | +---------------------------------+---------------------------------+ | ||
68 | | :guilabel:`Rollback On Failure` | Select this check box if you | | ||
69 | | | want the service to roll back | | ||
70 | | | changes if the stack fails to | | ||
71 | | | launch. | | ||
72 | +---------------------------------+---------------------------------+ | ||
73 | | :guilabel:`Password for user` | Specify the password that | | ||
74 | | :guilabel:`"demo"` | the default user uses when the | | ||
75 | | | stack is created. | | ||
76 | +---------------------------------+---------------------------------+ | ||
77 | | :guilabel:`DBUsername` | Specify the name of the | | ||
78 | | | database user. | | ||
79 | +---------------------------------+---------------------------------+ | ||
80 | | :guilabel:`LinuxDistribution` | Specify the Linux distribution | | ||
81 | | | that is used in the stack. | | ||
82 | +---------------------------------+---------------------------------+ | ||
83 | | :guilabel:`DBRootPassword` | Specify the root password for | | ||
84 | | | the database. | | ||
85 | +---------------------------------+---------------------------------+ | ||
86 | | :guilabel:`KeyName` | Specify the name of the key pair| | ||
87 | | | to use to log in to the stack. | | ||
88 | +---------------------------------+---------------------------------+ | ||
89 | | :guilabel:`DBName` | Specify the name of the | | ||
90 | | | database. | | ||
91 | +---------------------------------+---------------------------------+ | ||
92 | | :guilabel:`DBPassword` | Specify the password of the | | ||
93 | | | database. | | ||
94 | +---------------------------------+---------------------------------+ | ||
95 | | :guilabel:`InstanceType` | Specify the flavor for the | | ||
96 | | | instance. | | ||
97 | +---------------------------------+---------------------------------+ | ||
98 | |||
99 | #. Click :guilabel:`Launch` to create a stack. The :guilabel:`Stacks` | ||
100 | tab shows the stack. | ||
101 | |||
102 | After the stack is created, click on the stack name to see the | ||
103 | following details: | ||
104 | |||
105 | Topology | ||
106 | The topology of the stack. | ||
107 | |||
108 | Overview | ||
109 | The parameters and details of the stack. | ||
110 | |||
111 | Resources | ||
112 | The resources used by the stack. | ||
113 | |||
114 | Events | ||
115 | The events related to the stack. | ||
116 | |||
117 | Template | ||
118 | The template for the stack. | ||
119 | |||
120 | Manage a stack | ||
121 | ~~~~~~~~~~~~~~ | ||
122 | |||
123 | #. Log in to the dashboard. | ||
124 | #. Select the appropriate project from the drop down menu at the top left. | ||
125 | #. On the :guilabel:`Project` tab, open the :guilabel:`Orchestration` tab and | ||
126 | click :guilabel:`Stacks` category. | ||
127 | #. Select the stack that you want to update. | ||
128 | #. Click :guilabel:`Change Stack Template`. | ||
129 | #. In the :guilabel:`Select Template` dialog box, select the | ||
130 | new template source or environment source. | ||
131 | #. Click :guilabel:`Next`. | ||
132 | |||
133 | The :guilabel:`Update Stack Parameters` window appears. | ||
134 | #. Enter new values for any parameters that you want to update. | ||
135 | #. Click :guilabel:`Update`. | ||
136 | |||
137 | Delete a stack | ||
138 | ~~~~~~~~~~~~~~ | ||
139 | |||
140 | When you delete a stack, you cannot undo this action. | ||
141 | |||
142 | #. Log in to the dashboard. | ||
143 | #. Select the appropriate project from the drop down menu at the top left. | ||
144 | #. On the :guilabel:`Project` tab, open the :guilabel:`Orchestration` tab and | ||
145 | click :guilabel:`Stacks` category. | ||
146 | #. Select the stack that you want to delete. | ||
147 | #. Click :guilabel:`Delete Stack`. | ||
148 | #. In the confirmation dialog box, click :guilabel:`Delete Stack` | ||
149 | to confirm the deletion. | ||
diff --git a/openstack_dashboard/api/__init__.py b/openstack_dashboard/api/__init__.py index 6262470..9483b39 100644 --- a/openstack_dashboard/api/__init__.py +++ b/openstack_dashboard/api/__init__.py | |||
@@ -34,7 +34,6 @@ Keystone/Nova/Glance/Swift et. al. | |||
34 | from openstack_dashboard.api import base | 34 | from openstack_dashboard.api import base |
35 | from openstack_dashboard.api import cinder | 35 | from openstack_dashboard.api import cinder |
36 | from openstack_dashboard.api import glance | 36 | from openstack_dashboard.api import glance |
37 | from openstack_dashboard.api import heat | ||
38 | from openstack_dashboard.api import keystone | 37 | from openstack_dashboard.api import keystone |
39 | from openstack_dashboard.api import network | 38 | from openstack_dashboard.api import network |
40 | from openstack_dashboard.api import neutron | 39 | from openstack_dashboard.api import neutron |
@@ -46,7 +45,6 @@ __all__ = [ | |||
46 | "base", | 45 | "base", |
47 | "cinder", | 46 | "cinder", |
48 | "glance", | 47 | "glance", |
49 | "heat", | ||
50 | "keystone", | 48 | "keystone", |
51 | "network", | 49 | "network", |
52 | "neutron", | 50 | "neutron", |
diff --git a/openstack_dashboard/api/heat.py b/openstack_dashboard/api/heat.py deleted file mode 100644 index c31c61d..0000000 --- a/openstack_dashboard/api/heat.py +++ /dev/null | |||
@@ -1,265 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
2 | # not use this file except in compliance with the License. You may obtain | ||
3 | # a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
10 | # License for the specific language governing permissions and limitations | ||
11 | # under the License. | ||
12 | |||
13 | import contextlib | ||
14 | |||
15 | from django.conf import settings | ||
16 | from heatclient import client as heat_client | ||
17 | from heatclient.common import template_format | ||
18 | from heatclient.common import template_utils | ||
19 | from heatclient.common import utils as heat_utils | ||
20 | from oslo_serialization import jsonutils | ||
21 | import six | ||
22 | from six.moves.urllib import request | ||
23 | |||
24 | from horizon import exceptions | ||
25 | from horizon.utils import functions as utils | ||
26 | from horizon.utils.memoized import memoized | ||
27 | from openstack_dashboard.api import base | ||
28 | from openstack_dashboard.contrib.developer.profiler import api as profiler | ||
29 | |||
30 | |||
31 | def format_parameters(params): | ||
32 | parameters = {} | ||
33 | for count, p in enumerate(params, 1): | ||
34 | parameters['Parameters.member.%d.ParameterKey' % count] = p | ||
35 | parameters['Parameters.member.%d.ParameterValue' % count] = params[p] | ||
36 | return parameters | ||
37 | |||
38 | |||
39 | @memoized | ||
40 | def heatclient(request, password=None): | ||
41 | api_version = "1" | ||
42 | insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) | ||
43 | cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None) | ||
44 | endpoint = base.url_for(request, 'orchestration') | ||
45 | kwargs = { | ||
46 | 'token': request.user.token.id, | ||
47 | 'insecure': insecure, | ||
48 | 'ca_file': cacert, | ||
49 | 'username': request.user.username, | ||
50 | 'password': password | ||
51 | # 'timeout': args.timeout, | ||
52 | # 'ca_file': args.ca_file, | ||
53 | # 'cert_file': args.cert_file, | ||
54 | # 'key_file': args.key_file, | ||
55 | } | ||
56 | client = heat_client.Client(api_version, endpoint, **kwargs) | ||
57 | client.format_parameters = format_parameters | ||
58 | return client | ||
59 | |||
60 | |||
61 | @profiler.trace | ||
62 | def stacks_list(request, marker=None, sort_dir='desc', sort_key='created_at', | ||
63 | paginate=False, filters=None): | ||
64 | limit = getattr(settings, 'API_RESULT_LIMIT', 1000) | ||
65 | page_size = utils.get_page_size(request) | ||
66 | |||
67 | if paginate: | ||
68 | request_size = page_size + 1 | ||
69 | else: | ||
70 | request_size = limit | ||
71 | |||
72 | kwargs = {'sort_dir': sort_dir, 'sort_key': sort_key} | ||
73 | if marker: | ||
74 | kwargs['marker'] = marker | ||
75 | |||
76 | if filters: | ||
77 | kwargs.update(filters) | ||
78 | if 'status' in kwargs: | ||
79 | kwargs['status'] = kwargs['status'].replace(' ', '_').upper() | ||
80 | |||
81 | stacks_iter = heatclient(request).stacks.list(limit=request_size, | ||
82 | **kwargs) | ||
83 | |||
84 | has_prev_data = False | ||
85 | has_more_data = False | ||
86 | stacks = list(stacks_iter) | ||
87 | |||
88 | if paginate: | ||
89 | if len(stacks) > page_size: | ||
90 | stacks.pop() | ||
91 | has_more_data = True | ||
92 | if marker is not None: | ||
93 | has_prev_data = True | ||
94 | elif sort_dir == 'asc' and marker is not None: | ||
95 | has_more_data = True | ||
96 | elif marker is not None: | ||
97 | has_prev_data = True | ||
98 | return (stacks, has_more_data, has_prev_data) | ||
99 | |||
100 | |||
101 | def _ignore_if(key, value): | ||
102 | if key != 'get_file' and key != 'type': | ||
103 | return True | ||
104 | if not isinstance(value, six.string_types): | ||
105 | return True | ||
106 | if (key == 'type' and | ||
107 | not value.endswith(('.yaml', '.template'))): | ||
108 | return True | ||
109 | return False | ||
110 | |||
111 | |||
112 | @profiler.trace | ||
113 | def get_template_files(template_data=None, template_url=None): | ||
114 | if template_data: | ||
115 | tpl = template_data | ||
116 | elif template_url: | ||
117 | with contextlib.closing(request.urlopen(template_url)) as u: | ||
118 | tpl = u.read() | ||
119 | else: | ||
120 | return {}, None | ||
121 | if not tpl: | ||
122 | return {}, None | ||
123 | if isinstance(tpl, six.binary_type): | ||
124 | tpl = tpl.decode('utf-8') | ||
125 | template = template_format.parse(tpl) | ||
126 | files = {} | ||
127 | _get_file_contents(template, files) | ||
128 | return files, template | ||
129 | |||
130 | |||
131 | def _get_file_contents(from_data, files): | ||
132 | if not isinstance(from_data, (dict, list)): | ||
133 | return | ||
134 | if isinstance(from_data, dict): | ||
135 | recurse_data = from_data.values() | ||
136 | for key, value in from_data.items(): | ||
137 | if _ignore_if(key, value): | ||
138 | continue | ||
139 | if not value.startswith(('http://', 'https://')): | ||
140 | raise exceptions.GetFileError(value, 'get_file') | ||
141 | if value not in files: | ||
142 | file_content = heat_utils.read_url_content(value) | ||
143 | if template_utils.is_template(file_content): | ||
144 | template = get_template_files(template_url=value)[1] | ||
145 | file_content = jsonutils.dumps(template) | ||
146 | files[value] = file_content | ||
147 | else: | ||
148 | recurse_data = from_data | ||
149 | for value in recurse_data: | ||
150 | _get_file_contents(value, files) | ||
151 | |||
152 | |||
153 | @profiler.trace | ||
154 | def stack_delete(request, stack_id): | ||
155 | return heatclient(request).stacks.delete(stack_id) | ||
156 | |||
157 | |||
158 | @profiler.trace | ||
159 | def stack_get(request, stack_id): | ||
160 | return heatclient(request).stacks.get(stack_id) | ||
161 | |||
162 | |||
163 | @profiler.trace | ||
164 | def template_get(request, stack_id): | ||
165 | return heatclient(request).stacks.template(stack_id) | ||
166 | |||
167 | |||
168 | @profiler.trace | ||
169 | def stack_create(request, password=None, **kwargs): | ||
170 | return heatclient(request, password).stacks.create(**kwargs) | ||
171 | |||
172 | |||
173 | @profiler.trace | ||
174 | def stack_preview(request, password=None, **kwargs): | ||
175 | return heatclient(request, password).stacks.preview(**kwargs) | ||
176 | |||
177 | |||
178 | @profiler.trace | ||
179 | def stack_update(request, stack_id, password=None, **kwargs): | ||
180 | return heatclient(request, password).stacks.update(stack_id, **kwargs) | ||
181 | |||
182 | |||
183 | @profiler.trace | ||
184 | def snapshot_create(request, stack_id): | ||
185 | return heatclient(request).stacks.snapshot(stack_id) | ||
186 | |||
187 | |||
188 | @profiler.trace | ||
189 | def snapshot_list(request, stack_id): | ||
190 | return heatclient(request).stacks.snapshot_list(stack_id) | ||
191 | |||
192 | |||
193 | @profiler.trace | ||
194 | def snapshot_show(request, stack_id, snapshot_id): | ||
195 | return heatclient(request).stacks.snapshot_show(stack_id, snapshot_id) | ||
196 | |||
197 | |||
198 | @profiler.trace | ||
199 | def snapshot_delete(request, stack_id, snapshot_id): | ||
200 | return heatclient(request).stacks.snapshot_delete(stack_id, snapshot_id) | ||
201 | |||
202 | |||
203 | @profiler.trace | ||
204 | def events_list(request, stack_name): | ||
205 | return heatclient(request).events.list(stack_name) | ||
206 | |||
207 | |||
208 | @profiler.trace | ||
209 | def resources_list(request, stack_name): | ||
210 | return heatclient(request).resources.list(stack_name) | ||
211 | |||
212 | |||
213 | @profiler.trace | ||
214 | def resource_get(request, stack_id, resource_name): | ||
215 | return heatclient(request).resources.get(stack_id, resource_name) | ||
216 | |||
217 | |||
218 | @profiler.trace | ||
219 | def resource_metadata_get(request, stack_id, resource_name): | ||
220 | return heatclient(request).resources.metadata(stack_id, resource_name) | ||
221 | |||
222 | |||
223 | @profiler.trace | ||
224 | def template_validate(request, **kwargs): | ||
225 | return heatclient(request).stacks.validate(**kwargs) | ||
226 | |||
227 | |||
228 | @profiler.trace | ||
229 | def action_check(request, stack_id): | ||
230 | return heatclient(request).actions.check(stack_id) | ||
231 | |||
232 | |||
233 | @profiler.trace | ||
234 | def action_suspend(request, stack_id): | ||
235 | return heatclient(request).actions.suspend(stack_id) | ||
236 | |||
237 | |||
238 | @profiler.trace | ||
239 | def action_resume(request, stack_id): | ||
240 | return heatclient(request).actions.resume(stack_id) | ||
241 | |||
242 | |||
243 | @profiler.trace | ||
244 | def resource_types_list(request, filters=None): | ||
245 | return heatclient(request).resource_types.list(filters=filters) | ||
246 | |||
247 | |||
248 | @profiler.trace | ||
249 | def resource_type_get(request, resource_type): | ||
250 | return heatclient(request).resource_types.get(resource_type) | ||
251 | |||
252 | |||
253 | @profiler.trace | ||
254 | def service_list(request): | ||
255 | return heatclient(request).services.list() | ||
256 | |||
257 | |||
258 | @profiler.trace | ||
259 | def template_version_list(request): | ||
260 | return heatclient(request).template_versions.list() | ||
261 | |||
262 | |||
263 | @profiler.trace | ||
264 | def template_function_list(request, template_version): | ||
265 | 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 925cbb0..11df2d5 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. | |||
24 | from openstack_dashboard.api.rest import cinder | 24 | from openstack_dashboard.api.rest import cinder |
25 | from openstack_dashboard.api.rest import config | 25 | from openstack_dashboard.api.rest import config |
26 | from openstack_dashboard.api.rest import glance | 26 | from openstack_dashboard.api.rest import glance |
27 | from openstack_dashboard.api.rest import heat | ||
28 | from openstack_dashboard.api.rest import keystone | 27 | from openstack_dashboard.api.rest import keystone |
29 | from openstack_dashboard.api.rest import network | 28 | from openstack_dashboard.api.rest import network |
30 | from openstack_dashboard.api.rest import neutron | 29 | from openstack_dashboard.api.rest import neutron |
@@ -37,7 +36,6 @@ __all__ = [ | |||
37 | 'cinder', | 36 | 'cinder', |
38 | 'config', | 37 | 'config', |
39 | 'glance', | 38 | 'glance', |
40 | 'heat', | ||
41 | 'keystone', | 39 | 'keystone', |
42 | 'network', | 40 | 'network', |
43 | 'neutron', | 41 | 'neutron', |
diff --git a/openstack_dashboard/api/rest/heat.py b/openstack_dashboard/api/rest/heat.py deleted file mode 100644 index df8752f..0000000 --- a/openstack_dashboard/api/rest/heat.py +++ /dev/null | |||
@@ -1,51 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
2 | # you may not use this file except in compliance with the License. | ||
3 | # You may obtain a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
10 | # See the License for the specific language governing permissions and | ||
11 | # limitations under the License. | ||
12 | """API for the heat service.""" | ||
13 | |||
14 | from django.views import generic | ||
15 | |||
16 | from openstack_dashboard import api | ||
17 | from openstack_dashboard.api.rest import urls | ||
18 | from openstack_dashboard.api.rest import utils as rest_utils | ||
19 | |||
20 | |||
21 | @urls.register | ||
22 | class Validate(generic.View): | ||
23 | """API for validating a template""" | ||
24 | url_regex = r'heat/validate/$' | ||
25 | |||
26 | @rest_utils.ajax(data_required=True) | ||
27 | def post(self, request): | ||
28 | """Validate a template | ||
29 | |||
30 | The following parameters may be passed in the POST | ||
31 | application/json object. The parameters are: | ||
32 | request: | ||
33 | |||
34 | :param template_url: The template to validate | ||
35 | """ | ||
36 | return api.heat.template_validate(request, **(request.DATA)) | ||
37 | |||
38 | |||
39 | @urls.register | ||
40 | class Services(generic.View): | ||
41 | """API for heat services.""" | ||
42 | url_regex = r'heat/services/$' | ||
43 | |||
44 | @rest_utils.ajax() | ||
45 | def get(self, request): | ||
46 | """Get a list of heat services.""" | ||
47 | if api.base.is_service_enabled(request, 'orchestration'): | ||
48 | result = api.heat.service_list(request) | ||
49 | return {'items': [u.to_dict() for u in result]} | ||
50 | else: | ||
51 | 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 b40b1ee..0000000 --- a/openstack_dashboard/conf/heat_policy.json +++ /dev/null | |||
@@ -1,92 +0,0 @@ | |||
1 | { | ||
2 | "context_is_admin": "role:admin", | ||
3 | "deny_stack_user": "not role:heat_stack_user", | ||
4 | "deny_everybody": "!", | ||
5 | |||
6 | "cloudformation:ListStacks": "rule:deny_stack_user", | ||
7 | "cloudformation:CreateStack": "rule:deny_stack_user", | ||
8 | "cloudformation:DescribeStacks": "rule:deny_stack_user", | ||
9 | "cloudformation:DeleteStack": "rule:deny_stack_user", | ||
10 | "cloudformation:UpdateStack": "rule:deny_stack_user", | ||
11 | "cloudformation:CancelUpdateStack": "rule:deny_stack_user", | ||
12 | "cloudformation:DescribeStackEvents": "rule:deny_stack_user", | ||
13 | "cloudformation:ValidateTemplate": "rule:deny_stack_user", | ||
14 | "cloudformation:GetTemplate": "rule:deny_stack_user", | ||
15 | "cloudformation:EstimateTemplateCost": "rule:deny_stack_user", | ||
16 | "cloudformation:DescribeStackResource": "", | ||
17 | "cloudformation:DescribeStackResources": "rule:deny_stack_user", | ||
18 | "cloudformation:ListStackResources": "rule:deny_stack_user", | ||
19 | |||
20 | "cloudwatch:DeleteAlarms": "rule:deny_stack_user", | ||
21 | "cloudwatch:DescribeAlarmHistory": "rule:deny_stack_user", | ||
22 | "cloudwatch:DescribeAlarms": "rule:deny_stack_user", | ||
23 | "cloudwatch:DescribeAlarmsForMetric": "rule:deny_stack_user", | ||
24 | "cloudwatch:DisableAlarmActions": "rule:deny_stack_user", | ||
25 | "cloudwatch:EnableAlarmActions": "rule:deny_stack_user", | ||
26 | "cloudwatch:GetMetricStatistics": "rule:deny_stack_user", | ||
27 | "cloudwatch:ListMetrics": "rule:deny_stack_user", | ||
28 | "cloudwatch:PutMetricAlarm": "rule:deny_stack_user", | ||
29 | "cloudwatch:PutMetricData": "", | ||
30 | "cloudwatch:SetAlarmState": "rule:deny_stack_user", | ||
31 | |||
32 | "actions:action": "rule:deny_stack_user", | ||
33 | "build_info:build_info": "rule:deny_stack_user", | ||
34 | "events:index": "rule:deny_stack_user", | ||
35 | "events:show": "rule:deny_stack_user", | ||
36 | "resource:index": "rule:deny_stack_user", | ||
37 | "resource:metadata": "", | ||
38 | "resource:signal": "", | ||
39 | "resource:mark_unhealthy": "rule:deny_stack_user", | ||
40 | "resource:show": "rule:deny_stack_user", | ||
41 | "stacks:abandon": "rule:deny_stack_user", | ||
42 | "stacks:create": "rule:deny_stack_user", | ||
43 | "stacks:delete": "rule:deny_stack_user", | ||
44 | "stacks:detail": "rule:deny_stack_user", | ||
45 | "stacks:export": "rule:deny_stack_user", | ||
46 | "stacks:generate_template": "rule:deny_stack_user", | ||
47 | "stacks:global_index": "rule:deny_everybody", | ||
48 | "stacks:index": "rule:deny_stack_user", | ||
49 | "stacks:list_resource_types": "rule:deny_stack_user", | ||
50 | "stacks:list_template_versions": "rule:deny_stack_user", | ||
51 | "stacks:list_template_functions": "rule:deny_stack_user", | ||
52 | "stacks:lookup": "", | ||
53 | "stacks:preview": "rule:deny_stack_user", | ||
54 | "stacks:resource_schema": "rule:deny_stack_user", | ||
55 | "stacks:show": "rule:deny_stack_user", | ||
56 | "stacks:template": "rule:deny_stack_user", | ||
57 | "stacks:environment": "rule:deny_stack_user", | ||
58 | "stacks:update": "rule:deny_stack_user", | ||
59 | "stacks:update_patch": "rule:deny_stack_user", | ||
60 | "stacks:preview_update": "rule:deny_stack_user", | ||
61 | "stacks:preview_update_patch": "rule:deny_stack_user", | ||
62 | "stacks:validate_template": "rule:deny_stack_user", | ||
63 | "stacks:snapshot": "rule:deny_stack_user", | ||
64 | "stacks:show_snapshot": "rule:deny_stack_user", | ||
65 | "stacks:delete_snapshot": "rule:deny_stack_user", | ||
66 | "stacks:list_snapshots": "rule:deny_stack_user", | ||
67 | "stacks:restore_snapshot": "rule:deny_stack_user", | ||
68 | "stacks:list_outputs": "rule:deny_stack_user", | ||
69 | "stacks:show_output": "rule:deny_stack_user", | ||
70 | |||
71 | "software_configs:global_index": "rule:deny_everybody", | ||
72 | "software_configs:index": "rule:deny_stack_user", | ||
73 | "software_configs:create": "rule:deny_stack_user", | ||
74 | "software_configs:show": "rule:deny_stack_user", | ||
75 | "software_configs:delete": "rule:deny_stack_user", | ||
76 | "software_deployments:index": "rule:deny_stack_user", | ||
77 | "software_deployments:create": "rule:deny_stack_user", | ||
78 | "software_deployments:show": "rule:deny_stack_user", | ||
79 | "software_deployments:update": "rule:deny_stack_user", | ||
80 | "software_deployments:delete": "rule:deny_stack_user", | ||
81 | "software_deployments:metadata": "", | ||
82 | |||
83 | "service:index": "rule:context_is_admin", | ||
84 | |||
85 | "resource_types:OS::Nova::Flavor": "rule:context_is_admin", | ||
86 | "resource_types:OS::Cinder::EncryptedVolumeType": "rule:context_is_admin", | ||
87 | "resource_types:OS::Cinder::VolumeType": "rule:context_is_admin", | ||
88 | "resource_types:OS::Manila::ShareType": "rule:context_is_admin", | ||
89 | "resource_types:OS::Neutron::QoSPolicy": "rule:context_is_admin", | ||
90 | "resource_types:OS::Neutron::QoSBandwidthLimitRule": "rule:context_is_admin", | ||
91 | "resource_types:OS::Nova::HostAggregate": "rule:context_is_admin" | ||
92 | } | ||
diff --git a/openstack_dashboard/dashboards/admin/dashboard.py b/openstack_dashboard/dashboards/admin/dashboard.py index a1eb5fe..f526d95 100644 --- a/openstack_dashboard/dashboards/admin/dashboard.py +++ b/openstack_dashboard/dashboards/admin/dashboard.py | |||
@@ -28,8 +28,7 @@ class Admin(horizon.Dashboard): | |||
28 | ('image', 'context_is_admin'), | 28 | ('image', 'context_is_admin'), |
29 | ('volume', 'context_is_admin'), | 29 | ('volume', 'context_is_admin'), |
30 | ('compute', 'context_is_admin'), | 30 | ('compute', 'context_is_admin'), |
31 | ('network', 'context_is_admin'), | 31 | ('network', 'context_is_admin'),) |
32 | ('orchestration', 'context_is_admin'),) | ||
33 | else: | 32 | else: |
34 | permissions = (tuple(utils.get_admin_permissions()),) | 33 | permissions = (tuple(utils.get_admin_permissions()),) |
35 | 34 | ||
diff --git a/openstack_dashboard/dashboards/admin/info/panel.py b/openstack_dashboard/dashboards/admin/info/panel.py index 8979032..85a9642 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): | |||
26 | slug = 'info' | 26 | slug = 'info' |
27 | policy_rules = (("compute", "context_is_admin"), | 27 | policy_rules = (("compute", "context_is_admin"), |
28 | ("volume", "context_is_admin"), | 28 | ("volume", "context_is_admin"), |
29 | ("network", "context_is_admin"), | 29 | ("network", "context_is_admin"),) |
30 | ("orchestration", "context_is_admin"),) | ||
diff --git a/openstack_dashboard/dashboards/admin/info/tables.py b/openstack_dashboard/dashboards/admin/info/tables.py index 44f0758..40894f0 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): | |||
236 | table_actions = (NetworkAgentsFilterAction, ) | 236 | table_actions = (NetworkAgentsFilterAction, ) |
237 | row_actions = (NetworkL3AgentRoutersLinkAction, ) | 237 | row_actions = (NetworkL3AgentRoutersLinkAction, ) |
238 | multi_select = False | 238 | multi_select = False |
239 | |||
240 | |||
241 | class HeatServiceFilterAction(tables.FilterAction): | ||
242 | filter_field = 'type' | ||
243 | |||
244 | def filter(self, table, services, filter_string): | ||
245 | q = filter_string.lower() | ||
246 | |||
247 | def comp(service): | ||
248 | attr = getattr(service, self.filter_field, '') | ||
249 | if attr is not None and q in attr.lower(): | ||
250 | return True | ||
251 | return False | ||
252 | |||
253 | return filter(comp, services) | ||
254 | |||
255 | |||
256 | class HeatServiceTable(tables.DataTable): | ||
257 | hostname = tables.Column('hostname', verbose_name=_('Hostname')) | ||
258 | binary = tables.Column("binary", verbose_name=_('Name')) | ||
259 | engine_id = tables.Column('engine_id', verbose_name=_('Engine Id')) | ||
260 | host = tables.Column('host', verbose_name=_('Host')) | ||
261 | topic = tables.Column('topic', verbose_name=_('Topic')) | ||
262 | # For consistent with other tables in system info, set column name to | ||
263 | # 'state' | ||
264 | state = tables.Column('status', verbose_name=_('State'), | ||
265 | display_choices=SERVICE_STATE_DISPLAY_CHOICES) | ||
266 | updated_at = tables.Column('updated_at', | ||
267 | verbose_name=pgettext_lazy( | ||
268 | 'Time since the last update', | ||
269 | u'Last Updated'), | ||
270 | filters=(utils_filters.parse_isotime, | ||
271 | filters.timesince)) | ||
272 | |||
273 | def get_object_id(self, obj): | ||
274 | return "%s" % obj.engine_id | ||
275 | |||
276 | class Meta(object): | ||
277 | name = "heat_services" | ||
278 | verbose_name = _("Orchestration Services") | ||
279 | table_actions = (HeatServiceFilterAction,) | ||
280 | multi_select = False | ||
diff --git a/openstack_dashboard/dashboards/admin/info/tabs.py b/openstack_dashboard/dashboards/admin/info/tabs.py index cdda728..5cd265f 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 | |||
18 | from horizon import tabs | 18 | from horizon import tabs |
19 | from openstack_dashboard.api import base | 19 | from openstack_dashboard.api import base |
20 | from openstack_dashboard.api import cinder | 20 | from openstack_dashboard.api import cinder |
21 | from openstack_dashboard.api import heat | ||
22 | from openstack_dashboard.api import neutron | 21 | from openstack_dashboard.api import neutron |
23 | from openstack_dashboard.api import nova | 22 | from openstack_dashboard.api import nova |
24 | from openstack_dashboard.dashboards.admin.info import constants | 23 | from openstack_dashboard.dashboards.admin.info import constants |
@@ -118,32 +117,8 @@ class NetworkAgentsTab(tabs.TableTab): | |||
118 | return agents | 117 | return agents |
119 | 118 | ||
120 | 119 | ||
121 | class HeatServiceTab(tabs.TableTab): | ||
122 | table_classes = (tables.HeatServiceTable,) | ||
123 | name = tables.HeatServiceTable.Meta.verbose_name | ||
124 | slug = tables.HeatServiceTable.Meta.name | ||
125 | template_name = constants.INFO_DETAIL_TEMPLATE_NAME | ||
126 | |||
127 | def allowed(self, request): | ||
128 | try: | ||
129 | return base.is_service_enabled(request, 'orchestration') | ||
130 | except Exception: | ||
131 | exceptions.handle(request, _('Orchestration service is disabled.')) | ||
132 | return False | ||
133 | |||
134 | def get_heat_services_data(self): | ||
135 | try: | ||
136 | services = heat.service_list(self.tab_group.request) | ||
137 | except Exception: | ||
138 | msg = _('Unable to get Orchestration service list.') | ||
139 | exceptions.check_message(["Connection", "refused"], msg) | ||
140 | exceptions.handle(self.request, msg) | ||
141 | services = [] | ||
142 | return services | ||
143 | |||
144 | |||
145 | class SystemInfoTabs(tabs.TabGroup): | 120 | class SystemInfoTabs(tabs.TabGroup): |
146 | slug = "system_info" | 121 | slug = "system_info" |
147 | tabs = (ServicesTab, NovaServicesTab, CinderServicesTab, | 122 | tabs = (ServicesTab, NovaServicesTab, CinderServicesTab, |
148 | NetworkAgentsTab, HeatServiceTab) | 123 | NetworkAgentsTab) |
149 | sticky = True | 124 | sticky = True |
diff --git a/openstack_dashboard/dashboards/admin/info/tests.py b/openstack_dashboard/dashboards/admin/info/tests.py index c7405d3..9cbe865 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): | |||
29 | api.nova: ('service_list',), | 29 | api.nova: ('service_list',), |
30 | api.neutron: ('agent_list', 'is_extension_supported'), | 30 | api.neutron: ('agent_list', 'is_extension_supported'), |
31 | api.cinder: ('service_list',), | 31 | api.cinder: ('service_list',), |
32 | api.heat: ('service_list',)}) | 32 | }) |
33 | def _test_base_index(self): | 33 | def _test_base_index(self): |
34 | api.base.is_service_enabled(IsA(http.HttpRequest), IgnoreArg()) \ | 34 | api.base.is_service_enabled(IsA(http.HttpRequest), IgnoreArg()) \ |
35 | .MultipleTimes().AndReturn(True) | 35 | .MultipleTimes().AndReturn(True) |
@@ -49,10 +49,6 @@ class SystemInfoViewTests(test.BaseAdminViewTests): | |||
49 | api.cinder.service_list(IsA(http.HttpRequest)).\ | 49 | api.cinder.service_list(IsA(http.HttpRequest)).\ |
50 | AndReturn(cinder_services) | 50 | AndReturn(cinder_services) |
51 | 51 | ||
52 | heat_services = self.heat_services.list() | ||
53 | api.heat.service_list(IsA(http.HttpRequest)).\ | ||
54 | AndReturn(heat_services) | ||
55 | |||
56 | self.mox.ReplayAll() | 52 | self.mox.ReplayAll() |
57 | 53 | ||
58 | res = self.client.get(INDEX_URL) | 54 | res = self.client.get(INDEX_URL) |
@@ -88,14 +84,3 @@ class SystemInfoViewTests(test.BaseAdminViewTests): | |||
88 | ) | 84 | ) |
89 | 85 | ||
90 | self.mox.VerifyAll() | 86 | self.mox.VerifyAll() |
91 | |||
92 | def test_heat_index(self): | ||
93 | res = self._test_base_index() | ||
94 | heat_services_tab = res.context['tab_group'].\ | ||
95 | get_tab('heat_services') | ||
96 | self.assertQuerysetEqual( | ||
97 | heat_services_tab._tables['heat_services'].data, | ||
98 | [service.__repr__() for service in self.heat_services.list()] | ||
99 | ) | ||
100 | |||
101 | 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 e69de29..0000000 --- a/openstack_dashboard/dashboards/project/stacks/__init__.py +++ /dev/null | |||
diff --git a/openstack_dashboard/dashboards/project/stacks/api.py b/openstack_dashboard/dashboards/project/stacks/api.py deleted file mode 100644 index ce5affd..0000000 --- a/openstack_dashboard/dashboards/project/stacks/api.py +++ /dev/null | |||
@@ -1,83 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
2 | # not use this file except in compliance with the License. You may obtain | ||
3 | # a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
10 | # License for the specific language governing permissions and limitations | ||
11 | # under the License. | ||
12 | |||
13 | import json | ||
14 | |||
15 | from openstack_dashboard.api import heat | ||
16 | |||
17 | from openstack_dashboard.dashboards.project.stacks import mappings | ||
18 | from openstack_dashboard.dashboards.project.stacks import sro | ||
19 | |||
20 | |||
21 | class Stack(object): | ||
22 | pass | ||
23 | |||
24 | |||
25 | def d3_data(request, stack_id=''): | ||
26 | try: | ||
27 | stack = heat.stack_get(request, stack_id) | ||
28 | except Exception: | ||
29 | stack = Stack() | ||
30 | stack.id = stack_id | ||
31 | stack.stack_name = request.session.get('stack_name', '') | ||
32 | stack.stack_status = 'DELETE_COMPLETE' | ||
33 | stack.stack_status_reason = 'DELETE_COMPLETE' | ||
34 | |||
35 | try: | ||
36 | resources = heat.resources_list(request, stack.stack_name) | ||
37 | except Exception: | ||
38 | resources = [] | ||
39 | |||
40 | d3_data = {"nodes": [], "stack": {}} | ||
41 | if stack: | ||
42 | stack_image = mappings.get_resource_image(stack.stack_status, 'stack') | ||
43 | stack_node = { | ||
44 | 'stack_id': stack.id, | ||
45 | 'name': stack.stack_name, | ||
46 | 'status': stack.stack_status, | ||
47 | 'image': stack_image, | ||
48 | 'image_size': 60, | ||
49 | 'image_x': -30, | ||
50 | 'image_y': -30, | ||
51 | 'text_x': 40, | ||
52 | 'text_y': ".35em", | ||
53 | 'in_progress': (stack.status == 'IN_PROGRESS'), | ||
54 | 'info_box': sro.stack_info(stack, stack_image) | ||
55 | } | ||
56 | d3_data['stack'] = stack_node | ||
57 | |||
58 | if resources: | ||
59 | for resource in resources: | ||
60 | resource_image = mappings.get_resource_image( | ||
61 | resource.resource_status, | ||
62 | resource.resource_type) | ||
63 | resource_status = mappings.get_resource_status( | ||
64 | resource.resource_status) | ||
65 | if resource_status in ('IN_PROGRESS', 'INIT'): | ||
66 | in_progress = True | ||
67 | else: | ||
68 | in_progress = False | ||
69 | resource_node = { | ||
70 | 'name': resource.resource_name, | ||
71 | 'status': resource.resource_status, | ||
72 | 'image': resource_image, | ||
73 | 'required_by': resource.required_by, | ||
74 | 'image_size': 50, | ||
75 | 'image_x': -25, | ||
76 | 'image_y': -25, | ||
77 | 'text_x': 35, | ||
78 | 'text_y': ".35em", | ||
79 | 'in_progress': in_progress, | ||
80 | 'info_box': sro.resource_info(resource) | ||
81 | } | ||
82 | d3_data['nodes'].append(resource_node) | ||
83 | 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 e8b9702..0000000 --- a/openstack_dashboard/dashboards/project/stacks/forms.py +++ /dev/null | |||
@@ -1,488 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
2 | # not use this file except in compliance with the License. You may obtain | ||
3 | # a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
10 | # License for the specific language governing permissions and limitations | ||
11 | # under the License. | ||
12 | |||
13 | import json | ||
14 | import logging | ||
15 | |||
16 | import django | ||
17 | from django.conf import settings | ||
18 | from django.utils import html | ||
19 | from django.utils.translation import ugettext_lazy as _ | ||
20 | from django.views.decorators.debug import sensitive_variables | ||
21 | |||
22 | from oslo_utils import strutils | ||
23 | import six | ||
24 | |||
25 | from horizon import exceptions | ||
26 | from horizon import forms | ||
27 | from horizon import messages | ||
28 | |||
29 | from openstack_dashboard import api | ||
30 | from openstack_dashboard.dashboards.project.images \ | ||
31 | import utils as image_utils | ||
32 | from openstack_dashboard.dashboards.project.instances \ | ||
33 | import utils as instance_utils | ||
34 | |||
35 | |||
36 | LOG = logging.getLogger(__name__) | ||
37 | |||
38 | |||
39 | def create_upload_form_attributes(prefix, input_type, name): | ||
40 | """Creates attribute dicts for the switchable upload form | ||
41 | |||
42 | :type prefix: str | ||
43 | :param prefix: prefix (environment, template) of field | ||
44 | :type input_type: str | ||
45 | :param input_type: field type (file, raw, url) | ||
46 | :type name: str | ||
47 | :param name: translated text label to display to user | ||
48 | :rtype: dict | ||
49 | :return: an attribute set to pass to form build | ||
50 | """ | ||
51 | attributes = {'class': 'switched', 'data-switch-on': prefix + 'source'} | ||
52 | attributes['data-' + prefix + 'source-' + input_type] = name | ||
53 | return attributes | ||
54 | |||
55 | |||
56 | class TemplateForm(forms.SelfHandlingForm): | ||
57 | |||
58 | class Meta(object): | ||
59 | name = _('Select Template') | ||
60 | help_text = _('Select a template to launch a stack.') | ||
61 | |||
62 | # TODO(jomara) - update URL choice for template & environment files | ||
63 | # w/ client side download when applicable | ||
64 | base_choices = [('file', _('File')), | ||
65 | ('raw', _('Direct Input'))] | ||
66 | url_choice = [('url', _('URL'))] | ||
67 | attributes = {'class': 'switchable', 'data-slug': 'templatesource'} | ||
68 | template_source = forms.ChoiceField(label=_('Template Source'), | ||
69 | choices=base_choices + url_choice, | ||
70 | widget=forms.ThemableSelectWidget( | ||
71 | attrs=attributes)) | ||
72 | |||
73 | attributes = create_upload_form_attributes( | ||
74 | 'template', | ||
75 | 'file', | ||
76 | _('Template File')) | ||
77 | template_upload = forms.FileField( | ||
78 | label=_('Template File'), | ||
79 | help_text=_('A local template to upload.'), | ||
80 | widget=forms.FileInput(attrs=attributes), | ||
81 | required=False) | ||
82 | |||
83 | attributes = create_upload_form_attributes( | ||
84 | 'template', | ||
85 | 'url', | ||
86 | _('Template URL')) | ||
87 | template_url = forms.URLField( | ||
88 | label=_('Template URL'), | ||
89 | help_text=_('An external (HTTP) URL to load the template from.'), | ||
90 | widget=forms.TextInput(attrs=attributes), | ||
91 | required=False) | ||
92 | |||
93 | attributes = create_upload_form_attributes( | ||
94 | 'template', | ||
95 | 'raw', | ||
96 | _('Template Data')) | ||
97 | template_data = forms.CharField( | ||
98 | label=_('Template Data'), | ||
99 | help_text=_('The raw contents of the template.'), | ||
100 | widget=forms.widgets.Textarea(attrs=attributes), | ||
101 | required=False) | ||
102 | |||
103 | attributes = {'data-slug': 'envsource', 'class': 'switchable'} | ||
104 | environment_source = forms.ChoiceField( | ||
105 | label=_('Environment Source'), | ||
106 | choices=base_choices, | ||
107 | widget=forms.ThemableSelectWidget(attrs=attributes), | ||
108 | required=False) | ||
109 | |||
110 | attributes = create_upload_form_attributes( | ||
111 | 'env', | ||
112 | 'file', | ||
113 | _('Environment File')) | ||
114 | environment_upload = forms.FileField( | ||
115 | label=_('Environment File'), | ||
116 | help_text=_('A local environment to upload.'), | ||
117 | widget=forms.FileInput(attrs=attributes), | ||
118 | required=False) | ||
119 | |||
120 | attributes = create_upload_form_attributes( | ||
121 | 'env', | ||
122 | 'raw', | ||
123 | _('Environment Data')) | ||
124 | environment_data = forms.CharField( | ||
125 | label=_('Environment Data'), | ||
126 | help_text=_('The raw contents of the environment file.'), | ||
127 | widget=forms.widgets.Textarea(attrs=attributes), | ||
128 | required=False) | ||
129 | |||
130 | if django.VERSION >= (1, 9): | ||
131 | # Note(Itxaka): On django>=1.9 Charfield has an strip option that | ||
132 | # we need to set to False as to not hit | ||
133 | # https://bugs.launchpad.net/python-heatclient/+bug/1546166 | ||
134 | environment_data.strip = False | ||
135 | template_data.strip = False | ||
136 | |||
137 | def __init__(self, *args, **kwargs): | ||
138 | self.next_view = kwargs.pop('next_view') | ||
139 | super(TemplateForm, self).__init__(*args, **kwargs) | ||
140 | |||
141 | def clean(self): | ||
142 | cleaned = super(TemplateForm, self).clean() | ||
143 | |||
144 | files = self.request.FILES | ||
145 | self.clean_uploaded_files('template', _('template'), cleaned, files) | ||
146 | self.clean_uploaded_files('environment', _('environment'), cleaned, | ||
147 | files) | ||
148 | |||
149 | # Validate the template and get back the params. | ||
150 | kwargs = {} | ||
151 | if cleaned['environment_data']: | ||
152 | kwargs['environment'] = cleaned['environment_data'] | ||
153 | try: | ||
154 | files, tpl =\ | ||
155 | api.heat.get_template_files(cleaned.get('template_data'), | ||
156 | cleaned.get('template_url')) | ||
157 | kwargs['files'] = files | ||
158 | kwargs['template'] = tpl | ||
159 | validated = api.heat.template_validate(self.request, **kwargs) | ||
160 | cleaned['template_validate'] = validated | ||
161 | cleaned['template_validate']['files'] = files | ||
162 | cleaned['template_validate']['template'] = tpl | ||
163 | except Exception as e: | ||
164 | raise forms.ValidationError(six.text_type(e)) | ||
165 | |||
166 | return cleaned | ||
167 | |||
168 | def clean_uploaded_files(self, prefix, field_label, cleaned, files): | ||
169 | """Cleans Template & Environment data from form upload. | ||
170 | |||
171 | Does some of the crunchy bits for processing uploads vs raw | ||
172 | data depending on what the user specified. Identical process | ||
173 | for environment data & template data. | ||
174 | |||
175 | :type prefix: str | ||
176 | :param prefix: prefix (environment, template) of field | ||
177 | :type field_label: str | ||
178 | :param field_label: translated prefix str for messages | ||
179 | :type input_type: dict | ||
180 | :param prefix: existing cleaned fields from form | ||
181 | :rtype: dict | ||
182 | :return: cleaned dict including environment & template data | ||
183 | """ | ||
184 | |||
185 | upload_str = prefix + "_upload" | ||
186 | data_str = prefix + "_data" | ||
187 | url = cleaned.get(prefix + '_url') | ||
188 | data = cleaned.get(prefix + '_data') | ||
189 | |||
190 | has_upload = upload_str in files | ||
191 | # Uploaded file handler | ||
192 | if has_upload and not url: | ||
193 | log_template_name = files[upload_str].name | ||
194 | LOG.info('got upload %s', log_template_name) | ||
195 | |||
196 | tpl = files[upload_str].read() | ||
197 | if tpl.startswith('{'): | ||
198 | try: | ||
199 | json.loads(tpl) | ||
200 | except Exception as e: | ||
201 | msg = _('There was a problem parsing the' | ||
202 | ' %(prefix)s: %(error)s') | ||
203 | msg = msg % {'prefix': prefix, 'error': six.text_type(e)} | ||
204 | raise forms.ValidationError(msg) | ||
205 | cleaned[data_str] = tpl | ||
206 | |||
207 | # URL handler | ||
208 | elif url and (has_upload or data): | ||
209 | msg = _('Please specify a %s using only one source method.') | ||
210 | msg = msg % field_label | ||
211 | raise forms.ValidationError(msg) | ||
212 | |||
213 | elif prefix == 'template': | ||
214 | # Check for raw template input - blank environment allowed | ||
215 | if not url and not data: | ||
216 | msg = _('You must specify a template via one of the ' | ||
217 | 'available sources.') | ||
218 | raise forms.ValidationError(msg) | ||
219 | |||
220 | def create_kwargs(self, data): | ||
221 | kwargs = {'parameters': data['template_validate'], | ||
222 | 'environment_data': data['environment_data']} | ||
223 | if data.get('stack_id'): | ||
224 | kwargs['stack_id'] = data['stack_id'] | ||
225 | return kwargs | ||
226 | |||
227 | def handle(self, request, data): | ||
228 | kwargs = self.create_kwargs(data) | ||
229 | # NOTE (gabriel): This is a bit of a hack, essentially rewriting this | ||
230 | # request so that we can chain it as an input to the next view... | ||
231 | # but hey, it totally works. | ||
232 | request.method = 'GET' | ||
233 | |||
234 | return self.next_view.as_view()(request, **kwargs) | ||
235 | |||
236 | |||
237 | class ChangeTemplateForm(TemplateForm): | ||
238 | class Meta(object): | ||
239 | name = _('Edit Template') | ||
240 | help_text = _('Select a new template to re-launch a stack.') | ||
241 | stack_id = forms.CharField(label=_('Stack ID'), | ||
242 | widget=forms.widgets.HiddenInput) | ||
243 | stack_name = forms.CharField(label=_('Stack Name'), | ||
244 | widget=forms.TextInput(attrs={'readonly': | ||
245 | 'readonly'})) | ||
246 | |||
247 | |||
248 | class PreviewTemplateForm(TemplateForm): | ||
249 | class Meta(object): | ||
250 | name = _('Preview Template') | ||
251 | help_text = _('Select a new template to preview a stack.') | ||
252 | |||
253 | |||
254 | class CreateStackForm(forms.SelfHandlingForm): | ||
255 | |||
256 | param_prefix = '__param_' | ||
257 | |||
258 | class Meta(object): | ||
259 | name = _('Create Stack') | ||
260 | |||
261 | environment_data = forms.CharField( | ||
262 | widget=forms.widgets.HiddenInput, | ||
263 | required=False) | ||
264 | if django.VERSION >= (1, 9): | ||
265 | # Note(Itxaka): On django>=1.9 Charfield has an strip option that | ||
266 | # we need to set to False as to not hit | ||
267 | # https://bugs.launchpad.net/python-heatclient/+bug/1546166 | ||
268 | environment_data.strip = False | ||
269 | |||
270 | parameters = forms.CharField( | ||
271 | widget=forms.widgets.HiddenInput) | ||
272 | stack_name = forms.RegexField( | ||
273 | max_length=255, | ||
274 | label=_('Stack Name'), | ||
275 | help_text=_('Name of the stack to create.'), | ||
276 | regex=r"^[a-zA-Z][a-zA-Z0-9_.-]*$", | ||
277 | error_messages={'invalid': | ||
278 | _('Name must start with a letter and may ' | ||
279 | 'only contain letters, numbers, underscores, ' | ||
280 | 'periods and hyphens.')}) | ||
281 | timeout_mins = forms.IntegerField( | ||
282 | initial=60, | ||
283 | label=_('Creation Timeout (minutes)'), | ||
284 | help_text=_('Stack creation timeout in minutes.')) | ||
285 | enable_rollback = forms.BooleanField( | ||
286 | label=_('Rollback On Failure'), | ||
287 | help_text=_('Enable rollback on create/update failure.'), | ||
288 | required=False) | ||
289 | |||
290 | def __init__(self, *args, **kwargs): | ||
291 | parameters = kwargs.pop('parameters') | ||
292 | # special case: load template data from API, not passed in params | ||
293 | if kwargs.get('validate_me'): | ||
294 | parameters = kwargs.pop('validate_me') | ||
295 | super(CreateStackForm, self).__init__(*args, **kwargs) | ||
296 | |||
297 | if self._stack_password_enabled(): | ||
298 | self.fields['password'] = forms.CharField( | ||
299 | label=_('Password for user "%s"') % self.request.user.username, | ||
300 | help_text=_('This is required for operations to be performed ' | ||
301 | 'throughout the lifecycle of the stack'), | ||
302 | widget=forms.PasswordInput()) | ||
303 | |||
304 | self._build_parameter_fields(parameters) | ||
305 | |||
306 | def _stack_password_enabled(self): | ||
307 | stack_settings = getattr(settings, 'OPENSTACK_HEAT_STACK', {}) | ||
308 | return stack_settings.get('enable_user_pass', True) | ||
309 | |||
310 | def _build_parameter_fields(self, template_validate): | ||
311 | self.help_text = template_validate['Description'] | ||
312 | |||
313 | params = template_validate.get('Parameters', {}) | ||
314 | if template_validate.get('ParameterGroups'): | ||
315 | params_in_order = [] | ||
316 | for group in template_validate['ParameterGroups']: | ||
317 | for param in group.get('parameters', []): | ||
318 | if param in params: | ||
319 | params_in_order.append((param, params[param])) | ||
320 | else: | ||
321 | # no parameter groups, simply sorted to make the order fixed | ||
322 | params_in_order = sorted(params.items()) | ||
323 | for param_key, param in params_in_order: | ||
324 | field = None | ||
325 | field_key = self.param_prefix + param_key | ||
326 | initial = param.get('Value', | ||
327 | param.get('DefaultValue', | ||
328 | param.get('Default'))) | ||
329 | field_args = { | ||
330 | 'initial': initial, | ||
331 | 'label': param.get('Label', param_key), | ||
332 | 'help_text': html.escape(param.get('Description', '')), | ||
333 | 'required': initial is None, | ||
334 | } | ||
335 | |||
336 | param_type = param.get('Type', None) | ||
337 | hidden = strutils.bool_from_string(param.get('NoEcho', 'false')) | ||
338 | if 'CustomConstraint' in param: | ||
339 | choices = self._populate_custom_choices( | ||
340 | param['CustomConstraint']) | ||
341 | field_args['choices'] = choices | ||
342 | field = forms.ChoiceField(**field_args) | ||
343 | |||
344 | elif 'AllowedValues' in param: | ||
345 | choices = map(lambda x: (x, x), param['AllowedValues']) | ||
346 | field_args['choices'] = choices | ||
347 | field = forms.ChoiceField(**field_args) | ||
348 | |||
349 | elif param_type == 'Json' and 'Default' in param: | ||
350 | field_args['initial'] = json.dumps(param['Default']) | ||
351 | field = forms.CharField(**field_args) | ||
352 | |||
353 | elif param_type in ('CommaDelimitedList', 'String', 'Json'): | ||
354 | if 'MinLength' in param: | ||
355 | field_args['min_length'] = int(param['MinLength']) | ||
356 | field_args['required'] = field_args['min_length'] > 0 | ||
357 | if 'MaxLength' in param: | ||
358 | field_args['max_length'] = int(param['MaxLength']) | ||
359 | if hidden: | ||
360 | field_args['widget'] = forms.PasswordInput( | ||
361 | render_value=True) | ||
362 | field = forms.CharField(**field_args) | ||
363 | |||
364 | elif param_type == 'Number': | ||
365 | if 'MinValue' in param: | ||
366 | field_args['min_value'] = int(param['MinValue']) | ||
367 | if 'MaxValue' in param: | ||
368 | field_args['max_value'] = int(param['MaxValue']) | ||
369 | field = forms.IntegerField(**field_args) | ||
370 | |||
371 | elif param_type == 'Boolean': | ||
372 | field_args['required'] = False | ||
373 | field = forms.BooleanField(**field_args) | ||
374 | |||
375 | if field: | ||
376 | self.fields[field_key] = field | ||
377 | |||
378 | @sensitive_variables('password') | ||
379 | def handle(self, request, data): | ||
380 | prefix_length = len(self.param_prefix) | ||
381 | params_list = [(k[prefix_length:], v) for (k, v) in data.items() | ||
382 | if k.startswith(self.param_prefix)] | ||
383 | fields = { | ||
384 | 'stack_name': data.get('stack_name'), | ||
385 | 'timeout_mins': data.get('timeout_mins'), | ||
386 | 'disable_rollback': not(data.get('enable_rollback')), | ||
387 | 'parameters': dict(params_list), | ||
388 | 'files': json.loads(data.get('parameters')).get('files'), | ||
389 | 'template': json.loads(data.get('parameters')).get('template') | ||
390 | } | ||
391 | if data.get('password'): | ||
392 | fields['password'] = data.get('password') | ||
393 | |||
394 | if data.get('environment_data'): | ||
395 | fields['environment'] = data.get('environment_data') | ||
396 | |||
397 | try: | ||
398 | api.heat.stack_create(self.request, **fields) | ||
399 | messages.info(request, _("Stack creation started.")) | ||
400 | return True | ||
401 | except Exception: | ||
402 | exceptions.handle(request) | ||
403 | |||
404 | def _populate_custom_choices(self, custom_type): | ||
405 | if custom_type == 'neutron.network': | ||
406 | return instance_utils.network_field_data(self.request, True) | ||
407 | if custom_type == 'nova.keypair': | ||
408 | return instance_utils.keypair_field_data(self.request, True) | ||
409 | if custom_type == 'glance.image': | ||
410 | return image_utils.image_field_data(self.request, True) | ||
411 | if custom_type == 'nova.flavor': | ||
412 | return instance_utils.flavor_field_data(self.request, True) | ||
413 | return [] | ||
414 | |||
415 | |||
416 | class EditStackForm(CreateStackForm): | ||
417 | |||
418 | class Meta(object): | ||
419 | name = _('Update Stack Parameters') | ||
420 | |||
421 | stack_id = forms.CharField( | ||
422 | label=_('Stack ID'), | ||
423 | widget=forms.widgets.HiddenInput) | ||
424 | stack_name = forms.CharField( | ||
425 | label=_('Stack Name'), | ||
426 | widget=forms.TextInput(attrs={'readonly': 'readonly'})) | ||
427 | |||
428 | @sensitive_variables('password') | ||
429 | def handle(self, request, data): | ||
430 | prefix_length = len(self.param_prefix) | ||
431 | params_list = [(k[prefix_length:], v) for (k, v) in data.items() | ||
432 | if k.startswith(self.param_prefix)] | ||
433 | |||
434 | stack_id = data.get('stack_id') | ||
435 | fields = { | ||
436 | 'stack_name': data.get('stack_name'), | ||
437 | 'timeout_mins': data.get('timeout_mins'), | ||
438 | 'disable_rollback': not(data.get('enable_rollback')), | ||
439 | 'parameters': dict(params_list), | ||
440 | 'files': json.loads(data.get('parameters')).get('files'), | ||
441 | 'template': json.loads(data.get('parameters')).get('template') | ||
442 | } | ||
443 | if data.get('password'): | ||
444 | fields['password'] = data.get('password') | ||
445 | |||
446 | if data.get('environment_data'): | ||
447 | fields['environment'] = data.get('environment_data') | ||
448 | |||
449 | try: | ||
450 | api.heat.stack_update(self.request, stack_id=stack_id, **fields) | ||
451 | messages.info(request, _("Stack update started.")) | ||
452 | return True | ||
453 | except Exception: | ||
454 | exceptions.handle(request) | ||
455 | |||
456 | |||
457 | class PreviewStackForm(CreateStackForm): | ||
458 | |||
459 | class Meta(object): | ||
460 | name = _('Preview Stack Parameters') | ||
461 | |||
462 | def __init__(self, *args, **kwargs): | ||
463 | self.next_view = kwargs.pop('next_view') | ||
464 | super(CreateStackForm, self).__init__(*args, **kwargs) | ||
465 | |||
466 | def handle(self, request, data): | ||
467 | prefix_length = len(self.param_prefix) | ||
468 | params_list = [(k[prefix_length:], v) for (k, v) in data.items() | ||
469 | if k.startswith(self.param_prefix)] | ||
470 | fields = { | ||
471 | 'stack_name': data.get('stack_name'), | ||
472 | 'timeout_mins': data.get('timeout_mins'), | ||
473 | 'disable_rollback': not(data.get('enable_rollback')), | ||
474 | 'parameters': dict(params_list), | ||
475 | 'files': json.loads(data.get('parameters')).get('files'), | ||
476 | 'template': json.loads(data.get('parameters')).get('template') | ||
477 | } | ||
478 | |||
479 | if data.get('environment_data'): | ||
480 | fields['environment'] = data.get('environment_data') | ||
481 | |||
482 | try: | ||
483 | stack_preview = api.heat.stack_preview(self.request, **fields) | ||
484 | request.method = 'GET' | ||
485 | return self.next_view.as_view()(request, | ||
486 | stack_preview=stack_preview) | ||
487 | except Exception: | ||
488 | 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 eeab18c..0000000 --- a/openstack_dashboard/dashboards/project/stacks/mappings.py +++ /dev/null | |||
@@ -1,350 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
2 | # not use this file except in compliance with the License. You may obtain | ||
3 | # a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
10 | # License for the specific language governing permissions and limitations | ||
11 | # under the License. | ||
12 | |||
13 | import json | ||
14 | import logging | ||
15 | |||
16 | from django.conf import settings | ||
17 | from django.core.urlresolvers import reverse | ||
18 | from django.template.defaultfilters import register | ||
19 | from django.utils import html | ||
20 | from django.utils import safestring | ||
21 | import six | ||
22 | import six.moves.urllib.parse as urlparse | ||
23 | |||
24 | from openstack_dashboard.api import swift | ||
25 | |||
26 | LOG = logging.getLogger(__name__) | ||
27 | |||
28 | |||
29 | resource_urls = { | ||
30 | "AWS::AutoScaling::AutoScalingGroup": { | ||
31 | 'link': 'horizon:project:stacks:detail'}, | ||
32 | "AWS::CloudFormation::Stack": { | ||
33 | 'link': 'horizon:project:stacks:detail'}, | ||
34 | "AWS::EC2::Instance": { | ||
35 | 'link': 'horizon:project:instances:detail'}, | ||
36 | "AWS::EC2::InternetGateway": { | ||
37 | 'link': 'horizon:project:networks:ports:detail'}, | ||
38 | "AWS::EC2::NetworkInterface": { | ||
39 | 'link': 'horizon:project:networks:ports:detail'}, | ||
40 | "AWS::EC2::RouteTable": { | ||
41 | 'link': 'horizon:project:routers:detail'}, | ||
42 | "AWS::EC2::SecurityGroup": { | ||
43 | 'link': 'horizon:project:security_groups:index'}, | ||
44 | "AWS::EC2::Subnet": { | ||
45 | 'link': 'horizon:project:networks:subnets:detail'}, | ||
46 | "AWS::EC2::Volume": { | ||
47 | 'link': 'horizon:project:volumes:detail'}, | ||
48 | "AWS::EC2::VPC": { | ||
49 | 'link': 'horizon:project:networks:detail'}, | ||
50 | "AWS::S3::Bucket": { | ||
51 | 'link': 'horizon:project:containers:index'}, | ||
52 | "OS::Cinder::Volume": { | ||
53 | 'link': 'horizon:project:volumes:detail'}, | ||
54 | "OS::Heat::AccessPolicy": { | ||
55 | 'link': 'horizon:project:stacks:detail'}, | ||
56 | "OS::Heat::AutoScalingGroup": { | ||
57 | 'link': 'horizon:project:stacks:detail'}, | ||
58 | "OS::Heat::CloudConfig": { | ||
59 | 'link': 'horizon:project:stacks:detail'}, | ||
60 | "OS::Neutron::Firewall": { | ||
61 | 'link': 'horizon:project:firewalls:firewalldetails'}, | ||
62 | "OS::Neutron::FirewallPolicy": { | ||
63 | 'link': 'horizon:project:firewalls:policydetails'}, | ||
64 | "OS::Neutron::FirewallRule": { | ||
65 | 'link': 'horizon:project:firewalls:ruledetails'}, | ||
66 | "OS::Heat::HARestarter": { | ||
67 | 'link': 'horizon:project:stacks:detail'}, | ||
68 | "OS::Heat::InstanceGroup": { | ||
69 | 'link': 'horizon:project:stacks:detail'}, | ||
70 | "OS::Heat::MultipartMime": { | ||
71 | 'link': 'horizon:project:stacks:detail'}, | ||
72 | "OS::Heat::ResourceGroup": { | ||
73 | 'link': 'horizon:project:stacks:detail'}, | ||
74 | "OS::Heat::SoftwareConfig": { | ||
75 | 'link': 'horizon:project:stacks:detail'}, | ||
76 | "OS::Heat::StructuredConfig": { | ||
77 | 'link': 'horizon:project:stacks:detail'}, | ||
78 | "OS::Heat::StructuredDeployment": { | ||
79 | 'link': 'horizon:project:stacks:detail'}, | ||
80 | "OS::Heat::Stack": { | ||
81 | 'link': 'horizon:project:stacks:detail'}, | ||
82 | "OS::Heat::WaitCondition": { | ||
83 | 'link': 'horizon:project:stacks:detail'}, | ||
84 | "OS::Heat::WaitConditionHandle": { | ||
85 | 'link': 'horizon:project:stacks:detail'}, | ||
86 | "OS::Neutron::IKEPolicy": { | ||
87 | 'link': 'horizon:project:vpn:ikepolicydetails'}, | ||
88 | "OS::Neutron::IPsecPolicy": { | ||
89 | 'link': 'horizon:project:vpn:ipsecpolicydetails'}, | ||
90 | "OS::Neutron::IPsecSiteConnection": { | ||
91 | 'link': 'horizon:project:vpn:ipsecsiteconnectiondetails'}, | ||
92 | "OS::Neutron::Net": { | ||
93 | 'link': 'horizon:project:networks:detail'}, | ||
94 | "OS::Neutron::Port": { | ||
95 | 'link': 'horizon:project:networks:ports:detail'}, | ||
96 | "OS::Neutron::Router": { | ||
97 | 'link': 'horizon:project:routers:detail'}, | ||
98 | "OS::Neutron::Subnet": { | ||
99 | 'link': 'horizon:project:networks:subnets:detail'}, | ||
100 | "OS::Neutron::VPNService": { | ||
101 | 'link': 'horizon:project:vpn:vpnservicedetails'}, | ||
102 | "OS::Nova::KeyPair": { | ||
103 | 'link': 'horizon:project:key_pairs:index'}, | ||
104 | "OS::Nova::Server": { | ||
105 | 'link': 'horizon:project:instances:detail'}, | ||
106 | "OS::Swift::Container": { | ||
107 | 'link': 'horizon:project:containers:index', | ||
108 | 'format_pattern': '%s' + swift.FOLDER_DELIMITER}, | ||
109 | } | ||
110 | |||
111 | |||
112 | def resource_to_url(resource): | ||
113 | if (not resource or | ||
114 | not resource.physical_resource_id or | ||
115 | not hasattr(resource, 'resource_type')): | ||
116 | return None | ||
117 | |||
118 | mapping = resource_urls.get(resource.resource_type, {}) | ||
119 | try: | ||
120 | if 'link' not in mapping: | ||
121 | return None | ||
122 | format_pattern = mapping.get('format_pattern') or '%s' | ||
123 | rid = format_pattern % resource.physical_resource_id | ||
124 | url = reverse(mapping['link'], args=(rid,)) | ||
125 | except Exception as e: | ||
126 | LOG.exception(e) | ||
127 | return None | ||
128 | return url | ||
129 | |||
130 | |||
131 | @register.filter | ||
132 | def stack_output(output): | ||
133 | if not output: | ||
134 | return u'' | ||
135 | if isinstance(output, six.string_types): | ||
136 | parts = urlparse.urlsplit(output) | ||
137 | if parts.netloc and parts.scheme in ('http', 'https'): | ||
138 | url = html.escape(output) | ||
139 | safe_link = u'<a href="%s" target="_blank">%s</a>' % (url, url) | ||
140 | return safestring.mark_safe(safe_link) | ||
141 | if isinstance(output, dict) or isinstance(output, list): | ||
142 | output = json.dumps(output, indent=2) | ||
143 | return safestring.mark_safe(u'<pre>%s</pre>' % html.escape(output)) | ||
144 | |||
145 | static_url = getattr(settings, "STATIC_URL", "/static/") | ||
146 | resource_images = { | ||
147 | 'LB_FAILED': static_url + 'dashboard/img/lb-red.svg', | ||
148 | 'LB_DELETE': static_url + 'dashboard/img/lb-red.svg', | ||
149 | 'LB_IN_PROGRESS': static_url + 'dashboard/img/lb-gray.gif', | ||
150 | 'LB_INIT': static_url + 'dashboard/img/lb-gray.svg', | ||
151 | 'LB_COMPLETE': static_url + 'dashboard/img/lb-green.svg', | ||
152 | 'DB_FAILED': static_url + 'dashboard/img/db-red.svg', | ||
153 | 'DB_DELETE': static_url + 'dashboard/img/db-red.svg', | ||
154 | 'DB_IN_PROGRESS': static_url + 'dashboard/img/db-gray.gif', | ||
155 | 'DB_INIT': static_url + 'dashboard/img/db-gray.svg', | ||
156 | 'DB_COMPLETE': static_url + 'dashboard/img/db-green.svg', | ||
157 | 'STACK_FAILED': static_url + 'dashboard/img/stack-red.svg', | ||
158 | 'STACK_DELETE': static_url + 'dashboard/img/stack-red.svg', | ||
159 | 'STACK_IN_PROGRESS': static_url + 'dashboard/img/stack-gray.gif', | ||
160 | 'STACK_INIT': static_url + 'dashboard/img/stack-gray.svg', | ||
161 | 'STACK_COMPLETE': static_url + 'dashboard/img/stack-green.svg', | ||
162 | 'SERVER_FAILED': static_url + 'dashboard/img/server-red.svg', | ||
163 | 'SERVER_DELETE': static_url + 'dashboard/img/server-red.svg', | ||
164 | 'SERVER_IN_PROGRESS': static_url + 'dashboard/img/server-gray.gif', | ||
165 | 'SERVER_INIT': static_url + 'dashboard/img/server-gray.svg', | ||
166 | 'SERVER_COMPLETE': static_url + 'dashboard/img/server-green.svg', | ||
167 | 'ALARM_FAILED': static_url + 'dashboard/img/alarm-red.svg', | ||
168 | 'ALARM_DELETE': static_url + 'dashboard/img/alarm-red.svg', | ||
169 | 'ALARM_IN_PROGRESS': static_url + 'dashboard/img/alarm-gray.gif', | ||
170 | 'ALARM_INIT': static_url + 'dashboard/img/alarm-gray.svg', | ||
171 | 'ALARM_COMPLETE': static_url + 'dashboard/img/alarm-green.svg', | ||
172 | 'VOLUME_FAILED': static_url + 'dashboard/img/volume-red.svg', | ||
173 | 'VOLUME_DELETE': static_url + 'dashboard/img/volume-red.svg', | ||
174 | 'VOLUME_IN_PROGRESS': static_url + 'dashboard/img/volume-gray.gif', | ||
175 | 'VOLUME_INIT': static_url + 'dashboard/img/volume-gray.svg', | ||
176 | 'VOLUME_COMPLETE': static_url + 'dashboard/img/volume-green.svg', | ||
177 | 'IMAGE_FAILED': static_url + 'dashboard/img/image-red.svg', | ||
178 | 'IMAGE_DELETE': static_url + 'dashboard/img/image-red.svg', | ||
179 | 'IMAGE_IN_PROGRESS': static_url + 'dashboard/img/image-gray.gif', | ||
180 | 'IMAGE_INIT': static_url + 'dashboard/img/image-gray.svg', | ||
181 | 'IMAGE_COMPLETE': static_url + 'dashboard/img/image-green.svg', | ||
182 | 'WAIT_FAILED': static_url + 'dashboard/img/wait-red.svg', | ||
183 | 'WAIT_DELETE': static_url + 'dashboard/img/wait-red.svg', | ||
184 | 'WAIT_IN_PROGRESS': static_url + 'dashboard/img/wait-gray.gif', | ||
185 | 'WAIT_INIT': static_url + 'dashboard/img/wait-gray.svg', | ||
186 | 'WAIT_COMPLETE': static_url + 'dashboard/img/wait-green.svg', | ||
187 | 'FIREWALL_FAILED': static_url + 'dashboard/img/firewall-red.svg', | ||
188 | 'FIREWALL_DELETE': static_url + 'dashboard/img/firewall-red.svg', | ||
189 | 'FIREWALL_IN_PROGRESS': static_url + 'dashboard/img/firewall-gray.gif', | ||
190 | 'FIREWALL_INIT': static_url + 'dashboard/img/firewall-gray.svg', | ||
191 | 'FIREWALL_COMPLETE': static_url + 'dashboard/img/firewall-green.svg', | ||
192 | 'FLOATINGIP_FAILED': static_url + 'dashboard/img/floatingip-red.svg', | ||
193 | 'FLOATINGIP_DELETE': static_url + 'dashboard/img/floatingip-red.svg', | ||
194 | 'FLOATINGIP_IN_PROGRESS': static_url + 'dashboard/img/floatingip-gray.gif', | ||
195 | 'FLOATINGIP_INIT': static_url + 'dashboard/img/floatingip-gray.svg', | ||
196 | 'FLOATINGIP_COMPLETE': static_url + 'dashboard/img/floatingip-green.svg', | ||
197 | 'ROUTER_FAILED': static_url + 'dashboard/img/router-red.svg', | ||
198 | 'ROUTER_DELETE': static_url + 'dashboard/img/router-red.svg', | ||
199 | 'ROUTER_IN_PROGRESS': static_url + 'dashboard/img/router-gray.gif', | ||
200 | 'ROUTER_INIT': static_url + 'dashboard/img/router-gray.svg', | ||
201 | 'ROUTER_COMPLETE': static_url + 'dashboard/img/router-green.svg', | ||
202 | 'POLICY_FAILED': static_url + 'dashboard/img/policy-red.svg', | ||
203 | 'POLICY_DELETE': static_url + 'dashboard/img/policy-red.svg', | ||
204 | 'POLICY_IN_PROGRESS': static_url + 'dashboard/img/policy-gray.gif', | ||
205 | 'POLICY_INIT': static_url + 'dashboard/img/policy-gray.svg', | ||
206 | 'POLICY_COMPLETE': static_url + 'dashboard/img/policy-green.svg', | ||
207 | 'CONFIG_FAILED': static_url + 'dashboard/img/config-red.svg', | ||
208 | 'CONFIG_DELETE': static_url + 'dashboard/img/config-red.svg', | ||
209 | 'CONFIG_IN_PROGRESS': static_url + 'dashboard/img/config-gray.gif', | ||
210 | 'CONFIG_INIT': static_url + 'dashboard/img/config-gray.svg', | ||
211 | 'CONFIG_COMPLETE': static_url + 'dashboard/img/config-green.svg', | ||
212 | 'NETWORK_FAILED': static_url + 'dashboard/img/network-red.svg', | ||
213 | 'NETWORK_DELETE': static_url + 'dashboard/img/network-red.svg', | ||
214 | 'NETWORK_IN_PROGRESS': static_url + 'dashboard/img/network-gray.gif', | ||
215 | 'NETWORK_INIT': static_url + 'dashboard/img/network-gray.svg', | ||
216 | 'NETWORK_COMPLETE': static_url + 'dashboard/img/network-green.svg', | ||
217 | 'PORT_FAILED': static_url + 'dashboard/img/port-red.svg', | ||
218 | 'PORT_DELETE': static_url + 'dashboard/img/port-red.svg', | ||
219 | 'PORT_IN_PROGRESS': static_url + 'dashboard/img/port-gray.gif', | ||
220 | 'PORT_INIT': static_url + 'dashboard/img/port-gray.svg', | ||
221 | 'PORT_COMPLETE': static_url + 'dashboard/img/port-green.svg', | ||
222 | 'SECURITYGROUP_FAILED': static_url + 'dashboard/img/securitygroup-red.svg', | ||
223 | 'SECURITYGROUP_DELETE': static_url + 'dashboard/img/securitygroup-red.svg', | ||
224 | 'SECURITYGROUP_IN_PROGRESS': | ||
225 | static_url + 'dashboard/img/securitygroup-gray.gif', | ||
226 | 'SECURITYGROUP_INIT': static_url + 'dashboard/img/securitygroup-gray.svg', | ||
227 | 'SECURITYGROUP_COMPLETE': | ||
228 | static_url + 'dashboard/img/securitygroup-green.svg', | ||
229 | 'VPN_FAILED': static_url + 'dashboard/img/vpn-red.svg', | ||
230 | 'VPN_DELETE': static_url + 'dashboard/img/vpn-red.svg', | ||
231 | 'VPN_IN_PROGRESS': static_url + 'dashboard/img/vpn-gray.gif', | ||
232 | 'VPN_INIT': static_url + 'dashboard/img/vpn-gray.svg', | ||
233 | 'VPN_COMPLETE': static_url + 'dashboard/img/vpn-green.svg', | ||
234 | 'FLAVOR_FAILED': static_url + 'dashboard/img/flavor-red.svg', | ||
235 | 'FLAVOR_DELETE': static_url + 'dashboard/img/flavor-red.svg', | ||
236 | 'FLAVOR_IN_PROGRESS': static_url + 'dashboard/img/flavor-gray.gif', | ||
237 | 'FLAVOR_INIT': static_url + 'dashboard/img/flavor-gray.svg', | ||
238 | 'FLAVOR_COMPLETE': static_url + 'dashboard/img/flavor-green.svg', | ||
239 | 'KEYPAIR_FAILED': static_url + 'dashboard/img/keypair-red.svg', | ||
240 | 'KEYPAIR_DELETE': static_url + 'dashboard/img/keypair-red.svg', | ||
241 | 'KEYPAIR_IN_PROGRESS': static_url + 'dashboard/img/keypair-gray.gif', | ||
242 | 'KEYPAIR_INIT': static_url + 'dashboard/img/keypair-gray.svg', | ||
243 | 'KEYPAIR_COMPLETE': static_url + 'dashboard/img/keypair-green.svg', | ||
244 | 'UNKNOWN_FAILED': static_url + 'dashboard/img/unknown-red.svg', | ||
245 | 'UNKNOWN_DELETE': static_url + 'dashboard/img/unknown-red.svg', | ||
246 | 'UNKNOWN_IN_PROGRESS': static_url + 'dashboard/img/unknown-gray.gif', | ||
247 | 'UNKNOWN_INIT': static_url + 'dashboard/img/unknown-gray.svg', | ||
248 | 'UNKNOWN_COMPLETE': static_url + 'dashboard/img/unknown-green.svg', | ||
249 | } | ||
250 | |||
251 | |||
252 | resource_types = { | ||
253 | # LB | ||
254 | 'LoadBalance': 'LB', | ||
255 | 'HealthMonitor': 'LB', | ||
256 | 'PoolMember': 'LB', | ||
257 | 'Pool': 'LB', | ||
258 | # DB | ||
259 | 'DBInstance': 'DB', | ||
260 | 'Database': 'DB', | ||
261 | # SERVER | ||
262 | 'Instance': 'SERVER', | ||
263 | 'Server': 'SERVER', | ||
264 | # ALARM | ||
265 | 'Alarm': 'ALARM', | ||
266 | 'CombinationAlarm': 'ALARM', | ||
267 | 'CWLiteAlarm': 'ALARM', | ||
268 | # VOLUME | ||
269 | 'Volume': 'VOLUME', | ||
270 | 'VolumeAttachment': 'VOLUME', | ||
271 | # STACK | ||
272 | 'stack': 'STACK', | ||
273 | 'AutoScalingGroup': 'STACK', | ||
274 | 'InstanceGroup': 'STACK', | ||
275 | 'ServerGroup': 'STACK', | ||
276 | 'ResourceGroup': 'STACK', | ||
277 | # IMAGE | ||
278 | 'Image': 'IMAGE', | ||
279 | # WAIT | ||
280 | 'WaitCondition': 'WAIT', | ||
281 | 'WaitConditionHandle': 'WAIT', | ||
282 | 'UpdateWaitConditionHandle': 'WAIT', | ||
283 | # FIREWALL | ||
284 | 'Firewall': 'FIREWALL', | ||
285 | 'FirewallPolicy': 'FIREWALL', | ||
286 | 'FirewallRule': 'FIREWALL', | ||
287 | # FLOATINGIP | ||
288 | 'FloatingIP': 'FLOATINGIP', | ||
289 | 'FloatingIPAssociation': 'FLOATINGIP', | ||
290 | # ROUTER | ||
291 | 'Router': 'ROUTER', | ||
292 | 'RouterGateway': 'ROUTER', | ||
293 | 'RouterInterface': 'ROUTER', | ||
294 | # POLICY | ||
295 | 'ScalingPolicy': 'POLICY', | ||
296 | # CONFIG | ||
297 | 'CloudConfig': 'CONFIG', | ||
298 | 'MultipartMime': 'CONFIG', | ||
299 | 'SoftwareConfig': 'CONFIG', | ||
300 | 'SoftwareDeployment': 'CONFIG', | ||
301 | 'StructuredConfig': 'CONFIG', | ||
302 | 'StructuredDeployment': 'CONFIG', | ||
303 | # NETWORK | ||
304 | 'Net': 'NETWORK', | ||
305 | 'Subnet': 'NETWORK', | ||
306 | 'NetworkGateway': 'NETWORK', | ||
307 | 'ProviderNet': 'NETWORK', | ||
308 | # PORT | ||
309 | 'Port': 'PORT', | ||
310 | # SECURITYGROUP | ||
311 | 'SecurityGroup': 'SECURITYGROUP', | ||
312 | # VPN | ||
313 | 'VPNService': 'VPN', | ||
314 | # FLAVOR | ||
315 | 'Flavor': 'FLAVOR', | ||
316 | # KEYPAIR | ||
317 | 'KeyPair': 'KEYPAIR', | ||
318 | } | ||
319 | |||
320 | |||
321 | def get_resource_type(type): | ||
322 | for key, value in resource_types.items(): | ||
323 | if key in type: | ||
324 | return value | ||
325 | |||
326 | return 'UNKNOWN' | ||
327 | |||
328 | |||
329 | def get_resource_status(status): | ||
330 | if ('IN_PROGRESS' in status): | ||
331 | return 'IN_PROGRESS' | ||
332 | elif ('FAILED' in status): | ||
333 | return 'FAILED' | ||
334 | elif ('DELETE' in status): | ||
335 | return 'DELETE' | ||
336 | elif ('INIT' in status): | ||
337 | return 'INIT' | ||
338 | else: | ||
339 | return 'COMPLETE' | ||
340 | |||
341 | |||
342 | def get_resource_image(status, type): | ||
343 | """Sets the image url and in_progress action sw based on status.""" | ||
344 | resource_type = get_resource_type(type) | ||
345 | resource_status = get_resource_status(status) | ||
346 | resource_state = resource_type + "_" + resource_status | ||
347 | |||
348 | for key in resource_images: | ||
349 | if key == resource_state: | ||
350 | 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 f9e8800..0000000 --- a/openstack_dashboard/dashboards/project/stacks/panel.py +++ /dev/null | |||
@@ -1,21 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
2 | # not use this file except in compliance with the License. You may obtain | ||
3 | # a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
10 | # License for the specific language governing permissions and limitations | ||
11 | # under the License. | ||
12 | |||
13 | from django.utils.translation import ugettext_lazy as _ | ||
14 | |||
15 | import horizon | ||
16 | |||
17 | |||
18 | class Stacks(horizon.Panel): | ||
19 | name = _("Stacks") | ||
20 | slug = "stacks" | ||
21 | 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 e69de29..0000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/__init__.py +++ /dev/null | |||
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 b1f3744..0000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/panel.py +++ /dev/null | |||
@@ -1,23 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
2 | # you may not use this file except in compliance with the License. | ||
3 | # You may obtain a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
10 | # implied. | ||
11 | # See the License for the specific language governing permissions and | ||
12 | # limitations under the License. | ||
13 | |||
14 | from django.utils.translation import ugettext_lazy as _ | ||
15 | |||
16 | import horizon | ||
17 | |||
18 | |||
19 | class ResourceTypes(horizon.Panel): | ||
20 | name = _("Resource Types") | ||
21 | slug = "stacks.resource_types" | ||
22 | permissions = ('openstack.services.orchestration',) | ||
23 | 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 66e7702..0000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/tables.py +++ /dev/null | |||
@@ -1,36 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
2 | # you may not use this file except in compliance with the License. | ||
3 | # You may obtain a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
10 | # implied. | ||
11 | # See the License for the specific language governing permissions and | ||
12 | # limitations under the License. | ||
13 | |||
14 | from django.utils.translation import ugettext_lazy as _ | ||
15 | |||
16 | from horizon import tables | ||
17 | |||
18 | |||
19 | class ResourceTypesFilterAction(tables.FilterAction): | ||
20 | filter_type = 'server' | ||
21 | filter_choices = (('name', _('Type ='), True, _("Case sensitive")),) | ||
22 | |||
23 | |||
24 | class ResourceTypesTable(tables.DataTable): | ||
25 | name = tables.Column("resource_type", | ||
26 | verbose_name=_("Type"), | ||
27 | link="horizon:project:stacks.resource_types:details",) | ||
28 | |||
29 | def get_object_id(self, resource): | ||
30 | return resource.resource_type | ||
31 | |||
32 | class Meta(object): | ||
33 | name = "resource_types" | ||
34 | verbose_name = _("Resource Types") | ||
35 | table_actions = (ResourceTypesFilterAction,) | ||
36 | 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 b0d5467..0000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/tabs.py +++ /dev/null | |||
@@ -1,32 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
2 | # you may not use this file except in compliance with the License. | ||
3 | # You may obtain a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
10 | # implied. | ||
11 | # See the License for the specific language governing permissions and | ||
12 | # limitations under the License. | ||
13 | |||
14 | from django.utils.translation import ugettext_lazy as _ | ||
15 | |||
16 | from horizon import tabs | ||
17 | |||
18 | |||
19 | class ResourceTypeOverviewTab(tabs.Tab): | ||
20 | name = _("Overview") | ||
21 | slug = "resource_type_overview" | ||
22 | template_name = "project/stacks.resource_types/_details.html" | ||
23 | |||
24 | def get_context_data(self, request): | ||
25 | return {"r_type": self.tab_group.kwargs['rt'], | ||
26 | "r_type_attributes": self.tab_group.kwargs['rt_attributes'], | ||
27 | "r_type_properties": self.tab_group.kwargs['rt_properties']} | ||
28 | |||
29 | |||
30 | class ResourceTypeDetailsTabs(tabs.TabGroup): | ||
31 | slug = "resource_type_details" | ||
32 | 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 704787e..0000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/templates/stacks.resource_types/_details.html +++ /dev/null | |||
@@ -1,15 +0,0 @@ | |||
1 | {% load i18n %} | ||
2 | |||
3 | <div class="detail"> | ||
4 | <dl> | ||
5 | <dd>{{ r_type }}</dd> | ||
6 | </dl> | ||
7 | |||
8 | <h4>{% trans "Attributes" %}</h4> | ||
9 | <pre>{{ r_type_attributes }} | ||
10 | </pre> | ||
11 | |||
12 | <h4>{% trans "Properties" %}</h4> | ||
13 | <pre>{{ r_type_properties }} | ||
14 | </pre> | ||
15 | </div> | ||
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 4c73644..0000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/tests.py +++ /dev/null | |||
@@ -1,52 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
2 | # you may not use this file except in compliance with the License. | ||
3 | # You may obtain a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
10 | # implied. | ||
11 | # See the License for the specific language governing permissions and | ||
12 | # limitations under the License. | ||
13 | |||
14 | from django.core.urlresolvers import reverse | ||
15 | from django import http | ||
16 | |||
17 | from mox3.mox import IsA | ||
18 | |||
19 | from openstack_dashboard import api | ||
20 | from openstack_dashboard.test import helpers as test | ||
21 | |||
22 | |||
23 | class ResourceTypesTests(test.TestCase): | ||
24 | |||
25 | @test.create_stubs({api.heat: ('resource_types_list',)}) | ||
26 | def test_index(self): | ||
27 | filters = {} | ||
28 | api.heat.resource_types_list( | ||
29 | IsA(http.HttpRequest), filters=filters).AndReturn( | ||
30 | self.resource_types.list()) | ||
31 | self.mox.ReplayAll() | ||
32 | |||
33 | res = self.client.get( | ||
34 | reverse('horizon:project:stacks.resource_types:index')) | ||
35 | self.assertTemplateUsed( | ||
36 | res, 'horizon/common/_data_table_view.html') | ||
37 | self.assertContains(res, 'AWS::CloudFormation::Stack') | ||
38 | |||
39 | @test.create_stubs({api.heat: ('resource_type_get',)}) | ||
40 | def test_detail_view(self): | ||
41 | rt = self.api_resource_types.first() | ||
42 | |||
43 | api.heat.resource_type_get( | ||
44 | IsA(http.HttpRequest), rt['resource_type']).AndReturn(rt) | ||
45 | self.mox.ReplayAll() | ||
46 | |||
47 | url = reverse('horizon:project:stacks.resource_types:details', | ||
48 | args=[rt['resource_type']]) | ||
49 | res = self.client.get(url) | ||
50 | |||
51 | self.assertTemplateUsed(res, 'horizon/common/_detail.html') | ||
52 | 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 8ab8cb0..0000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/urls.py +++ /dev/null | |||
@@ -1,22 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
2 | # you may not use this file except in compliance with the License. | ||
3 | # You may obtain a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
10 | # implied. | ||
11 | # See the License for the specific language governing permissions and | ||
12 | # limitations under the License. | ||
13 | |||
14 | from django.conf.urls import url | ||
15 | |||
16 | from openstack_dashboard.dashboards.project.stacks.resource_types import views | ||
17 | |||
18 | urlpatterns = [ | ||
19 | url(r'^$', views.ResourceTypesView.as_view(), name='index'), | ||
20 | url(r'^(?P<resource_type>[^/]+)/$', | ||
21 | views.DetailView.as_view(), name='details'), | ||
22 | ] | ||
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 8afcdf8..0000000 --- a/openstack_dashboard/dashboards/project/stacks/resource_types/views.py +++ /dev/null | |||
@@ -1,78 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
2 | # you may not use this file except in compliance with the License. | ||
3 | # You may obtain a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
10 | # implied. | ||
11 | # See the License for the specific language governing permissions and | ||
12 | # limitations under the License. | ||
13 | |||
14 | import yaml | ||
15 | |||
16 | from django.core.urlresolvers import reverse | ||
17 | from django.utils.translation import ugettext_lazy as _ | ||
18 | |||
19 | from horizon import exceptions | ||
20 | from horizon import tables | ||
21 | from horizon import tabs | ||
22 | |||
23 | from openstack_dashboard import api | ||
24 | import openstack_dashboard.dashboards.project.stacks.resource_types.tables \ | ||
25 | as project_tables | ||
26 | import openstack_dashboard.dashboards.project.stacks.resource_types.tabs \ | ||
27 | as project_tabs | ||
28 | |||
29 | |||
30 | class ResourceTypesView(tables.DataTableView): | ||
31 | table_class = project_tables.ResourceTypesTable | ||
32 | page_title = _("Resource Types") | ||
33 | |||
34 | def get_data(self): | ||
35 | try: | ||
36 | filters = self.get_filters() | ||
37 | if 'name' in filters: | ||
38 | filters['name'] = '.*' + filters['name'] | ||
39 | r_types = sorted(api.heat.resource_types_list(self.request, | ||
40 | filters=filters), | ||
41 | key=lambda resource: resource.resource_type) | ||
42 | except Exception: | ||
43 | r_types = [] | ||
44 | msg = _('Unable to retrieve stack resource types.') | ||
45 | exceptions.handle(self.request, msg) | ||
46 | return r_types | ||
47 | |||
48 | |||
49 | class DetailView(tabs.TabView): | ||
50 | tab_group_class = project_tabs.ResourceTypeDetailsTabs | ||
51 | template_name = 'horizon/common/_detail.html' | ||
52 | page_title = "{{ resource_type }}" | ||
53 | |||
54 | def get_resource_type(self, request, **kwargs): | ||
55 | try: | ||
56 | resource_type_overview = api.heat.resource_type_get( | ||
57 | request, | ||
58 | kwargs['resource_type']) | ||
59 | return resource_type_overview | ||
60 | except Exception: | ||
61 | msg = _('Unable to retrieve resource type details.') | ||
62 | exceptions.handle(request, msg, redirect=self.get_redirect_url()) | ||
63 | |||
64 | def get_tabs(self, request, **kwargs): | ||
65 | resource_type_overview = self.get_resource_type(request, **kwargs) | ||
66 | r_type = resource_type_overview['resource_type'] | ||
67 | r_type_attributes = resource_type_overview['attributes'] | ||
68 | r_type_properties = resource_type_overview['properties'] | ||
69 | return self.tab_group_class( | ||
70 | request, | ||
71 | rt=r_type, | ||
72 | rt_attributes=yaml.safe_dump(r_type_attributes, indent=2), | ||
73 | rt_properties=yaml.safe_dump(r_type_properties, indent=2), | ||
74 | **kwargs) | ||
75 | |||
76 | @staticmethod | ||
77 | def get_redirect_url(): | ||
78 | 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 4250e7e..0000000 --- a/openstack_dashboard/dashboards/project/stacks/sro.py +++ /dev/null | |||
@@ -1,44 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
2 | # not use this file except in compliance with the License. You may obtain | ||
3 | # a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
10 | # License for the specific language governing permissions and limitations | ||
11 | # under the License. | ||
12 | |||
13 | from django.template.defaultfilters import title | ||
14 | from django.template.loader import render_to_string | ||
15 | |||
16 | from horizon.utils import filters | ||
17 | |||
18 | |||
19 | def stack_info(stack, stack_image): | ||
20 | stack.stack_status_desc = title( | ||
21 | filters.replace_underscores(stack.stack_status)) | ||
22 | if stack.stack_status_reason: | ||
23 | stack.stack_status_reason = title( | ||
24 | filters.replace_underscores(stack.stack_status_reason) | ||
25 | ) | ||
26 | context = {} | ||
27 | context['stack'] = stack | ||
28 | context['stack_image'] = stack_image | ||
29 | return render_to_string('project/stacks/_stack_info.html', | ||
30 | context) | ||
31 | |||
32 | |||
33 | def resource_info(resource): | ||
34 | resource.resource_status_desc = title( | ||
35 | filters.replace_underscores(resource.resource_status) | ||
36 | ) | ||
37 | if resource.resource_status_reason: | ||
38 | resource.resource_status_reason = title( | ||
39 | filters.replace_underscores(resource.resource_status_reason) | ||
40 | ) | ||
41 | context = {} | ||
42 | context['resource'] = resource | ||
43 | return render_to_string('project/stacks/_resource_info.html', | ||
44 | context) | ||
diff --git a/openstack_dashboard/dashboards/project/stacks/tables.py b/openstack_dashboard/dashboards/project/stacks/tables.py deleted file mode 100644 index 96391ab..0000000 --- a/openstack_dashboard/dashboards/project/stacks/tables.py +++ /dev/null | |||
@@ -1,413 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
2 | # not use this file except in compliance with the License. You may obtain | ||
3 | # a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
10 | # License for the specific language governing permissions and limitations | ||
11 | # under the License. | ||
12 | |||
13 | from django.core import urlresolvers | ||
14 | from django.http import Http404 | ||
15 | from django.template.defaultfilters import title | ||
16 | from django.utils.translation import pgettext_lazy | ||
17 | from django.utils.translation import ugettext_lazy as _ | ||
18 | from django.utils.translation import ungettext_lazy | ||
19 | from heatclient import exc | ||
20 | |||
21 | from horizon import exceptions | ||
22 | from horizon import messages | ||
23 | from horizon import tables | ||
24 | from horizon.utils import filters | ||
25 | |||
26 | from openstack_dashboard import api | ||
27 | from openstack_dashboard.dashboards.project.stacks import mappings | ||
28 | |||
29 | |||
30 | class LaunchStack(tables.LinkAction): | ||
31 | name = "launch" | ||
32 | verbose_name = _("Launch Stack") | ||
33 | url = "horizon:project:stacks:select_template" | ||
34 | classes = ("ajax-modal",) | ||
35 | icon = "plus" | ||
36 | policy_rules = (("orchestration", "stacks:validate_template"), | ||
37 | ("orchestration", "stacks:create"),) | ||
38 | |||
39 | |||
40 | class PreviewStack(tables.LinkAction): | ||
41 | name = "preview" | ||
42 | verbose_name = _("Preview Stack") | ||
43 | url = "horizon:project:stacks:preview_template" | ||
44 | classes = ("ajax-modal",) | ||
45 | icon = "eye" | ||
46 | policy_rules = (("orchestration", "stacks:validate_template"), | ||
47 | ("orchestration", "stacks:preview"),) | ||
48 | |||
49 | |||
50 | class CheckStack(tables.BatchAction): | ||
51 | name = "check" | ||
52 | verbose_name = _("Check Stack") | ||
53 | policy_rules = (("orchestration", "actions:action"),) | ||
54 | icon = "check-square" | ||
55 | |||
56 | @staticmethod | ||
57 | def action_present(count): | ||
58 | return ungettext_lazy( | ||
59 | u"Check Stack", | ||
60 | u"Check Stacks", | ||
61 | count | ||
62 | ) | ||
63 | |||
64 | @staticmethod | ||
65 | def action_past(count): | ||
66 | return ungettext_lazy( | ||
67 | u"Checked Stack", | ||
68 | u"Checked Stacks", | ||
69 | count | ||
70 | ) | ||
71 | |||
72 | def action(self, request, stack_id): | ||
73 | api.heat.action_check(request, stack_id) | ||
74 | |||
75 | |||
76 | class SuspendStack(tables.BatchAction): | ||
77 | name = "suspend" | ||
78 | verbose_name = _("Suspend Stack") | ||
79 | policy_rules = (("orchestration", "actions:action"),) | ||
80 | icon = "pause" | ||
81 | |||
82 | @staticmethod | ||
83 | def action_present(count): | ||
84 | return ungettext_lazy( | ||
85 | u"Suspend Stack", | ||
86 | u"Suspend Stacks", | ||
87 | count | ||
88 | ) | ||
89 | |||
90 | @staticmethod | ||
91 | def action_past(count): | ||
92 | return ungettext_lazy( | ||
93 | u"Suspended Stack", | ||
94 | u"Suspended Stacks", | ||
95 | count | ||
96 | ) | ||
97 | |||
98 | def action(self, request, stack_id): | ||
99 | try: | ||
100 | api.heat.action_suspend(request, stack_id) | ||
101 | except Exception: | ||
102 | msg = _('Failed to suspend stack.') | ||
103 | exceptions.handle(request, msg) | ||
104 | |||
105 | |||
106 | class ResumeStack(tables.BatchAction): | ||
107 | name = "resume" | ||
108 | verbose_name = _("Resume Stack") | ||
109 | policy_rules = (("orchestration", "actions:action"),) | ||
110 | icon = "play" | ||
111 | |||
112 | @staticmethod | ||
113 | def action_present(count): | ||
114 | return ungettext_lazy( | ||
115 | u"Resume Stack", | ||
116 | u"Resume Stacks", | ||
117 | count | ||
118 | ) | ||
119 | |||
120 | @staticmethod | ||
121 | def action_past(count): | ||
122 | return ungettext_lazy( | ||
123 | u"Resumed Stack", | ||
124 | u"Resumed Stacks", | ||
125 | count | ||
126 | ) | ||
127 | |||
128 | def action(self, request, stack_id): | ||
129 | try: | ||
130 | api.heat.action_resume(request, stack_id) | ||
131 | except Exception: | ||
132 | msg = _('Failed to resume stack.') | ||
133 | exceptions.handle(request, msg) | ||
134 | |||
135 | |||
136 | class ChangeStackTemplate(tables.LinkAction): | ||
137 | name = "edit" | ||
138 | verbose_name = _("Change Stack Template") | ||
139 | url = "horizon:project:stacks:change_template" | ||
140 | classes = ("ajax-modal",) | ||
141 | icon = "pencil" | ||
142 | |||
143 | def get_link_url(self, stack): | ||
144 | return urlresolvers.reverse(self.url, args=[stack.id]) | ||
145 | |||
146 | |||
147 | class DeleteStack(tables.DeleteAction): | ||
148 | @staticmethod | ||
149 | def action_present(count): | ||
150 | return ungettext_lazy( | ||
151 | u"Delete Stack", | ||
152 | u"Delete Stacks", | ||
153 | count | ||
154 | ) | ||
155 | |||
156 | @staticmethod | ||
157 | def action_past(count): | ||
158 | return ungettext_lazy( | ||
159 | u"Deleted Stack", | ||
160 | u"Deleted Stacks", | ||
161 | count | ||
162 | ) | ||
163 | |||
164 | policy_rules = (("orchestration", "stacks:delete"),) | ||
165 | |||
166 | def delete(self, request, stack_id): | ||
167 | try: | ||
168 | api.heat.stack_delete(request, stack_id) | ||
169 | except Exception: | ||
170 | msg = _('Failed to delete stack.') | ||
171 | exceptions.handle(request, msg) | ||
172 | |||
173 | def allowed(self, request, stack): | ||
174 | if stack is not None: | ||
175 | return stack.stack_status != 'DELETE_COMPLETE' | ||
176 | return True | ||
177 | |||
178 | |||
179 | class StacksUpdateRow(tables.Row): | ||
180 | ajax = True | ||
181 | |||
182 | def can_be_selected(self, datum): | ||
183 | return datum.stack_status != 'DELETE_COMPLETE' | ||
184 | |||
185 | def get_data(self, request, stack_id): | ||
186 | try: | ||
187 | stack = api.heat.stack_get(request, stack_id) | ||
188 | if stack.stack_status == 'DELETE_COMPLETE': | ||
189 | # returning 404 to the ajax call removes the | ||
190 | # row from the table on the ui | ||
191 | raise Http404 | ||
192 | return stack | ||
193 | except Http404: | ||
194 | raise | ||
195 | except Exception as e: | ||
196 | messages.error(request, e) | ||
197 | raise | ||
198 | |||
199 | |||
200 | class StacksFilterAction(tables.FilterAction): | ||
201 | filter_type = 'server' | ||
202 | filter_choices = (('name', _('Stack Name ='), True, _('Case-sensitive')), | ||
203 | ('id', _('Stack ID ='), True), | ||
204 | ('status', _('Status ='), True)) | ||
205 | |||
206 | |||
207 | class StacksTable(tables.DataTable): | ||
208 | STATUS_CHOICES = ( | ||
209 | ("Complete", True), | ||
210 | ("Failed", False), | ||
211 | ) | ||
212 | STACK_STATUS_DISPLAY_CHOICES = ( | ||
213 | ("init_in_progress", pgettext_lazy("current status of stack", | ||
214 | u"Init In Progress")), | ||
215 | ("init_complete", pgettext_lazy("current status of stack", | ||
216 | u"Init Complete")), | ||
217 | ("init_failed", pgettext_lazy("current status of stack", | ||
218 | u"Init Failed")), | ||
219 | ("create_in_progress", pgettext_lazy("current status of stack", | ||
220 | u"Create In Progress")), | ||
221 | ("create_complete", pgettext_lazy("current status of stack", | ||
222 | u"Create Complete")), | ||
223 | ("create_failed", pgettext_lazy("current status of stack", | ||
224 | u"Create Failed")), | ||
225 | ("delete_in_progress", pgettext_lazy("current status of stack", | ||
226 | u"Delete In Progress")), | ||
227 | ("delete_complete", pgettext_lazy("current status of stack", | ||
228 | u"Delete Complete")), | ||
229 | ("delete_failed", pgettext_lazy("current status of stack", | ||
230 | u"Delete Failed")), | ||
231 | ("update_in_progress", pgettext_lazy("current status of stack", | ||
232 | u"Update In Progress")), | ||
233 | ("update_complete", pgettext_lazy("current status of stack", | ||
234 | u"Update Complete")), | ||
235 | ("update_failed", pgettext_lazy("current status of stack", | ||
236 | u"Update Failed")), | ||
237 | ("rollback_in_progress", pgettext_lazy("current status of stack", | ||
238 | u"Rollback In Progress")), | ||
239 | ("rollback_complete", pgettext_lazy("current status of stack", | ||
240 | u"Rollback Complete")), | ||
241 | ("rollback_failed", pgettext_lazy("current status of stack", | ||
242 | u"Rollback Failed")), | ||
243 | ("suspend_in_progress", pgettext_lazy("current status of stack", | ||
244 | u"Suspend In Progress")), | ||
245 | ("suspend_complete", pgettext_lazy("current status of stack", | ||
246 | u"Suspend Complete")), | ||
247 | ("suspend_failed", pgettext_lazy("current status of stack", | ||
248 | u"Suspend Failed")), | ||
249 | ("resume_in_progress", pgettext_lazy("current status of stack", | ||
250 | u"Resume In Progress")), | ||
251 | ("resume_complete", pgettext_lazy("current status of stack", | ||
252 | u"Resume Complete")), | ||
253 | ("resume_failed", pgettext_lazy("current status of stack", | ||
254 | u"Resume Failed")), | ||
255 | ("adopt_in_progress", pgettext_lazy("current status of stack", | ||
256 | u"Adopt In Progress")), | ||
257 | ("adopt_complete", pgettext_lazy("current status of stack", | ||
258 | u"Adopt Complete")), | ||
259 | ("adopt_failed", pgettext_lazy("current status of stack", | ||
260 | u"Adopt Failed")), | ||
261 | ("snapshot_in_progress", pgettext_lazy("current status of stack", | ||
262 | u"Snapshot In Progress")), | ||
263 | ("snapshot_complete", pgettext_lazy("current status of stack", | ||
264 | u"Snapshot Complete")), | ||
265 | ("snapshot_failed", pgettext_lazy("current status of stack", | ||
266 | u"Snapshot Failed")), | ||
267 | ("check_in_progress", pgettext_lazy("current status of stack", | ||
268 | u"Check In Progress")), | ||
269 | ("check_complete", pgettext_lazy("current status of stack", | ||
270 | u"Check Complete")), | ||
271 | ("check_failed", pgettext_lazy("current status of stack", | ||
272 | u"Check Failed")), | ||
273 | ) | ||
274 | name = tables.Column("stack_name", | ||
275 | verbose_name=_("Stack Name"), | ||
276 | link="horizon:project:stacks:detail",) | ||
277 | created = tables.Column("creation_time", | ||
278 | verbose_name=_("Created"), | ||
279 | filters=(filters.parse_isotime, | ||
280 | filters.timesince_sortable), | ||
281 | attrs={'data-type': 'timesince'}) | ||
282 | updated = tables.Column("updated_time", | ||
283 | verbose_name=_("Updated"), | ||
284 | filters=(filters.parse_isotime, | ||
285 | filters.timesince_or_never)) | ||
286 | status = tables.Column("status", | ||
287 | hidden=True, | ||
288 | status=True, | ||
289 | status_choices=STATUS_CHOICES) | ||
290 | |||
291 | stack_status = tables.Column("stack_status", | ||
292 | verbose_name=_("Status"), | ||
293 | display_choices=STACK_STATUS_DISPLAY_CHOICES) | ||
294 | |||
295 | def get_object_display(self, stack): | ||
296 | return stack.stack_name | ||
297 | |||
298 | class Meta(object): | ||
299 | name = "stacks" | ||
300 | verbose_name = _("Stacks") | ||
301 | pagination_param = 'stack_marker' | ||
302 | status_columns = ["status", ] | ||
303 | row_class = StacksUpdateRow | ||
304 | table_actions_menu = (CheckStack, | ||
305 | SuspendStack, | ||
306 | ResumeStack,) | ||
307 | table_actions = (LaunchStack, | ||
308 | PreviewStack, | ||
309 | DeleteStack, | ||
310 | StacksFilterAction,) | ||
311 | row_actions = (CheckStack, | ||
312 | SuspendStack, | ||
313 | ResumeStack, | ||
314 | ChangeStackTemplate, | ||
315 | DeleteStack,) | ||
316 | |||
317 | |||
318 | def get_resource_url(obj): | ||
319 | if obj.physical_resource_id == obj.stack_id: | ||
320 | return None | ||
321 | return urlresolvers.reverse('horizon:project:stacks:resource', | ||
322 | args=(obj.stack_id, obj.resource_name)) | ||
323 | |||
324 | |||
325 | class EventsTable(tables.DataTable): | ||
326 | |||
327 | logical_resource = tables.Column('resource_name', | ||
328 | verbose_name=_("Stack Resource"), | ||
329 | link=get_resource_url) | ||
330 | physical_resource = tables.Column('physical_resource_id', | ||
331 | verbose_name=_("Resource")) | ||
332 | timestamp = tables.Column('event_time', | ||
333 | verbose_name=_("Time Since Event"), | ||
334 | filters=(filters.parse_isotime, | ||
335 | filters.timesince_or_never)) | ||
336 | status = tables.Column("resource_status", | ||
337 | filters=(title, filters.replace_underscores), | ||
338 | verbose_name=_("Status"),) | ||
339 | |||
340 | statusreason = tables.Column("resource_status_reason", | ||
341 | verbose_name=_("Status Reason"),) | ||
342 | |||
343 | class Meta(object): | ||
344 | name = "events" | ||
345 | verbose_name = _("Stack Events") | ||
346 | |||
347 | |||
348 | class ResourcesUpdateRow(tables.Row): | ||
349 | ajax = True | ||
350 | |||
351 | def get_data(self, request, resource_name): | ||
352 | try: | ||
353 | stack = self.table.stack | ||
354 | stack_identifier = '%s/%s' % (stack.stack_name, stack.id) | ||
355 | return api.heat.resource_get( | ||
356 | request, stack_identifier, resource_name) | ||
357 | except exc.HTTPNotFound: | ||
358 | # returning 404 to the ajax call removes the | ||
359 | # row from the table on the ui | ||
360 | raise Http404 | ||
361 | except Exception as e: | ||
362 | messages.error(request, e) | ||
363 | |||
364 | |||
365 | class ResourcesTable(tables.DataTable): | ||
366 | class StatusColumn(tables.Column): | ||
367 | def get_raw_data(self, datum): | ||
368 | return datum.resource_status.partition("_")[2] | ||
369 | |||
370 | STATUS_CHOICES = ( | ||
371 | ("Complete", True), | ||
372 | ("Failed", False), | ||
373 | ) | ||
374 | STATUS_DISPLAY_CHOICES = StacksTable.STACK_STATUS_DISPLAY_CHOICES | ||
375 | |||
376 | logical_resource = tables.Column('resource_name', | ||
377 | verbose_name=_("Stack Resource"), | ||
378 | link=get_resource_url) | ||
379 | physical_resource = tables.Column('physical_resource_id', | ||
380 | verbose_name=_("Resource"), | ||
381 | link=mappings.resource_to_url) | ||
382 | resource_type = tables.Column("resource_type", | ||
383 | verbose_name=_("Stack Resource Type"),) | ||
384 | updated_time = tables.Column('updated_time', | ||
385 | verbose_name=_("Date Updated"), | ||
386 | filters=(filters.parse_isotime, | ||
387 | filters.timesince_or_never)) | ||
388 | status = tables.Column("resource_status", | ||
389 | verbose_name=_("Status"), | ||
390 | display_choices=STATUS_DISPLAY_CHOICES) | ||
391 | |||
392 | statusreason = tables.Column("resource_status_reason", | ||
393 | verbose_name=_("Status Reason"),) | ||
394 | |||
395 | status_hidden = StatusColumn("status", | ||
396 | hidden=True, | ||
397 | status=True, | ||
398 | status_choices=STATUS_CHOICES) | ||
399 | |||
400 | def __init__(self, request, data=None, | ||
401 | needs_form_wrapper=None, **kwargs): | ||
402 | super(ResourcesTable, self).__init__( | ||
403 | request, data, needs_form_wrapper, **kwargs) | ||
404 | self.stack = kwargs['stack'] | ||
405 | |||
406 | def get_object_id(self, datum): | ||
407 | return datum.resource_name | ||
408 | |||
409 | class Meta(object): | ||
410 | name = "resources" | ||
411 | verbose_name = _("Stack Resources") | ||
412 | status_columns = ["status_hidden", ] | ||
413 | 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 1e6c92b..0000000 --- a/openstack_dashboard/dashboards/project/stacks/tabs.py +++ /dev/null | |||
@@ -1,173 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
2 | # not use this file except in compliance with the License. You may obtain | ||
3 | # a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
10 | # License for the specific language governing permissions and limitations | ||
11 | # under the License. | ||
12 | |||
13 | import logging | ||
14 | |||
15 | from django.utils.translation import ugettext_lazy as _ | ||
16 | |||
17 | from horizon import messages | ||
18 | from horizon import tabs | ||
19 | from openstack_dashboard import api | ||
20 | from openstack_dashboard import policy | ||
21 | |||
22 | from openstack_dashboard.dashboards.project.stacks \ | ||
23 | import api as project_api | ||
24 | from openstack_dashboard.dashboards.project.stacks import mappings | ||
25 | from openstack_dashboard.dashboards.project.stacks \ | ||
26 | import tables as project_tables | ||
27 | |||
28 | |||
29 | LOG = logging.getLogger(__name__) | ||
30 | |||
31 | |||
32 | class StackTopologyTab(tabs.Tab): | ||
33 | name = _("Topology") | ||
34 | slug = "topology" | ||
35 | template_name = "project/stacks/_detail_topology.html" | ||
36 | preload = False | ||
37 | |||
38 | def allowed(self, request): | ||
39 | return policy.check( | ||
40 | (("orchestration", "stacks:template"), | ||
41 | ("orchestration", "stacks:lookup"), | ||
42 | ("orchestration", "stacks:show"), | ||
43 | ("orchestration", "resource:index"),), | ||
44 | request) | ||
45 | |||
46 | def get_context_data(self, request): | ||
47 | context = {} | ||
48 | stack = self.tab_group.kwargs['stack'] | ||
49 | context['stack_id'] = stack.id | ||
50 | context['d3_data'] = project_api.d3_data(request, stack_id=stack.id) | ||
51 | return context | ||
52 | |||
53 | |||
54 | class StackOverviewTab(tabs.Tab): | ||
55 | name = _("Overview") | ||
56 | slug = "overview" | ||
57 | template_name = "project/stacks/_detail_overview.html" | ||
58 | |||
59 | def allowed(self, request): | ||
60 | return policy.check( | ||
61 | (("orchestration", "stacks:template"), | ||
62 | ("orchestration", "stacks:lookup"), | ||
63 | ("orchestration", "stacks:show"),), | ||
64 | request) | ||
65 | |||
66 | def get_context_data(self, request): | ||
67 | return {"stack": self.tab_group.kwargs['stack']} | ||
68 | |||
69 | |||
70 | class ResourceOverviewTab(tabs.Tab): | ||
71 | name = _("Overview") | ||
72 | slug = "resource_overview" | ||
73 | template_name = "project/stacks/_resource_overview.html" | ||
74 | |||
75 | def get_context_data(self, request): | ||
76 | resource = self.tab_group.kwargs['resource'] | ||
77 | resource_url = mappings.resource_to_url(resource) | ||
78 | return { | ||
79 | "resource": resource, | ||
80 | "resource_url": resource_url, | ||
81 | "metadata": self.tab_group.kwargs['metadata']} | ||
82 | |||
83 | |||
84 | class StackEventsTab(tabs.Tab): | ||
85 | name = _("Events") | ||
86 | slug = "events" | ||
87 | template_name = "project/stacks/_detail_events.html" | ||
88 | preload = False | ||
89 | |||
90 | def allowed(self, request): | ||
91 | return policy.check( | ||
92 | (("orchestration", "stacks:template"), | ||
93 | ("orchestration", "stacks:lookup"), | ||
94 | ("orchestration", "stacks:show"), | ||
95 | ("orchestration", "events:index"),), | ||
96 | request) | ||
97 | |||
98 | def get_context_data(self, request): | ||
99 | stack = self.tab_group.kwargs['stack'] | ||
100 | try: | ||
101 | stack_identifier = '%s/%s' % (stack.stack_name, stack.id) | ||
102 | events = api.heat.events_list(self.request, stack_identifier) | ||
103 | LOG.debug('got events %s', events) | ||
104 | # The stack id is needed to generate the resource URL. | ||
105 | for event in events: | ||
106 | event.stack_id = stack.id | ||
107 | except Exception: | ||
108 | events = [] | ||
109 | messages.error(request, _( | ||
110 | 'Unable to get events for stack "%s".') % stack.stack_name) | ||
111 | return {"stack": stack, | ||
112 | "table": project_tables.EventsTable(request, data=events), } | ||
113 | |||
114 | |||
115 | class StackResourcesTab(tabs.Tab): | ||
116 | name = _("Resources") | ||
117 | slug = "resources" | ||
118 | template_name = "project/stacks/_detail_resources.html" | ||
119 | preload = False | ||
120 | |||
121 | def allowed(self, request): | ||
122 | return policy.check( | ||
123 | (("orchestration", "stacks:template"), | ||
124 | ("orchestration", "stacks:lookup"), | ||
125 | ("orchestration", "stacks:show"), | ||
126 | ("orchestration", "resource:index"),), | ||
127 | request) | ||
128 | |||
129 | def get_context_data(self, request): | ||
130 | stack = self.tab_group.kwargs['stack'] | ||
131 | try: | ||
132 | stack_identifier = '%s/%s' % (stack.stack_name, stack.id) | ||
133 | resources = api.heat.resources_list(self.request, stack_identifier) | ||
134 | LOG.debug('got resources %s', resources) | ||
135 | # The stack id is needed to generate the resource URL. | ||
136 | for r in resources: | ||
137 | r.stack_id = stack.id | ||
138 | except Exception: | ||
139 | resources = [] | ||
140 | messages.error(request, _( | ||
141 | 'Unable to get resources for stack "%s".') % stack.stack_name) | ||
142 | return {"stack": stack, | ||
143 | "table": project_tables.ResourcesTable( | ||
144 | request, data=resources, stack=stack), } | ||
145 | |||
146 | |||
147 | class StackTemplateTab(tabs.Tab): | ||
148 | name = _("Template") | ||
149 | slug = "stack_template" | ||
150 | template_name = "project/stacks/_stack_template.html" | ||
151 | |||
152 | def allowed(self, request): | ||
153 | return policy.check( | ||
154 | (("orchestration", "stacks:template"), | ||
155 | ("orchestration", "stacks:lookup"), | ||
156 | ("orchestration", "stacks:show"),), | ||
157 | request) | ||
158 | |||
159 | def get_context_data(self, request): | ||
160 | return {"stack_template": self.tab_group.kwargs['stack_template']} | ||
161 | |||
162 | |||
163 | class StackDetailTabs(tabs.TabGroup): | ||
164 | slug = "stack_details" | ||
165 | tabs = (StackTopologyTab, StackOverviewTab, StackResourcesTab, | ||
166 | StackEventsTab, StackTemplateTab) | ||
167 | sticky = True | ||
168 | |||
169 | |||
170 | class ResourceDetailTabs(tabs.TabGroup): | ||
171 | slug = "resource_details" | ||
172 | tabs = (ResourceOverviewTab,) | ||
173 | 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 e69de29..0000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/__init__.py +++ /dev/null | |||
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 3492d59..0000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/panel.py +++ /dev/null | |||
@@ -1,23 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
2 | # you may not use this file except in compliance with the License. | ||
3 | # You may obtain a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
10 | # implied. | ||
11 | # See the License for the specific language governing permissions and | ||
12 | # limitations under the License. | ||
13 | |||
14 | from django.utils.translation import ugettext_lazy as _ | ||
15 | |||
16 | import horizon | ||
17 | |||
18 | |||
19 | class TemplateVersions(horizon.Panel): | ||
20 | name = _("Template Versions") | ||
21 | slug = "stacks.template_versions" | ||
22 | permissions = ('openstack.services.orchestration',) | ||
23 | 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 8c63d14..0000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/tables.py +++ /dev/null | |||
@@ -1,52 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
2 | # you may not use this file except in compliance with the License. | ||
3 | # You may obtain a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
10 | # implied. | ||
11 | # See the License for the specific language governing permissions and | ||
12 | # limitations under the License. | ||
13 | |||
14 | from django.template import defaultfilters as filters | ||
15 | from django.utils.translation import ugettext_lazy as _ | ||
16 | |||
17 | from horizon import tables | ||
18 | |||
19 | |||
20 | class TemplateVersionsTable(tables.DataTable): | ||
21 | version = tables.Column( | ||
22 | "version", | ||
23 | verbose_name=_("Version"), | ||
24 | link="horizon:project:stacks.template_versions:details",) | ||
25 | type = tables.Column( | ||
26 | "type", | ||
27 | verbose_name=_("Type"), | ||
28 | filters=(filters.upper,)) | ||
29 | |||
30 | def get_object_id(self, template_versions): | ||
31 | return template_versions.version | ||
32 | |||
33 | class Meta(object): | ||
34 | name = "template_versions" | ||
35 | table_actions = (tables.FilterAction,) | ||
36 | verbose_name = _("Template Versions") | ||
37 | table_actions = (tables.FilterAction,) | ||
38 | multi_select = False | ||
39 | |||
40 | |||
41 | class TemplateFunctionsTable(tables.DataTable): | ||
42 | functions = tables.Column('functions', verbose_name=_("Function")) | ||
43 | description = tables.Column('description', verbose_name=_("Description")) | ||
44 | |||
45 | def get_object_id(self, template_functions): | ||
46 | return template_functions.functions | ||
47 | |||
48 | class Meta(object): | ||
49 | name = "template_functions" | ||
50 | verbose_name = _("Template Functions") | ||
51 | table_actions = (tables.FilterAction,) | ||
52 | 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 9cb9096..0000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/tabs.py +++ /dev/null | |||
@@ -1,51 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
2 | # you may not use this file except in compliance with the License. | ||
3 | # You may obtain a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
10 | # implied. | ||
11 | # See the License for the specific language governing permissions and | ||
12 | # limitations under the License. | ||
13 | |||
14 | from django.utils.translation import ugettext_lazy as _ | ||
15 | |||
16 | from horizon import messages | ||
17 | from horizon import tabs | ||
18 | from openstack_dashboard import api | ||
19 | from openstack_dashboard import policy | ||
20 | |||
21 | from openstack_dashboard.dashboards.project.stacks.template_versions \ | ||
22 | import tables as project_tables | ||
23 | |||
24 | |||
25 | class TemplateFunctionsTab(tabs.Tab): | ||
26 | name = _("Template Functions") | ||
27 | slug = "template_functions" | ||
28 | template_name = "project/stacks.template_versions/_details.html" | ||
29 | preload = False | ||
30 | |||
31 | def allowed(self, request): | ||
32 | return policy.check( | ||
33 | (("orchestration", "stacks:list_template_functions"),), | ||
34 | request) | ||
35 | |||
36 | def get_context_data(self, request): | ||
37 | template_version = self.tab_group.kwargs['template_version'] | ||
38 | try: | ||
39 | template_functions = api.heat.template_function_list( | ||
40 | self.request, template_version) | ||
41 | except Exception: | ||
42 | template_functions = [] | ||
43 | messages.error(request, _('Unable to get functions for template ' | ||
44 | 'version "%s".') % template_version) | ||
45 | return {"table": project_tables.TemplateFunctionsTable( | ||
46 | request, data=template_functions), } | ||
47 | |||
48 | |||
49 | class TemplateVersionDetailsTabs(tabs.TabGroup): | ||
50 | slug = "template_version_details" | ||
51 | 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 9976f88..0000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/templates/stacks.template_versions/_details.html +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | {% load i18n %} | ||
2 | |||
3 | {{ 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 0a39d4a..0000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/templates/stacks.template_versions/index.html +++ /dev/null | |||
@@ -1,7 +0,0 @@ | |||
1 | {% extends 'base.html' %} | ||
2 | {% load i18n %} | ||
3 | {% block title %}{% trans "Template Versions" %}{% endblock %} | ||
4 | |||
5 | {% block main %} | ||
6 | {{ table.render }} | ||
7 | {% 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 fcace15..0000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/tests.py +++ /dev/null | |||
@@ -1,79 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
2 | # you may not use this file except in compliance with the License. | ||
3 | # You may obtain a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
10 | # implied. | ||
11 | # See the License for the specific language governing permissions and | ||
12 | # limitations under the License. | ||
13 | |||
14 | from django.core.urlresolvers import reverse | ||
15 | from django import http | ||
16 | |||
17 | from mox3.mox import IsA | ||
18 | |||
19 | from openstack_dashboard import api | ||
20 | from openstack_dashboard.test import helpers as test | ||
21 | |||
22 | |||
23 | class TemplateVersionsTests(test.TestCase): | ||
24 | INDEX_URL = reverse('horizon:project:stacks.template_versions:index') | ||
25 | |||
26 | @test.create_stubs({api.heat: ('template_version_list',)}) | ||
27 | def test_index(self): | ||
28 | api.heat.template_version_list( | ||
29 | IsA(http.HttpRequest)).AndReturn(self.template_versions.list()) | ||
30 | self.mox.ReplayAll() | ||
31 | |||
32 | res = self.client.get(self.INDEX_URL) | ||
33 | self.assertTemplateUsed( | ||
34 | res, 'project/stacks.template_versions/index.html') | ||
35 | self.assertContains(res, 'HeatTemplateFormatVersion.2012-12-12') | ||
36 | |||
37 | @test.create_stubs({api.heat: ('template_version_list',)}) | ||
38 | def test_index_exception(self): | ||
39 | api.heat.template_version_list( | ||
40 | IsA(http.HttpRequest)).AndRaise(self.exceptions.heat) | ||
41 | self.mox.ReplayAll() | ||
42 | |||
43 | res = self.client.get(self.INDEX_URL) | ||
44 | self.assertTemplateUsed( | ||
45 | res, 'project/stacks.template_versions/index.html') | ||
46 | self.assertEqual(len(res.context['table'].data), 0) | ||
47 | self.assertMessageCount(res, error=1) | ||
48 | |||
49 | @test.create_stubs({api.heat: ('template_function_list',)}) | ||
50 | def test_detail_view(self): | ||
51 | t_version = self.template_versions.first().version | ||
52 | t_functions = self.template_functions.list() | ||
53 | |||
54 | api.heat.template_function_list( | ||
55 | IsA(http.HttpRequest), t_version).AndReturn(t_functions) | ||
56 | self.mox.ReplayAll() | ||
57 | |||
58 | url = reverse('horizon:project:stacks.template_versions:details', | ||
59 | args=[t_version]) | ||
60 | res = self.client.get(url) | ||
61 | |||
62 | self.assertTemplateUsed(res, 'horizon/common/_detail.html') | ||
63 | self.assertNoMessages() | ||
64 | |||
65 | @test.create_stubs({api.heat: ('template_function_list',)}) | ||
66 | def test_detail_view_with_exception(self): | ||
67 | t_version = self.template_versions.first().version | ||
68 | |||
69 | api.heat.template_function_list( | ||
70 | IsA(http.HttpRequest), t_version).AndRaise(self.exceptions.heat) | ||
71 | self.mox.ReplayAll() | ||
72 | |||
73 | url = reverse('horizon:project:stacks.template_versions:details', | ||
74 | args=[t_version]) | ||
75 | res = self.client.get(url) | ||
76 | |||
77 | self.assertTemplateUsed(res, 'horizon/common/_detail.html') | ||
78 | self.assertEqual(len(res.context['table'].data), 0) | ||
79 | 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 5e2bbc2..0000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/urls.py +++ /dev/null | |||
@@ -1,24 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
2 | # you may not use this file except in compliance with the License. | ||
3 | # You may obtain a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
10 | # implied. | ||
11 | # See the License for the specific language governing permissions and | ||
12 | # limitations under the License. | ||
13 | |||
14 | from django.conf.urls import url | ||
15 | |||
16 | from openstack_dashboard.dashboards.project.stacks.template_versions \ | ||
17 | import views | ||
18 | |||
19 | |||
20 | urlpatterns = [ | ||
21 | url(r'^$', views.TemplateVersionsView.as_view(), name='index'), | ||
22 | url(r'^(?P<template_version>[^/]+)/$', | ||
23 | views.DetailView.as_view(), name='details'), | ||
24 | ] | ||
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 22eabd7..0000000 --- a/openstack_dashboard/dashboards/project/stacks/template_versions/views.py +++ /dev/null | |||
@@ -1,61 +0,0 @@ | |||
1 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
2 | # you may not use this file except in compliance with the License. | ||
3 | # You may obtain a copy of the License at | ||
4 | # | ||
5 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
6 | # | ||
7 | # Unless required by applicable law or agreed to in writing, software | ||
8 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
10 | # implied. | ||
11 | # See the License for the specific language governing permissions and | ||
12 | # limitations under the License. | ||
13 | |||
14 | from django.core.urlresolvers import reverse | ||
15 | from django.utils.translation import ugettext_lazy as _ | ||
16 | |||
17 | from horizon import exceptions | ||
18 | from horizon import tables | ||
19 | from horizon import tabs | ||
20 | |||
21 | from openstack_dashboard import api | ||
22 | import openstack_dashboard.dashboards.project.stacks.template_versions.tables \ | ||
23 | as project_tables | ||
24 | import openstack_dashboard.dashboards.project.stacks.template_versions.tabs \ | ||
25 | as project_tabs | ||
26 | |||
27 | |||
28 | class TemplateVersionsView(tables.DataTableView): | ||
29 | table_class = project_tables.TemplateVersionsTable | ||
30 | template_name = 'project/stacks.template_versions/index.html' | ||
31 | page_title = _("Template Versions") | ||
32 | |||
33 | def get_data(self): | ||
34 | try: | ||
35 | template_versions = sorted( | ||
36 | api.heat.template_version_list(self.request), | ||
37 | key=lambda template_version: template_version.version) | ||
38 | except Exception: | ||
39 | template_versions = [] | ||
40 | msg = _('Unable to retrieve template versions.') | ||
41 | exceptions.handle(self.request, msg) | ||
42 | return template_versions | ||
43 | |||
44 | |||
45 | class DetailView(tabs.TabView): | ||
46 | tab_group_class = project_tabs.TemplateVersionDetailsTabs | ||
47 | template_name = 'horizon/common/_detail.html' | ||
48 | page_title = "{{ template_version }}" | ||
49 | |||
50 | def get_template_version(self, request, **kwargs): | ||
51 | try: | ||
52 | template_functions = api.heat.template_function_list( | ||
53 | request, kwargs['template_version']) | ||
54 | return template_functions | ||
55 | except Exception: | ||
56 | msg = _('Unable to retrieve template functions.') | ||
57 | exceptions.handle(request, msg, redirect=self.get_redirect_url()) | ||
58 | |||
59 | @staticmethod | ||
60 | def get_redirect_url(): | ||
61 | 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 bd31f70..0000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_change_template.html +++ /dev/null | |||
@@ -1,7 +0,0 @@ | |||
1 | {% extends "horizon/common/_modal_form.html" %} | ||
2 | {% load i18n %} | ||
3 | {% block form_attrs %}enctype="multipart/form-data"{% endblock %} | ||
4 | {% block modal-body-right %} | ||
5 | <h3>{% trans "Description:" %}</h3> | ||
6 | <p>{% trans "Use one of the available template source options to specify the template to be used in creating this stack." %}</p> | ||
7 | {% 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 19a3f1b..0000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_create.html +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | {% extends "horizon/common/_modal_form.html" %} | ||
2 | {% load i18n %} | ||
3 | {% block modal-body-right %} | ||
4 | <h3>{% trans "Description:" %}</h3> | ||
5 | <p>{% trans "Create a new stack with the provided values." %}</p> | ||
6 | {% 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 9976f88..0000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_events.html +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | {% load i18n %} | ||
2 | |||
3 | {{ 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 56534bb..0000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_overview.html +++ /dev/null | |||
@@ -1,55 +0,0 @@ | |||
1 | {% load i18n sizeformat %} | ||
2 | |||
3 | <div class="detail"> | ||
4 | <dl class="dl-horizontal"> | ||
5 | <dt>{% trans "Name" %}</dt> | ||
6 | <dd data-display="{{ stack.stack_name|default:stack.id }}">{{ stack.stack_name }}</dd> | ||
7 | <dt>{% trans "ID" %}</dt> | ||
8 | <dd>{{ stack.id }}</dd> | ||
9 | <dt>{% trans "Description" %}</dt> | ||
10 | <dd>{{ stack.description }}</dd> | ||
11 | </dl> | ||
12 | |||
13 | <h4>{% trans "Status" %}</h4> | ||
14 | <hr class="header_rule"> | ||
15 | <dl class="dl-horizontal"> | ||
16 | <dt>{% trans "Created" %}</dt> | ||
17 | <dd>{{ stack.creation_time|parse_isotime|timesince_or_never }}</dd> | ||
18 | <dt>{% trans "Last Updated" %}</dt> | ||
19 | <dd>{{ stack.updated_time|parse_isotime|timesince_or_never }}</dd> | ||
20 | <dt>{% trans "Status" %}</dt> | ||
21 | <dd> | ||
22 | {% blocktrans with stack_status_title=stack.stack_status|title stack_status_reason=stack.stack_status_reason %}{{ stack_status_title }}: {{ stack_status_reason }}{% endblocktrans %} | ||
23 | </dd> | ||
24 | </dl> | ||
25 | |||
26 | <h4>{% trans "Outputs" %}</h4> | ||
27 | <hr class="header_rule"> | ||
28 | <dl class="dl-horizontal"> | ||
29 | {% for output in stack.outputs %} | ||
30 | <dt>{{ output.output_key }}</dt> | ||
31 | <dd>{{ output.description }}</dd> | ||
32 | <dd> | ||
33 | {{ output.output_value|stack_output }} | ||
34 | </dd> | ||
35 | {% endfor %} | ||
36 | </dl> | ||
37 | |||
38 | <h4>{% trans "Stack Parameters" %}</h4> | ||
39 | <hr class="header_rule"> | ||
40 | <dl class="dl-horizontal"> | ||
41 | {% for key, value in stack.parameters.items %} | ||
42 | <dt>{{ key }}</dt> | ||
43 | <dd>{{ value }}</dd> | ||
44 | {% endfor %} | ||
45 | </dl> | ||
46 | |||
47 | <h4>{% trans "Launch Parameters" %}</h4> | ||
48 | <hr class="header_rule"> | ||
49 | <dl class="dl-horizontal"> | ||
50 | <dt>{% trans "Timeout" %}</dt> | ||
51 | <dd>{{ stack.timeout_mins }} {% trans "Minutes" %}</dd> | ||
52 | <dt>{% trans "Rollback" %}</dt> | ||
53 | <dd>{% if stack.disable_rollback %}{% trans "Disabled" %}{% else %}{% trans "Enabled" %}{% endif %}</dd> | ||
54 | </dl> | ||
55 | </div> | ||
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 9976f88..0000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_resources.html +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | {% load i18n %} | ||
2 | |||
3 | {{ 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 d906cea..0000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_detail_topology.html +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | {% load i18n sizeformat %} | ||
2 | |||
3 | <div id="resource_container"> | ||
4 | <div id="info_box"></div> | ||
5 | <div id="stack_box"></div> | ||
6 | <div id="heat_resource_topology"></div> | ||
7 | <div id="stack_id" data-stack_id="{{ stack_id }}"></div> | ||
8 | <div id="d3_data" data-d3_data="{{ d3_data }}"></div> | ||
9 | </div> \ 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 2402478..0000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview.html +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | {% extends "horizon/common/_modal_form.html" %} | ||
2 | {% load i18n %} | ||
3 | {% block modal-body-right %} | ||
4 | <h3>{% trans "Description:" %}</h3> | ||
5 | <p>{% trans "Preview a new stack with the provided values." %}</p> | ||
6 | {% 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 12d131a..0000000 --- a/openstack_dashboard/dashboards/project/stacks/templates/stacks/_preview_details.html +++ /dev/null | |||
@@ -1,58 +0,0 @@ | |||
1 | {% extends "horizon/common/_modal.html" %} | ||
2 | {% load i18n %} | ||
3 | |||
4 | {% block modal-header %}{% trans "Stack Preview" %}{% endblock %} | ||
5 | |||
6 | {% block modal-body %} | ||
7 | <div class="row-fluid stack-preview detail"> | ||
8 | <form> | ||
9 | <dl class="dl-horizontal"> | ||
10 | {% for key, value in stack_preview.items %} | ||
11 | {% if key != 'parameters' and key != 'resources' and key != 'links' %} | ||
12 | <dt>{{ key }}</dt> | ||
13 | <dd>{{ value }}</dd> | ||
14 | {% endif %} | ||
15 | {% endfor %} | ||
16 | </dl> | ||
17 | |||
18 | {% if stack_preview.parameters %} | ||
19 | <dt>{% trans "Parameters" %}</dt> | ||
20 | <hr class="header_rule"> | ||
21 | <dl class="dl-horizontal"> | ||
22 | {% for key, value in stack_preview.parameters.items %} | ||
23 | <dt>{{ key }}</dt> | ||
24 | <dd>{{ value }}</dd> | ||
25 | {% endfor %} | ||
26 | </dl> | ||
27 | {% endif %} | ||
28 | |||
29 | {% if stack_preview.links %} | ||
30 | <dt>{% trans "Links" %}</dt> | ||
31 | <hr class="header_rule"> | ||
32 | {% for link in stack_preview.links %} | ||
33 | <dl class="dl-horizontal"> | ||
34 | <dt>{{ link.rel }}</dt> | ||
35 | <dd>{{ link.href }}</dd> | ||
36 | </dl> | ||
37 | {% endfor %} | ||
38 | {% endif %} | ||
39 | |||
40 | {% if stack_preview.resources %} | ||
41 | <dt>{% trans "Resources" %}</dt> | ||
42 | {% for resource in stack_preview.resources %} | ||
43 | <hr class="header_rule"> | ||
44 | <dl class="dl-horizontal"> | ||
45 | {% for key, value in resource.items %} | ||
46 | <dt>{{ key }}</dt> | ||
47 | <dd>{{ value }}</dd> | ||
48 | {% endfor %} | ||
49 | </dl> | ||
50 | {% endfor %} | ||
51 | {% endif %} | ||
52 | </form> | ||
53 | </div> | ||
54 | {% endblock %} | ||
55 | |||
56 | {% block modal-footer %} | ||