Merge branch 'onelab' of ssh://git.onelab.eu/git/myslice into onelab
authorJavier Garcia <javier.garcial@atos.net>
Mon, 30 Nov 2015 11:49:25 +0000 (12:49 +0100)
committerJavier Garcia <javier.garcial@atos.net>
Mon, 30 Nov 2015 11:49:25 +0000 (12:49 +0100)
manifoldapi/static/js/manifold.js
myslice/urls.py
portal/projectrequestview.py
portal/slicerequestview.py
portal/slicetabcloud.py [new file with mode: 0644]
portal/templates/_footer.html
portal/templates/projectrequest_view.html
portal/templates/slice-tab-cloud.html [new file with mode: 0644]
portal/templates/slice-tab-info.html
portal/templates/slicerequest_view.html
rest/sfa_api.py

index 5b5e6af..b8bd502 100644 (file)
@@ -1702,9 +1702,10 @@ case TYPE_LIST_OF_VALUES:
         var query, data;
 
         query = query_ext.query;
-
-        var testbeds_with_sla = localStorage.getItem("sla_testbeds").split(",");
-
+        var testbeds_with_sla = Array();
+        if(localStorage.getItem("sla_testbeds")!=null){
+            testbeds_with_sla = localStorage.getItem("sla_testbeds").split(",");
+        }
         switch(query.object) {
 
             case 'resource':
index 61f779f..b2e88bb 100644 (file)
@@ -46,6 +46,7 @@ import portal.sliceresourceview
 import portal.resources
 
 import portal.slicetabexperiment
+import portal.slicetabcloud
 import portal.slicetabinfo
 import portal.slicetabtestbeds
 import portal.slicetabusers
@@ -116,6 +117,7 @@ urls = [
     (r'^testbeds/(?P<slicename>[^/]+)/?$', portal.slicetabtestbeds.SliceTabTestbeds.as_view()),
     (r'^measurements/(?P<slicename>[^/]+)/?$', portal.slicetabmeasurements.SliceTabMeasurements.as_view()),
     (r'^experiment/(?P<slicename>[^/]+)/?$', portal.slicetabexperiment.ExperimentView.as_view()),
+    (r'^cloud/(?P<slicename>[^/]+)/?$', portal.slicetabcloud.CloudView.as_view()),
     
     
     url(r'^about/?$', portal.about.AboutView.as_view(), name='about'),
index 1f6e921..2757b94 100644 (file)
@@ -106,6 +106,7 @@ class ProjectRequestView(LoginRequiredAutoLogoutView, ThemeView):
                     'authority_hrn'     : wsgi_request.POST.get('authority_name', ''),
                     'project_name'      : project_name,
                     'purpose'           : wsgi_request.POST.get('purpose', ''),
+                    'url'               : wsgi_request.POST.get('url', ''),
                 }
 
                 # for new projects max project_name length is 10
index a2b0dd0..9ccaf76 100644 (file)
@@ -39,8 +39,8 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView):
 
         errors = []
         slice_name =''
-        purpose=''
-        url=''
+        #purpose=''
+        #url=''
         authority_hrn = None
         authority_name = None
         # Retrieve the list of authorities
@@ -138,8 +138,8 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView):
                 'authority_hrn'     : authority_hrn,
                 'organization'      : request.POST.get('org_name', ''),
                 'slice_name'        : slice_name,
-                'url'               : request.POST.get('url', ''),
-                'purpose'           : request.POST.get('purpose', ''),
+                #'url'               : request.POST.get('url', ''),
+                #'purpose'           : request.POST.get('purpose', ''),
                 'current_site'      : current_site
             }
 
@@ -174,11 +174,11 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView):
 
 
     
-            purpose = slice_request['purpose']
-            if purpose is None or purpose == '':
-                errors.append('Experiment purpose is mandatory')
+           # purpose = slice_request['purpose']
+           # if purpose is None or purpose == '':
+           #     errors.append('Experiment purpose is mandatory')
 
-            url = slice_request['url']
+            #url = slice_request['url']
 
             if not errors:
                 if is_pi(request, user_hrn, authority_hrn):
@@ -203,10 +203,10 @@ class SliceRequestView (LoginRequiredAutoLogoutView, ThemeView):
             'topmenu_items': topmenu_items_live('Request a slice', page),
             'errors': errors,
             'slice_name': slice_name,
-            'purpose': purpose,
+            #'purpose': purpose,
             'email': user_email,
             'user_hrn': user_hrn,
-            'url': url,
+            #'url': url,
             'pi': pi,
             'authority_name': authority_name,        
             'authority_hrn': user_authority,        
