Orchestration Resource types panel
This patch set adds 'Resources' panel to 'Orchestration' to provide user with a list of all available resource types. Partially implements blueprint: heat-ui-improvement Change-Id: I0e26a1009500cc4fa78ef27ca06553cdc987fd48
This commit is contained in:
parent
6ff1234187
commit
36273e917b
|
@ -124,3 +124,11 @@ def resource_metadata_get(request, stack_id, resource_name):
|
|||
|
||||
def template_validate(request, **kwargs):
|
||||
return heatclient(request).stacks.validate(**kwargs)
|
||||
|
||||
|
||||
def resource_types_list(request):
|
||||
return heatclient(request).resource_types.list()
|
||||
|
||||
|
||||
def resource_type_get(request, resource_type):
|
||||
return heatclient(request).resource_types.get(resource_type)
|
||||
|
|
|
@ -47,7 +47,8 @@ class ObjectStorePanels(horizon.PanelGroup):
|
|||
class OrchestrationPanels(horizon.PanelGroup):
|
||||
name = _("Orchestration")
|
||||
slug = "orchestration"
|
||||
panels = ('stacks',)
|
||||
panels = ('stacks',
|
||||
'stacks.resource_types',)
|
||||
|
||||
|
||||
class DatabasePanels(horizon.PanelGroup):
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.dashboards.project import dashboard
|
||||
|
||||
|
||||
class ResourceTypes(horizon.Panel):
|
||||
name = _("Resource Types")
|
||||
slug = "stacks.resource_types"
|
||||
permissions = ('openstack.services.orchestration',)
|
||||
|
||||
dashboard.Project.register(ResourceTypes)
|
|
@ -0,0 +1,44 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
|
||||
|
||||
class ResourceTypesTable(tables.DataTable):
|
||||
class ResourceColumn(tables.Column):
|
||||
def get_raw_data(self, datum):
|
||||
attr_list = ['implementation', 'component', 'resource']
|
||||
info_list = datum.resource_type.split('::')
|
||||
info_list[0] = info_list[0].replace("AWS", "Amazon Web Services").\
|
||||
replace("OS", "OpenStack")
|
||||
info_dict = dict(zip(attr_list, info_list))
|
||||
return info_dict[self.transform]
|
||||
|
||||
name = tables.Column("resource_type",
|
||||
verbose_name=_("Type"),
|
||||
link="horizon:project:stacks.resource_types:details",)
|
||||
implementation = ResourceColumn("implementation",
|
||||
verbose_name=_("Implementation"),)
|
||||
component = ResourceColumn("component",
|
||||
verbose_name=_("Component"),)
|
||||
resource = ResourceColumn("resource",
|
||||
verbose_name=_("Resource"),)
|
||||
|
||||
def get_object_id(self, resource):
|
||||
return resource.resource_type
|
||||
|
||||
class Meta:
|
||||
name = "resource_types"
|
||||
verbose_name = _("Resource Types")
|
|
@ -0,0 +1,38 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tabs
|
||||
from openstack_dashboard import policy
|
||||
|
||||
|
||||
class ResourceTypeOverviewTab(tabs.Tab):
|
||||
name = _("Overview")
|
||||
slug = "resource_type_overview"
|
||||
template_name = "project/stacks.resource_types/_details.html"
|
||||
|
||||
def allowed(self, request):
|
||||
return policy.check(
|
||||
(("orchestration", "cloudformation:DescribeStacks"),),
|
||||
request)
|
||||
|
||||
def get_context_data(self, request):
|
||||
return {"r_type": self.tab_group.kwargs['rt'],
|
||||
"r_type_attributes": self.tab_group.kwargs['rt_attributes'],
|
||||
"r_type_properties": self.tab_group.kwargs['rt_properties']}
|
||||
|
||||
|
||||
class ResourceTypeDetailsTabs(tabs.TabGroup):
|
||||
slug = "resource_type_details"
|
||||
tabs = (ResourceTypeOverviewTab,)
|
|
@ -0,0 +1,21 @@
|
|||
{% load i18n %}
|
||||
|
||||
<div class="resource_type row detail">
|
||||
<h4>{% trans "Resource Type" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl>
|
||||
<dd>{{ r_type }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="attributes row detail">
|
||||
<h4>{% trans "Attributes" %}</h4>
|
||||
<pre>{{ r_type_attributes }}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="properties row detail">
|
||||
<h4>{% trans "Properties" %}</h4>
|
||||
<pre>{{ r_type_properties }}
|
||||
</pre>
|
||||
</div>
|
|
@ -0,0 +1,15 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Resource Type Details" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Resource Type Details") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Resource Types" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Resource Types") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{{ table.render }}
|
||||
{% endblock %}
|
|
@ -0,0 +1,52 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django import http
|
||||
|
||||
from mox import IsA # noqa
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
class ResourceTypesTests(test.TestCase):
|
||||
INDEX_URL = reverse('horizon:project:stacks.resource_types:index')
|
||||
|
||||
@test.create_stubs({api.heat: ('resource_types_list',)})
|
||||
def test_index(self):
|
||||
api.heat.resource_types_list(
|
||||
IsA(http.HttpRequest)).AndReturn(self.resource_types.list())
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(self.INDEX_URL)
|
||||
self.assertTemplateUsed(
|
||||
res, 'project/stacks.resource_types/index.html')
|
||||
self.assertContains(res, 'AWS::CloudFormation::Stack')
|
||||
|
||||
@test.create_stubs({api.heat: ('resource_type_get',)})
|
||||
def test_detail_view(self):
|
||||
rt = self.api_resource_types.first()
|
||||
|
||||
api.heat.resource_type_get(
|
||||
IsA(http.HttpRequest), rt['resource_type']).AndReturn(rt)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:project:stacks.resource_types:details',
|
||||
args=[rt['resource_type']])
|
||||
res = self.client.get(url)
|
||||
|
||||
self.assertTemplateUsed(
|
||||
res, 'project/stacks.resource_types/details.html')
|
||||
self.assertContains(res, "<h1>Resource Type Details</h1>", 1, 200)
|
||||
self.assertNoMessages()
|
|
@ -0,0 +1,24 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.conf.urls import patterns
|
||||
from django.conf.urls import url
|
||||
|
||||
from openstack_dashboard.dashboards.project.stacks.resource_types import views
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^$', views.ResourceTypesView.as_view(), name='index'),
|
||||
url(r'^(?P<resource_type>[^/]+)/$',
|
||||
views.DetailView.as_view(), name='details'),
|
||||
)
|
|
@ -0,0 +1,73 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import yaml
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tables
|
||||
from horizon import tabs
|
||||
|
||||
from openstack_dashboard import api
|
||||
import openstack_dashboard.dashboards.project.stacks.resource_types.tables \
|
||||
as project_tables
|
||||
import openstack_dashboard.dashboards.project.stacks.resource_types.tabs \
|
||||
as project_tabs
|
||||
|
||||
|
||||
class ResourceTypesView(tables.DataTableView):
|
||||
table_class = project_tables.ResourceTypesTable
|
||||
template_name = 'project/stacks.resource_types/index.html'
|
||||
|
||||
def get_data(self):
|
||||
try:
|
||||
r_types = sorted(api.heat.resource_types_list(self.request),
|
||||
key=lambda resource: resource.resource_type)
|
||||
except Exception:
|
||||
r_types = []
|
||||
msg = _('Unable to retrieve stack resource types.')
|
||||
exceptions.handle(self.request, msg)
|
||||
return r_types
|
||||
|
||||
|
||||
class DetailView(tabs.TabView):
|
||||
tab_group_class = project_tabs.ResourceTypeDetailsTabs
|
||||
template_name = 'project/stacks.resource_types/details.html'
|
||||
|
||||
def get_resource_type(self, request, **kwargs):
|
||||
try:
|
||||
resource_type_overview = api.heat.resource_type_get(
|
||||
request,
|
||||
kwargs['resource_type'])
|
||||
return resource_type_overview
|
||||
except Exception:
|
||||
msg = _('Unable to retrieve resource type details.')
|
||||
exceptions.handle(request, msg, redirect=self.get_redirect_url())
|
||||
|
||||
def get_tabs(self, request, **kwargs):
|
||||
resource_type_overview = self.get_resource_type(request, **kwargs)
|
||||
r_type = resource_type_overview['resource_type']
|
||||
r_type_attributes = resource_type_overview['attributes']
|
||||
r_type_properties = resource_type_overview['properties']
|
||||
return self.tab_group_class(
|
||||
request,
|
||||
rt=r_type,
|
||||
rt_attributes=yaml.safe_dump(r_type_attributes, indent=2),
|
||||
rt_properties=yaml.safe_dump(r_type_properties, indent=2),
|
||||
**kwargs)
|
||||
|
||||
@staticmethod
|
||||
def get_redirect_url():
|
||||
return reverse('horizon:project:stacks.resources:index')
|
|
@ -10,6 +10,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from heatclient.v1 import resource_types
|
||||
from heatclient.v1 import stacks
|
||||
|
||||
from openstack_dashboard.test.test_data import utils
|
||||
|
@ -326,6 +327,10 @@ def data(TEST):
|
|||
TEST.stacks = utils.TestDataContainer()
|
||||
TEST.stack_templates = utils.TestDataContainer()
|
||||
TEST.stack_environments = utils.TestDataContainer()
|
||||
TEST.resource_types = utils.TestDataContainer()
|
||||
|
||||
# Data return by heatclient.
|
||||
TEST.api_resource_types = utils.TestDataContainer()
|
||||
|
||||
for i in range(10):
|
||||
stack_data = {
|
||||
|
@ -360,3 +365,57 @@ def data(TEST):
|
|||
|
||||
TEST.stack_templates.add(Template(TEMPLATE, VALIDATE))
|
||||
TEST.stack_environments.add(Environment(ENVIRONMENT))
|
||||
|
||||
# Resource types list
|
||||
r_type_1 = {
|
||||
"resource_type": "AWS::CloudFormation::Stack",
|
||||
"attributes": {},
|
||||
"properties": {
|
||||
"Parameters": {
|
||||
"description":
|
||||
"The set of parameters passed to this nested stack.",
|
||||
"immutable": False,
|
||||
"required": False,
|
||||
"type": "map",
|
||||
"update_allowed": True},
|
||||
"TemplateURL": {
|
||||
"description": "The URL of a template that specifies"
|
||||
" the stack to be created as a resource.",
|
||||
"immutable": False,
|
||||
"required": True,
|
||||
"type": "string",
|
||||
"update_allowed": True},
|
||||
"TimeoutInMinutes": {
|
||||
"description": "The length of time, in minutes,"
|
||||
" to wait for the nested stack creation.",
|
||||
"immutable": False,
|
||||
"required": False,
|
||||
"type": "number",
|
||||
"update_allowed": True}
|
||||
}
|
||||
}
|
||||
|
||||
r_type_2 = {
|
||||
"resource_type": "OS::Heat::CloudConfig",
|
||||
"attributes": {
|
||||
"config": {
|
||||
"description": "The config value of the software config."}
|
||||
},
|
||||
"properties": {
|
||||
"cloud_config": {
|
||||
"description": "Map representing the cloud-config data"
|
||||
" structure which will be formatted as YAML.",
|
||||
"immutable": False,
|
||||
"required": False,
|
||||
"type": "map",
|
||||
"update_allowed": False}
|
||||
}
|
||||
}
|
||||
|
||||
r_types_list = [r_type_1, r_type_2]
|
||||
|
||||
for rt in r_types_list:
|
||||
r_type = resource_types.ResourceType(
|
||||
resource_types.ResourceTypeManager(None), rt['resource_type'])
|
||||
TEST.resource_types.add(r_type)
|
||||
TEST.api_resource_types.add(rt)
|
||||
|
|
Loading…
Reference in New Issue