much faster means of computing matrices
[plstackapi.git] / planetstack / core / plus / views.py
index 37165c0..7a11315 100644 (file)
@@ -13,10 +13,12 @@ from hpc.models import ContentProvider
 from operator import attrgetter
 from django import template
 from django.views.decorators.csrf import csrf_exempt
-from django.http import HttpResponse, HttpResponseServerError
+from django.http import HttpResponse, HttpResponseServerError, HttpResponseForbidden
 from django.core import urlresolvers
 from django.contrib.gis.geoip import GeoIP
+from django.db.models import Q
 from ipware.ip import get_ip
+from operator import itemgetter, attrgetter
 import traceback
 import socket
 
@@ -34,15 +36,10 @@ class DashboardWelcomeView(TemplateView):
 
     def get(self, request, *args, **kwargs):
         context = self.get_context_data(**kwargs)
-        userDetails = getUserSliceInfo(request.user)
-        #context['site'] = userDetails['site']
-
-        context['userSliceInfo'] = userDetails['userSliceInfo']
-        context['cdnData'] = userDetails['cdnData']
-        context['cdnContentProviders'] = userDetails['cdnContentProviders']
+        context = getDashboardContext(request.user, context)
         return self.render_to_response(context=context)
 
-class DashboardView(TemplateView):
+class DashboardDynamicView(TemplateView):
     head_template = r"""{% extends "admin/dashboard/dashboard_base.html" %}
        {% load admin_static %}
        {% block content %}
@@ -50,17 +47,57 @@ class DashboardView(TemplateView):
 
     tail_template = r"{% endblock %}"
 
-    def get(self, request, name="hpc_historical", *args, **kwargs):
+    def get(self, request, name="root", *args, **kwargs):
         context = self.get_context_data(**kwargs)
+        context = getDashboardContext(request.user, context)
+
+        if name=="root":
+            return self.multiDashboardView(request, context)
+        else:
+            return self.singleDashboardView(request, name, context)
+
+    def readDashboard(self, fn):
+        try:
+            template= open("/opt/planetstack/templates/admin/dashboard/%s.html" % fn, "r").read()
+            if (fn=="tenant"):
+                # fix for tenant view - it writes html to a div called tabs-5
+                template = '<div id="tabs-5"></div>' + template
+            return template
+        except:
+            return "failed to open %s" % fn
+
+    def multiDashboardView(self, request, context):
+        head_template = self.head_template
+        tail_template = self.tail_template
+
+        body = """
+         <div id="hometabs" >
+         <ul id="suit_form_tabs" class="nav nav-tabs nav-tabs-suit" data-tab-prefix="suit-tab">
+        """
+
+        dashboards = request.user.get_dashboards()
+
+        # customize is a special dashboard they always get
+        customize = DashboardView.objects.filter(name="Customize")
+        if customize:
+            dashboards.append(customize[0])
+
+        for i,view in enumerate(dashboards):
+            body = body + '<li><a href="#dashtab-%d">%s</a></li>\n' % (i, view.name)
+
+        body = body + "</ul>\n"
 
-        t = template.Template(self.head_template + open("/opt/planetstack/templates/admin/dashboard/%s.html" % name, "r").read() + self.tail_template)
+        for i,view in enumerate(dashboards):
+            url = view.url
+            body = body + '<div id="dashtab-%d">\n' % i
+            if url.startswith("template:"):
+                fn = url[9:]
+                body = body + self.readDashboard(fn)
+            body = body + '</div>\n'
 
-        userDetails = getUserSliceInfo(request.user)
-        #context['site'] = userDetails['site']
+        body=body+"</div>\n"
 
-        context['userSliceInfo'] = userDetails['userSliceInfo']
-        context['cdnData'] = userDetails['cdnData']
-        context['cdnContentProviders'] = userDetails['cdnContentProviders']
+        t = template.Template(head_template + body + self.tail_template)
 
         response_kwargs = {}
         response_kwargs.setdefault('content_type', self.content_type)
@@ -70,43 +107,99 @@ class DashboardView(TemplateView):
             context = context,\r
             **response_kwargs)
 
-def getUserSliceInfo(user, tableFormat = False):
-        userDetails = {}
+    def singleDashboardView(self, request, name, context):
+        head_template = self.head_template
+        tail_template = self.tail_template
+
+        t = template.Template(head_template + self.readDashboard(name) + self.tail_template)
+
+        response_kwargs = {}
+        response_kwargs.setdefault('content_type', self.content_type)
+        return self.response_class(\r
+            request = request,\r
+            template = t,\r
+            context = context,\r
+            **response_kwargs)
+
+def getDashboardContext(user, context={}, tableFormat = False):
+        context = {}
 
         userSliceData = getSliceInfo(user)
         if (tableFormat):
-#            pprint("*******      GET USER SLICE INFO")
-            userDetails['userSliceInfo'] = userSliceTableFormatter(userSliceData)
+            context['userSliceInfo'] = userSliceTableFormatter(userSliceData)
         else:
-            userDetails['userSliceInfo'] = userSliceData
-        userDetails['cdnData'] = getCDNOperatorData(wait=False)
-        userDetails['cdnContentProviders'] = getCDNContentProviderData()
-        return userDetails
+            context['userSliceInfo'] = userSliceData
+        context['cdnData'] = getCDNOperatorData(wait=False)
+        context['cdnContentProviders'] = getCDNContentProviderData()
+
+        (dashboards, unusedDashboards)= getDashboards(user)
+        unusedDashboards=[x for x in unusedDashboards if x!="Customize"]
+        context['dashboards'] = dashboards
+        context['unusedDashboards'] = unusedDashboards
+
+        return context
+
+def getDashboards(user):
+    #dashboards = sorted(list(user.dashboardViews.all()), key=attrgetter('order'))
+    dashboards = user.get_dashboards()
+
+    dashboard_names = [d.name for d in dashboards]
+
+    unused_dashboard_names = []
+    for dashboardView in DashboardView.objects.all():
+        if not dashboardView.name in dashboard_names:
+            unused_dashboard_names.append(dashboardView.name)
+
+    return (dashboard_names, unused_dashboard_names)
 
 class TenantCreateSlice(View):
     def post(self, request, *args, **kwargs):
+        if request.user.isReadOnlyUser():
+            return HttpResponseForbidden("User is in read-only mode")
+
         sliceName = request.POST.get("sliceName", "0")
         serviceClass = request.POST.get("serviceClass", "0")
         imageName = request.POST.get("imageName", "0")
         actionToDo = request.POST.get("actionToDo", "0")
-        #network = request.POST.get("network","0")
+        networkPorts = request.POST.get("network","0")
         mountDataSets = request.POST.get("mountDataSets","0")
+        privateVolume = request.POST.get("privateVolume","0")
         if (actionToDo == "add"):
            serviceClass = ServiceClass.objects.get(name=serviceClass)
            site = request.user.site
            image = Image.objects.get(name=imageName)
            newSlice = Slice(name=sliceName,serviceClass=serviceClass,site=site,imagePreference=image,mountDataSets=mountDataSets)
            newSlice.save()
+          privateTemplate="Private"
+          publicTemplate="Public shared IPv4"\r
+          privateNetworkName = sliceName+"-"+privateTemplate\r
+          publicNetworkName = sliceName+"-"+publicTemplate\r
+          slice=Slice.objects.get(name=sliceName)\r
+          addNetwork(privateNetworkName,privateTemplate,slice)\r
+          addNetwork(publicNetworkName,publicTemplate,slice)\r
+          addOrModifyPorts(networkPorts,sliceName)\r
+          if privateVolume=="true":\r
+               privateVolForSlice(request.user,sliceName)
         return HttpResponse("Slice created")
 
+def privateVolForSlice(user,sliceName):
+       if not hasPrivateVolume(sliceName):\r
+          volumeName=createPrivateVolume(user,sliceName)\r
+          readWrite="true"\r
+          mountVolume(sliceName,volumeName,readWrite)
+
 class TenantUpdateSlice(View):
     def post(self, request, *args, **kwargs):\r
+        if request.user.isReadOnlyUser():\r
+            return HttpResponseForbidden("User is in read-only mode")\r
+\r
         sliceName = request.POST.get("sliceName", "0")\r
         serviceClass = request.POST.get("serviceClass", "0")\r
         imageName = request.POST.get("imageName", "0")\r
         actionToDo = request.POST.get("actionToDo", "0")\r
-        #network = request.POST.get("network","0")\r
+        networkPorts = request.POST.get("networkPorts","0")\r
         dataSet = request.POST.get("dataSet","0")\r
+        privateVolume = request.POST.get("privateVolume","0")\r
         slice = Slice.objects.all()\r
         for entry in slice:\r
                 serviceClass = ServiceClass.objects.get(name=serviceClass)\r
@@ -114,12 +207,39 @@ class TenantUpdateSlice(View):
                          if (actionToDo == "update"):\r
                                 setattr(entry,'serviceClass',serviceClass)\r
                                 setattr(entry,'imagePreference',imageName)\r
-                                #setattr(entry,'network',network)\r
                                 setattr(entry,'mountDataSets',dataSet)\r
                                 entry.save()\r
                                 break\r
+       addOrModifyPorts(networkPorts,sliceName)\r
+       if privateVolume=="true":\r
+                privateVolForSlice(request.user,sliceName)\r
         return HttpResponse("Slice updated")\r
 \r
+def addNetwork(name,template,sliceName):\r
+       networkTemplate=NetworkTemplate.objects.get(name=template)\r
+       newNetwork = Network(name = name,\r
+                              template = networkTemplate,\r
+                              owner = sliceName)\r
+        newNetwork.save()\r
+       addNetworkSlice(newNetwork,sliceName)\r
+\r
+def addNetworkSlice(networkSlice,sliceName):\r
+       newNetworkSlice=NetworkSlice(network =networkSlice,\r
+                                    slice=sliceName)\r
+       newNetworkSlice.save()\r
+\r
+def addOrModifyPorts(networkPorts,sliceName):\r
+       networkList = Network.objects.all()\r
+        networkInfo = []\r
+        if networkPorts:\r
+           for networkEntry in networkList:\r
+               networkSlices = networkEntry.slices.all()\r
+               for slice in networkSlices:\r
+                   if slice.name==sliceName:\r
+                          if networkEntry.template.name=="Public shared IPv4":\r
+                             setattr(networkEntry,'ports',networkPorts)\r
+                             networkEntry.save()\r
+\r
 def getTenantSliceInfo(user, tableFormat = False):
     tenantSliceDetails = {}
     tenantSliceData = getTenantInfo(user)
@@ -131,7 +251,6 @@ def getTenantSliceInfo(user, tableFormat = False):
        tenantSliceDetails['userSliceInfo'] = tenantSliceData
     tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
     tenantSliceDetails['image']=userSliceTableFormatter(getImageInfo(user))
-    #tenantSliceDetails['network']=userSliceTableFormatter(getNetworkInfo(user))
     tenantSliceDetails['deploymentSites']=userSliceTableFormatter(getDeploymentSites())
     tenantSliceDetails['sites'] = userSliceTableFormatter(getTenantSitesInfo())
     tenantSliceDetails['mountDataSets'] = userSliceTableFormatter(getMountDataSets())
@@ -146,13 +265,14 @@ def getTenantInfo(user):
        slice = Slice.objects.get(name=Slice.objects.get(id=entry.id).name)
        sliceServiceClass = entry.serviceClass.name
        preferredImage =  entry.imagePreference
-       sliceDataSet = entry.mountDataSets
-       #sliceNetwork = entry.network
+       #sliceDataSet = entry.mountDataSets
+       sliceNetwork = {}
        numSliver = 0
        sliceImage=""
        sliceSite = {}
        sliceNode = {}
        sliceInstance= {}
+       #createPrivateVolume(user,sliceName)
        for sliver in slice.slivers.all():
            if sliver.node.site.name in BLESSED_SITES:
                 sliceSite[sliver.node.site.name] = sliceSite.get(sliver.node.site.name,0) + 1
@@ -160,7 +280,7 @@ def getTenantInfo(user):
                 sliceNode[str(sliver)] = sliver.node.name
        numSliver = sum(sliceSite.values())
        numSites = len(sliceSite)
-       userSliceInfo.append({'sliceName': sliceName,'sliceServiceClass': sliceServiceClass,'preferredImage':preferredImage,'numOfSites':numSites, 'sliceSite':sliceSite,'sliceImage':sliceImage,'numOfSlivers':numSliver,'sliceDataSet':sliceDataSet,'instanceNodePair':sliceNode})
+       userSliceInfo.append({'sliceName': sliceName,'sliceServiceClass': sliceServiceClass,'preferredImage':preferredImage,'numOfSites':numSites, 'sliceSite':sliceSite,'sliceImage':sliceImage,'numOfSlivers':numSliver,'instanceNodePair':sliceNode})
     return userSliceInfo
 
 def getTenantSitesInfo():
@@ -207,6 +327,7 @@ def createPrivateVolume(user, sliceName):
            getattr(Volume.default_gateway_caps,"host files")
     v = Volume(name="private_" + sliceName, owner_id=user, description="private volume for %s" % sliceName, blocksize=61440, private=True, archive=False, default_gateway_caps = caps)
     v.save()
+    return v
 
 SYNDICATE_REPLICATE_PORTNUM = 1025
 
@@ -217,6 +338,8 @@ def get_free_port():
         inuse[vs.peer_portnum]=True
         inuse[vs.replicate_portnum]=True
     for network in Network.objects.all():
+        if not network.ports:
+            continue
         network_ports = [x.strip() for x in network.ports.split(",")]
         for network_port in network_ports:
             try:
@@ -254,15 +377,6 @@ def getMountDataSets():
 \r
         return dataSetInfo
 
-def getNetworkInfo(user):
-   #networkList = Network.objects.all()
-    networkList = ['Private Only','Private and Publicly Routable']
-    networkInfo = []
-    for networkEntry in networkList:
-          #networkInfo.append({'Network':networkEntry.name})
-          networkInfo.append({'Network':networkEntry})
-    return networkInfo
-
 def getDeploymentSites():
     deploymentList = Deployment.objects.all()
     deploymentInfo = []
@@ -408,7 +522,7 @@ class SimulatorView(View):
 
 class DashboardUserSiteView(View):
     def get(self, request, **kwargs):
-        return HttpResponse(json.dumps(getUserSliceInfo(request.user, True)), mimetype='application/javascript')
+        return HttpResponse(json.dumps(getDashboardContext(request.user, tableFormat=True)), mimetype='application/javascript')
 
 class TenantViewData(View):
     def get(self, request, **kwargs):
@@ -526,6 +640,8 @@ def slice_decrease_slivers(user, siteList, slice, count, noAct=False):
 
 class TenantDeleteSliceView(View):
         def post(self,request):\r
+                if request.user.isReadOnlyUser():\r
+                    return HttpResponseForbidden("User is in read-only mode")\r
                 sliceName = request.POST.get("sliceName",None)\r
                 slice = Slice.objects.get(name=sliceName)\r
                 #print slice, slice.id\r
@@ -627,6 +743,9 @@ class DashboardAddOrRemoveSliverView(View):
         siteList = [Site.objects.get(name=siteName)]
         slice = Slice.objects.get(name="HyperCache")
 
+        if request.user.isReadOnlyUser():
+            return HttpResponseForbidden("User is in read-only mode")
+
         if (actionToDo == "add"):
             user_ip = request.GET.get("ip", get_ip(request))
             slice_increase_slivers(request.user, user_ip, siteList, slice, 1)
@@ -635,7 +754,7 @@ class DashboardAddOrRemoveSliverView(View):
 
         print '*' * 50
         print 'Ask for site: ' + siteName + ' to ' + actionToDo + ' another HPC Sliver'
-        return HttpResponse('This is POST request ')
+        return HttpResponse(json.dumps("Success"), mimetype='application/javascript')
 
 class DashboardAjaxView(View):
     def get(self, request, **kwargs):
@@ -646,7 +765,7 @@ class DashboardAnalyticsAjaxView(View):
         if (name == "hpcSummary"):
             return HttpResponse(json.dumps(hpc_wizard.get_hpc_wizard().get_summary_for_view()), mimetype='application/javascript')
         elif (name == "hpcUserSite"):
-            return HttpResponse(json.dumps(getUserSliceInfo(request.user, True)), mimetype='application/javascript')
+            return HttpResponse(json.dumps(getDashboardContext(request.user, tableFormat=True)), mimetype='application/javascript')
         elif (name == "hpcMap"):
             return HttpResponse(json.dumps(getCDNOperatorData(True)), mimetype='application/javascript')
         elif (name == "bigquery"):
@@ -655,3 +774,127 @@ class DashboardAnalyticsAjaxView(View):
         else:
             return HttpResponse(json.dumps("Unknown"), mimetype='application/javascript')
 
+class DashboardCustomize(View):
+    def post(self, request, *args, **kwargs):\r
+        if request.user.isReadOnlyUser():\r
+            return HttpResponseForbidden("User is in read-only mode")\r
+\r
+        dashboards = request.POST.get("dashboards", None)\r
+        if not dashboards:\r
+            dashboards=[]\r
+        else:\r
+            dashboards = [x.strip() for x in dashboards.split(",")]\r
+            dashboards = [DashboardView.objects.get(name=x) for x in dashboards]\r
+\r
+        request.user.dashboardViews.all().delete()\r
+\r
+        for i,dashboard in enumerate(dashboards):\r
+            udbv = UserDashboardView(user=request.user, dashboardView=dashboard, order=i)\r
+            udbv.save()\r
+\r
+        return HttpResponse(json.dumps("Success"), mimetype='application/javascript')\r
+
+class DashboardSliceInteractions(View):
+    def get(self, request, name="users", **kwargs):
+        colors = ["#005586", "#6ebe49", "orange", "#707170", "#00c4b3", "#077767", "dodgerblue", "#a79b94", "#c4e76a", "red"]
+
+        groups = []
+        matrix = []
+        slices = list(Slice.objects.all())
+
+        ids_by_slice = self.build_id_list(slices, name)
+
+        slices = [x for x in slices if (len(ids_by_slice[x])>0)]
+
+        for i,slice in enumerate(slices):
+            groups.append({"name": slice.name, "color": colors[i%len(colors)]})
+            row=self.buildMatrix(slice, slices, name, ids_by_slice)
+            matrix.append(row)
+
+        result = {"groups": groups, "matrix": matrix}
+
+        if name=="users":
+            result["title"] = "Slice interactions by user privilege"
+            result["objectName"] = "users"
+        elif name=="networks":
+            result["title"] = "Slice interactions by network membership"
+            result["objectName"] = "networks"
+        elif name=="sites":
+            result["title"] = "Slice interactions by site ownership"
+            result["objectName"] = "sites"
+        elif name=="sliver_sites":
+            result["title"] = "Slice interactions by sliver sites"
+            result["objectName"] = "sites"
+        elif name=="sliver_nodes":
+            result["title"] = "Slice interactions by sliver nodes"
+            result["objectName"] = "nodes"
+
+        return HttpResponse(json.dumps(result), mimetype='application/javascript')
+
+    def build_id_list(self, slices, name):
+        ids_by_slice = {}
+        for slice in slices:
+            # build up a list of object ids that are used by each slice
+            ids_by_slice[slice] = self.getIds(slice, name)
+
+        return ids_by_slice
+
+    def buildMatrix(self, slice, slices, name, ids_by_slice):
+        not_only_my_ids = []
+
+        # build up a list of object ids that are used by other slices
+        for otherSlice in ids_by_slice.keys():
+            if (slice != otherSlice):
+                for id in ids_by_slice[otherSlice]:
+                    if not id in not_only_my_ids:
+                        not_only_my_ids.append(id)
+
+        # build up a list of ids that are used only by the slice, and not
+        # shared with any other slice
+        only_my_ids = []
+        for id in ids_by_slice[slice]:
+             if id not in not_only_my_ids:
+                  only_my_ids.append(id)
+
+        row = []
+        for otherSlice in ids_by_slice.keys():
+            if (otherSlice == slice):
+                row.append(len(only_my_ids))
+            else:
+                row.append(self.inCommonIds(ids_by_slice[slice], ids_by_slice[otherSlice]))
+
+        return row
+
+    def getIds(self, slice, name):
+        ids=[]
+        if name=="users":
+            for sp in slice.slice_privileges.all():
+                    if sp.user.id not in ids:
+                        ids.append(sp.user.id)
+        elif name=="networks":
+            for sp in slice.networkslice_set.all():
+                    if sp.network.id not in ids:
+                        ids.append(sp.network.id)
+        elif name=="sites":
+            ids = [slice.site.id]
+        elif name=="sliver_sites":
+            for sp in slice.slivers.all():
+                 if sp.node.site.id not in ids:
+                     ids.append(sp.node.site.id)
+        elif name=="sliver_nodes":
+            for sp in slice.slivers.all():
+                 if sp.node.id not in ids:
+                     ids.append(sp.node.id)
+        return ids
+
+    def inCommonIds(self, ids1, ids2):
+        count = 0
+        for id in ids1:
+            if id in ids2:
+                count+=1
+        return count
+
+    def isEmpty(self, slice, name):
+        if (name in ["users", "networks", "sites", "sliver_nodes", "sliver_sites"]):
+            return (len(self.getIds(slice,name)) == 0)
+