diff --git a/portal/slicetabcloud.py b/portal/slicetabcloud.py
new file mode 100644 (file)
index 0000000..ce1dc31
--- /dev/null
@@ -0,0 +1,86 @@
+# this somehow is not used anymore - should it not be ?
+from django.core.context_processors import csrf
+from django.http import HttpResponseRedirect
+from django.contrib.auth import authenticate, login, logout
+from django.template import RequestContext
+from django.shortcuts import render_to_response
+from django.shortcuts import render
+
+from unfold.loginrequired import LoginRequiredView
+
+from manifold.core.query        import Query
+from manifoldapi.manifoldapi    import execute_query
+from manifoldapi.manifoldresult import ManifoldResult
+from ui.topmenu import topmenu_items, the_user
+from myslice.configengine import ConfigEngine
+
+from myslice.theme import ThemeView
+from myslice.configengine import ConfigEngine
+from myslice.settings import logger
+
+from sfa.planetlab.plxrn import hash_loginbase
+
+import urllib2,json
+
+class CloudView (LoginRequiredView, ThemeView):
+    # parent View is portal/sliceview.py
+
+    def get_platforms (self, request):
+        username = self.request.user    
+        
+        pf_query = Query().get('local:platform').filter_by('disabled', '==', '0').filter_by('gateway_type', '==', 'sfa').select('platform')
+        res_platforms = execute_query(request, pf_query)
+        platforms = [p['platform'] for p in res_platforms]
+        return platforms
+
+
+    template_name = 'slice-tab-cloud.html'
+    def post (self, request, slicename):
+        logger.debug("---------------- POST CloudView ------------------")
+        logger.debug(request.POST)
+
+        username = self.request.user    
+        platforms = self.get_platforms(request)
+        cloud_platforms = ["onelab-cloud"]
+        len_platforms = len(platforms)
+
+        #if 'action' in request.POST:
+        #    if request.POST['action'] == 'add':
+
+        #    elif request.POST['action'] == 'delete':
+        #        for key,val in request.POST:
+        #            if key.endswith('_vm'):
+        #                request.POST['platform']
+        #                
+        #    elif request.POST['action'] == 'reserve':
+        #    
+        #    else:
+        #        log.error("action %s not supported" % request.POST['action'])
+
+        env = { 'theme' : self.theme,
+                'slicename':slicename,
+                'platforms':platforms,
+                'cloud_platforms':cloud_platforms,
+                'len_platforms': len_platforms,
+                'post_values': request.POST,
+                'request':self.request,
+              }
+        return render_to_response(self.template, env, context_instance=RequestContext(request))
+
+
+    def get (self, request, slicename, state=None):
+  
+        username = self.request.user    
+        platforms = self.get_platforms(request)
+        cloud_platforms = ["onelab-cloud"]
+        len_platforms = len(platforms)
+
+        env = { 'theme' : self.theme,
+                'slicename':slicename, 
+                'platforms':platforms,
+                'cloud_platforms':cloud_platforms,
+                'len_platforms': len_platforms,
+                'request':self.request,
+              }
+        return render_to_response(self.template, env, context_instance=RequestContext(request))
+
index 6bf5bb8..be7462e 100644 (file)
@@ -12,7 +12,7 @@
             Copyrights &copy; 2015 UPMC Sorbonne Universit&eacute;s. All rights reserved.
         </div>
         <div class="col-md-3 copy">
-        powerd by &nbsp;<a href="http://myslice.info" target="_blank"><img src="//myslice.info/images/logo.png" style="width:50px;"></a>&nbsp;&nbsp;
+        powered by &nbsp;<a href="http://myslice.info" target="_blank"><img src="//myslice.info/images/logo.png" style="width:50px;"></a>&nbsp;&nbsp;
         <a href="/portal/release_notes" id="portal_version"></a>
         </div>
     </div>
index 8558228..aabda75 100644 (file)
                         {% endif %}
                         </select>
                     </div>
-                  
+                    <div class="form-group">
+                        <label>
+                            Provide an URL for your project (not required)
+                        </label>
+                        <input type="text" class="form-control" name="url" id="url" style="width:100%" placeholder="Experiment URL (if one exists)"
+                        title="Please provide the url of your project" value="{{url}}">
+                    </div> 
                     <label>
                         Please provide a description of the purpose for your project
                     </label>
