Browse Source

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
changes/62/201062/8
Timur Sufiev 4 years ago
parent
commit
7fc50585be

+ 17
- 0
extensions/enabled/_51_add_hotbuilder_panel.py View File

@@ -0,0 +1,17 @@
1
+# The name of the panel to be added to HORIZON_CONFIG. Required.
2
+PANEL = 'hotbuilder'
3
+# The name of the dashboard the PANEL associated with. Required.
4
+PANEL_DASHBOARD = 'project'
5
+# The name of the panel group the PANEL is associated with.
6
+PANEL_GROUP = 'orchestration'
7
+
8
+ADD_INSTALLED_APPS = ['merlin', 'hotbuilder']
9
+
10
+# Python panel class of the PANEL to be added.
11
+ADD_PANEL = 'hotbuilder.panel.HotBuilderPanel'
12
+
13
+ADD_ANGULAR_MODULES = ['merlin', 'hotbuilder']
14
+ADD_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']

+ 0
- 0
extensions/hotbuilder/__init__.py View File


+ 30
- 0
extensions/hotbuilder/api.py View File

@@ -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
+
15
+import logging
16
+from openstack_dashboard.api import heat
17
+
18
+logger = logging.getLogger(__name__)
19
+
20
+
21
+def resource_type_list(request):
22
+    client = heat.heatclient(request)
23
+    kwargs = {}
24
+    return client.resource_types.list(**kwargs)
25
+
26
+
27
+def resource_type_show(request, resource_type):
28
+    client = heat.heatclient(request)
29
+    return client.resource_types.get(resource_type)
30
+

+ 20
- 0
extensions/hotbuilder/panel.py View File

@@ -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
+
15
+import horizon
16
+
17
+
18
+class HotBuilderPanel(horizon.Panel):
19
+    name = 'HOT Templates'
20
+    slug = 'hotbuilder'

+ 46
- 0
extensions/hotbuilder/static/hotbuilder/js/hotbuilder.controllers.js View File

@@ -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
+})();

+ 14
- 0
extensions/hotbuilder/static/hotbuilder/js/hotbuilder.init.js View File

@@ -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
+})();

+ 10
- 0
extensions/hotbuilder/static/hotbuilder/js/hotbuilder.models.js View File

@@ -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
+})();

+ 88
- 0
extensions/hotbuilder/templates/hotbuilder/index.html View File

@@ -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 %}

+ 26
- 0
extensions/hotbuilder/urls.py View File

@@ -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
+
15
+from django.conf.urls import patterns
16
+from django.conf.urls import url
17
+
18
+from hotbuilder import views
19
+
20
+urlpatterns = 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
+)

+ 45
- 0
extensions/hotbuilder/views.py View File

@@ -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
+
15
+import json
16
+
17
+from django.views.generic import TemplateView, View
18
+from django.http import HttpResponse
19
+
20
+from hotbuilder import api
21
+
22
+
23
+class 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
+
33
+class 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
+
39
+class ShowResourceView(JSONView):
40
+    def get_data(self, request, *args, **kwargs):
41
+        return api.resource_type_show(request, kwargs['resource_type'])
42
+
43
+
44
+class IndexView(TemplateView):
45
+    template_name = 'hotbuilder/index.html'

+ 6
- 29
extensions/mistral/templates/mistral/create.html View File

@@ -1,41 +1,18 @@
1
-{% extends "base.html" %}
1
+{% extends "merlin/base.html" %}
2 2
 {% load i18n %}
3
-{% load url from future %}
4
-{% load static %}
5
-{% load compress %}
6 3
 {% block title %}{% trans "Create Workbook" %}{% endblock %}
7 4
 
8 5
 {% block page_header %}
9 6
   {% include "horizon/common/_page_header.html" with title=_("Workbooks") %}
10 7
 {% endblock page_header %}
11 8
 
12
-{% block js %}
13
-{% include "horizon/_scripts.html" %}
14
-<script type="text/javascript" src="{{ STATIC_URL }}merlin/js/custom-libs/barricade.js"></script>
15
-<script type="text/javascript" src="{{ STATIC_URL }}merlin/libs/js-yaml/dist/js-yaml.min.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
-
9
+{% block merlin-js %}
10
+  <script type="text/javascript" src="{{ STATIC_URL }}mistral/js/mistral.init.js"></script>
11
+  <script type="text/javascript" src="{{ STATIC_URL }}mistral/js/mistral.workbook.controllers.js"></script>
12
+  <script type="text/javascript" src="{{ STATIC_URL }}mistral/js/mistral.workbook.models.js"></script>
29 13
 {% endblock %}
30 14
 
31
-{% block css %}
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 %}
15
+{% block merlin-css %}{% endblock %}
39 16
 
40 17
 {% block main %}
41 18
 <h3>Create Workbook</h3>

+ 18
- 0
merlin/static/merlin/js/merlin.directives.js View File

@@ -42,6 +42,7 @@
42 42
     .directive('typedField', typedField)
43 43
 
44 44
     .directive('editableTitle', editableTitle)
45
+.directive('draggableEntry', draggableEntry)
45 46
     .directive('labeled', labeled);
46 47
 
47 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
 })();

+ 12
- 1
merlin/static/merlin/scss/merlin.scss View File

@@ -61,4 +61,15 @@ i.fa-times-circle {
61 61
   .section .section .section-heading & {
62 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
+

+ 13
- 0
merlin/static/merlin/templates/draggable-entry.html View File

@@ -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>

+ 26
- 0
merlin/templates/merlin/base.html View File

@@ -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 %}

+ 1
- 1
package.json View File

@@ -44,6 +44,6 @@
44 44
     "postinstall": "bower install",
45 45
     "test-unit": "grunt test:unit",
46 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
 }

Loading…
Cancel
Save