summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimur Sufiev <tsufiev@mirantis.com>2015-04-29 09:57:12 -0700
committerTimur Sufiev <tsufiev@mirantis.com>2015-04-30 21:29:27 +0300
commitd9f94958c26875d62507d62dde00d6bdb7f6f5b8 (patch)
tree1720c752dbe147d2a16e8b65df70b7823e3841c5
parent531dc56c64b1266713f08e842aebb48877f657bc (diff)
Add stub api calls for Mistral server integration
Store the workbooks being edited inside sqlite database of Horizon django app. Now it's possible to: * create a workbook; * see it in the list of workbooks; * edit it; * delete it. To use the models.py DATABASES variable in openstack_dashboard settings needs to be set at least to sqlite3. Change-Id: I9d4c013470e0fc13ef65484c8f6fae69cdad0a05 Implements: blueprint mistral-server-integration
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: Thu, 30 Apr 2015 18:39:57 +0000 Reviewed-on: https://review.openstack.org/178765 Project: stackforge/merlin Branch: refs/heads/master
-rw-r--r--extensions/mistral/api.py57
-rw-r--r--extensions/mistral/models.py7
-rw-r--r--extensions/mistral/static/mistral/js/mistral.workbook.controllers.js101
-rw-r--r--extensions/mistral/tables.py18
-rw-r--r--extensions/mistral/templates/mistral/create.html20
-rw-r--r--extensions/mistral/test/js/workbookSpec.js10
-rw-r--r--extensions/mistral/urls.py8
-rw-r--r--extensions/mistral/views.py45
8 files changed, 177 insertions, 89 deletions
diff --git a/extensions/mistral/api.py b/extensions/mistral/api.py
index 54b404c..4cc1a0e 100644
--- a/extensions/mistral/api.py
+++ b/extensions/mistral/api.py
@@ -14,56 +14,37 @@
14 14
15from horizon.test import utils as test_utils 15from horizon.test import utils as test_utils
16 16
17from mistral import models
17 18
18_workbooks = []
19 19
20 20def create_workbook(request, name, yaml):
21def find_max_id(): 21 wb = models.Workbook.objects.create(name=name, yaml=yaml)
22 max_id = 0 22 wb.save()
23 for workbook in _workbooks:
24 if max_id < int(workbook.id):
25 max_id = int(workbook.id)
26
27 return max_id
28
29
30def create_workbook(request, json):
31 name = json['name']
32 for workbook in _workbooks:
33 if name == workbook['name']:
34 raise LookupError('Workbook with that name already exists!')
35
36 obj = test_utils.ObjDictWrapper(id=find_max_id()+1, **json)
37 _workbooks.append(obj)
38 return True 23 return True
39 24
40 25
41def modify_workbook(request, json): 26def modify_workbook(request, id, name, yaml):
42 id = json['id'] 27 try:
43 for i, workbook in enumerate(_workbooks[:]): 28 wb = models.Workbook.objects.get(id=id)
44 if unicode(id) == unicode(workbook.id): 29 wb.name = name
45 _workbooks[i] = test_utils.ObjDictWrapper(**json) 30 wb.yaml = yaml
46 return True 31 wb.save()
32 except models.Workbook.DoesNotExist:
33 return False
47 34
48 return False 35 return True
49 36
50 37
51def remove_workbook(request, id): 38def remove_workbook(request, id):
52 for i, workbook in enumerate(_workbooks[:]): 39 models.Workbook.objects.get(id=id).delete()
53 if unicode(id) == unicode(workbook.id):
54 del _workbooks[i]
55 return True
56
57 return False
58 40
59 41
60def list_workbooks(request): 42def list_workbooks(request):
61 return _workbooks 43 return models.Workbook.objects.values('id', 'name')
62 44
63 45
64def get_workbook(request, id): 46def get_workbook(request, id):
65 for workbook in _workbooks: 47 try:
66 if unicode(id) == unicode(workbook.id): 48 return models.Workbook.objects.get(id=id)
67 return workbook.__dict__ 49 except models.Workbook.DoesNotExist:
68 50 return None
69 return None
diff --git a/extensions/mistral/models.py b/extensions/mistral/models.py
new file mode 100644
index 0000000..192d048
--- /dev/null
+++ b/extensions/mistral/models.py
@@ -0,0 +1,7 @@
1from django.db import models
2
3
4class Workbook(models.Model):
5 name = models.CharField(max_length=50, unique=True)
6 yaml = models.TextField()
7
diff --git a/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js b/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js
index 1a72232..6159623 100644
--- a/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js
+++ b/extensions/mistral/static/mistral/js/mistral.workbook.controllers.js
@@ -8,41 +8,70 @@
8 .value('baseActionID', 'action') 8 .value('baseActionID', 'action')
9 .value('baseWorkflowID', 'workflow') 9 .value('baseWorkflowID', 'workflow')
10 .controller('workbookCtrl', 10 .controller('workbookCtrl',
11 ['$scope', 'mistral.workbook.models', 'baseActionID', 'baseWorkflowID', 11 ['$scope', 'mistral.workbook.models', '$http',
12 function($scope, models, baseActionId, baseWorkflowId) { 12 'baseActionID', 'baseWorkflowID',
13 $scope.workbook = models.Workbook.create({name: 'My Workbook'}); 13 function($scope, models, $http, baseActionId, baseWorkflowId) {
14 14 $scope.init = function(id, yaml, commitUrl, discardUrl) {
15 function getNextIDSuffix(container, regexp) { 15 $scope.workbookID = id;
16 var max = Math.max.apply(Math, container.getIDs().map(function(id) { 16 $scope.commitUrl = commitUrl;
17 var match = regexp.exec(id); 17 $scope.discardUrl = discardUrl;
18 return match && +match[2]; 18 if ( id !== undefined ) {
19 })); 19 $scope.workbook = models.Workbook.create(jsyaml.safeLoad(yaml));
20 return max > 0 ? max + 1 : 1; 20 } else {
21 } 21 $scope.workbook = models.Workbook.create({name: 'My Workbook'});
22 22 }
23 function getWorkbookNextIDSuffix(base) { 23 };
24 var containerName = base + 's', 24
25 regexp = /(workflow|action)([0-9]+)/, 25 function getNextIDSuffix(container, regexp) {
26 container = $scope.workbook.get(containerName); 26 var max = Math.max.apply(Math, container.getIDs().map(function(id) {
27 if ( !container ) { 27 var match = regexp.exec(id);
28 throw 'Base should be either "action" or "workflow"!'; 28 return match && +match[2];
29 }));
30 return max > 0 ? max + 1 : 1;
31 }
32
33 function getWorkbookNextIDSuffix(base) {
34 var containerName = base + 's',
35 regexp = /(workflow|action)([0-9]+)/,
36 container = $scope.workbook.get(containerName);
37 if ( !container ) {
38 throw 'Base should be either "action" or "workflow"!';
39 }
40 return getNextIDSuffix(container, regexp);
29 } 41 }
30 return getNextIDSuffix(container, regexp); 42
31 } 43 $scope.addAction = function() {
32 44 var nextSuffix = getWorkbookNextIDSuffix(baseActionId),
33 $scope.addAction = function() { 45 newID = baseActionId + nextSuffix;
34 var nextSuffix = getWorkbookNextIDSuffix(baseActionId), 46 $scope.workbook.get('actions').push(
35 newID = baseActionId + nextSuffix; 47 {name: 'Action ' + nextSuffix}, {id: newID});
36 $scope.workbook.get('actions').push( 48 };
37 {name: 'Action ' + nextSuffix}, {id: newID}); 49
38 }; 50 $scope.addWorkflow = function() {
39 51 var nextSuffix = getWorkbookNextIDSuffix(baseWorkflowId),
40 $scope.addWorkflow = function() { 52 newID = baseWorkflowId + nextSuffix;
41 var nextSuffix = getWorkbookNextIDSuffix(baseWorkflowId), 53 $scope.workbook.get('workflows').push(
42 newID = baseWorkflowId + nextSuffix; 54 {name: 'Workflow ' + nextSuffix}, {id: newID});
43 $scope.workbook.get('workflows').push( 55 };
44 {name: 'Workflow ' + nextSuffix}, {id: newID}); 56
45 }; 57 $scope.commitWorkbook = function() {
46 58 var data = {
47 }]) 59 name: $scope.workbook.get('name').get(),
60 yaml: $scope.workbook.toYAML()
61 };
62
63 $http({
64 url: $scope.commitUrl,
65 method: 'POST',
66 data: data
67 }).success(function(data, status, headers, config) {
68 document.location = $scope.discardUrl;
69 });
70 };
71
72 $scope.discardWorkbook = function() {
73 document.location = $scope.discardUrl;
74 };
75
76 }])
48})(); \ No newline at end of file 77})(); \ No newline at end of file
diff --git a/extensions/mistral/tables.py b/extensions/mistral/tables.py
index c7fa693..540d376 100644
--- a/extensions/mistral/tables.py
+++ b/extensions/mistral/tables.py
@@ -12,6 +12,7 @@
12# License for the specific language governing permissions and limitations 12# License for the specific language governing permissions and limitations
13# under the License. 13# under the License.
14 14
15from django.core.urlresolvers import reverse_lazy, reverse
15from django.utils.translation import ugettext_lazy as _ 16from django.utils.translation import ugettext_lazy as _
16from django.template import defaultfilters 17from django.template import defaultfilters
17from horizon import tables 18from horizon import tables
@@ -22,10 +23,19 @@ from mistral import api
22class CreateWorkbook(tables.LinkAction): 23class CreateWorkbook(tables.LinkAction):
23 name = 'create' 24 name = 'create'
24 verbose_name = _('Create Workbook') 25 verbose_name = _('Create Workbook')
25 url = 'horizon:project:mistral:create' 26 url = reverse_lazy('horizon:project:mistral:edit', args=())
26 icon = 'plus' 27 icon = 'plus'
27 28
28 29
30class ModifyWorkbook(tables.LinkAction):
31 name = 'modify'
32 verbose_name = _('Modify Workbook')
33
34 def get_link_url(self, datum):
35 return reverse('horizon:project:mistral:edit',
36 args=(self.table.get_object_id(datum),))
37
38
29class RemoveWorkbook(tables.DeleteAction): 39class RemoveWorkbook(tables.DeleteAction):
30 name = 'remove' 40 name = 'remove'
31 verbose_name = _('Remove Workbook') 41 verbose_name = _('Remove Workbook')
@@ -37,9 +47,11 @@ class RemoveWorkbook(tables.DeleteAction):
37 47
38class WorkbooksTable(tables.DataTable): 48class WorkbooksTable(tables.DataTable):
39 name = tables.Column('name', verbose_name=_('Workbook Name')) 49 name = tables.Column('name', verbose_name=_('Workbook Name'))
40 running = tables.Column('running', verbose_name=_('Running'), 50
41 filters=(defaultfilters.yesno,)) 51 def get_object_id(self, datum):
52 return datum['id']
42 53
43 class Meta: 54 class Meta:
44 table_actions = (CreateWorkbook,) 55 table_actions = (CreateWorkbook,)
56 row_actions = (ModifyWorkbook, RemoveWorkbook)
45 name = 'workbooks' 57 name = 'workbooks'
diff --git a/extensions/mistral/templates/mistral/create.html b/extensions/mistral/templates/mistral/create.html
index b114455..430d208 100644
--- a/extensions/mistral/templates/mistral/create.html
+++ b/extensions/mistral/templates/mistral/create.html
@@ -38,7 +38,8 @@
38 38
39{% block main %} 39{% block main %}
40<h3>Create Workbook</h3> 40<h3>Create Workbook</h3>
41 <div id="create-workbook" class="fluid-container" ng-cloak ng-controller="workbookCtrl"> 41 <div id="create-workbook" class="fluid-container" ng-cloak ng-controller="workbookCtrl"
42 ng-init="init({{ id|default:'undefined' }}, '{{ yaml }}', '{{ commit_url }}', '{{ discard_url }}')">
42 <div class="well"> 43 <div class="well">
43 <div class="two-panels"> 44 <div class="two-panels">
44 <div class="left-panel"> 45 <div class="left-panel">
@@ -54,8 +55,10 @@
54 </div> 55 </div>
55 <div class="right-panel"> 56 <div class="right-panel">
56 <div class="btn-group btn-toggle pull-right"> 57 <div class="btn-group btn-toggle pull-right">
57 <button class="btn btn-sm btn-default">Graph</button> 58 <button ng-click="isGraphMode = true" class="btn btn-sm"
58 <button class="btn btn-sm btn-primary active">YAML</button> 59 ng-class="isGraphMode ? 'active btn-primary' : 'btn-default'">Graph</button>
60 <button ng-click="isGraphMode = false" class="btn btn-sm"
61 ng-class="!isGraphMode ? 'active btn-primary' : 'btn-default'">YAML</button>
59 </div> 62 </div>
60 </div> 63 </div>
61 </div> 64 </div>
@@ -78,9 +81,12 @@
78 <!-- YAML Panel --> 81 <!-- YAML Panel -->
79 <div class="right-panel"> 82 <div class="right-panel">
80 <div class="panel panel-default"> 83 <div class="panel panel-default">
81 <div class="panel-body"> 84 <div class="panel-body" ng-show="!isGraphMode">
82 <pre>{$ workbook.toYAML() $}</pre> 85 <pre>{$ workbook.toYAML() $}</pre>
83 </div> 86 </div>
87 <div class="panel-body" ng-show="isGraphMode">
88 Here will be a fancy Graph View as soon as we implement it!
89 </div>
84 </div> 90 </div>
85 </div> 91 </div>
86 </div> 92 </div>
@@ -88,8 +94,10 @@
88 <div class="two-panels"> 94 <div class="two-panels">
89 <div class="full-width"> 95 <div class="full-width">
90 <div class="pull-right"> 96 <div class="pull-right">
91 <button class="btn btn-default cancel">Cancel</button> 97 <button ng-click="discardWorkbook()" class="btn btn-default cancel">Cancel</button>
92 <button class="btn btn-primary">Create</button> 98 <button ng-click="commitWorkbook()" class="btn btn-primary">
99 {$ workbookID ? 'Modify' : 'Create' $}
100 </button>
93 </div> 101 </div>
94 </div> 102 </div>
95 </div> 103 </div>
diff --git a/extensions/mistral/test/js/workbookSpec.js b/extensions/mistral/test/js/workbookSpec.js
index 60e94b6..504ef25 100644
--- a/extensions/mistral/test/js/workbookSpec.js
+++ b/extensions/mistral/test/js/workbookSpec.js
@@ -305,5 +305,15 @@ describe('workbook model logic', function() {
305 305
306 }); 306 });
307 307
308 describe("'Create'/'Modify'/'Cancel' actions", function() {
309 it('edit causes a request to an api and a return to main page', function() {
310
311 });
312
313 it('cancel causes just a return to main page', function() {
314
315 });
316 });
317
308 }) 318 })
309}); 319});
diff --git a/extensions/mistral/urls.py b/extensions/mistral/urls.py
index 2436518..56415b0 100644
--- a/extensions/mistral/urls.py
+++ b/extensions/mistral/urls.py
@@ -19,6 +19,10 @@ from mistral import views
19 19
20urlpatterns = patterns('', 20urlpatterns = patterns('',
21 url(r'^$', views.IndexView.as_view(), name='index'), 21 url(r'^$', views.IndexView.as_view(), name='index'),
22 url(r'^create$', views.CreateWorkbookView.as_view(), name='create'), 22 url(r'^edit/(?:(?P<workbook_id>[^/]+))?$',
23 url(r'^actions/types$', views.ActionTypesView.as_view(), name='action_types') 23 views.EditWorkbookView.as_view(), name='edit'),
24 url(r'^commit/(?:/(?P<workbook_id>[^/]+))?$',
25 views.CommitWorkbookView.as_view(), name='commit'),
26 url(r'^actions/types$', views.ActionTypesView.as_view(),
27 name='action_types')
24) 28)
diff --git a/extensions/mistral/views.py b/extensions/mistral/views.py
index 2e8f673..d3121cb 100644
--- a/extensions/mistral/views.py
+++ b/extensions/mistral/views.py
@@ -14,9 +14,10 @@
14 14
15import json 15import json
16 16
17from django.core.urlresolvers import reverse_lazy 17from django.core.urlresolvers import reverse_lazy, reverse
18from django import http 18from django import http
19from django.views.generic import View 19from django.views import generic as generic_views
20from horizon import messages
20from horizon import tables 21from horizon import tables
21from horizon.views import APIView 22from horizon.views import APIView
22import yaml 23import yaml
@@ -26,11 +27,47 @@ from mistral import forms as mistral_forms
26from mistral import tables as mistral_tables 27from mistral import tables as mistral_tables
27 28
28 29
29class CreateWorkbookView(APIView): 30class EditWorkbookView(APIView):
30 template_name = 'project/mistral/create.html' 31 template_name = 'project/mistral/create.html'
31 32
33 def get_context_data(self, workbook_id=None, **kwargs):
34 commit_ns = 'horizon:project:mistral:commit'
35 if workbook_id is None:
36 commit_url = reverse(commit_ns, args=())
37 else:
38 commit_url = reverse(commit_ns, args=(workbook_id,))
39 context = {
40 'commit_url': commit_url,
41 'discard_url': reverse('horizon:project:mistral:index')
42 }
43 if workbook_id is not None:
44 context['id'] = workbook_id
45 context['yaml'] = api.get_workbook(self.request, workbook_id).yaml
46 return context
47
48
49class CommitWorkbookView(generic_views.View):
50 def post(self, request, workbook_id=None, **kwargs):
51 def read_data():
52 data = json.loads(request.read())
53 return data['name'], data['yaml']
54
55 if workbook_id is None:
56 name, yaml = read_data()
57 api.create_workbook(request, name, yaml)
58 message = "The workbook {0} has been successfully created".format(
59 name)
60 else:
61 name, yaml = read_data()
62 api.modify_workbook(request, workbook_id, name, yaml)
63 message = "The workbook {0} has been successfully modified".format(
64 name)
65 messages.success(request, message)
66 return http.HttpResponseRedirect(
67 reverse_lazy('horizon:project:mistral:index'))
68
32 69
33class ActionTypesView(View): 70class ActionTypesView(generic_views.View):
34 def get(self, request, *args, **kwargs): 71 def get(self, request, *args, **kwargs):
35 key = request.GET.get('key') 72 key = request.GET.get('key')
36 schema = { 73 schema = {