1 {% extends "workflow/viewport-element.html" %}
3 <link href="/static/css/graph_common.css" rel="stylesheet">
4 <title>Pod Definition Prototype</title>
6 <!-- Loads and initializes the library -->
8 var mxLoadStylesheets = false;
10 <script type="text/javascript" src="/static/js/mxClient.min.js" ></script>
17 <script type="text/javascript">
21 var netColors = ['red', 'blue', 'purple', 'green', 'orange', '#8CCDF5', '#1E9BAC'];
23 var lastHostBottom = 100;
24 var networks = new Set([]);
25 var has_public_net = false;
27 function main(graphContainer, overviewContainer, toolbarContainer) {
28 //check if the browser is supported
29 if (!mxClient.isBrowserSupported()) {
30 mxUtils.error('Browser is not supported', 200, false);
34 // Workaround for Internet Explorer ignoring certain styles
35 if (mxClient.IS_QUIRKS) {
36 document.body.style.overflow = 'hidden';
37 new mxDivResizer(graphContainer);
39 var editor = new mxEditor();
40 var graph = editor.graph;
41 var model = graph.getModel();
42 editor.setGraphContainer(graphContainer);
44 doGlobalConfig(graph);
48 restoreFromXml('{{xml|safe}}', editor);
50 {% for host in hosts %}
51 var host = {{host|safe}};
56 {% for host in added_hosts %}
57 var host = {{host|safe}}
63 addToolbarButton(editor, toolbarContainer, 'zoomIn', '', "/static/img/mxgraph/zoom_in.png", true);
64 addToolbarButton(editor, toolbarContainer, 'zoomOut', '', "/static/img/mxgraph/zoom_out.png", true);
67 editor.addAction('printXML', function(editor, cell) {
68 mxLog.write(encodeGraph(graph));
71 addToolbarButton(editor, toolbarContainer, 'printXML', '', '/static/img/mxgraph/fit_to_size.png', true);
74 var outline = new mxOutline(graph, overviewContainer);
76 var checkAllowed = function(edge, terminal, source) {
77 //check if other terminal is null, and that they are different
78 otherTerminal = edge.getTerminal(!source);
79 if(terminal != null && otherTerminal != null) {
80 if( terminal.getParent().getId().split('_')[0] == //'host' or 'network'
81 otherTerminal.getParent().getId().split('_')[0] ) {
83 graph.removeCells([edge]);
90 var colorEdge = function(edge, terminal, source) {
91 if(terminal.getParent().getId().indexOf('network') >= 0) {
92 styles = terminal.getParent().getStyle().split(';');
94 for(var i=0; i<styles.length; i++){
95 kvp = styles[i].split('=');
96 if(kvp[0] == "fillColor"){
100 edge.setStyle('strokeColor=' + color);
104 var alertVlan = function(edge, terminal, source) {
105 if( terminal == null || edge.getTerminal(!source) == null) {
108 var vlanHTML = '<form> <input type="radio" name="tagged" value="True" checked> Tagged<br>'
109 vlanHTML += '<input type="radio" name="tagged" value="False"> Untagged </form>'
110 vlanHTML += '<button onclick=parseVlanWindow(' + edge.getId() + ');>Okay</button>'
111 vlanHTML += '<button onclick=deleteVlanWindow(' + edge.getId() + ');>Cancel</button>'
112 content = document.createElement('div');
113 content.innerHTML = vlanHTML;
114 showWindow(graph, "Vlan Selection", content, 200, 200);
117 //sets the edge color to be the same as the network
118 graph.addListener(mxEvent.CELL_CONNECTED, function(sender, event){
119 edge = event.getProperty('edge');
120 terminal = event.getProperty('terminal')
121 source = event.getProperty('source');
122 if(checkAllowed(edge, terminal, source)) {
123 colorEdge(edge, terminal, source);
124 alertVlan(edge, terminal, source);
128 createDeleteDialog = function(id) {
129 var content = document.createElement('div');
130 var innerHTML = "<button style='width: 46%;' onclick=deleteCell('" + id + "');>Remove</button>"
131 innerHTML += "<button style='width: 46%;' onclick='currentWindow.destroy();'>Cancel</button>"
132 content.innerHTML = innerHTML;
133 showWindow(currentGraph, 'Do you want to delete this network?', content, 200, 62);
136 graph.dblClick = function(evt, cell) {
139 if( cell.getParent() != null && cell.getParent().getId().indexOf("network") > -1) {
140 cell = cell.getParent();
142 if( cell.isEdge() || cell.getId().indexOf("network") > -1 ) {
143 createDeleteDialog(cell.getId());
146 showDetailWindow(cell);
151 updateHosts({{ removed_hosts|default:"[]"|safe }});
157 function showDetailWindow(cell) {
158 var info = JSON.parse(cell.getValue());
159 var content = document.createElement("div");
160 var inner = "<pre>Name: " + info.name + "\n";
161 inner += "Description:\n" + info.description + "</pre>";
162 inner += '<button onclick="currentWindow.destroy();">Okay</button>'
163 content.innerHTML = inner
164 showWindow(currentGraph, 'Details', content, 400, 400);
167 function restoreFromXml(xml, editor) {
168 var doc = mxUtils.parseXml(xml);
169 var node = doc.documentElement;
170 editor.readGraphModel(node);
172 //Iterate over all children, and parse the networks to add them to the sidebar
173 var root = currentGraph.getDefaultParent();
174 for( var i=0; i<root.getChildCount(); i++) {
175 var cell = root.getChildAt(i);
176 if(cell.getId().indexOf("network") > -1) {
177 var info = JSON.parse(cell.getValue());
178 var name = info['name'];
180 var styles = cell.getStyle().split(";");
182 for(var j=0; j< styles.length; j++){
183 var kvp = styles[j].split('=');
184 if(kvp[0] == "fillColor") {
190 has_public_net = true;
193 makeSidebarNetwork(name, color, cell.getId());
198 function deleteCell(cellId) {
199 var cell = currentGraph.getModel().getCell(cellId);
200 if( cellId.indexOf("network") > -1 ) {
201 elem = document.getElementById(cellId);
202 elem.parentElement.removeChild(elem);
204 currentGraph.removeCells([cell]);
205 currentWindow.destroy();
208 function newNetworkWindow() {
209 var innerHtml = 'Name: <input type="text" name="net_name" id="net_name_input" style="margin:5px;"><br>';
210 innerHtml += '<button style="width: 46%;" onclick="parseNetworkWindow()">Okay</button>';
211 innerHtml += '<button style="width: 46%;" onclick="currentWindow.destroy();">Cancel</button><br>';
212 innerHtml += '<div id="current_window_errors"/>';
213 var content = document.createElement("div");
214 content.innerHTML = innerHtml;
216 showWindow(currentGraph, "Network Creation", content, 300, 300);
219 function parseNetworkWindow() {
220 var net_name = document.getElementById("net_name_input").value
221 var error_div = document.getElementById("current_window_errors");
222 if( networks.has(net_name) ){
223 error_div.innerHTML = "All network names must be unique";
226 addNetwork(net_name);
227 currentWindow.destroy();
230 function addToolbarButton(editor, toolbar, action, label, image, isTransparent)
232 var button = document.createElement('button');
233 button.style.fontSize = '10';
236 var img = document.createElement('img');
237 img.setAttribute('src', image);
238 img.style.width = '16px';
239 img.style.height = '16px';
240 img.style.verticalAlign = 'middle';
241 img.style.marginRight = '2px';
242 button.appendChild(img);
246 button.style.background = 'transparent';
247 button.style.color = '#FFFFFF';
248 button.style.border = 'none';
250 mxEvent.addListener(button, 'click', function(evt)
252 editor.execute(action);
254 mxUtils.write(button, label);
255 toolbar.appendChild(button);
258 function encodeGraph(graph) {
259 var encoder = new mxCodec();
260 var xml = encoder.encode(graph.getModel());
261 return mxUtils.getXml(xml);
264 function doGlobalConfig(graph) {
265 //general graph stuff
266 graph.setMultigraph(false);
267 graph.setCellsSelectable(false);
268 graph.setCellsMovable(false);
271 graph.vertexLabelIsMovable = true;
275 graph.setConnectable(true);
276 graph.setAllowDanglingEdges(false);
277 mxEdgeHandler.prototype.snapToTerminals = true;
278 mxConstants.MIN_HOTSPOT_SIZE = 16;
279 mxConstants.DEFAULT_HOTSPOT = 1;
280 //edge 'style' (still affects behavior greatly)
281 style = graph.getStylesheet().getDefaultEdgeStyle();
282 style[mxConstants.STYLE_EDGE] = mxConstants.EDGESTYLE_ELBOW;
283 style[mxConstants.STYLE_ENDARROW] = mxConstants.NONE;
284 style[mxConstants.STYLE_ROUNDED] = true;
285 style[mxConstants.STYLE_FONTCOLOR] = 'black';
286 style[mxConstants.STYLE_STROKECOLOR] = 'red';
288 style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = '#FFFFFF';
289 style[mxConstants.STYLE_STROKEWIDTH] = '3';
290 style[mxConstants.STYLE_ROUNDED] = true;
291 style[mxConstants.STYLE_EDGE] = mxEdgeStyle.EntityRelation;
293 hostStyle = graph.getStylesheet().getDefaultVertexStyle();
294 hostStyle[mxConstants.STYLE_ROUNDED] = 1;
296 // TODO: Proper override
297 graph.convertValueToString = function(cell) {
299 //changes value for edges with xml value
301 if(JSON.parse(cell.getValue())["tagged"]) {
306 return JSON.parse(cell.getValue())['name'];
310 return cell.getValue();
315 function showWindow(graph, title, content, width, height) {
316 //create transparent black background
317 var background = document.createElement('div');
318 background.style.position = 'absolute';
319 background.style.left = '0px';
320 background.style.top = '0px';
321 background.style.right = '0px';
322 background.style.bottom = '0px';
323 background.style.background = 'black';
324 mxUtils.setOpacity(background, 50);
325 document.body.appendChild(background);
328 if (mxClient.IS_IE) {
329 new mxDivResizer(background);
332 var x = Math.max(0, document.body.scrollWidth/2-width/2);
333 var y = Math.max(10, (document.body.scrollHeight ||
334 document.documentElement.scrollHeight)/2-height*2/3);
336 var wnd = new mxWindow(title, content, x, y, width, height, false, true);
337 wnd.setClosable(false);
339 wnd.addListener(mxEvent.DESTROY, function(evt) {
340 graph.setEnabled(true);
341 mxEffects.fadeOut(background, 50, true, 10, 30, true);
345 graph.setEnabled(false);
346 wnd.setVisible(true);
349 function closeWindow() {
350 //allows the current window to be destroyed
351 currentWindow.destroy();
354 function othersUntagged(edgeID) {
355 var edge = currentGraph.getModel().getCell(edgeID);
356 var end1 = edge.getTerminal(true);
357 var end2 = edge.getTerminal(false);
359 if( end1.getParent().getId().split('_')[0] == 'host' ){
365 var edges = netint.edges;
367 for( var i=0; i < edges.length; i++ ) {
368 if( edges[i].getValue() ) {
369 var tagged = JSON.parse(edges[i].getValue()).tagged;
381 function deleteVlanWindow(edgeID) {
382 var cell = currentGraph.getModel().getCell(edgeID);
383 currentGraph.removeCells([cell]);
384 currentWindow.destroy();
387 function parseVlanWindow(edgeID) {
388 //do parsing and data manipulation
389 var radios = document.getElementsByName("tagged");
390 edge = currentGraph.getModel().getCell(edgeID);
392 for(var i=0; i<radios.length; i++) {
393 if(radios[i].checked) {
394 //set edge to be tagged or untagged
395 //cellValue.setAttribute("tagged", radios[i].value);
396 if( radios[i].value == "False")
398 if( othersUntagged(edgeID) )
400 alert("Only one untagged VLAN is allowed per interface");
405 edgeVal['tagged'] = radios[i].value == "True";
406 edge.setValue(JSON.stringify(edgeVal));
410 currentGraph.refresh(edge);
414 function makeMxNetwork(net_name, public = false) {
415 model = currentGraph.getModel();
418 xoff = 400 + (30 * netCount);
420 var color = netColors[netCount];
421 if( netCount > (netColors.length - 1)) {
422 color = Math.floor(Math.random() * 16777215); //int in possible color space
423 color = '#' + color.toString(16).toUpperCase(); //convert to hex
426 var net_val = Object();
427 net_val['name'] = net_name;
428 net_val['public'] = public;
429 net = currentGraph.insertVertex(
430 currentGraph.getDefaultParent(),
431 'network_' + netCount,
432 JSON.stringify(net_val),
437 'fillColor=' + color,
441 for(var i=0; i<num_ports; i++){
442 port = currentGraph.insertVertex(
450 'fillColor=black;opacity=0',
455 var retVal = Object();
456 retVal['color'] = color;
457 retVal['element_id'] = "network_" + netCount;
459 networks.add(net_name);
465 function addPublicNetwork() {
466 var net = makeMxNetwork("public", true);
467 makeSidebarNetwork("public", net['color'], net['element_id']);
468 has_public_net = true;
471 function addNetwork(net_name) {
472 var ret = makeMxNetwork(net_name);
473 var color = ret['color'];
474 var net_id = ret['element_id'];
475 makeSidebarNetwork(net_name, color, net_id);
478 function updateHosts(removed) {
479 for(var i=0; i < removed.length; i++)
481 var hoststring = removed[i];
482 var hostid = "host_" + hoststring.split("*")[0];
483 var cell = currentGraph.getModel().getCell(hostid);
484 currentGraph.removeCells([cell]);
487 var hosts = currentGraph.getChildVertices(currentGraph.getDefaultParent());
489 for(var i=0; i<hosts.length; i++) {
491 if(!host.id.startsWith("host_"))
495 var geometry = host.getGeometry();
496 geometry.y = topdist + 50;
497 topdist = geometry.y + geometry.height;
498 host.setGeometry(geometry);
502 function makeSidebarNetwork(net_name, color, net_id){
503 var newNet = document.createElement("li");
504 var colorBlob = document.createElement("div");
505 colorBlob.className = "colorblob";
506 var textContainer = document.createElement("p");
507 textContainer.className = "network_innertext";
509 var deletebutton = document.createElement("button");
510 deletebutton.className = "btn btn-danger";
511 deletebutton.style = "float: right; height: 20px; line-height: 8px; vertical-align: middle; width: 20px; padding-left: 5px;";
512 deleteButtonText = document.createTextNode("X");
513 deletebutton.appendChild(deleteButtonText);
514 deletebutton.addEventListener("click", function() {
515 createDeleteDialog(net_id);
518 var newNetValue = document.createTextNode(text);
519 textContainer.appendChild(newNetValue);
520 colorBlob.style['background'] = color;
521 newNet.appendChild(colorBlob);
522 newNet.appendChild(textContainer);
523 if( net_name != "public" )
525 newNet.appendChild(deletebutton);
527 document.getElementById("network_list").appendChild(newNet);
530 function makeHost(hostInfo) {
531 value = JSON.stringify(hostInfo['value']);
532 interfaces = hostInfo['interfaces'];
533 graph = currentGraph;
535 height = (25 * interfaces.length) + 25;
537 yoff = lastHostBottom + 50;
538 lastHostBottom = yoff + height;
539 host = graph.insertVertex(
540 graph.getDefaultParent(),
541 'host_' + hostInfo['id'],
550 host.getGeometry().offset = new mxPoint(-50,0);
551 host.setConnectable(false);
554 for(var i=0; i<interfaces.length; i++) {
555 port = graph.insertVertex(
558 JSON.stringify(interfaces[i]),
563 'fillColor=blue;editable=0',
566 port.getGeometry().offset = new mxPoint(-4*interfaces[i].name.length -2,0);
567 currentGraph.refresh(port);
569 currentGraph.refresh(host);
572 function submitForm() {
573 var form = document.getElementById("xml_form");
574 var input_elem = document.getElementById("hidden_xml_input");
575 var s = encodeGraph(currentGraph);
576 input_elem.value = s;
577 req = new XMLHttpRequest();
578 req.open("POST", "/wf/workflow/", false);
579 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
580 req.onerror = function() { alert("problem with form submission"); }
581 var formData = $("#xml_form").serialize();
585 {% endblock extrahead %}
587 <!-- Calls the main function after the page has loaded. Container is dynamically created. -->
589 <div id="graphParent"
590 style="position:absolute;overflow:hidden;top:0px;bottom:0px;width:75%;left:0px;">
591 <div id="graphContainer"
592 style="position:relative;overflow:hidden;top:36px;bottom:0px;left:0px;right:0px;background-image:url('/static/img/mxgraph/grid.gif');cursor:default;">
596 <!-- Creates a container for the sidebar -->
597 <div id="toolbarContainer"
598 style="position:absolute;white-space:nowrap;overflow:hidden;top:0px;left:0px;right:0px;padding:6px;">
601 <!-- Creates a container for the outline -->
602 <div id="outlineContainer"
603 style="position:absolute;overflow:hidden;top:36px;right:0px;width:200px;height:140px;background:transparent;border-style:solid;border-color:black;">
631 vertical-align: middle;
635 list-style-type: none;
642 display: inline-block;
643 vertical-align: middle;
646 display: inline-block;
648 vertical-align: middle;
657 <div id="network_select" style="position:absolute;top:0px;bottom:0px;width:25%;right:0px;left:auto;">
658 <div id="toolbar_extension">
659 <button id="btn_add_network" type="button" class="btn btn-primary" onclick="newNetworkWindow();">Add Network</button>
661 <ul id="network_list">
663 <button type="button" style="display: none" onclick="submitForm();">Submit</button>
665 <form id="xml_form" method="post" action="/wf/workflow/">
667 <input type="hidden" id="hidden_xml_input" name="xml" />
672 document.getElementById('graphContainer'),
673 document.getElementById('outlineContainer'),
674 document.getElementById('toolbarContainer'),
675 document.getElementById('sidebarContainer')
678 {% endblock content %}