summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimur Sufiev <tsufiev@mirantis.com>2015-07-06 19:08:31 +0300
committerTimur Sufiev <tsufiev@mirantis.com>2015-08-05 10:12:03 +0000
commit7fc50585becb17c680bcbf299ba393c8bdc6581f (patch)
tree32645436e14aa7d64df731e1e6cae8ae7891db11
parent5ee471c93059b84a57019f613993c1effcf38d07 (diff)
Start developing HOT Builder on Merlin
Create basic file structure and use common Django template for both views. Also rework forms/YAML/Graph divs layout to make it more flexible. Create draft version of 'Add Resource' panel for HotBuilder - with resources filtering and a list of all available resources obtained from server-side Heat API. Change-Id: Ia2e4f8a63d85d8d5dd7cdd731cc4878836176070
Notes
Notes (review): Verified+2: Jenkins Code-Review+2: Timur Sufiev <tsufiev@mirantis.com> Workflow+1: Timur Sufiev <tsufiev@mirantis.com> Submitted-by: Jenkins Submitted-at: Wed, 05 Aug 2015 10:38:25 +0000 Reviewed-on: https://review.openstack.org/201062 Project: stackforge/merlin Branch: refs/heads/master
-rw-r--r--extensions/enabled/_51_add_hotbuilder_panel.py17
-rw-r--r--extensions/hotbuilder/__init__.py0
-rw-r--r--extensions/hotbuilder/api.py30
-rw-r--r--extensions/hotbuilder/panel.py20
-rw-r--r--extensions/hotbuilder/static/hotbuilder/js/hotbuilder.controllers.js46
-rw-r--r--extensions/hotbuilder/static/hotbuilder/js/hotbuilder.init.js14
-rw-r--r--extensions/hotbuilder/static/hotbuilder/js/hotbuilder.models.js10
-rw-r--r--extensions/hotbuilder/templates/hotbuilder/index.html88
-rw-r--r--extensions/hotbuilder/urls.py26
-rw-r--r--extensions/hotbuilder/views.py45
-rw-r--r--extensions/mistral/templates/mistral/create.html35
-rw-r--r--merlin/static/merlin/js/merlin.directives.js18
-rw-r--r--merlin/static/merlin/scss/merlin.scss13
-rw-r--r--merlin/static/merlin/templates/draggable-entry.html13
-rw-r--r--merlin/templates/merlin/base.html26
-rw-r--r--package.json2
16 files changed, 372 insertions, 31 deletions
diff --git a/extensions/enabled/_51_add_hotbuilder_panel.py b/extensions/enabled/_51_add_hotbuilder_panel.py
new file mode 100644
index 0000000..20054e3
--- /dev/null
+++ b/extensions/enabled/_51_add_hotbuilder_panel.py
@@ -0,0 +1,17 @@
1# The name of the panel to be added to HORIZON_CONFIG. Required.
2PANEL = 'hotbuilder'
3# The name of the dashboard the PANEL associated with. Required.
4PANEL_DASHBOARD = 'project'
5# The name of the panel group the PANEL is associated with.
6PANEL_GROUP = 'orchestration'
7
8ADD_INSTALLED_APPS = ['merlin', 'hotbuilder']
9
10# Python panel class of the PANEL to be added.
11ADD_PANEL = 'hotbuilder.panel.HotBuilderPanel'
12
13ADD_ANGULAR_MODULES = ['merlin', 'hotbuilder']
14ADD_JS_FILES = ['merlin/js/custom-libs/ui-bootstrap-tpls-0.12.1.js',
15 'merlin/js/merlin.init.js',
16 'merlin/js/merlin.templates.js',
17 'hotbuilder/js/hotbuilder.init.js']
diff --git a/extensions/hotbuilder/__init__.py b/extensions/hotbuilder/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/extensions/hotbuilder/__init__.py
diff --git a/extensions/hotbuilder/api.py b/extensions/hotbuilder/api.py
new file mode 100644
index 0000000..c24ec6d
--- /dev/null
+++ b/extensions/hotbuilder/api.py
@@ -0,0 +1,30 @@
1# Copyright (c) 2015 Mirantis, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15import logging
16from openstack_dashboard.api import heat
17
18logger = logging.getLogger(__name__)
19
20
21def resource_type_list(request):
22 client = heat.heatclient(request)
23 kwargs = {}
24 return client.resource_types.list(**kwargs)
25
26
27def resource_type_show(request, resource_type):
28 client = heat.heatclient(request)
29 return client.resource_types.get(resource_type)
30
diff --git a/extensions/hotbuilder/panel.py b/extensions/hotbuilder/panel.py
new file mode 100644
index 0000000..32f5b5d
--- /dev/null
+++ b/extensions/hotbuilder/panel.py
@@ -0,0 +1,20 @@
1# Copyright (c) 2014 Mirantis, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15import horizon
16
17
18class HotBuilderPanel(horizon.Panel):
19 name = 'HOT Templates'
20 slug = 'hotbuilder'
diff --git a/extensions/hotbuilder/static/hotbuilder/js/hotbuilder.controllers.js b/extensions/hotbuilder/static/hotbuilder/js/hotbuilder.controllers.js
new file mode 100644
index 0000000..d8216ab
--- /dev/null
+++ b/extensions/hotbuilder/static/hotbuilder/js/hotbuilder.controllers.js
@@ -0,0 +1,46 @@
1(function() {
2 'use strict';
3
4 angular
5 .module('hotbuilder')
6 .controller('HotbuilderController', HotbuilderController);
7
8 HotbuilderController.$inject = ['$q', '$http'];
9
10 function HotbuilderController($q, $http) {
11 var deferred = $q.defer();
12 var vm = this;
13
14 deferred.promise.then(function(url) {
15 $http.get(url).success(function(data) {
16 data.forEach(function(resourceType) {
17 $http.get(url + '/' + resourceType).success(function(data) {
18 vm.allEntries.push(data);
19 });
20 });
21 });
22 });
23
24 vm.setUrl = setUrl;
25
26 vm.allEntries = [];
27
28 vm.entries = [
29
30 ];
31
32 vm.panel = {
33 title: 'Add Resource'
34 };
35
36 vm.tabs = [
37 {title: 'Parameters', content: 'There'},
38 {title: 'Outputs', content: 'Where'}
39 ];
40
41 function setUrl(url) {
42 deferred.resolve(url);
43 }
44 }
45
46})();
diff --git a/extensions/hotbuilder/static/hotbuilder/js/hotbuilder.init.js b/extensions/hotbuilder/static/hotbuilder/js/hotbuilder.init.js
new file mode 100644
index 0000000..6edc4e6
--- /dev/null
+++ b/extensions/hotbuilder/static/hotbuilder/js/hotbuilder.init.js
@@ -0,0 +1,14 @@
1(function() {
2 'use strict';
3
4 angular
5 .module('hotbuilder', ['merlin'])
6 .run(initModule);
7
8 initModule.$inject = ['merlin.templates'];
9
10 function initModule(templates) {
11 templates.prefetch('/static/hotbuilder/templates/fields/', []);
12 }
13
14})();
diff --git a/extensions/hotbuilder/static/hotbuilder/js/hotbuilder.models.js b/extensions/hotbuilder/static/hotbuilder/js/hotbuilder.models.js
new file mode 100644
index 0000000..249ea83
--- /dev/null
+++ b/extensions/hotbuilder/static/hotbuilder/js/hotbuilder.models.js
@@ -0,0 +1,10 @@
1/**
2 * Created by tsufiev on 2/24/15.
3 */
4(function() {
5 'use strict';
6
7 angular
8 .module('hotbuilder');
9
10})();
diff --git a/extensions/hotbuilder/templates/hotbuilder/index.html b/extensions/hotbuilder/templates/hotbuilder/index.html
new file mode 100644
index 0000000..e1c7c1b
--- /dev/null
+++ b/extensions/hotbuilder/templates/hotbuilder/index.html
@@ -0,0 +1,88 @@
1{% extends 'merlin/base.html' %}
2{% load i18n %}
3{% block title %}{% trans "Template Builder" %}{% endblock %}
4
5{% block merlin-css %}
6{% endblock %}
7
8{% block merlin-js %}
9 <script type="text/javascript" src="{{ STATIC_URL }}hotbuilder/js/hotbuilder.init.js"></script>
10 <script type="text/javascript" src="{{ STATIC_URL }}hotbuilder/js/hotbuilder.controllers.js"></script>
11 <script type="text/javascript" src="{{ STATIC_URL }}hotbuilder/js/hotbuilder.models.js"></script>
12{% endblock %}
13
14{% block page_header %}
15 {% include "horizon/common/_page_header.html" with title=_("Template Builder") %}
16{% endblock page_header %}
17
18{% block main %}
19 <h3>Compose Template</h3>
20 <div ng-controller="HotbuilderController as hotbuilder"
21 ng-init="hotbuilder.setUrl('{% url "horizon:project:hotbuilder:resource_types" %}')">
22 <div class="well">
23 <div class="row">
24 <div class="col-xs">
25 <tabset>
26 <tab heading="Resources">
27 <panel content="hotbuilder.panel">
28
29 <collapsible-group class="col-xs-12" content="{}"
30 title="'Recent resources'">
31 <div class="col-xs-6">
32 <draggable-entry
33 ng-repeat="entry in hotbuilder.entries"
34 entry="entry" entry-icon="server">
35 </draggable-entry>
36 </div>
37 </collapsible-group>
38 <collapsible-group class="col-xs-12" content="{}"
39 title="'More resources'">
40 <div class="col-xs-6">
41 <div class="has-feedback">
42 <input type="text" ng-model="filterValue" placeholder="Filter">
43 <span class="form-control-feedback fa fa-filter"></span>
44 </div>
45 <draggable-entry
46 ng-repeat="entry in hotbuilder.allEntries | filter:filterValue"
47 entry="entry" entry-icon="server">
48 </draggable-entry>
49 </div>
50 </collapsible-group>
51 </panel>
52 </tab>
53 <tab ng-repeat="tab in hotbuilder.tabs" heading="{$ tab.title $}" active="tab.active">
54 <panel>
55 {$ tab.content $}
56 </panel>
57 </tab>
58 </tabset>
59 </div>
60 <div class="col-xs end-xs">
61 <div class="btn-group btn-toggle">
62 <button ng-click="isGraphMode = true" class="btn btn-sm"
63 ng-class="isGraphMode ? 'active btn-primary' : 'btn-default'">Graph</button>
64 <button ng-click="isGraphMode = false" class="btn btn-sm"
65 ng-class="!isGraphMode ? 'active btn-primary' : 'btn-default'">YAML</button>
66 </div>
67 <div class="panel panel-default">
68 <div class="panel-body" ng-show="!isGraphMode">
69 <pre>{$ workbook.toYAML() $}</pre>
70 </div>
71 <div class="panel-body" ng-show="isGraphMode">
72 Here will be a fancy Graph View as soon as we implement it!
73 </div>
74 </div>
75 </div>
76 </div>
77 <!-- page footer -->
78 <div class="row">
79 <div class="col-xs end-xs">
80 <button ng-click="discardWorkbook()" class="btn btn-default cancel">Cancel</button>
81 <button ng-click="commitWorkbook()" class="btn btn-primary">
82 {$ workbookID ? 'Modify' : 'Create' $}
83 </button>
84 </div>
85 </div>
86 </div>
87 </div>
88{% endblock %}
diff --git a/extensions/hotbuilder/urls.py b/extensions/hotbuilder/urls.py
new file mode 100644
index 0000000..8e0fae2
--- /dev/null
+++ b/extensions/hotbuilder/urls.py
@@ -0,0 +1,26 @@
1# Copyright (c) 2014 Mirantis, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15from django.conf.urls import patterns
16from django.conf.urls import url
17
18from hotbuilder import views
19
20urlpatterns = patterns('',
21 url(r'^$', views.IndexView.as_view(), name='index'),
22 url(r'^heat_resources$', views.ResourceTypesView.as_view(),
23 name='resource_types'),
24 url(r'^heat_resources/(?P<resource_type>[^/]+)$',
25 views.ShowResourceView.as_view(), name='show_resource')
26)
diff --git a/extensions/hotbuilder/views.py b/extensions/hotbuilder/views.py
new file mode 100644
index 0000000..f35067f
--- /dev/null
+++ b/extensions/hotbuilder/views.py
@@ -0,0 +1,45 @@
1# Copyright 2014 Rackspace
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import json
16
17from django.views.generic import TemplateView, View
18from django.http import HttpResponse
19
20from hotbuilder import api
21
22
23class JSONView(View):
24 def get_data(self, request, *args, **kwargs):
25 pass
26
27 def get(self, request, *args, **kwargs):
28 return HttpResponse(
29 json.dumps(self.get_data(request, *args, **kwargs)),
30 content_type='application/json')
31
32
33class ResourceTypesView(JSONView):
34 def get_data(self, request, *args, **kwargs):
35 return [resource.resource_type for resource in
36 api.resource_type_list(request)]
37
38
39class ShowResourceView(JSONView):
40 def get_data(self, request, *args, **kwargs):
41 return api.resource_type_show(request, kwargs['resource_type'])
42
43
44class IndexView(TemplateView):
45 template_name = 'hotbuilder/index.html'
diff --git a/extensions/mistral/templates/mistral/create.html b/extensions/mistral/templates/mistral/create.html
index 931f969..69a671e 100644
--- a/extensions/mistral/templates/mistral/create.html
+++ b/extensions/mistral/templates/mistral/create.html
@@ -1,41 +1,18 @@
1{% extends "base.html" %} 1{% extends "merlin/base.html" %}
2{% load i18n %} 2{% load i18n %}
3{% load url from future %}
4{% load static %}
5{% load compress %}
6{% block title %}{% trans "Create Workbook" %}{% endblock %} 3{% block title %}{% trans "Create Workbook" %}{% endblock %}
7 4
8{% block page_header %} 5{% block page_header %}
9 {% include "horizon/common/_page_header.html" with title=_("Workbooks") %} 6 {% include "horizon/common/_page_header.html" with title=_("Workbooks") %}
10{% endblock page_header %} 7{% endblock page_header %}
11 8
12{% block js %} 9{% block merlin-js %}
13{% include "horizon/_scripts.html" %} 10 <script type="text/javascript" src="{{ STATIC_URL }}mistral/js/mistral.init.js"></script>
14<script type="text/javascript" src="{{ STATIC_URL }}merlin/js/custom-libs/barricade.js"></script> 11 <script type="text/javascript" src="{{ STATIC_URL }}mistral/js/mistral.workbook.controllers.js"></script>
15<script type="text/javascript" src="{{ STATIC_URL }}merlin/libs/js-yaml/dist/js-yaml.min.js"></script> 12 <script type="text/javascript" src="{{ STATIC_URL }}mistral/js/mistral.workbook.models.js"></script>
16<script type="text/javascript" src="{{ STATIC_URL }}merlin/libs/underscore/underscore-min.js"></script>
17<script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.init.js"></script>
18<script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.templates.js"></script>
19<script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.filters.js"></script>
20<script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.directives.js"></script>
21<script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.field.models.js"></script>
22<script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.panel.models.js"></script>
23<script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.utils.js"></script>
24
25<script type="text/javascript" src="{{ STATIC_URL }}mistral/js/mistral.init.js"></script>
26<script type="text/javascript" src="{{ STATIC_URL }}mistral/js/mistral.workbook.controllers.js"></script>
27<script type="text/javascript" src="{{ STATIC_URL }}mistral/js/mistral.workbook.models.js"></script>
28
29{% endblock %} 13{% endblock %}
30 14
31{% block css %} 15{% block merlin-css %}{% endblock %}
32 {% include "_stylesheets.html" %}
33 {% compress css %}
34 <link href='{{ STATIC_URL }}merlin/scss/merlin.scss' type='text/scss' media='screen' rel='stylesheet' />
35 {% endcompress %}
36<link href='{{ STATIC_URL }}merlin/libs/flexboxgrid/dist/flexboxgrid.css' type='text/css' media='screen' rel='stylesheet' />
37 {% block merlin-css %}{% endblock %}
38{% endblock %}
39 16
40{% block main %} 17{% block main %}
41<h3>Create Workbook</h3> 18<h3>Create Workbook</h3>
diff --git a/merlin/static/merlin/js/merlin.directives.js b/merlin/static/merlin/js/merlin.directives.js
index fa0e69d..57030b8 100644
--- a/merlin/static/merlin/js/merlin.directives.js
+++ b/merlin/static/merlin/js/merlin.directives.js
@@ -42,6 +42,7 @@
42 .directive('typedField', typedField) 42 .directive('typedField', typedField)
43 43
44 .directive('editableTitle', editableTitle) 44 .directive('editableTitle', editableTitle)
45.directive('draggableEntry', draggableEntry)
45 .directive('labeled', labeled); 46 .directive('labeled', labeled);
46 47
47 function labeled() { 48 function labeled() {
@@ -230,4 +231,21 @@
230 } 231 }
231 }; 232 };
232 } 233 }
234
235 function draggableEntry() {
236 return {
237 restrict: 'E',
238 scope: {
239 entry: '='
240 },
241 link: link,
242 templateUrl: '/static/merlin/templates/draggable-entry.html'
243 };
244
245 function link(scope, element, attrs) {
246 if (angular.isDefined(attrs.entryIcon)) {
247 scope.iconCls = 'fa-' + attrs.entryIcon;
248 }
249 }
250 }
233})(); 251})();
diff --git a/merlin/static/merlin/scss/merlin.scss b/merlin/static/merlin/scss/merlin.scss
index fa84b67..8db93e1 100644
--- a/merlin/static/merlin/scss/merlin.scss
+++ b/merlin/static/merlin/scss/merlin.scss
@@ -61,4 +61,15 @@ i.fa-times-circle {
61 .section .section .section-heading & { 61 .section .section .section-heading & {
62 margin-top: 7px; 62 margin-top: 7px;
63 } 63 }
64}; 64}
65
66.has-feedback {
67 input {
68 width: 100%;
69 }
70 .form-control-feedback {
71 top: -5px;
72 right: 7px;
73 }
74}
75
diff --git a/merlin/static/merlin/templates/draggable-entry.html b/merlin/static/merlin/templates/draggable-entry.html
new file mode 100644
index 0000000..5ad7238
--- /dev/null
+++ b/merlin/static/merlin/templates/draggable-entry.html
@@ -0,0 +1,13 @@
1<div class="col-xs-12">
2 <div class="row middle-xs">
3 <div class="col-xs-10">
4 <span ng-show="iconCls" class="fa" ng-class="iconCls"></span>
5 {$ entry.resource_type $}
6 </div>
7 <div class="add-btn add-entry col-xs">
8 <button class="btn btn-default btn-sm" ng-click="onAdd()">
9 <i class="fa fa-plus"></i></button>
10 </div>
11 </div>
12
13</div>
diff --git a/merlin/templates/merlin/base.html b/merlin/templates/merlin/base.html
new file mode 100644
index 0000000..785b0ad
--- /dev/null
+++ b/merlin/templates/merlin/base.html
@@ -0,0 +1,26 @@
1{% extends "base.html" %}
2{% load compress %}
3
4{% block js %}
5 {% include "horizon/_scripts.html" %}
6 <script type="text/javascript" src="{{ STATIC_URL }}merlin/js/custom-libs/barricade.js"></script>
7 <script type="text/javascript" src="{{ STATIC_URL }}merlin/libs/js-yaml/dist/js-yaml.min.js"></script>
8 <script type="text/javascript" src="{{ STATIC_URL }}merlin/libs/underscore/underscore-min.js"></script>
9 <script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.init.js"></script>
10 <script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.templates.js"></script>
11 <script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.filters.js"></script>
12 <script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.directives.js"></script>
13 <script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.field.models.js"></script>
14 <script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.panel.models.js"></script>
15 <script type="text/javascript" src="{{ STATIC_URL }}merlin/js/merlin.utils.js"></script>
16 {% block merlin-js %}{% endblock %}
17{% endblock %}
18
19{% block css %}
20 {% include "_stylesheets.html" %}
21 {% compress css %}
22 <link href='{{ STATIC_URL }}merlin/libs/flexboxgrid/dist/flexboxgrid.css' type='text/css' media='screen' rel='stylesheet' />
23 <link href='{{ STATIC_URL }}merlin/scss/merlin.scss' type='text/scss' media='screen' rel='stylesheet' />
24 {% endcompress %}
25 {% block merlin-css %}{% endblock %}
26{% endblock %}
diff --git a/package.json b/package.json
index ad05b7e..9f95eeb 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,6 @@
44 "postinstall": "bower install", 44 "postinstall": "bower install",
45 "test-unit": "grunt test:unit", 45 "test-unit": "grunt test:unit",
46 "test": "karma start ./karma-unit.conf.js", 46 "test": "karma start ./karma-unit.conf.js",
47 "lint": "eslint --no-color ./merlin/static ./extensions/mistral/static" 47 "lint": "eslint --no-color ./merlin/static ./extensions/mistral/static ./extensions/hotbuilder/static"
48 } 48 }
49} 49}