summaryrefslogtreecommitdiffstats
path: root/dashboard
diff options
context:
space:
mode:
Diffstat (limited to 'dashboard')
-rw-r--r--dashboard/src/api/migrations/0003_auto_20190102_1956.py18
-rw-r--r--dashboard/src/static/css/detail_view.css14
-rw-r--r--dashboard/src/static/css/graph_common.css32
-rw-r--r--dashboard/src/templates/account/booking_list.html16
-rw-r--r--dashboard/src/templates/account/configuration_list.html7
-rw-r--r--dashboard/src/templates/account/details.html8
-rw-r--r--dashboard/src/templates/account/image_list.html4
-rw-r--r--dashboard/src/templates/account/resource_list.html6
-rw-r--r--dashboard/src/templates/base.html1
-rw-r--r--dashboard/src/templates/booking/steps/booking_meta.html1
-rw-r--r--dashboard/src/templates/dashboard/searchable_select_multiple.html6
-rw-r--r--dashboard/src/templates/resource/steps/pod_definition.html105
-rw-r--r--dashboard/src/templates/workflow/confirm.html4
-rw-r--r--dashboard/src/templates/workflow/exit_redirect.html6
-rw-r--r--dashboard/src/templates/workflow/no_workflow.html10
-rw-r--r--dashboard/src/templates/workflow/viewport-base.html119
-rw-r--r--dashboard/src/workflow/booking_workflow.py73
-rw-r--r--dashboard/src/workflow/forms.py24
-rw-r--r--dashboard/src/workflow/models.py51
-rw-r--r--dashboard/src/workflow/resource_bundle_workflow.py2
-rw-r--r--dashboard/src/workflow/sw_bundle_workflow.py24
-rw-r--r--dashboard/src/workflow/views.py14
-rw-r--r--dashboard/src/workflow/workflow_factory.py39
-rw-r--r--dashboard/src/workflow/workflow_manager.py149
24 files changed, 415 insertions, 318 deletions
diff --git a/dashboard/src/api/migrations/0003_auto_20190102_1956.py b/dashboard/src/api/migrations/0003_auto_20190102_1956.py
new file mode 100644
index 0000000..2ea5d70
--- /dev/null
+++ b/dashboard/src/api/migrations/0003_auto_20190102_1956.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.1 on 2019-01-02 19:56
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0002_remove_job_delta'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='accessconfig',
+ name='delta',
+ field=models.TextField(default='{}'),
+ ),
+ ]
diff --git a/dashboard/src/static/css/detail_view.css b/dashboard/src/static/css/detail_view.css
new file mode 100644
index 0000000..89d0867
--- /dev/null
+++ b/dashboard/src/static/css/detail_view.css
@@ -0,0 +1,14 @@
+.detail_card {
+ border: 2px;
+ border-color: black;
+ border-radius: 5px;
+ margin: 5px;
+ padding: 5px;
+ box-shadow: 0px 0px 7px 0px rgba(0,0,0,0.75);
+}
+
+.detail_btn_group {
+ margin: 3px;
+ padding: 3px;
+ padding-bottom: 5px;
+}
diff --git a/dashboard/src/static/css/graph_common.css b/dashboard/src/static/css/graph_common.css
index 7f90a66..cff1516 100644
--- a/dashboard/src/static/css/graph_common.css
+++ b/dashboard/src/static/css/graph_common.css
@@ -26,9 +26,6 @@ div.mxRubberband {
margin: 0px;
}
div.mxWindow {
- -webkit-box-shadow: 3px 3px 12px #C0C0C0;
- -moz-box-shadow: 3px 3px 12px #C0C0C0;
- box-shadow: 3px 3px 12px #C0C0C0;
background: url('../img/mxgraph/window.gif');
border:1px solid #c3c3c3;
position: absolute;
@@ -67,19 +64,32 @@ td.mxWindowPane td {
font-size: 8pt;
}
td.mxWindowPane input, td.mxWindowPane select, td.mxWindowPane textarea, td.mxWindowPane radio {
- border-color: #8C8C8C;
- border-style: solid;
- border-width: 1px;
font-family: Arial;
font-size: 8pt;
padding: 1px;
}
td.mxWindowPane button {
- background: url('/static/img/mxgraph/button.gif') repeat-x;
- font-family: Arial;
- font-size: 8pt;
- padding: 2px;
- float: left;
+ color: #fff;
+ background-color: #337ab7;
+ border-color: #2e6da4;
+ display: inline-block;
+ margin: 2%;
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 1.42857143;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -ms-touch-action: manipulation;
+ touch-action: manipulation;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ background-image: none;
+ border: 1px solid transparent;
+ border-radius: 4px;
}
img.mxToolbarItem {
margin-right: 6px;
diff --git a/dashboard/src/templates/account/booking_list.html b/dashboard/src/templates/account/booking_list.html
index ef4df3a..21f6ff7 100644
--- a/dashboard/src/templates/account/booking_list.html
+++ b/dashboard/src/templates/account/booking_list.html
@@ -17,7 +17,7 @@ function edit_booking(pk){
</script>
<h2>Bookings I Own</h2>
{% for booking in bookings %}
- <div style="border:2px;border-style:solid;border-color:grey;margin:5px">
+ <div class="detail_card">
<ul>
<li>id: {{booking.id}}</li>
<li>lab: {{booking.resource.template.lab.lab_user.username}}</li>
@@ -26,15 +26,15 @@ function edit_booking(pk){
<li>end: {{booking.end}}</li>
<li>purpose: {{booking.purpose}}</li>
</ul>
- <div style="display:inline;margin:3px;padding:3px">
- <button onclick="edit_booking({{booking.id}});">Edit</button>
- <button onclick="location.href='/booking/detail/{{booking.id}}/';">Details</button>
+ <div class="detail_btn_group">
+ <button style="display: none" class="btn" onclick="edit_booking({{booking.id}});">Edit</button>
+ <button class="btn" onclick="location.href='/booking/detail/{{booking.id}}/';">Details</button>
</div>
</div>
{% endfor %}
<h2>Bookings I Collaborate On</h2>
{% for booking in collab_bookings %}
- <div style="border:2px;border-style:solid;border-color:grey;margin:5px">
+ <div class="detail_card">
<ul>
<li>id: {{booking.id}}</li>
<li>lab: {{booking.lab}}</li>
@@ -43,9 +43,9 @@ function edit_booking(pk){
<li>end: {{booking.end}}</li>
<li>purpose: {{booking.purpose}}</li>
</ul>
- <div style="display:inline;margin:3px;padding:3px">
- <button disabled=true onclick="edit_booking({{booking.id}});">Edit</button>
- <button onclick="location.href='/booking/detail/{{booking.id}}/';">Details</button>
+ <div class="detail_btn_group">
+ <button style="display: none" class="btn" disabled=true onclick="edit_booking({{booking.id}});">Edit</button>
+ <button class="btn" onclick="location.href='/booking/detail/{{booking.id}}/';">Details</button>
</div>
</div>
{% endfor %}
diff --git a/dashboard/src/templates/account/configuration_list.html b/dashboard/src/templates/account/configuration_list.html
index ee61e97..b04535b 100644
--- a/dashboard/src/templates/account/configuration_list.html
+++ b/dashboard/src/templates/account/configuration_list.html
@@ -16,13 +16,16 @@ function edit_configuration(pk){
}
</script>
{% for config in configurations %}
- <div style="border:2px;border-style:solid;border-color:grey;margin:5px">
+ <div class="detail_card">
<ul>
<li>id: {{config.id}}</li>
<li>name: {{config.name}}</li>
<li>description: {{config.description}}</li>
+ <li>resource: {{config.bundle}}</li>
</ul>
- <button onclick="edit_configuration({{config.id}});">Edit</button>
+ <div class="detail_btn_group">
+ <button style="display: none" class="btn" onclick="edit_configuration({{config.id}});">Edit</button>
+ </div>
</div>
{% endfor %}
{% endblock %}
diff --git a/dashboard/src/templates/account/details.html b/dashboard/src/templates/account/details.html
index 5641064..740dce6 100644
--- a/dashboard/src/templates/account/details.html
+++ b/dashboard/src/templates/account/details.html
@@ -2,8 +2,8 @@
{% load staticfiles %}
{% block content %}
<h1>Account Details</h1>
-<button onclick="location.href = '{% url 'account:my-resources' %}'">My Resources</button>
-<button onclick="location.href = '{% url 'account:my-bookings' %}'">My Bookings</button>
-<button onclick="location.href = '{% url 'account:my-configurations' %}'">My Configurations</button>
-<button onclick="location.href = '{% url 'account:my-images' %}'">My Snapshots</button>
+<button class="btn" onclick="location.href = '{% url 'account:my-resources' %}'">My Resources</button>
+<button class="btn" onclick="location.href = '{% url 'account:my-bookings' %}'">My Bookings</button>
+<button class="btn" onclick="location.href = '{% url 'account:my-configurations' %}'">My Configurations</button>
+<button class="btn" onclick="location.href = '{% url 'account:my-images' %}'">My Snapshots</button>
{% endblock content %}
diff --git a/dashboard/src/templates/account/image_list.html b/dashboard/src/templates/account/image_list.html
index fb436df..72ea1f5 100644
--- a/dashboard/src/templates/account/image_list.html
+++ b/dashboard/src/templates/account/image_list.html
@@ -2,7 +2,7 @@
{% block content %}
<h2>Images I Own</h2>
{% for image in images %}
- <div style="border:2px;border-style:solid;border-color:grey;margin:5px">
+ <div class="detail_card">
<ul>
<li>id: {{image.id}}</li>
<li>lab: {{image.from_lab.name}}</li>
@@ -14,7 +14,7 @@
{% endfor %}
<h2>Public Images</h2>
{% for image in public_images %}
- <div style="border:2px;border-style:solid;border-color:grey;margin:5px">
+ <div class="detail_card">
<ul>
<li>id: {{image.id}}</li>
<li>lab: {{image.from_lab.name}}</li>
diff --git a/dashboard/src/templates/account/resource_list.html b/dashboard/src/templates/account/resource_list.html
index 482a000..2ef293b 100644
--- a/dashboard/src/templates/account/resource_list.html
+++ b/dashboard/src/templates/account/resource_list.html
@@ -16,13 +16,15 @@ function edit_resource(pk){
}
</script>
{% for resource in resources %}
- <div style="border:2px;border-style:solid;border-color:grey;margin:5px">
+ <div class="detail_card">
<ul>
<li>id: {{resource.id}}</li>
<li>name: {{resource.name}}</li>
<li>description: {{resource.description}}</li>
</ul>
- <button onclick="edit_resource({{resource.id}});">Edit</button>
+ <div class="detail_btn_group">
+ <button style="display: none" class="btn" onclick="edit_resource({{resource.id}});">Edit</button>
+ </div>
</div>
{% endfor %}
{% endblock %}
diff --git a/dashboard/src/templates/base.html b/dashboard/src/templates/base.html
index c63db8c..067c3e6 100644
--- a/dashboard/src/templates/base.html
+++ b/dashboard/src/templates/base.html
@@ -8,6 +8,7 @@
<link href="{% static "bower_components/startbootstrap-sb-admin-2-blackrockdigital/dist/css/sb-admin-2.min.css" %}"
rel="stylesheet">
<link href="{% static "css/theme.css" %}" rel="stylesheet">
+ <link href="{% static "css/detail_view.css" %}" rel="stylesheet">
<script type="text/javascript">
function cwf(type)
diff --git a/dashboard/src/templates/booking/steps/booking_meta.html b/dashboard/src/templates/booking/steps/booking_meta.html
index a42e158..e4881ae 100644
--- a/dashboard/src/templates/booking/steps/booking_meta.html
+++ b/dashboard/src/templates/booking/steps/booking_meta.html
@@ -58,7 +58,6 @@
{% block onleave %}
var ajaxForm = $("#booking_meta_form");
var formData = ajaxForm.serialize();
-console.log(formData);
req = new XMLHttpRequest();
req.open("POST", "/wf/workflow/", false);
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
diff --git a/dashboard/src/templates/dashboard/searchable_select_multiple.html b/dashboard/src/templates/dashboard/searchable_select_multiple.html
index e7128b0..ee460dd 100644
--- a/dashboard/src/templates/dashboard/searchable_select_multiple.html
+++ b/dashboard/src/templates/dashboard/searchable_select_multiple.html
@@ -1,6 +1,12 @@
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<div class="autocomplete" style="width:400px;">
+ <div id="warning_pane" style="background: #FFFFFF; color: #CC0000;">
+ {% if incompatible == "true" %}
+ <h3>Warning: Incompatible Configuration</h3>
+ <p>Please make a different selection, as the current config conflicts with the selected pod</p>
+ {% endif %}
+ </div>
<input id="user_field" name="ignore_this" class="form-control" autocomplete="off" type="text" placeholder="{{placeholder}}" value="{{initial.name}}" oninput="search(this.value)"
{% if disabled %} disabled {% endif %}
>
diff --git a/dashboard/src/templates/resource/steps/pod_definition.html b/dashboard/src/templates/resource/steps/pod_definition.html
index b2b4998..8599bb0 100644
--- a/dashboard/src/templates/resource/steps/pod_definition.html
+++ b/dashboard/src/templates/resource/steps/pod_definition.html
@@ -85,7 +85,11 @@ function main(graphContainer, overviewContainer, toolbarContainer) {
addToolbarButton(editor, toolbarContainer, 'zoomIn', '', "/static/img/mxgraph/zoom_in.png", true);
addToolbarButton(editor, toolbarContainer, 'zoomOut', '', "/static/img/mxgraph/zoom_out.png", true);
+
+ {% if debug %}
addToolbarButton(editor, toolbarContainer, 'printXML', '', '/static/img/mxgraph/fit_to_size.png', true);
+ {% endif %}
+
var outline = new mxOutline(graph, overviewContainer);
@@ -141,6 +145,14 @@ function main(graphContainer, overviewContainer, toolbarContainer) {
}
});
+ createDeleteDialog = function(id)
+ {
+ var content = document.createElement('div');
+ var innerHTML = "<button style='width: 46%;' onclick=deleteCell('" + id + "');>Remove</button>"
+ innerHTML += "<button style='width: 46%;' onclick='currentWindow.destroy();'>Cancel</button>"
+ content.innerHTML = innerHTML;
+ showWindow(currentGraph, 'Do you want to delete this network?', content, 200, 62);
+ }
graph.dblClick = function(evt, cell) {
if( cell != null ){
@@ -148,11 +160,7 @@ function main(graphContainer, overviewContainer, toolbarContainer) {
cell = cell.getParent();
}
if( cell.isEdge() || cell.getId().indexOf("network") > -1 ) {
- var content = document.createElement('div');
- var innerHTML = "<button onclick=deleteCell('" + cell.getId() + "');>Remove</button>"
- innerHTML += "<button onclick='currentWindow.destroy();'>Cancel</button>"
- content.innerHTML = innerHTML;
- showWindow(this, 'Delete?', content, 200, 200);
+ createDeleteDialog(cell.getId());
}
else {
showDetailWindow(cell);
@@ -226,8 +234,8 @@ function deleteCell(cellId) {
function newNetworkWindow() {
var innerHtml = 'Name: <input type="text" name="net_name" id="net_name_input" style="margin:5px;"><br>';
innerHtml += 'Vlan: <input type="number" step="1" name="vlan_id" id="vlan_id_input" style="margin:5px;"><br>';
- innerHtml += '<button type="button" onclick="parseNetworkWindow()">Okay</button>';
- innerHtml += '<button type="button" onclick="currentWindow.destroy();">Cancel</button><br>';
+ innerHtml += '<button style="width: 46%;" onclick="parseNetworkWindow()">Okay</button>';
+ innerHtml += '<button style="width: 46%;" onclick="currentWindow.destroy();">Cancel</button><br>';
innerHtml += '<div id="current_window_vlans"/>';
innerHtml += '<div id="current_window_errors"/>';
var content = document.createElement("div");
@@ -503,6 +511,7 @@ function makeMxNetwork(vlan_id, net_name) {
function addPublicNetwork() {
var net = makeMxNetwork(-1, "public");
+ network_names.add("public");
makeSidebarNetwork("public", "", net['color'], net['element_id']);
}
@@ -542,14 +551,32 @@ function updateHosts(removed) {
function makeSidebarNetwork(net_name, vlan_id, color, net_id){
var newNet = document.createElement("li");
+ var colorBlob = document.createElement("div");
+ colorBlob.className = "colorblob";
+ var textContainer = document.createElement("p");
+ textContainer.className = "network_innertext";
newNet.id = net_id;
+ var deletebutton = document.createElement("button");
+ deletebutton.className = "btn btn-danger";
+ deletebutton.style = "float: right; height: 20px; line-height: 8px; vertical-align: middle; width: 20px; padding-left: 5px;";
+ deleteButtonText = document.createTextNode("X");
+ deletebutton.appendChild(deleteButtonText);
+ deletebutton.addEventListener("click", function() {
+ createDeleteDialog(net_id);
+ }, false);
var text = net_name;
if(vlan_id){
text += " : " + vlan_id;
}
var newNetValue = document.createTextNode(text);
- newNet.appendChild(newNetValue);
- newNet.style['background'] = color;
+ textContainer.appendChild(newNetValue);
+ colorBlob.style['background'] = color;
+ newNet.appendChild(colorBlob);
+ newNet.appendChild(textContainer);
+ if( net_name != "public" )
+ {
+ newNet.appendChild(deletebutton);
+ }
document.getElementById("network_list").appendChild(newNet);
}
@@ -609,7 +636,7 @@ function submitForm() {
<!-- Calls the main function after the page has loaded. Container is dynamically created. -->
{% block content %}
<div id="graphParent"
- style="position:absolute;overflow:hidden;top:0px;bottom:0px;width:65%;left:0px;">
+ style="position:absolute;overflow:hidden;top:0px;bottom:0px;width:75%;left:0px;">
<div id="graphContainer"
style="position:relative;overflow:hidden;top:36px;bottom:0px;left:0px;right:0px;background-image:url('/static/img/mxgraph/grid.gif');cursor:default;">
</div>
@@ -617,7 +644,7 @@ function submitForm() {
<!-- Creates a container for the sidebar -->
<div id="toolbarContainer"
- style="position:absolute;white-space:nowrap;overflow:hidden;top:0px;left:0px;max-height:24px;height:36px;right:0px;padding:6px;background-image:url('/static/img/mxgraph/toolbar_bg.gif');">
+ style="position:absolute;white-space:nowrap;overflow:hidden;top:0px;left:0px;right:0px;padding:6px;">
</div>
<!-- Creates a container for the outline -->
@@ -626,8 +653,60 @@ function submitForm() {
</div>
</div>
- <div id="network_select" style="position:absolute;top:0px;bottom:0px;width:35%;right:0px;left:auto;background:grey">
- <button type="button" onclick="newNetworkWindow();">Add Network</button>
+ <style>
+ #network_select {
+ background: inherit;
+ padding: 0px;
+ padding-top: 0px;
+ }
+ #toolbarContainer {
+ background: #DDDDDD;
+ height: 36px;
+ }
+ #toolbar_extension {
+ height: 36px;
+ background: #DDDDDD;
+ }
+ #btn_add_network {
+ width: 100%;
+ }
+ #vlan_notice {
+ margin: 20px;
+ }
+ #network_list li {
+ border-radius: 2px;
+ margin: 5px;
+ padding: 5px;
+ vertical-align: middle;
+ background: #DDDDDD;
+ }
+ #network_list {
+ list-style-type: none;
+ padding: 0;
+ }
+ .colorblob {
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ display: inline-block;
+ vertical-align: middle;
+ }
+ .network_innertext {
+ display: inline-block;
+ padding-left: 10px;
+ vertical-align: middle;
+ padding-bottom: 0px;
+ margin-bottom: 2px;
+ }
+ .mxWindow {
+ background: #FFFFFF;
+ }
+ </style>
+
+ <div id="network_select" style="position:absolute;top:0px;bottom:0px;width:25%;right:0px;left:auto;">
+ <div id="toolbar_extension">
+ <button id="btn_add_network" type="button" class="btn btn-primary" onclick="newNetworkWindow();">Add Network</button>
+ </div>
<ul id="network_list">
</ul>
<p id="vlan_notice"></p>
diff --git a/dashboard/src/templates/workflow/confirm.html b/dashboard/src/templates/workflow/confirm.html
index 29b90c8..2510204 100644
--- a/dashboard/src/templates/workflow/confirm.html
+++ b/dashboard/src/templates/workflow/confirm.html
@@ -66,7 +66,9 @@
req.open("POST", "/wf/workflow/finish/", false);
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
req.onerror = function() { alert("problem with cleaning up session"); }
- req.onreadystatechange = function() { if(req.readyState === 4 ) { parent.redirect_root(); } }
+ req.onreadystatechange = function() { if(req.readyState === 4 ) {
+ window.top.refresh_iframe();
+ }}
req.send(formData);
}
diff --git a/dashboard/src/templates/workflow/exit_redirect.html b/dashboard/src/templates/workflow/exit_redirect.html
new file mode 100644
index 0000000..b08df78
--- /dev/null
+++ b/dashboard/src/templates/workflow/exit_redirect.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+ <script>
+ top.window.location.href='/';
+ </script>
+</html>
diff --git a/dashboard/src/templates/workflow/no_workflow.html b/dashboard/src/templates/workflow/no_workflow.html
index ff8aab3..0ac6549 100644
--- a/dashboard/src/templates/workflow/no_workflow.html
+++ b/dashboard/src/templates/workflow/no_workflow.html
@@ -1,7 +1,3 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-
-{% block content %}
-<h3>If you would like to create a booking or a resource, please use the links on the sidebar or from the homepage</h3>
-<a href="/">Go Home</a>
-{% endblock content %}
+<script>
+ top.window.location.href='/';
+</script>
diff --git a/dashboard/src/templates/workflow/viewport-base.html b/dashboard/src/templates/workflow/viewport-base.html
index 37eff27..82c1324 100644
--- a/dashboard/src/templates/workflow/viewport-base.html
+++ b/dashboard/src/templates/workflow/viewport-base.html
@@ -71,7 +71,7 @@
.step_untouched
{
- background: #98B0AF;
+ background: #DDDDDD;
}
.step_invalid
@@ -96,10 +96,12 @@
<button id="gob" onclick="go(step-1)" class="btn btn go_btn go_back">Go Back</button>
<div class="options">
- <button class="btn" onclick="cancel_wf()">Cancel</button>
+ <button id="cancel_btn" class="btn" onclick="cancel_wf()">Cancel</button>
</div>
<div class="btn_wrapper">
-<div id="breadcrumbs">
+<div id="breadcrumbs" class="btn-group">
+ <div class="btn-group" id="breadcrumb-wrapper">
+ </div>
</div>
</div>
{% csrf_token %}
@@ -206,6 +208,14 @@
{
context_data = data;
update_breadcrumbs(data);
+ if(data["workflow_count"] == 1)
+ {
+ document.getElementById("cancel_btn").innerText = "Exit Workflow";
+ }
+ else
+ {
+ document.getElementById("cancel_btn").innerText = "Return to Parent";
+ }
}
function update_breadcrumbs(meta_json) {
@@ -240,60 +250,16 @@
while(container.firstChild){
container.removeChild(container.firstChild);
}
- //draw enough rows for all steps
- var depth = meta_json['max_depth'];
- for(var i=0; i<=depth; i++){
- var div = document.createElement("DIV");
- div.id = "row"+i;
- if(i<depth){
- div.style['margin-bottom'] = "7px";
- }
- if(i>0){
- div.style['margin-top'] = "7px";
- }
- container.appendChild(div);
- }
+
draw_steps(meta_json);
}
function draw_steps(meta_json){
- var all_relations = meta_json['relations'];
- var relations = [];
- var active_steps = [];
- var active_step = step;
- while(active_step < meta_json['steps'].length){
- active_steps.push(active_step);
- var index = meta_json['parents'][active_step];
- var relation = all_relations[index];
- relations.push(relation);
- active_step = relation['parent'];
- }
- var child_index = meta_json['children'][step];
- var my_children = all_relations[child_index];
- if(my_children){
- relations.push(my_children);
- }
- draw_relations(relations, meta_json, active_steps);
- }
-
- function draw_relations(relations, meta_json, active_steps){
- for(var i=0; i<relations.length; i++){
- var relation = relations[i];
- var children_container = document.createElement("DIV");
- children_container.style['display'] = "inline";
- children_container.style['margin'] = "3px";
- children_container.style['padding'] = "3px";
- console.log("meta_json: ");
- console.log(meta_json);
- for(var j=0; j<relation['children'].length; j++){
- var step_json = meta_json['steps'][relation['children'][j]];
- step_json['index'] = relation['children'][j];
- var active = active_steps.indexOf(step_json['index']) > -1;
- var step_button = create_step(meta_json['steps'][relation['children'][j]], active);
- children_container.appendChild(step_button);
- }
- var parent_div = document.getElementById("row" + relation['depth']);
- parent_div.appendChild(children_container);
+ for( var i = 0; i < meta_json["steps"].length; i++ )
+ {
+ meta_json["steps"][i]["index"] = i;
+ var step_btn = create_step(meta_json["steps"][i], i == meta_json["active"]);
+ document.getElementById("breadcrumbs").appendChild(step_btn);
}
}
@@ -301,7 +267,6 @@
var step_dom = document.createElement("DIV");
if(active){
step_dom.className = "step_active";
- console.log(step_json['message']);
} else{
step_dom.className = "step";
@@ -333,30 +298,45 @@
{
update_message(msg, stat);
}
+ step_dom.classList.add("btn");
var step_number = step_json['index'];
step_dom.onclick = function(){ go(step_number); }
- //TODO: background color and other style
return step_dom;
}
function cancel_wf(){
- $.ajax({
- type: "POST",
- url: "/wf/manager/",
- data: {"cancel":"",},
- beforeSend: function(request) {
- request.setRequestHeader("X-CSRFToken",
- $('input[name="csrfmiddlewaretoken"]').val()
- );
- },
- success: redirect_root()
- });
+ var form = $("#workflow_pop_form");
+ var formData = form.serialize();
+ var req = new XMLHttpRequest();
+ req.open("POST", "/wf/workflow/finish/", false);
+ req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+ req.onerror = function() { alert("problem occurred while trying to cancel current workflow"); }
+ req.onreadystatechange = function() { if(req.readyState === 4){
+ refresh_iframe();
+ }};
+ req.send(formData);
+ }
+
+ function refresh_iframe() {
+ req = new XMLHttpRequest();
+ url = "/wf/workflow/";
+ req.open("GET", url, true);
+ req.onload = function(e) {
+ var doc = document.getElementById("viewport-iframe").contentWindow.document;
+ doc.open(); doc.write(this.responseText); doc.close();
+ }
+ req.send();
+ }
+
+ function write_iframe(contents)
+ {
+ document.getElementById("viewport-iframe").contentWindow.document.innerHTML= contents;
}
function redirect_root()
{
- window.location.replace('/');
+ window.location.replace('/wf/');
}
function add_wf(type){
@@ -448,6 +428,11 @@
</script>
<!-- /.col-lg-12 -->
</div>
+<div style="display: none;" id="workflow_pop_form_div">
+<form id="workflow_pop_form" action="/wf/workflow/finish/" method="post">
+ {% csrf_token %}
+</form>
+</div>
<iframe src="/wf/workflow" style="position: absolute; left: 351px; right: 105px; width: calc(100% - 450px); border-style: none; border-width: 1px; border-color: #888888;" scrolling="yes" id="viewport-iframe" onload="resize_iframe();"></iframe>
{% endblock content %}
diff --git a/dashboard/src/workflow/booking_workflow.py b/dashboard/src/workflow/booking_workflow.py
index cd12ab6..76950b8 100644
--- a/dashboard/src/workflow/booking_workflow.py
+++ b/dashboard/src/workflow/booking_workflow.py
@@ -33,32 +33,15 @@ class Resource_Select(WorkflowStep):
self.repo_check_key = False
self.confirm_key = "booking"
- def get_default_entry(self):
- return None
-
def get_context(self):
context = super(Resource_Select, self).get_context()
default = []
- chosen_bundle = None
- default_bundle = self.get_default_entry()
- if default_bundle:
- context['disabled'] = True
- chosen_bundle = default_bundle
- if chosen_bundle.id:
- default.append(chosen_bundle.id)
- else:
- default.append("repo bundle")
- else:
- chosen_bundle = self.repo_get(self.repo_key, False)
- if chosen_bundle:
- if chosen_bundle.id:
- default.append(chosen_bundle.id)
- else:
- default.append("repo bundle")
-
- bundle = default_bundle
- if not bundle:
- bundle = chosen_bundle
+
+ chosen_bundle = self.repo_get(self.repo_key, False)
+ if chosen_bundle:
+ default.append(chosen_bundle.id)
+
+ bundle = chosen_bundle
edit = self.repo_get(self.repo.EDIT, False)
user = self.repo_get(self.repo.SESSION_USER)
context['form'] = ResourceSelectorForm(
@@ -79,6 +62,9 @@ class Resource_Select(WorkflowStep):
self.metastep.set_invalid("Please select a valid bundle")
return render(request, self.template, context)
selected_bundle = json.loads(data)
+ if len(selected_bundle) < 1:
+ self.metastep.set_invalid("Please select a valid bundle")
+ return render(request, self.template, context)
selected_id = selected_bundle[0]['id']
gresource_bundle = None
try:
@@ -112,23 +98,9 @@ class Booking_Resource_Select(Resource_Select):
def __init__(self, *args, **kwargs):
super(Booking_Resource_Select, self).__init__(*args, **kwargs)
- self.repo_key = self.repo.BOOKING_SELECTED_GRB
+ self.repo_key = self.repo.SELECTED_GRESOURCE_BUNDLE
self.confirm_key = "booking"
- def get_default_entry(self):
- default = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}).get("bundle")
- mine = self.repo_get(self.repo_key)
- if mine:
- return None
- try:
- config_bundle = self.repo_get(self.repo.BOOKING_MODELS)['booking'].config_bundle
- if default:
- return default # select created grb, even if preselected config bundle
- return config_bundle.bundle
- except:
- pass
- return default
-
def get_context(self):
context = super(Booking_Resource_Select, self).get_context()
return context
@@ -166,12 +138,20 @@ class SWConfig_Select(WorkflowStep):
self.metastep.set_invalid("Please select a valid config")
return self.render(request)
bundle_json = json.loads(bundle_json)
+ if len(bundle_json) < 1:
+ self.metastep.set_invalid("Please select a valid config")
+ return self.render(request)
bundle = None
- try:
- id = int(bundle_json[0]['id'])
- bundle = ConfigBundle.objects.get(id=id)
- except ValueError:
- bundle = self.repo_get(self.repo.CONFIG_MODELS).get("bundle")
+ id = int(bundle_json[0]['id'])
+ bundle = ConfigBundle.objects.get(id=id)
+
+ grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE)
+
+ if grb and bundle.bundle != grb:
+ self.metastep.set_invalid("Incompatible config selected for resource bundle")
+ return self.render(request)
+ if not grb:
+ self.repo_set(self.repo.SELECTED_GRESOURCE_BUNDLE, bundle.bundle)
models = self.repo_get(self.repo.BOOKING_MODELS, {})
if "booking" not in models:
@@ -196,7 +176,7 @@ class SWConfig_Select(WorkflowStep):
default = []
bundle = None
chosen_bundle = None
- created_bundle = self.repo_get(self.repo.CONFIG_MODELS, {}).get("bundle", False)
+ created_bundle = self.repo_get(self.repo.SELECTED_CONFIG_BUNDLE)
booking = self.repo_get(self.repo.BOOKING_MODELS, {}).get("booking", False)
try:
chosen_bundle = booking.config_bundle
@@ -204,11 +184,10 @@ class SWConfig_Select(WorkflowStep):
bundle = chosen_bundle
except:
if created_bundle:
- default.append("repo bundle")
+ default.append(created_bundle.id)
bundle = created_bundle
- context['disabled'] = True
edit = self.repo_get(self.repo.EDIT, False)
- grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB)
+ grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE)
context['form'] = SWConfigSelectorForm(chosen_software=default, bundle=bundle, edit=edit, resource=grb)
return context
diff --git a/dashboard/src/workflow/forms.py b/dashboard/src/workflow/forms.py
index feb32f2..f781663 100644
--- a/dashboard/src/workflow/forms.py
+++ b/dashboard/src/workflow/forms.py
@@ -41,6 +41,7 @@ class SearchableSelectMultipleWidget(widgets.SelectMultiple):
self.default_entry = attrs.get("default_entry", "")
self.edit = attrs.get("edit", False)
self.wf_type = attrs.get("wf_type")
+ self.incompatible = attrs.get("incompatible", "false")
super(SearchableSelectMultipleWidget, self).__init__(attrs)
@@ -61,7 +62,8 @@ class SearchableSelectMultipleWidget(widgets.SelectMultiple):
'initial': self.initial,
'default_entry': self.default_entry,
'edit': self.edit,
- 'wf_type': self.wf_type
+ 'wf_type': self.wf_type,
+ 'incompatible': self.incompatible
}
@@ -101,13 +103,6 @@ class ResourceSelectorForm(forms.Form):
displayable['id'] = res.id
resources[res.id] = displayable
- if bundle:
- displayable = {}
- displayable['small_name'] = bundle.name
- displayable['expanded_name'] = "Current bundle"
- displayable['string'] = bundle.description
- displayable['id'] = "repo bundle"
- resources["repo bundle"] = displayable
attrs = {
'set': resources,
'show_from_noentry': "true",
@@ -159,13 +154,15 @@ class SWConfigSelectorForm(forms.Form):
displayable['id'] = config.id
configs[config.id] = displayable
- if bundle:
+ incompatible_choice = "false"
+ if bundle and bundle.id not in configs:
displayable = {}
displayable['small_name'] = bundle.name
- displayable['expanded_name'] = "Current configuration"
+ displayable['expanded_name'] = bundle.owner.username
displayable['string'] = bundle.description
- displayable['id'] = "repo bundle"
- configs['repo bundle'] = displayable
+ displayable['id'] = bundle.id
+ configs[bundle.id] = displayable
+ incompatible_choice = "true"
attrs = {
'set': configs,
@@ -177,7 +174,8 @@ class SWConfigSelectorForm(forms.Form):
'placeholder': "config",
'initial': chosen,
'edit': edit,
- 'wf_type': 2
+ 'wf_type': 2,
+ 'incompatible': incompatible_choice
}
return attrs
diff --git a/dashboard/src/workflow/models.py b/dashboard/src/workflow/models.py
index 73a142e..495ce07 100644
--- a/dashboard/src/workflow/models.py
+++ b/dashboard/src/workflow/models.py
@@ -10,6 +10,7 @@
from django.shortcuts import render
from django.contrib import messages
+from django.http import HttpResponse
import yaml
import requests
@@ -141,7 +142,6 @@ class BookingAuthManager():
class WorkflowStep(object):
-
template = 'bad_request.html'
title = "Generic Step"
description = "You were led here by mistake"
@@ -189,9 +189,11 @@ class Confirmation_Step(WorkflowStep):
description = "Does this all look right?"
def get_vlan_warning(self):
- grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB, False)
+ grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False)
if not grb:
return 0
+ if self.repo.BOOKING_MODELS not in self.repo.el:
+ return 0
vlan_manager = grb.lab.vlan_manager
if vlan_manager is None:
return 0
@@ -233,9 +235,10 @@ class Confirmation_Step(WorkflowStep):
errors = self.flush_to_db()
if errors:
messages.add_message(request, messages.ERROR, "ERROR OCCURRED: " + errors)
- return render(request, self.template, context)
- messages.add_message(request, messages.SUCCESS, "Confirmed")
- return render(request, self.template, context)
+ else:
+ messages.add_message(request, messages.SUCCESS, "Confirmed")
+
+ return HttpResponse('')
elif data == "False":
context["bypassed"] = "true"
messages.add_message(request, messages.SUCCESS, "Canceled")
@@ -251,7 +254,7 @@ class Confirmation_Step(WorkflowStep):
pass
def translate_vlans(self):
- grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB, False)
+ grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False)
if not grb:
return 0
vlan_manager = grb.lab.vlan_manager
@@ -285,6 +288,7 @@ class Repository():
RESOURCE_SELECT = "resource_select"
CONFIRMATION = "confirmation"
SELECTED_GRESOURCE_BUNDLE = "selected generic bundle pk"
+ SELECTED_CONFIG_BUNDLE = "selected config bundle pk"
GRESOURCE_BUNDLE_MODELS = "generic_resource_bundle_models"
GRESOURCE_BUNDLE_INFO = "generic_resource_bundle_info"
BOOKING = "booking"
@@ -292,8 +296,6 @@ class Repository():
GRB_LAST_HOSTLIST = "grb_network_previous_hostlist"
BOOKING_FORMS = "booking_forms"
SWCONF_HOSTS = "swconf_hosts"
- SWCONF_SELECTED_GRB = "swconf_selected_grb_pk"
- BOOKING_SELECTED_GRB = "booking_selected_grb_pk"
BOOKING_MODELS = "booking models"
CONFIG_MODELS = "configuration bundle models"
SESSION_USER = "session owner user account"
@@ -307,6 +309,22 @@ class Repository():
SNAPSHOT_DESC = "description of the snapshot"
BOOKING_INFO_FILE = "the INFO.yaml file for this user's booking"
+ #migratory elements of segmented workflow
+ #each of these is the end result of a different workflow.
+ HAS_RESULT = "whether or not workflow has a result"
+ RESULT_KEY = "key for target index that result will be put into in parent"
+ RESULT = "result object from workflow"
+
+ def get_child_defaults(self):
+ return_tuples = []
+ for key in [self.SELECTED_GRESOURCE_BUNDLE, self.SESSION_USER]:
+ return_tuples.append((key, self.el.get(key)))
+ return return_tuples
+
+ def set_defaults(self, defaults):
+ for key, value in defaults:
+ self.el[key] = value
+
def get(self, key, default, id):
self.add_get_history(key, id)
return self.el.get(key, default)
@@ -337,11 +355,19 @@ class Repository():
errors = self.make_generic_resource_bundle()
if errors:
return errors
+ else:
+ self.el[self.HAS_RESULT] = True
+ self.el[self.RESULT_KEY] = self.SELECTED_GRESOURCE_BUNDLE
+ return
if self.CONFIG_MODELS in self.el:
errors = self.make_software_config_bundle()
if errors:
return errors
+ else:
+ self.el[self.HAS_RESULT] = True
+ self.el[self.RESULT_KEY] = self.SELECTED_CONFIG_BUNDLE
+ return
if self.BOOKING_MODELS in self.el:
errors = self.make_booking()
@@ -434,7 +460,7 @@ class Repository():
else:
return "GRB no models given. CODE:0x0001"
- self.el[self.VALIDATED_MODEL_GRB] = bundle
+ self.el[self.RESULT] = bundle
return False
def make_software_config_bundle(self):
@@ -472,15 +498,15 @@ class Repository():
else:
pass
- self.el[self.VALIDATED_MODEL_CONFIG] = bundle
+ self.el[self.RESULT] = bundle
return False
def make_booking(self):
models = self.el[self.BOOKING_MODELS]
owner = self.el[self.SESSION_USER]
- if self.BOOKING_SELECTED_GRB in self.el:
- selected_grb = self.el[self.BOOKING_SELECTED_GRB]
+ if self.SELECTED_GRESOURCE_BUNDLE in self.el:
+ selected_grb = self.el[self.SELECTED_GRESOURCE_BUNDLE]
else:
return "BOOK, no selected resource. CODE:0x000e"
@@ -570,5 +596,6 @@ class Repository():
def __init__(self):
self.el = {}
self.el[self.CONFIRMATION] = {}
+ self.el["active_step"] = 0
self.get_history = {}
self.put_history = {}
diff --git a/dashboard/src/workflow/resource_bundle_workflow.py b/dashboard/src/workflow/resource_bundle_workflow.py
index 11386f9..4858ebe 100644
--- a/dashboard/src/workflow/resource_bundle_workflow.py
+++ b/dashboard/src/workflow/resource_bundle_workflow.py
@@ -131,7 +131,7 @@ class Define_Hardware(WorkflowStep):
try:
self.form = HardwareDefinitionForm(request.POST)
if self.form.is_valid():
- if len(json.loads(self.form.cleaned_data['filter_field']).labs) != 1:
+ if len(json.loads(self.form.cleaned_data['filter_field'])['labs']) != 1:
self.metastep.set_invalid("Please select one lab")
else:
self.update_models(self.form.cleaned_data)
diff --git a/dashboard/src/workflow/sw_bundle_workflow.py b/dashboard/src/workflow/sw_bundle_workflow.py
index 26ade22..80d1b3d 100644
--- a/dashboard/src/workflow/sw_bundle_workflow.py
+++ b/dashboard/src/workflow/sw_bundle_workflow.py
@@ -20,16 +20,9 @@ from resource_inventory.models import Image, GenericHost, ConfigBundle, HostConf
class SWConf_Resource_Select(Resource_Select):
def __init__(self, *args, **kwargs):
super(SWConf_Resource_Select, self).__init__(*args, **kwargs)
- self.repo_key = self.repo.SWCONF_SELECTED_GRB
+ self.repo_key = self.repo.SELECTED_GRESOURCE_BUNDLE
self.confirm_key = "configuration"
- def get_default_entry(self):
- booking_grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB)
- if booking_grb:
- return booking_grb
- created_grb = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}).get("bundle", None)
- return created_grb
-
def post_render(self, request):
response = super(SWConf_Resource_Select, self).post_render(request)
models = self.repo_get(self.repo.CONFIG_MODELS, {})
@@ -80,7 +73,7 @@ class Define_Software(WorkflowStep):
break
excluded_images = Image.objects.exclude(owner=user).exclude(public=True)
excluded_images = excluded_images | Image.objects.exclude(host_type=host.profile)
- lab = self.repo_get(self.repo.SWCONF_SELECTED_GRB).lab
+ lab = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE).lab
excluded_images = excluded_images | Image.objects.exclude(from_lab=lab)
filter_data["id_form-" + str(i) + "-image"] = []
for image in excluded_images:
@@ -91,7 +84,7 @@ class Define_Software(WorkflowStep):
def get_host_list(self, grb=None):
if grb is None:
- grb = self.repo_get(self.repo.SWCONF_SELECTED_GRB, False)
+ grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False)
if not grb:
return []
if grb.id:
@@ -102,7 +95,7 @@ class Define_Software(WorkflowStep):
def get_context(self):
context = super(Define_Software, self).get_context()
- grb = self.repo_get(self.repo.SWCONF_SELECTED_GRB, False)
+ grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False)
if grb:
context["grb"] = grb
@@ -125,6 +118,7 @@ class Define_Software(WorkflowStep):
HostFormset = formset_factory(HostSoftwareDefinitionForm, extra=0)
formset = HostFormset(request.POST)
hosts = self.get_host_list()
+ has_jumphost = False
if formset.is_valid():
models['host_configs'] = []
i = 0
@@ -134,7 +128,7 @@ class Define_Software(WorkflowStep):
i += 1
image = form.cleaned_data['image']
# checks image compatability
- grb = self.repo_get(self.repo.SWCONF_SELECTED_GRB)
+ grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE)
lab = None
if grb:
lab = grb.lab
@@ -147,6 +141,8 @@ class Define_Software(WorkflowStep):
except:
self.metastep.set_invalid("Image " + image.name + " is not compatible with host " + host.resource.name)
role = form.cleaned_data['role']
+ if "jumphost" in role.name.lower():
+ has_jumphost = True
bundle = models['bundle']
hostConfig = HostConfiguration(
host=host,
@@ -158,6 +154,10 @@ class Define_Software(WorkflowStep):
confirm_host = {"name": host.resource.name, "image": image.name, "role": role.name}
confirm_hosts.append(confirm_host)
+ if not has_jumphost:
+ self.metastep.set_invalid('Must have at least one "Jumphost" per POD')
+ return self.render(request)
+
self.repo_put(self.repo.CONFIG_MODELS, models)
if "configuration" not in confirm:
confirm['configuration'] = {}
diff --git a/dashboard/src/workflow/views.py b/dashboard/src/workflow/views.py
index e5ef5c6..6d59b1c 100644
--- a/dashboard/src/workflow/views.py
+++ b/dashboard/src/workflow/views.py
@@ -30,6 +30,17 @@ def attempt_auth(request):
def delete_session(request):
+ manager = attempt_auth(request)
+
+ if not manager:
+ return HttpResponseGone("No session found that relates to current request")
+
+ if manager.pop_workflow():
+ return HttpResponse('')
+ else:
+ del ManagerTracker.managers[request.session['manager_session']]
+ return render(request, 'workflow/exit_redirect.html')
+
try:
del ManagerTracker.managers[request.session['manager_session']]
return HttpResponse('')
@@ -70,7 +81,8 @@ def manager_view(request):
logger.debug("edit found")
manager.add_workflow(workflow_type=request.POST.get('edit'), edit_object=int(request.POST.get('edit_id')))
elif request.POST.get('cancel') is not None:
- del ManagerTracker.managers[request.session['manager_session']]
+ if not manager.pop_workflow():
+ del ManagerTracker.managers[request.session['manager_session']]
return manager.status(request)
diff --git a/dashboard/src/workflow/workflow_factory.py b/dashboard/src/workflow/workflow_factory.py
index 9a42d86..1f4a28a 100644
--- a/dashboard/src/workflow/workflow_factory.py
+++ b/dashboard/src/workflow/workflow_factory.py
@@ -12,6 +12,7 @@ from workflow.booking_workflow import Booking_Resource_Select, SWConfig_Select,
from workflow.resource_bundle_workflow import Define_Hardware, Define_Nets, Resource_Meta_Info
from workflow.sw_bundle_workflow import Config_Software, Define_Software, SWConf_Resource_Select
from workflow.snapshot_workflow import Select_Host_Step, Image_Meta_Step
+from workflow.models import Repository, Confirmation_Step
import uuid
@@ -34,23 +35,6 @@ class ConfigMetaWorkflow(object):
workflow_type = 2
color = "#00ffcc"
-
-class MetaRelation(object):
- def __init__(self, *args, **kwargs):
- self.color = "#cccccc"
- self.parent = 0
- self.children = []
- self.depth = -1
-
- def to_json(self):
- return {
- 'color': self.color,
- 'parent': self.parent,
- 'children': self.children,
- 'depth': self.depth,
- }
-
-
class MetaStep(object):
UNTOUCHED = 0
@@ -92,12 +76,18 @@ class MetaStep(object):
def __ne__(self, other):
return self.id.int != other.id.int
+class Workflow(object):
+ def __init__(self, steps, metasteps, repository):
+ self.repository = repository
+ self.steps = steps
+ self.metasteps = metasteps
+ self.active_index = 0
class WorkflowFactory():
booking_steps = [
Booking_Resource_Select,
SWConfig_Select,
- Booking_Meta
+ Booking_Meta,
]
resource_steps = [
@@ -114,7 +104,7 @@ class WorkflowFactory():
snapshot_steps = [
Select_Host_Step,
- Image_Meta_Step
+ Image_Meta_Step,
]
def conjure(self, workflow_type=None, repo=None):
@@ -129,8 +119,17 @@ class WorkflowFactory():
meta_steps = self.metaize(steps=steps, wf_type=workflow_type)
return steps, meta_steps
+ def create_workflow(self, workflow_type=None, repo=None):
+ steps, meta_steps = self.conjure(workflow_type, repo)
+ c_step = self.make_step(Confirmation_Step, repo)
+ metaconfirm = MetaStep()
+ metaconfirm.short_title = "confirm"
+ metaconfirm.index = len(steps)
+ steps.append(c_step)
+ meta_steps.append(metaconfirm)
+ return Workflow(steps, meta_steps, repo)
+
def make_steps(self, step_types, repository):
- repository.el['steps'] += len(step_types)
steps = []
for step_type in step_types:
steps.append(self.make_step(step_type, repository))
diff --git a/dashboard/src/workflow/workflow_manager.py b/dashboard/src/workflow/workflow_manager.py
index 95fefbf..1d672cf 100644
--- a/dashboard/src/workflow/workflow_manager.py
+++ b/dashboard/src/workflow/workflow_manager.py
@@ -13,8 +13,8 @@ from django.http import JsonResponse
import random
from booking.models import Booking
-from workflow.workflow_factory import WorkflowFactory, MetaStep, MetaRelation
-from workflow.models import Repository, Confirmation_Step
+from workflow.workflow_factory import WorkflowFactory, MetaStep
+from workflow.models import Repository
from resource_inventory.models import (
GenericResourceBundle,
ConfigBundle,
@@ -27,103 +27,65 @@ logger = logging.getLogger(__name__)
class SessionManager():
+ def active_workflow(self):
+ return self.workflows[-1]
def __init__(self, request=None):
- self.repository = Repository()
- self.repository.el[self.repository.SESSION_USER] = request.user
- self.repository.el['active_step'] = 0
- self.steps = []
+ self.workflows = []
+
+ self.owner = request.user
+
self.factory = WorkflowFactory()
- c_step = WorkflowFactory().make_step(Confirmation_Step, self.repository)
- self.steps.append(c_step)
- metaconfirm = MetaStep()
- metaconfirm.index = 0
- metaconfirm.short_title = "confirm"
- self.repository.el['steps'] = 1
- self.metaworkflow = None
- self.metaworkflows = []
- self.metarelations = []
- self.relationreverselookup = {}
- self.initialized = False
- self.active_index = 0
- self.step_meta = [metaconfirm]
- self.relation_depth = 0
def add_workflow(self, workflow_type=None, target_id=None, **kwargs):
if target_id is not None:
self.prefill_repo(target_id, workflow_type)
- factory_steps, meta_info = self.factory.conjure(workflow_type=workflow_type, repo=self.repository)
- offset = len(meta_info)
- for relation in self.metarelations:
- if relation.depth > self.relation_depth:
- self.relation_depth = relation.depth
- if relation.parent >= self.repository.el['active_step']:
- relation.parent += offset
- for i in range(0, len(relation.children)):
- if relation.children[i] >= self.repository.el['active_step']:
- relation.children[i] += offset
- self.step_meta[self.active_index:self.active_index] = meta_info
- self.steps[self.active_index:self.active_index] = factory_steps
-
- if self.initialized:
- relation = MetaRelation()
- relation.parent = self.repository.el['active_step'] + offset
- relation.depth = self.relationreverselookup[self.step_meta[relation.parent]].depth + 1
- if relation.depth > self.relation_depth:
- self.relation_depth = relation.depth
- for i in range(self.repository.el['active_step'], offset + self.repository.el['active_step']):
- relation.children.append(i)
- self.relationreverselookup[self.step_meta[i]] = relation
- relation.color = "#%06x" % random.randint(0, 0xFFFFFF)
- self.metarelations.append(relation)
- else:
- relation = MetaRelation()
- relation.depth = 0
- relation.parent = 500000000000
- for i in range(0, len(self.step_meta)):
- relation.children.append(i)
- self.relationreverselookup[self.step_meta[i]] = relation
- self.metarelations.append(relation)
- self.initialized = True
+
+ repo = Repository()
+ if(len(self.workflows) >= 1):
+ defaults = self.workflows[-1].repository.get_child_defaults()
+ repo.set_defaults(defaults)
+ repo.el[repo.HAS_RESULT] = False
+ repo.el[repo.SESSION_USER] = self.owner
+ self.workflows.append(self.factory.create_workflow(workflow_type=workflow_type, repo = repo))
+
+ def pop_workflow(self):
+ if( len(self.workflows) <= 1 ):
+ return False
+
+ if self.workflows[-1].repository.el[self.workflows[-1].repository.HAS_RESULT]:
+ key = self.workflows[-1].repository.el[self.workflows[-1].repository.RESULT_KEY]
+ result = self.workflows[-1].repository.el[self.workflows[-1].repository.RESULT]
+ self.workflows[-2].repository.el[key] = result
+ self.workflows.pop()
+ return True
def status(self, request):
try:
- steps = []
- for step in self.step_meta:
- steps.append(step.to_json())
- parents = {}
- children = {}
+ meta_steps = []
+ for step in self.active_workflow().metasteps:
+ meta_steps.append(step.to_json())
responsejson = {}
- responsejson["steps"] = steps
- responsejson["active"] = self.repository.el['active_step']
- responsejson["relations"] = []
- i = 0
- for relation in self.metarelations:
- responsejson["relations"].append(relation.to_json())
- children[relation.parent] = i
- for child in relation.children:
- parents[child] = i
- i += 1
- responsejson['max_depth'] = self.relation_depth
- responsejson['parents'] = parents
- responsejson['children'] = children
+ responsejson["steps"] = meta_steps
+ responsejson["active"] = self.active_workflow().repository.el['active_step']
+ responsejson["workflow_count"] = len(self.workflows)
return JsonResponse(responsejson, safe=False)
- except Exception:
+ except Exception as e:
pass
def render(self, request, **kwargs):
# filter out when a step needs to handle post/form data
# if 'workflow' in post data, this post request was meant for me, not step
if request.method == 'POST' and request.POST.get('workflow', None) is None:
- return self.steps[self.active_index].post_render(request)
- return self.steps[self.active_index].render(request)
+ return self.active_workflow().steps[self.active_workflow().active_index].post_render(request)
+ return self.active_workflow().steps[self.active_workflow().active_index].render(request)
def post_render(self, request):
- return self.steps[self.active_index].post_render(request)
+ return self.active_workflow().steps[self.active_workflow().active_index].post_render(request)
def goto(self, num, **kwargs):
- self.repository.el['active_step'] = int(num)
- self.active_index = int(num)
+ self.active_workflow().repository.el['active_step'] = int(num)
+ self.active_workflow().active_index = int(num)
# TODO: change to include some checking
def prefill_repo(self, target_id, workflow_type):
@@ -142,29 +104,28 @@ class SessionManager():
def prefill_booking(self, booking):
models = self.make_booking_models(booking)
confirmation = self.make_booking_confirm(booking)
- self.repository.el[self.repository.BOOKING_MODELS] = models
- self.repository.el[self.repository.CONFIRMATION] = confirmation
- self.repository.el[self.repository.GRESOURCE_BUNDLE_MODELS] = self.make_grb_models(booking.resource.template)
- self.repository.el[self.repository.BOOKING_SELECTED_GRB] = self.make_grb_models(booking.resource.template)['bundle']
- self.repository.el[self.repository.CONFIG_MODELS] = self.make_config_models(booking.config_bundle)
+ self.active_workflow().repository.el[self.active_workflow().repository.BOOKING_MODELS] = models
+ self.active_workflow().repository.el[self.active_workflow().repository.CONFIRMATION] = confirmation
+ self.active_workflow().repository.el[self.active_workflow().repository.GRESOURCE_BUNDLE_MODELS] = self.make_grb_models(booking.resource.template)
+ self.active_workflow().repository.el[self.active_workflow().repository.SELECTED_GRESOURCE_BUNDLE] = self.make_grb_models(booking.resource.template)['bundle']
+ self.active_workflow().repository.el[self.active_workflow().repository.CONFIG_MODELS] = self.make_config_models(booking.config_bundle)
def prefill_resource(self, resource):
models = self.make_grb_models(resource)
confirm = self.make_grb_confirm(resource)
- self.repository.el[self.repository.GRESOURCE_BUNDLE_MODELS] = models
- self.repository.el[self.repository.CONFIRMATION] = confirm
+ self.active_workflow().repository.el[self.active_workflow().repository.GRESOURCE_BUNDLE_MODELS] = models
+ self.active_workflow().repository.el[self.active_workflow().repository.CONFIRMATION] = confirm
def prefill_config(self, config):
models = self.make_config_models(config)
confirm = self.make_config_confirm(config)
- self.repository.el[self.repository.CONFIG_MODELS] = models
- self.repository.el[self.repository.CONFIRMATION] = confirm
+ self.active_workflow().repository.el[self.active_workflow().repository.CONFIG_MODELS] = models
+ self.active_workflow().repository.el[self.active_workflow().repository.CONFIRMATION] = confirm
grb_models = self.make_grb_models(config.bundle)
- self.repository.el[self.repository.GRESOURCE_BUNDLE_MODELS] = grb_models
- self.repository.el[self.repository.SWCONF_SELECTED_GRB] = config.bundle
+ self.active_workflow().repository.el[self.active_workflow().repository.GRESOURCE_BUNDLE_MODELS] = grb_models
def make_grb_models(self, resource):
- models = self.repository.el.get(self.repository.GRESOURCE_BUNDLE_MODELS, {})
+ models = self.active_workflow().repository.el.get(self.active_workflow().repository.GRESOURCE_BUNDLE_MODELS, {})
models['hosts'] = []
models['bundle'] = resource
models['interfaces'] = {}
@@ -181,7 +142,7 @@ class SessionManager():
return models
def make_grb_confirm(self, resource):
- confirm = self.repository.el.get(self.repository.CONFIRMATION, {})
+ confirm = self.active_workflow().repository.el.get(self.active_workflow().repository.CONFIRMATION, {})
confirm['resource'] = {}
confirm['resource']['hosts'] = []
confirm['resource']['lab'] = resource.lab.lab_user.username
@@ -190,7 +151,7 @@ class SessionManager():
return confirm
def make_config_models(self, config):
- models = self.repository.el.get(self.repository.CONFIG_MODELS, {})
+ models = self.active_workflow().repository.el.get(self.active_workflow().repository.CONFIG_MODELS, {})
models['bundle'] = config
models['host_configs'] = []
for host_conf in HostConfiguration.objects.filter(bundle=config):
@@ -199,7 +160,7 @@ class SessionManager():
return models
def make_config_confirm(self, config):
- confirm = self.repository.el.get(self.repository.CONFIRMATION, {})
+ confirm = self.active_workflow().repository.el.get(self.active_workflow().repository.CONFIRMATION, {})
confirm['configuration'] = {}
confirm['configuration']['hosts'] = []
confirm['configuration']['name'] = config.name
@@ -213,7 +174,7 @@ class SessionManager():
return confirm
def make_booking_models(self, booking):
- models = self.repository.el.get(self.repository.BOOKING_MODELS, {})
+ models = self.active_workflow().repository.el.get(self.active_workflow().repository.BOOKING_MODELS, {})
models['booking'] = booking
models['collaborators'] = []
for user in booking.collaborators.all():
@@ -221,7 +182,7 @@ class SessionManager():
return models
def make_booking_confirm(self, booking):
- confirm = self.repository.el.get(self.repository.CONFIRMATION, {})
+ confirm = self.active_workflow().repository.el.get(self.active_workflow().repository.CONFIRMATION, {})
confirm['booking'] = {}
confirm['booking']['length'] = (booking.end - booking.start).days
confirm['booking']['project'] = booking.project