diff --git a/portal/templates/slice-tab-cloud.html b/portal/templates/slice-tab-cloud.html
new file mode 100644 (file)
index 0000000..436af69
--- /dev/null
@@ -0,0 +1,484 @@
+{% extends "layout_wide.html" %}
+
+{% block head %}
+<style>
+.disabled {
+    z-index: 1000;
+    background-color: #999999;
+    opacity: 0.3;
+    pointer-events: none;
+}
+</style>
+<script type="text/javascript">
+
+var global_list = {};
+var data = Array(); 
+var ad_rspec = Array(); 
+var request_rspec = Array(); 
+var manifest_rspec = Array(); 
+var deleted_nodes = {}; 
+var added_nodes = {}; 
+
+var len_platforms = {{len_platforms}};
+
+/* render_flavor & render_image */
+function render_option(obj){
+    var option = document.createElement("option");
+    option.text = obj["@name"];
+    option.value = obj["@name"];
+    return option;
+}
+function render_description(platform, obj, type){
+    if($('#'+platform+'_'+type).length==0){
+        $('#'+platform+'_add').append("<div id='"+platform+"_"+type+"'>");
+    }
+    var d = platform+'_'+type+'_'+obj['@name'];
+    d = d.replace(/ /g, '');
+    id = d.replace( /(:|\.|\[|\])/g, "\\$1" );
+    if($('#'+id).length==0){
+        $('#'+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>");
+    }
+    if($('#'+id+' div').length==0){
+        jQuery.each(obj, function(key,val){
+            if (key != 'openstack:image'){
+                $('#'+id).append("<div>"+key.replace('@','')+": "+val+"</div>");
+            }
+        });
+    }
+}
+function toogle_div(platform, value, type){
+    $("#"+platform+"_add").show();
+    // show the add button only if image is selected
+    if($("#"+platform+"_selectImage").val()!=null && $("#"+platform+"_selectImage").val()!=0 && value!=0){
+        $("#"+platform+"_add_button").show();
+    }else{
+        $("#"+platform+"_add_button").hide();
+    }
+    $("[id^='"+platform+"_"+type+"_"+"']").hide();
+    d = platform+'_'+type+'_'+value;
+    d = d.replace(/ /g, '');
+    id = d.replace( /(:|\.|\[|\])/g, "\\$1" );
+    $('#'+id).show();
+}
+function render_cloud(platform, node){
+    elm = document.getElementById(platform+'_select');
+    //newElement = document.createElement('p');
+    //elm.appendChild(newElement); 
+    global_list[platform]={};
+    if('openstack:sliver' in node){
+        selectFlavor = document.createElement('select');
+        selectFlavor.id = platform+"_selectFlavor";
+        selectFlavor.name = platform+"_selectFlavor";
+        selectFlavor.onchange = function(){
+            /* 1) Display corresponding Flavor div - hide others - reset selectImage value */
+            $("#"+platform+"_selectImage option[value=0]").prop('selected', true);
+            toogle_div(platform, this.value, 'flavor');
+            /* 3) Disable Images, Enable only compatible ones in selectImage */
+            $("[id^='"+platform+"_image_"+"']").hide();
+            $("#"+platform+"_selectImage option").attr("disabled",true);
+            $.each(global_list[platform][this.value], function (i,v){
+                $("#"+platform+"_selectImage option[value='" + v + "']").attr("disabled",false);
+            });
+            $("#"+platform+"_selectImage").attr("disabled",false);
+        }
+        var option = document.createElement("option");
+        option.text = "-- select a flavor --";
+        option.value = 0;
+        selectFlavor.appendChild(option);
+        jQuery.each( node['openstack:sliver'], function( i, sliver ) {
+            if('openstack:flavor' in sliver){
+                f = render_option(sliver['openstack:flavor']);
+                selectFlavor.appendChild(f);
+                flavor = sliver["openstack:flavor"];
+                /* 1) create hidden div to explain caracteristics of the flavor */
+                render_description(platform, flavor, 'flavor');
+                flavor_name = flavor['@name'];
+                global_list[platform][flavor_name]=[];
+                if ("openstack:image" in flavor){
+                    selectImage = document.createElement('select');
+                    selectImage.id = platform+"_selectImage";
+                    selectImage.name = platform+"_selectImage";
+                    selectImage.onchange = function(){
+                        /* 2) display corresponding Image div - hide others */
+                        toogle_div(platform, this.value, 'image');
+                    }
+                    var option = document.createElement("option");
+                    option.text = "-- select an image --";
+                    option.value = 0;
+                    selectImage.appendChild(option);
+                    if(flavor["openstack:image"] instanceof Array){
+                        jQuery.each( flavor["openstack:image"], function( i, img ) {
+                           image = render_option(img);
+                           image.disabled = true;
+                           selectImage.appendChild(image);
+                           /* 2) create hidden div to explain caracteristics of the image */
+                           render_description(platform, img, 'image');
+                           global_list[platform][flavor_name].push(img['@name']);
+                        });
+                    }else{
+                        image = render_option(flavor["openstack:image"]);
+                        image.disabled = true;
+                        selectImage.appendChild(image);
+                        /* 2) create hidden div to explain caracteristics of the image */
+                        render_description(platform, flavor["openstack:image"], 'image');
+                        global_list[platform][flavor_name].push(flavor['openstack:image']['@name']);
+                    }
+                }
+
+            }
+        });
+        elm.appendChild(selectFlavor); 
+        elm.appendChild(selectImage); 
+    }
+    $("#"+platform+"_selectFlavor").css("width","100px");
+    $("#"+platform+"_selectFlavor").css("height","30px");
+    $("#"+platform+"_selectImage").css("width","30px");
+    $("#"+platform+"_selectImage").css("height","30px");
+}
+function is_finished(len_platforms, pf_status){
+    if(len_platforms == pf_status){
+        return true;
+    }else{
+        return false;
+    }
+}
+function sliver_name_exists(sliver_name){
+    if(sliver_name in added_nodes){
+        return true;
+    }
+    /*
+    if (sliver_name in deleted_nodes){
+        return true;
+    }
+    */
+    return false;
+}
+function find_sliver_name(sliver_name, num){
+    for (j=Object.keys(added_nodes).length; j<Object.keys(added_nodes).length+num; i++){
+        vm_name = sliver_name+'_'+j;
+        if(!sliver_name_exists(vm_name)){
+            return vm_name
+        }
+    }
+}
+
+function send_add(platform){ 
+    $('#'+platform+'_pending_add').show();
+    sliver_name = $('#'+platform+'_sliver_name').val();
+    sliver_name = sliver_name.replace(' ','_');
+    flavor_name = $('#'+platform+'_selectFlavor').val();
+    image_name = $('#'+platform+'_selectImage').val();
+
+    num = $('#'+platform+'_number').val();
+    if (num > 1){
+        for (i = 0; i < num; i++){
+            // XXX Check if the name already exist in existing VMs and added_nodes
+            vm_name = sliver_name+"_"+i;
+            if (sliver_name_exists(vm_name)){
+                vm_name = find_sliver_name(sliver_name, num);
+            }
+            node = get_node(vm_name, flavor_name, image_name);
+            request_rspec['rspec']['node'].push(node);
+            render_node(platform, node, 'pending_add');
+            added_nodes[vm_name]=node;
+        }
+    }else{
+        if (sliver_name_exists(sliver_name)){
+            sliver_name = find_sliver_name(sliver_name, num);
+        }
+        node = get_node(sliver_name, flavor_name, image_name);
+        request_rspec['rspec']['node'].push(node);
+        render_node(platform, node, 'pending_add');
+        added_nodes[sliver_name]=node;
+    }
+    toogle_div(platform, flavor_name, 'flavor');
+    toogle_div(platform, image_name, 'image');
+    flavor_name = $('#'+platform+'_selectFlavor').val(0);
+    image_name = $('#'+platform+'_selectImage').val(0);
+    $('#'+platform+'_add').hide();
+    $('#'+platform+'_add_button').hide();
+    $('#'+platform+'_div_pending').show();
+    console.log(request_rspec);
+    //jQuery('#'+platform+'_form_delete').submit();
+}
+function get_node(vm_name, flavor_name, image_name){
+        var node = {};
+        if(ad_rspec['rspec']['node'] instanceof Array) {
+            // Deep copy of the Array to avoid reference
+            node = jQuery.extend(true, {}, ad_rspec['rspec']['node'][0]);
+        }else{
+            // Deep copy of the Array to avoid reference
+            node = jQuery.extend(true, {}, ad_rspec['rspec']['node']);
+        }
+        if(node['openstack:sliver'] instanceof Array) {
+            node['openstack:sliver'] = node['openstack:sliver'][0];
+        }
+        node['openstack:sliver']['@sliver_name'] = vm_name;
+        console.log(vm_name);
+
+        if(node['openstack:sliver']['openstack:flavor'] instanceof Array) {
+            node['openstack:sliver']['openstack:flavor'] = node['openstack:sliver']['openstack:flavor'][0];
+        }
+        node['openstack:sliver']['openstack:flavor'] = Array();
+        node['openstack:sliver']['openstack:flavor'] = {'@name':flavor_name,'openstack:image':{'@name':image_name}};
+
+        $.each(node['openstack:sliver']['openstack:security_group'], function(i, group){
+            if(group['@name']=='default'){
+                node['openstack:sliver']['openstack:security_group'] = group;
+                return false;
+            }
+        });
+        return node;
+
+}
+function send_delete(platform, sliver_name){
+    $('#'+platform+'_pending_delete').show();
+    jQuery('#'+platform+'_vm').val(sliver_name);
+    jQuery('#'+platform+'_existing_'+sliver_name).hide();
+    node = remove_node_from_request_rspec(sliver_name);
+    deleted_nodes[sliver_name]=node;
+    render_node(platform, node, 'pending_delete');
+    $('#'+platform+'_div_pending').show();
+    console.log(request_rspec);
+}
+function remove_node_from_request_rspec(sliver_name){
+    var save_node = Array();
+    jQuery.each( request_rspec['rspec']['node'], function( i, node ) {
+        if(node['openstack:sliver']['@sliver_name']==sliver_name){
+            request_rspec['rspec']['node'].splice(i,1);
+            save_node = node;
+            return false;
+        }
+    });
+    return save_node;
+}
+function cancel_add(platform,sliver_name){
+    // remove the canceled node from the pending list
+    $('#'+platform+'_pending_add_'+sliver_name).remove();
+    // remove the canceled node from the request rspec
+    node = remove_node_from_request_rspec(sliver_name);
+    delete added_nodes[sliver_name];
+    // hide the pending div if there are no more nodes
+    if ($('#'+platform+'_pending_add div').length==0){
+        $('#'+platform+'_pending_add').hide();
+        if ($('#'+platform+'_pending_delete div').length==0){
+            $('#'+platform+'_div_pending').hide();
+        }
+    }
+}
+function cancel_delete(platform,sliver_name){
+    // display the canceled node back in existing nodes
+    $('#'+platform+'_existing_'+sliver_name).show();
+    // remove the canceled node from the pending list
+    $('#'+platform+'_pending_delete_'+sliver_name).remove();
+    // Cancel the deletion of an existing node -> push it back to the request rspec
+    request_rspec['rspec']['node'].push(deleted_nodes[sliver_name]);
+    delete deleted_nodes[sliver_name];
+    // hide the pending div if there are no more nodes
+    if ($('#'+platform+'_pending_delete div').length==0){
+        $('#'+platform+'_pending_delete').hide();
+        if ($('#'+platform+'_pending_add div').length==0){
+            $('#'+platform+'_div_pending').hide();
+        }
+    }
+}
+
+function public_ip(platform,sliver_name,is_public){
+    // XXX Change ip status in request_rspec
+    if(is_public){
+        // external_ip="true"
+        node = remove_node_from_request_rspec(sliver_name);
+        node['external_ip']="true";
+        added_nodes[sliver_name]['external_ip']="true";
+        request_rspec['rspec']['node'].push(node);        
+    }else{
+        console.log(sliver_name+' NOT public ');
+        // external_ip="false"
+        node = remove_node_from_request_rspec(sliver_name);
+        node['external_ip']="false";
+        added_nodes[sliver_name]['external_ip']="false";
+        request_rspec['rspec']['node'].push(node);        
+    }
+}
+
+function render_node(platform, node, state){
+    if('openstack:sliver' in node){
+        sliver = node['openstack:sliver']
+        var d = platform+'_'+state+'_'+sliver['@sliver_name'];
+        d = d.replace(/ /g, '');
+        id = d.replace( /(:|\.|\[|\])/g, "\\$1" );
+        $("#"+platform+"_"+state).append("<div id='"+platform+'_'+state+'_'+sliver['@sliver_name']+"' class='row'></div>");
+        //$("#"+id).append("<input type='hidden' name='"+platform+"_"+sliver['@sliver_name']+"' value='"+sliver['@sliver_name']+"'>");
+        if(state=='pending_add'){
+            $("#"+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>");
+        }else{
+            $("#"+id).append("<div class='col-md-1' style='margin-left:0px;!important;'>&nbsp;</div>");
+        }
+        $("#"+id).append("<div class='col-md-2' style='padding-left:0px;'>"+sliver['@sliver_name']+"</div>");
+        $("#"+id).append("<div class='col-md-3'>"+sliver['openstack:flavor']['@name']+"</div>");
+        $("#"+id).append("<div class='col-md-3'>"+sliver['openstack:flavor']['openstack:image']['@name']+"</div>");
+
+        if(state=='existing'){
+            $("#"+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>");
+        }else if(state=='pending_add'){
+            $("#"+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>");
+        }else if (state=='pending_delete'){
+            $("#"+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>");
+        }else{
+            console.log("state: "+state+" not impemented");
+        }
+        /*
+        sliver['openstack:address']
+        sliver['openstack:flavor']
+        */
+    }
+}
+
+function allocate(platform, slicename){
+    $("#"+platform+"_main").addClass("disabled");
+    $("#"+platform+"_wait").show();
+    $("#"+platform+"_wait").spin();
+    $.post("/sfa/Allocate",{'hrn':slicename, 'type':'slice', 'platform':[platform], 'rspec':JSON.stringify(request_rspec)}, function( result ) {
+        console.log(result);
+        clear_data(platform);
+        load_data(platform,slicename);
+        $("#"+platform+"_main").removeClass("disabled");
+        $("#"+platform+"_wait").hide();
+        mysliceAlert('Success: changes applied','success', true);
+    });
+}
+function clear_data(platform){
+    $('#'+platform+'_existing').children().remove();
+    $('#'+platform+'_pending_delete').children().remove();
+    $("#"+platform+"_selectFlavor").remove();
+    $("#"+platform+"_selectImage").remove();
+    $('#'+platform+'_pending_add').children().remove();
+    $('#'+platform+'_div_pending').hide();
+}
+function load_data(platform, slicename){
+    var platform_status = Array();
+    var platform_empty = Array();
+
+    $.post("/sfa/Describe",{'hrn':slicename, 'type':'slice', 'platform':[platform]}, function( d ) {
+        data = d;
+        console.log(data);
+        if('parsed' in data[platform] && 'rspec' in data[platform]['parsed']){
+           manifest_rspec = data[platform]['parsed']['rspec'];
+           request_rspec = data[platform]['parsed'];
+           request_rspec['rspec']['@type']='request';
+           if('node' in manifest_rspec){
+               if(manifest_rspec['node'] instanceof Array) {
+                   jQuery.each( manifest_rspec['node'], function( i, node ) {
+                       render_node(platform,node, 'existing');
+                   });
+               }else{
+                   render_node(platform,manifest_rspec['node'], 'existing');
+               }
+               $('#'+platform+'_existing').show();
+           }
+        }
+    });
+    $.post("/sfa/ListResources",{'platform':[platform]}, function( d ) {
+        //$.extend(data,d);
+        //console.log(data);
+        if('parsed' in d[platform] && 'rspec' in d[platform]['parsed']){
+           ad_rspec = d[platform]['parsed'];
+           if('node' in ad_rspec['rspec']){
+               if(ad_rspec['rspec']['node'] instanceof Array) {
+                   jQuery.each( ad_rspec['rspec']['node'], function( i, node ) {
+                       render_cloud(platform,node);
+                   });
+               }else{
+                   render_cloud(platform,ad_rspec['rspec']['node']);
+               }
+           }else{
+               platform_empty.push(platform);
+           }
+        }else{
+            platform_empty.push(platform);
+        }
+        platform_status.push(platform);
+        if(is_finished(len_platforms,platform_status.length)){
+            $("#loading").hide();
+            if(platform_empty.length == len_platforms){
+                $("#warning_message").show();
+            }
+        }
+    });
+}
+$(document).ready(function() {
+{% for platform in platforms %}
+    {% if platform in cloud_platforms %}
+    load_data('{{platform}}', '{{slicename}}');
+    {% endif %}
+{% endfor %}
+});
+</script>
+{% endblock %}
+
+{% block content %}
+{{post_values}}
+{% for platform in platforms %}
+    {% if platform in cloud_platforms %}
+    <div id="{{platform}}_wait" style="display:none;margin-top:20px;position:absolute;margin-left:300px;"></div>
+    <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;">
+        <h2>{{ platform }}</h2>
+        // display only if VMs already in slice
+        <h4>VMs in slice {{slicename}}</h4>
+        <form id="{{platform}}_form_delete" method="post">
+        {% csrf_token %}
+        <div id="{{platform}}_existing" class="row alert alert-info" style="display:none;">
+        <input type="hidden" name="{{platform}}_vm" id="{{platform}}_vm">
+        <input type="hidden" name="action" id="action" value="delete">
+        <input type="hidden" name="platform" id="platform" value="{{platform}}">
+        </div> 
+        </form>
+        <h4><a href="#" onclick="$('#{{platform}}_div_add').toggle();"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Create new VMs</a></h4>
+        <div id="{{platform}}_div_add" class="alert" style="background-color:#f1f1f1;display:none;margin-bottom:0px !important;padding-bottom:6px !important;">
+        <form id="{{platform}}_form_add" method="post" class="form-inline">
+        {% csrf_token %}
+        <div id="{{platform}}_input" class="row">
+            <div class="col-md-1"></div>
+            <div class="col-md-3"><label for="{{platform}}_number">number:</label>
+            <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>
+            </div>
+            <div class="col-md-8"><label for="{{platform}}_sliver_name">name:</label>
+            <div class="form-group">
+            <input type="text" name="{{platform}}_sliver_name" id="{{platform}}_sliver_name" value="VM" maxlength="12" class="form-control" style="height:30px;" required>
+            </div>
+            </div>
+            <div class="col-md-1"></div>
+            <div id="{{platform}}_select" class="col-md-11"></div>
+        </div>
+        <div id="{{platform}}_add" class="row">
+            <div class="col-md-1">&nbsp; 
+            <input type="hidden" name="action" id="action" value="add">
+            <input type="hidden" name="platform" id="platform" value="{{platform}}">
+            </div>
+        </div>
+        <div class="row" style="padding-top:6px;">
+            <div class="col-md-1">&nbsp;</div>
+            <div class="col-md-11" id="{{platform}}_add_button" style="display:none;">
+            <input type="submit" form="{{platform}}_form_add" value="Add" onclick="send_add('{{platform}}');">
+            </div>
+        </div>
+        </form>
+        </div>
+        <br>
+        <div id="{{platform}}_div_pending" style="display:none;">
+        // display only pending changes
+        <h4>Pending changes</h4>
+        <form id="{{platform}}_form_reserve" method="post">
+        {% csrf_token %}
+        <div id="{{platform}}_pending_add" class="row alert alert-success" style="display:none;margin-bottom:3px !important;"></div> 
+        <div id="{{platform}}_pending_delete" class="row alert alert-danger" style="display:none;margin-bottom:3px !important;"></div> 
+        <br>
+        <input type="hidden" name="action" id="action" value="reserve">
+        <input type="hidden" name="platform" id="platform" value="{{platform}}">
+        <input type="submit" form="{{platform}}_form_reserve" value="Apply changes" onclick="allocate('{{platform}}','{{slicename}}');">
+        </form>
+        </div>
+    </div>
+    {% endif %}
+{% endfor %}
+{% endblock %}
index 7dfddc9..96e9fc8 100644 (file)
@@ -79,7 +79,9 @@
 
 </script>
 
+<!--
 <div>
     <button id="renewslices" type="button" class="btn btn-primary"><span class="glyphicon glyphicon-refresh"></span> Renew slice</button>
     <button id="deleteslices" type="button" class="btn btn-danger"><span class="glyphicon glyphicon-remove"></span> Delete slice</button>
-</div>
+</div> 
+-->
index 07a4597..fbbd2b1 100644 (file)
                                <input type="text" class="form-control" id="authority_hrn" name="org_name" style="width:100%" placeholder="Project" 
                                        title="Select a project under which you want to create your slice" required="required">
                          </div>
+              <!--  
                          <div class="form-group">
                              <label>
                                  Provide an URL for your experiment (not required)
                              </label>
-                             <input type="text" class="form-control" name="url" id="url" style="width:100%" placeholder="Experiment URL (if one exists)"
+                              <input type="text" class="form-control" name="url" id="url" style="width:100%" placeholder="Experiment URL (if one exists)"
                                        title="Please provide the url of your experiment" value="{{url}}">
                          </div>
                          <div class="form-group">
@@ -57,7 +58,7 @@
                    </label>
                                  <textarea id="purpose" name="purpose" class="form-control" rows="6" placeholder="Experiment description" style="width:100%" 
                                        title="Description of your experiment" required="required">{{ purpose }}</textarea>
-                         </div>
+                         </div> -->
                          <button type="submit" id=submit_pi class="btn btn-onelab"><span class="glyphicon glyphicon-plus"></span> Create slice</button>
                        </form>
        
index cc9f9a1..b35fa35 100644 (file)
@@ -2,36 +2,44 @@ import os
 import json
 import ConfigParser 
 import datetime
-from time                       import mktime
+from time                           import mktime
+import time
 import xmltodict
 
-from django.shortcuts           import render_to_response
-from django.http                import HttpResponse
+from django.shortcuts               import render_to_response
+from django.http                    import HttpResponse,QueryDict
 
-from sfa.trust.certificate      import Keypair, Certificate
-from sfa.client.sfaserverproxy  import SfaServerProxy
-from sfa.client.return_value    import ReturnValue
-from sfa.util.xrn               import Xrn, get_leaf, get_authority, hrn_to_urn, urn_to_hrn
+from sfa.trust.certificate          import Keypair, Certificate
+from sfa.client.sfaserverproxy      import SfaServerProxy
+from manifold.gateways.sfa.proxy    import SFAProxy
+from sfa.client.return_value        import ReturnValue
+from sfa.util.xrn                   import Xrn, get_leaf, get_authority, hrn_to_urn, urn_to_hrn
 
-from manifold.core.query        import Query
+from manifold.core.query            import Query
 
-from manifoldapi.manifoldapi    import execute_admin_query
+from manifoldapi.manifoldapi        import execute_admin_query
 
-from unfold.loginrequired       import LoginRequiredView
+from unfold.loginrequired           import LoginRequiredView
 
-from myslice.settings           import logger, config
+from myslice.settings               import logger, config
 
-from rest.json_encoder          import MyEncoder
+from repoze.lru                     import lru_cache
+from rest.json_encoder              import MyEncoder
+
+import uuid
+def unique_call_id(): return uuid.uuid4().urn
 
 def dispatch(request, method):
 
-    hrn = ''
-    urn = ''
-    object_type = ''
-    rspec = ''
+    hrn = None
+    urn = None
+    object_type = None
+    rspec = None
+    output_format = None
     recursive = False
-    options   = dict()
-    platforms = list()
+    # Have to be hashable for lru_cache
+    options   = frozenset() # dict()
+    platforms = frozenset() # list()
 
     results = dict()
     display = None
@@ -41,28 +49,45 @@ def dispatch(request, method):
     elif request.method == 'GET':
         req_items = request.GET
 
-    for el in req_items.items():
-        if el[0].startswith('rspec'):
-            rspec += el[1]
-        elif el[0].startswith('platform'):
-            platforms = req_items.getlist('platform[]')
-        #elif el[0].startswith('options'):
-        #    options += req_items.getlist('options[]')
-        elif el[0].startswith('hrn'):
-            hrn = el[1]
-        elif el[0].startswith('urn'):
-            urn = el[1]
-        elif el[0].startswith('type'):
-            object_type = el[1]
-        elif el[0].startswith('recursive'):
-            if el[1] == '1':
+    logger.debug("dispatch got = %s" % req_items.dict())
+    #t = dict(req_items.iterlists())
+    #rspec = req_items.getlist('rspec')
+    #logger.debug("dispatch got = %s" % t)
+
+    platforms = req_items.getlist('platform[]')
+    for k in req_items.dict():
+        logger.debug("key = %s - value = %s" % (k,req_items.get(k)))
+        if k == 'rspec':
+            rspec = req_items.get(k)
+        if k == 'options':
+            options = req_items.get(k)
+        if k == 'output_format':
+            output_format = req_items.get(k)
+        if k == 'hrn':
+            hrn = req_items.get(k)
+        if k == 'urn':
+            urn = req_items.get(k)
+        if k == 'type':
+            object_type = req_items.get(k)
+        if k == 'recursive':
+            if v == '1':
                 recursive = True
             else:
                 recursive = False
-        elif el[0].startswith('display'):
-            display = el[1]
+        if k == 'display':
+            display = req_items.get(k)
 
-    results = sfa_client(request, method, hrn=hrn, urn=urn, object_type=object_type, recursive=recursive, options=options, platforms=platforms)
+    if rspec is not None:
+        try:
+            rspec = json.loads(rspec)
+        except Exception,e:
+            logger.debug("rspec type = %s" % type(rspec))
+        if type(rspec) is dict:
+            rspec = xmltodict.unparse(rspec)
+
+    start_time = time.time()
+    results = sfa_client(request, method, hrn=hrn, urn=urn, object_type=object_type, rspec=rspec, recursive=recursive, options=options, platforms=platforms, output_format=output_format, admin=False)
+    logger.debug("EXEC TIME - sfa_client() - %s sec." % (time.time() - start_time))
     if display == 'table':
         return render_to_response('table-default.html', {'data' : data, 'fields' : columns, 'id' : '@component_id', 'options' : None})
     else:
@@ -90,7 +115,8 @@ def get_user_account(request, user_email, platform_name):
 
     return accounts[0]
 
-def sfa_client(request, method, hrn=None, urn=None, object_type=None, rspec=None, recursive=None, options=None, platforms=None, admin=False):
+#@lru_cache(100)
+def sfa_client(request, method, hrn=None, urn=None, object_type=None, rspec=None, recursive=False, options=None, platforms=None, output_format=None, admin=False):
 
     Config = ConfigParser.ConfigParser()
     monitor_file = os.path.abspath(os.path.dirname(__file__) + '/../myslice/monitor.ini')
@@ -112,6 +138,8 @@ def sfa_client(request, method, hrn=None, urn=None, object_type=None, rspec=None
         object_type = ''
     if rspec is None:
         rspec = ''
+    else:
+        logger.debug("RSPEC = %s" % rspec)
     if recursive is None:
         recursive = False
     if options is None:
@@ -139,22 +167,21 @@ def sfa_client(request, method, hrn=None, urn=None, object_type=None, rspec=None
     data = []
     columns = []
     api_options = {}
-    api_options['geni_rspec_version'] = {'type': 'GENI', 'version': '3'}
     api_options['list_leases'] = 'all'
     server_am = False
     for pf in platforms:
         platform = get_platform_config(request, pf)
-        logger.debug("platform={}".format(platform))
+        if 'rspec_type' in platform and 'rspec_version' in platform:
+            api_options['geni_rspec_version'] = {'type': platform['rspec_type'],'version': platform['rspec_version']}
+        else:
+            api_options['geni_rspec_version'] = {'type': 'GENI', 'version': '3'}
         if 'sm' in platform and len(platform['sm']) > 0:
-            logger.debug('sm')
             server_am = True
             server_url = platform['sm']
         if 'rm' in platform and len(platform['rm']) > 0:
-            logger.debug('rm')
             server_am = False
             server_url = platform['rm']
         if 'registry' in platform and len(platform['registry']) > 0:
-            logger.debug('registry')
             server_am = False
             server_url = platform['registry']
     
@@ -177,13 +204,31 @@ def sfa_client(request, method, hrn=None, urn=None, object_type=None, rspec=None
              return {'error' : '-2'}
  
         server = SfaServerProxy(server_url, pkey, cert)
+        #server = SFAProxy(server_url, pkey, cert)
+        if 'geni_rspec_version' in options:
+            # GetVersion to know if the AM supports the requested version
+            # if not ask for the default GENI v3
+            start_time = time.time()
+            result = server.GetVersion()
+            logger.debug("EXEC TIME - GetVersion() - %s sec." % (time.time() - start_time))
+            if 'geni_ad_rspec_versions' in result['value']:
+                for v in result['value']['geni_ad_rspec_versions']:
+                    if v['type'] == options['geni_rspec_version']:
+                        api_options['geni_rspec_version'] = {'type': options['geni_rspec_version']}
+                        break
+                    else:
+                        api_options['geni_rspec_version'] = {'type': 'GENI', 'version': '3'}
+        else:
+            api_options['geni_rspec_version'] = {'type': 'GENI', 'version': '3'}
 
         try:
             # Get user config from Manifold
             user_config = get_user_config(request, user_email, pf)
             if 'delegated_user_credential' in user_config:
+                logger.debug('delegated_user_credential')
                 user_cred = user_config['delegated_user_credential']
             elif 'user_credential' in user_config:
+                logger.debug('user_credential')
                 user_cred = user_config['user_credential']
             else:
                 logger.error("no user credentials for user = ", user_email)
@@ -191,10 +236,12 @@ def sfa_client(request, method, hrn=None, urn=None, object_type=None, rspec=None
 
             if object_type:
                 if 'delegated_%s_credentials'%object_type in user_config:
+                    logger.debug('delegated_%s_credentials'%object_type)
                     for obj_name, cred in user_config['delegated_%s_credentials'%object_type].items():
                         if obj_name == hrn:
                             object_cred = cred
                 elif '%s_credentials'%object_type in user_config:
+                    logger.debug('%s_credentials'%object_type)
                     for obj_name, cred in user_config['%s_credentials'%object_type].items():
                         if obj_name == hrn:
                             object_cred = cred
@@ -206,13 +253,19 @@ def sfa_client(request, method, hrn=None, urn=None, object_type=None, rspec=None
 
             # Both AM & Registry
             if method == "GetVersion": 
+                start_time = time.time()
                 result = server.GetVersion()
+                logger.debug("EXEC TIME - GetVersion() - %s sec." % (time.time() - start_time))
             else:
                 # AM API Calls
                 if server_am:
                     if method == "ListResources":
+                        logger.debug(api_options)
+                        #logger.debug(user_cred)
+                        start_time = time.time()
                         result = server.ListResources([user_cred], api_options)
-                        logger.debug(result)
+                        logger.debug("EXEC TIME - ListResources() - %s sec." % (time.time() - start_time))
+                        #logger.debug(result)
                         dict_result = xmltodict.parse(result['value'])
                         result['parsed'] = dict_result
                         if isinstance(dict_result['rspec']['node'], list):
@@ -221,7 +274,9 @@ def sfa_client(request, method, hrn=None, urn=None, object_type=None, rspec=None
                             columns.extend(dict_result['rspec']['node'].keys())
 
                     elif method == "Describe":
+                        start_time = time.time()
                         version = server.GetVersion()
+                        logger.debug("EXEC TIME - GetVersion() - %s sec." % (time.time() - start_time))
                         logger.debug(version['geni_api'])
                         # if GetVersion = v2
                         if version['geni_api'] == 2:
@@ -253,24 +308,26 @@ def sfa_client(request, method, hrn=None, urn=None, object_type=None, rspec=None
                     elif method == 'Allocate':
                         api_options['call_id']    = unique_call_id()
                         # List of users comes from the Registry
-                        api_options['sfa_users']  = sfa_users
-                        api_options['geni_users'] = geni_users
+                        users = get_users_in_slice(request, hrn)
+                        api_options['sfa_users']  = users
+                        api_options['geni_users'] = users
                         # if GetVersion = v2
                         version = server.GetVersion()
                         if version['geni_api'] == 2:
                             result = server.CreateSliver([urn] ,[object_cred], rspec, api_options)
                         # else GetVersion = v3
                         else:
-                            result = server.Allocate([urn] ,[object_cred], rspec, api_options)
+                            result = server.Allocate(urn ,[object_cred], rspec, api_options)
                     elif method == 'Provision':
                         # if GetVersion = v2
                         # Nothing it is not supported by v2 AMs
                         version = server.GetVersion()
+                        # List of users comes from the Registry
+                        users = get_users_in_slice(request, hrn)
+                        api_options['sfa_users']  = users
+                        api_options['geni_users'] = users
                         if version['geni_api'] == 3:
                             api_options['call_id']    = unique_call_id()
-                            # List of users comes from the Registry
-                            api_options['sfa_users']  = sfa_users
-                            api_options['geni_users'] = geni_users
                             result = server.Provision([urn] ,[object_cred], api_options)
                     elif method == 'Status':
                         result = server.Status([urn] ,[object_cred], api_options)
@@ -321,6 +378,14 @@ def sfa_client(request, method, hrn=None, urn=None, object_type=None, rspec=None
                         #return HttpResponse(json.dumps({'error' : '-3','msg':'method not supported by Registry'}), content_type="application/json")
                         logger.debug('method %s not handled by Registry' % method)
                         result = []
+            if output_format is not None:
+                logger.debug("result = " % result)
+                if 'value' in result:
+                    # TODO Python Caching 
+                    # to avoid translating the same RSpec in the same format several times
+                    start_time = time.time()
+                    result = translate(result['value'],output_format)
+                    logger.debug("EXEC TIME - translate() - %s sec." % (time.time() - start_time))
 
             results[pf] = result
             if dict_result:
@@ -333,11 +398,47 @@ def sfa_client(request, method, hrn=None, urn=None, object_type=None, rspec=None
             import traceback
             logger.error(traceback.format_exc())
             logger.error(e)
-            results[pf] = {'error':'-3', 'error_msg': str(e)}
+            results[pf] = {'error':'-3', 'result':result,'error_msg': str(e)}
 
     results['columns'] = columns
     return results
 
+@lru_cache(100)
+def translate(rspec, output_format):
+    import urllib
+    import urllib2
+
+    values = {'content' : rspec}
+    url = 'https://demo.fiteagle.org/omnweb/convert/to/' + output_format
+    data = urllib.urlencode(values)
+    req = urllib2.Request(url, data)
+    response = urllib2.urlopen(req)
+    return response.read()
+
+def rename(self,key,new_key):
+    ind = self._keys.index(key)  #get the index of old key, O(N) operation
+    self._keys[ind] = new_key    #replace old key with new key in self._keys
+    self[new_key] = self[key]    #add the new key, this is added at the end of self._keys
+    self._keys.pop(-1)           #pop the last item in self._keys
+
+def get_users_in_slice(request, slice_hrn):
+    # select users.user_hrn, users.user_email, users.keys  
+    # from myslice:slice 
+    # where slice_hrn=='onelab.upmc.r2d2.slice1'
+    users_query  = Query().get('myslice:slice').filter_by('slice_hrn', '==', slice_hrn).select('users.user_hrn', 'users.user_urn', 'users.user_email','users.keys')
+    users = execute_admin_query(request, users_query)
+    rmap = {'user_urn':'urn','user_email':'email','user_hrn':'hrn'}
+    res = list()
+    for u in users[0]['users']:
+        r_user = dict()
+        for k,v in u.items():
+            if k in rmap.keys():
+                r_user[rmap[k]] = v
+            else:
+                r_user[k]=v
+        res.append(r_user)
+    return res
+
 def get_user_config(request, user_email, platform_name):
     account = get_user_account(request, user_email, platform_name)
     return json.loads(account['config']) if account['config'] else {}