Cloud plugin: spin while waiting for apply changes
[myslice.git] / portal / templates / slice-tab-cloud.html
1 {% extends "layout_wide.html" %}
2
3 {% block head %}
4 <style>
5 .disabled {
6     z-index: 1000;
7     background-color: #999999;
8     opacity: 0.3;
9     pointer-events: none;
10 }
11 </style>
12 <script type="text/javascript">
13
14 var global_list = {};
15 var data = Array(); 
16 var ad_rspec = Array(); 
17 var request_rspec = Array(); 
18 var manifest_rspec = Array(); 
19 var deleted_nodes = {}; 
20 var added_nodes = {}; 
21
22 var len_platforms = {{len_platforms}};
23
24 /* render_flavor & render_image */
25 function render_option(obj){
26     var option = document.createElement("option");
27     option.text = obj["@name"];
28     option.value = obj["@name"];
29     return option;
30 }
31 function render_description(platform, obj, type){
32     if($('#'+platform+'_'+type).length==0){
33         $('#'+platform+'_add').append("<div id='"+platform+"_"+type+"'>");
34     }
35     var d = platform+'_'+type+'_'+obj['@name'];
36     d = d.replace(/ /g, '');
37     id = d.replace( /(:|\.|\[|\])/g, "\\$1" );
38     if($('#'+id).length==0){
39         $('#'+platform+'_'+type).append("<div id='"+d+"' class='alert-success col-md-5' style='margin-top:10px;display:none;border-style:solid;border-color:#f1f1f1;border-width:2px;'></div>");
40     }
41     if($('#'+id+' div').length==0){
42         jQuery.each(obj, function(key,val){
43             if (key != 'openstack:image'){
44                 $('#'+id).append("<div>"+key.replace('@','')+": "+val+"</div>");
45             }
46         });
47     }
48 }
49 function toogle_div(platform, value, type){
50     $("#"+platform+"_add").show();
51     // show the add button only if image is selected
52     if($("#"+platform+"_selectImage").val()!=null && $("#"+platform+"_selectImage").val()!=0 && value!=0){
53         $("#"+platform+"_add_button").show();
54     }else{
55         $("#"+platform+"_add_button").hide();
56     }
57     $("[id^='"+platform+"_"+type+"_"+"']").hide();
58     d = platform+'_'+type+'_'+value;
59     d = d.replace(/ /g, '');
60     id = d.replace( /(:|\.|\[|\])/g, "\\$1" );
61     $('#'+id).show();
62 }
63 function render_cloud(platform, node){
64     elm = document.getElementById(platform+'_select');
65     //newElement = document.createElement('p');
66     //elm.appendChild(newElement); 
67     global_list[platform]={};
68     if('openstack:sliver' in node){
69         selectFlavor = document.createElement('select');
70         selectFlavor.id = platform+"_selectFlavor";
71         selectFlavor.name = platform+"_selectFlavor";
72         selectFlavor.onchange = function(){
73             /* 1) Display corresponding Flavor div - hide others - reset selectImage value */
74             $("#"+platform+"_selectImage option[value=0]").prop('selected', true);
75             toogle_div(platform, this.value, 'flavor');
76             /* 3) Disable Images, Enable only compatible ones in selectImage */
77             $("[id^='"+platform+"_image_"+"']").hide();
78             $("#"+platform+"_selectImage option").attr("disabled",true);
79             $.each(global_list[platform][this.value], function (i,v){
80                 $("#"+platform+"_selectImage option[value='" + v + "']").attr("disabled",false);
81             });
82             $("#"+platform+"_selectImage").attr("disabled",false);
83         }
84         var option = document.createElement("option");
85         option.text = "-- select a flavor --";
86         option.value = 0;
87         selectFlavor.appendChild(option);
88         jQuery.each( node['openstack:sliver'], function( i, sliver ) {
89             if('openstack:flavor' in sliver){
90                 f = render_option(sliver['openstack:flavor']);
91                 selectFlavor.appendChild(f);
92                 flavor = sliver["openstack:flavor"];
93                 /* 1) create hidden div to explain caracteristics of the flavor */
94                 render_description(platform, flavor, 'flavor');
95                 flavor_name = flavor['@name'];
96                 global_list[platform][flavor_name]=[];
97                 if ("openstack:image" in flavor){
98                     selectImage = document.createElement('select');
99                     selectImage.id = platform+"_selectImage";
100                     selectImage.name = platform+"_selectImage";
101                     selectImage.onchange = function(){
102                         /* 2) display corresponding Image div - hide others */
103                         toogle_div(platform, this.value, 'image');
104                     }
105                     var option = document.createElement("option");
106                     option.text = "-- select an image --";
107                     option.value = 0;
108                     selectImage.appendChild(option);
109                     if(flavor["openstack:image"] instanceof Array){
110                         jQuery.each( flavor["openstack:image"], function( i, img ) {
111                            image = render_option(img);
112                            image.disabled = true;
113                            selectImage.appendChild(image);
114                            /* 2) create hidden div to explain caracteristics of the image */
115                            render_description(platform, img, 'image');
116                            global_list[platform][flavor_name].push(img['@name']);
117                         });
118                     }else{
119                         image = render_option(flavor["openstack:image"]);
120                         image.disabled = true;
121                         selectImage.appendChild(image);
122                         /* 2) create hidden div to explain caracteristics of the image */
123                         render_description(platform, flavor["openstack:image"], 'image');
124                         global_list[platform][flavor_name].push(flavor['openstack:image']['@name']);
125                     }
126                 }
127
128             }
129         });
130         elm.appendChild(selectFlavor); 
131         elm.appendChild(selectImage); 
132     }
133     $("#"+platform+"_selectFlavor").css("width","100px");
134     $("#"+platform+"_selectFlavor").css("height","30px");
135     $("#"+platform+"_selectImage").css("width","30px");
136     $("#"+platform+"_selectImage").css("height","30px");
137 }
138 function is_finished(len_platforms, pf_status){
139     if(len_platforms == pf_status){
140         return true;
141     }else{
142         return false;
143     }
144 }
145 function sliver_name_exists(sliver_name){
146     if(sliver_name in added_nodes){
147         return true;
148     }
149     /*
150     if (sliver_name in deleted_nodes){
151         return true;
152     }
153     */
154     return false;
155 }
156 function find_sliver_name(sliver_name, num){
157     for (j=Object.keys(added_nodes).length; j<Object.keys(added_nodes).length+num; i++){
158         vm_name = sliver_name+'_'+j;
159         if(!sliver_name_exists(vm_name)){
160             return vm_name
161         }
162     }
163 }
164
165 function send_add(platform){ 
166     $('#'+platform+'_pending_add').show();
167     sliver_name = $('#'+platform+'_sliver_name').val();
168     sliver_name = sliver_name.replace(' ','_');
169     flavor_name = $('#'+platform+'_selectFlavor').val();
170     image_name = $('#'+platform+'_selectImage').val();
171
172     num = $('#'+platform+'_number').val();
173     if (num > 1){
174         for (i = 0; i < num; i++){
175             // XXX Check if the name already exist in existing VMs and added_nodes
176             vm_name = sliver_name+"_"+i;
177             if (sliver_name_exists(vm_name)){
178                 vm_name = find_sliver_name(sliver_name, num);
179             }
180             node = get_node(vm_name, flavor_name, image_name);
181             request_rspec['rspec']['node'].push(node);
182             render_node(platform, node, 'pending_add');
183             added_nodes[vm_name]=node;
184         }
185     }else{
186         if (sliver_name_exists(sliver_name)){
187             sliver_name = find_sliver_name(sliver_name, num);
188         }
189         node = get_node(sliver_name, flavor_name, image_name);
190         request_rspec['rspec']['node'].push(node);
191         render_node(platform, node, 'pending_add');
192         added_nodes[sliver_name]=node;
193     }
194     toogle_div(platform, flavor_name, 'flavor');
195     toogle_div(platform, image_name, 'image');
196     flavor_name = $('#'+platform+'_selectFlavor').val(0);
197     image_name = $('#'+platform+'_selectImage').val(0);
198     $('#'+platform+'_add').hide();
199     $('#'+platform+'_add_button').hide();
200     $('#'+platform+'_div_pending').show();
201     console.log(request_rspec);
202     //jQuery('#'+platform+'_form_delete').submit();
203 }
204 function get_node(vm_name, flavor_name, image_name){
205         var node = {};
206         if(ad_rspec['rspec']['node'] instanceof Array) {
207             // Deep copy of the Array to avoid reference
208             node = jQuery.extend(true, {}, ad_rspec['rspec']['node'][0]);
209         }else{
210             // Deep copy of the Array to avoid reference
211             node = jQuery.extend(true, {}, ad_rspec['rspec']['node']);
212         }
213         if(node['openstack:sliver'] instanceof Array) {
214             node['openstack:sliver'] = node['openstack:sliver'][0];
215         }
216         node['openstack:sliver']['@sliver_name'] = vm_name;
217         console.log(vm_name);
218
219         if(node['openstack:sliver']['openstack:flavor'] instanceof Array) {
220             node['openstack:sliver']['openstack:flavor'] = node['openstack:sliver']['openstack:flavor'][0];
221         }
222         node['openstack:sliver']['openstack:flavor'] = Array();
223         node['openstack:sliver']['openstack:flavor'] = {'@name':flavor_name,'openstack:image':{'@name':image_name}};
224
225         $.each(node['openstack:sliver']['openstack:security_group'], function(i, group){
226             if(group['@name']=='default'){
227                 node['openstack:sliver']['openstack:security_group'] = group;
228                 return false;
229             }
230         });
231         return node;
232
233 }
234 function send_delete(platform, sliver_name){
235     $('#'+platform+'_pending_delete').show();
236     jQuery('#'+platform+'_vm').val(sliver_name);
237     jQuery('#'+platform+'_existing_'+sliver_name).hide();
238     node = remove_node_from_request_rspec(sliver_name);
239     deleted_nodes[sliver_name]=node;
240     render_node(platform, node, 'pending_delete');
241     $('#'+platform+'_div_pending').show();
242     console.log(request_rspec);
243 }
244 function remove_node_from_request_rspec(sliver_name){
245     var save_node = Array();
246     jQuery.each( request_rspec['rspec']['node'], function( i, node ) {
247         if(node['openstack:sliver']['@sliver_name']==sliver_name){
248             request_rspec['rspec']['node'].splice(i,1);
249             save_node = node;
250             return false;
251         }
252     });
253     return save_node;
254 }
255 function cancel_add(platform,sliver_name){
256     // remove the canceled node from the pending list
257     $('#'+platform+'_pending_add_'+sliver_name).remove();
258     // remove the canceled node from the request rspec
259     node = remove_node_from_request_rspec(sliver_name);
260     delete added_nodes[sliver_name];
261     // hide the pending div if there are no more nodes
262     if ($('#'+platform+'_pending_add div').length==0){
263         $('#'+platform+'_pending_add').hide();
264         if ($('#'+platform+'_pending_delete div').length==0){
265             $('#'+platform+'_div_pending').hide();
266         }
267     }
268 }
269 function cancel_delete(platform,sliver_name){
270     // display the canceled node back in existing nodes
271     $('#'+platform+'_existing_'+sliver_name).show();
272     // remove the canceled node from the pending list
273     $('#'+platform+'_pending_delete_'+sliver_name).remove();
274     // Cancel the deletion of an existing node -> push it back to the request rspec
275     request_rspec['rspec']['node'].push(deleted_nodes[sliver_name]);
276     delete deleted_nodes[sliver_name];
277     // hide the pending div if there are no more nodes
278     if ($('#'+platform+'_pending_delete div').length==0){
279         $('#'+platform+'_pending_delete').hide();
280         if ($('#'+platform+'_pending_add div').length==0){
281             $('#'+platform+'_div_pending').hide();
282         }
283     }
284 }
285
286 function public_ip(platform,sliver_name,is_public){
287     // XXX Change ip status in request_rspec
288     if(is_public){
289         // external_ip="true"
290         node = remove_node_from_request_rspec(sliver_name);
291         node['external_ip']="true";
292         added_nodes[sliver_name]['external_ip']="true";
293         request_rspec['rspec']['node'].push(node);        
294     }else{
295         console.log(sliver_name+' NOT public ');
296         // external_ip="false"
297         node = remove_node_from_request_rspec(sliver_name);
298         node['external_ip']="false";
299         added_nodes[sliver_name]['external_ip']="false";
300         request_rspec['rspec']['node'].push(node);        
301     }
302 }
303
304 function render_node(platform, node, state){
305     if('openstack:sliver' in node){
306         sliver = node['openstack:sliver']
307         var d = platform+'_'+state+'_'+sliver['@sliver_name'];
308         d = d.replace(/ /g, '');
309         id = d.replace( /(:|\.|\[|\])/g, "\\$1" );
310         $("#"+platform+"_"+state).append("<div id='"+platform+'_'+state+'_'+sliver['@sliver_name']+"' class='row'></div>");
311         //$("#"+id).append("<input type='hidden' name='"+platform+"_"+sliver['@sliver_name']+"' value='"+sliver['@sliver_name']+"'>");
312         if(state=='pending_add'){
313             $("#"+id).append("<div class='col-md-1' style='margin-left:0px;!important;'><input type='checkbox' id='publicip_"+platform+"_"+sliver['@sliver_name']+"' onclick=public_ip('"+platform+"','"+sliver['@sliver_name']+"',this.checked);></div>");
314         }else{
315             $("#"+id).append("<div class='col-md-1' style='margin-left:0px;!important;'>&nbsp;</div>");
316         }
317         $("#"+id).append("<div class='col-md-2' style='padding-left:0px;'>"+sliver['@sliver_name']+"</div>");
318         $("#"+id).append("<div class='col-md-3'>"+sliver['openstack:flavor']['@name']+"</div>");
319         $("#"+id).append("<div class='col-md-3'>"+sliver['openstack:flavor']['openstack:image']['@name']+"</div>");
320
321         if(state=='existing'){
322             $("#"+id).append("<div class='col-md-1'><input id='"+platform+"_delete_"+sliver['@sliver_name']+"' type='submit' form='"+platform+"_form_delete' value='Delete' onclick=send_delete('"+platform+"','"+sliver['@sliver_name']+"');></div>");
323         }else if(state=='pending_add'){
324             $("#"+id).append("<div class='col-md-1'><input id='"+platform+"_cancel_"+sliver['@sliver_name']+"' type='submit' form='"+platform+"_form_cancel' value='Cancel' onclick=cancel_add('"+platform+"','"+sliver['@sliver_name']+"');></div>");
325         }else if (state=='pending_delete'){
326             $("#"+id).append("<div class='col-md-1'><input id='"+platform+"_cancel_"+sliver['@sliver_name']+"' type='submit' form='"+platform+"_form_cancel' value='Cancel' onclick=cancel_delete('"+platform+"','"+sliver['@sliver_name']+"');></div>");
327         }else{
328             console.log("state: "+state+" not impemented");
329         }
330         /*
331         sliver['openstack:address']
332         sliver['openstack:flavor']
333         */
334     }
335 }
336
337 function allocate(platform, slicename){
338     $("#"+platform+"_main").addClass("disabled");
339     $("#"+platform+"_wait").show();
340     $("#"+platform+"_wait").spin();
341     $.post("/sfa/Allocate",{'hrn':slicename, 'type':'slice', 'platform':[platform], 'rspec':JSON.stringify(request_rspec)}, function( result ) {
342         console.log(result);
343         clear_data(platform);
344         load_data(platform,slicename);
345         $("#"+platform+"_main").removeClass("disabled");
346         $("#"+platform+"_wait").hide();
347         mysliceAlert('Success: changes applied','success', true);
348     });
349 }
350 function clear_data(platform){
351     $('#'+platform+'_existing').children().remove();
352     $('#'+platform+'_pending_delete').children().remove();
353     $("#"+platform+"_selectFlavor").remove();
354     $("#"+platform+"_selectImage").remove();
355     $('#'+platform+'_pending_add').children().remove();
356     $('#'+platform+'_div_pending').hide();
357 }
358 function load_data(platform, slicename){
359     var platform_status = Array();
360     var platform_empty = Array();
361
362     $.post("/sfa/Describe",{'hrn':slicename, 'type':'slice', 'platform':[platform]}, function( d ) {
363         data = d;
364         console.log(data);
365         if('parsed' in data[platform] && 'rspec' in data[platform]['parsed']){
366            manifest_rspec = data[platform]['parsed']['rspec'];
367            request_rspec = data[platform]['parsed'];
368            request_rspec['rspec']['@type']='request';
369            if('node' in manifest_rspec){
370                if(manifest_rspec['node'] instanceof Array) {
371                    jQuery.each( manifest_rspec['node'], function( i, node ) {
372                        render_node(platform,node, 'existing');
373                    });
374                }else{
375                    render_node(platform,manifest_rspec['node'], 'existing');
376                }
377                $('#'+platform+'_existing').show();
378            }
379         }
380     });
381     $.post("/sfa/ListResources",{'platform':[platform]}, function( d ) {
382         //$.extend(data,d);
383         //console.log(data);
384         if('parsed' in d[platform] && 'rspec' in d[platform]['parsed']){
385            ad_rspec = d[platform]['parsed'];
386            if('node' in ad_rspec['rspec']){
387                if(ad_rspec['rspec']['node'] instanceof Array) {
388                    jQuery.each( ad_rspec['rspec']['node'], function( i, node ) {
389                        render_cloud(platform,node);
390                    });
391                }else{
392                    render_cloud(platform,ad_rspec['rspec']['node']);
393                }
394            }else{
395                platform_empty.push(platform);
396            }
397         }else{
398             platform_empty.push(platform);
399         }
400         platform_status.push(platform);
401         if(is_finished(len_platforms,platform_status.length)){
402             $("#loading").hide();
403             if(platform_empty.length == len_platforms){
404                 $("#warning_message").show();
405             }
406         }
407     });
408 }
409 $(document).ready(function() {
410 {% for platform in platforms %}
411     {% if platform in cloud_platforms %}
412     load_data('{{platform}}', '{{slicename}}');
413     {% endif %}
414 {% endfor %}
415 });
416 </script>
417 {% endblock %}
418
419 {% block content %}
420 {{post_values}}
421 {% for platform in platforms %}
422     {% if platform in cloud_platforms %}
423     <div id="{{platform}}_wait" style="display:none;margin-top:20px;position:absolute;margin-left:300px;"></div>
424     <div id="{{platform}}_main" style="padding-left:20px;padding-top:20px;padding-right:20px;padding-bottom:20px;border-style:solid;border-width:1px;width:700px;">
425         <h2>{{ platform }}</h2>
426         // display only if VMs already in slice
427         <h4>VMs in slice {{slicename}}</h4>
428         <form id="{{platform}}_form_delete" method="post">
429         {% csrf_token %}
430         <div id="{{platform}}_existing" class="row alert alert-info" style="display:none;">
431         <input type="hidden" name="{{platform}}_vm" id="{{platform}}_vm">
432         <input type="hidden" name="action" id="action" value="delete">
433         <input type="hidden" name="platform" id="platform" value="{{platform}}">
434         </div> 
435         </form>
436         <h4><a href="#" onclick="$('#{{platform}}_div_add').toggle();"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Create new VMs</a></h4>
437         <div id="{{platform}}_div_add" class="alert" style="background-color:#f1f1f1;display:none;margin-bottom:0px !important;padding-bottom:6px !important;">
438         <form id="{{platform}}_form_add" method="post" class="form-inline">
439         {% csrf_token %}
440         <div id="{{platform}}_input" class="row">
441             <div class="col-md-1"></div>
442             <div class="col-md-3"><label for="{{platform}}_number">number:</label>
443             <input type="text" maxlength="2" id="{{platform}}_number" name="{{platform}}_number" style="width:2.2em;min-width:2.2em;height:30px;" value="1" class="form-control" required>
444             </div>
445             <div class="col-md-8"><label for="{{platform}}_sliver_name">name:</label>
446             <div class="form-group">
447             <input type="text" name="{{platform}}_sliver_name" id="{{platform}}_sliver_name" value="VM" maxlength="12" class="form-control" style="height:30px;" required>
448             </div>
449             </div>
450             <div class="col-md-1"></div>
451             <div id="{{platform}}_select" class="col-md-11"></div>
452         </div>
453         <div id="{{platform}}_add" class="row">
454             <div class="col-md-1">&nbsp; 
455             <input type="hidden" name="action" id="action" value="add">
456             <input type="hidden" name="platform" id="platform" value="{{platform}}">
457             </div>
458         </div>
459         <div class="row" style="padding-top:6px;">
460             <div class="col-md-1">&nbsp;</div>
461             <div class="col-md-11" id="{{platform}}_add_button" style="display:none;">
462             <input type="submit" form="{{platform}}_form_add" value="Add" onclick="send_add('{{platform}}');">
463             </div>
464         </div>
465         </form>
466         </div>
467         <br>
468         <div id="{{platform}}_div_pending" style="display:none;">
469         // display only pending changes
470         <h4>Pending changes</h4>
471         <form id="{{platform}}_form_reserve" method="post">
472         {% csrf_token %}
473         <div id="{{platform}}_pending_add" class="row alert alert-success" style="display:none;margin-bottom:3px !important;"></div> 
474         <div id="{{platform}}_pending_delete" class="row alert alert-danger" style="display:none;margin-bottom:3px !important;"></div> 
475         <br>
476         <input type="hidden" name="action" id="action" value="reserve">
477         <input type="hidden" name="platform" id="platform" value="{{platform}}">
478         <input type="submit" form="{{platform}}_form_reserve" value="Apply changes" onclick="allocate('{{platform}}','{{slicename}}');">
479         </form>
480         </div>
481     </div>
482     {% endif %}
483 {% endfor %}
484 {% endblock %}