add queryspec to compose_cached_query
[plstackapi.git] / planetstack / core / plus / views.py
index 38a7a96..705f4f6 100644 (file)
@@ -8,6 +8,7 @@ import datetime
 from pprint import pprint
 import json
 from core.models import *
+from hpc.models import ContentProvider
 from operator import attrgetter
 from django.views.decorators.csrf import csrf_exempt
 from django.http import HttpResponse, HttpResponseServerError
@@ -24,7 +25,7 @@ if os.path.exists("/home/smbaker/projects/vicci/cdn/bigquery"):
 else:
     sys.path.append("/opt/planetstack/hpc_wizard")
 import hpc_wizard
-from planetstack_analytics import DoPlanetStackAnalytics, PlanetStackAnalytics
+from planetstack_analytics import DoPlanetStackAnalytics, PlanetStackAnalytics, RED_LOAD, BLUE_LOAD
 
 class DashboardWelcomeView(TemplateView):
     template_name = 'admin/dashboard/welcome.html'
@@ -36,16 +37,11 @@ class DashboardWelcomeView(TemplateView):
 
         context['userSliceInfo'] = userDetails['userSliceInfo']
         context['cdnData'] = userDetails['cdnData']
+        context['cdnContentProviders'] = userDetails['cdnContentProviders']
         return self.render_to_response(context=context)
 
 def getUserSliceInfo(user, tableFormat = False):
         userDetails = {}
-#        try:
-# //           site = Site.objects.filter(id=user.site.id)
-#  //      except:
-#   //         site = Site.objects.filter(name="Princeton")
-#    //    userDetails['sitename'] = site[0].name
-#     //   userDetails['siteid'] = site[0].id
 
         userSliceData = getSliceInfo(user)
         if (tableFormat):
@@ -53,8 +49,8 @@ def getUserSliceInfo(user, tableFormat = False):
             userDetails['userSliceInfo'] = userSliceTableFormatter(userSliceData)
         else:
             userDetails['userSliceInfo'] = userSliceData
-        userDetails['cdnData'] = getCDNOperatorData();
-#        pprint( userDetails)
+        userDetails['cdnData'] = getCDNOperatorData(wait=False)
+        userDetails['cdnContentProviders'] = getCDNContentProviderData()
         return userDetails
 
 class TenantCreateSlice(View):
@@ -63,15 +59,43 @@ class TenantCreateSlice(View):
         serviceClass = request.POST.get("serviceClass", "0")
         imageName = request.POST.get("imageName", "0")
         actionToDo = request.POST.get("actionToDo", "0")
-        print sliceName
+        network = request.POST.get("network","0")
+        mountDataSets = request.POST.get("mountDataSets","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=imageName)
+           image = Image.objects.get(name=imageName)
+           newSlice = Slice(name=sliceName,serviceClass=serviceClass,site=site,imagePreference=image,mountDataSets=mountDataSets,network=network)
            newSlice.save()
-        return newSlice
-
+        return HttpResponse("Slice created")
+
+class TenantUpdateSlice(View):
+    def post(self, request, *args, **kwargs):\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
+        dataSet = request.POST.get("dataSet","0")\r
+        slice = Slice.objects.all()\r
+        for entry in slice:\r
+                serviceClass = ServiceClass.objects.get(name=serviceClass)\r
+                if(entry.name==sliceName):\r
+                         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
+        return HttpResponse("Slice updated")\r
+\r
+def  update_slice(sliceName,**fields):
+         slice = Slice.objects.filter(name = sliceName)\r
+         for (k,v) in fields.items():\r
+                setattr(slice, k, v)\r
+                slice.save()\r
+         return slice
 
 def getTenantSliceInfo(user, tableFormat = False):
     tenantSliceDetails = {}
@@ -99,17 +123,21 @@ 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
        numSliver = 0
        sliceImage=""
        sliceSite = {}
+       sliceNode = {}
+       sliceInstance= {}
        for sliver in slice.slivers.all():
-            numSliver +=sliver.numberCores
-           # sliceSite[sliver.deploymentNetwork.name] =sliceSite.get(sliver.deploymentNetwork.name,0) + 1
            if sliver.node.site.name in BLESSED_SITES:
                 sliceSite[sliver.node.site.name] = sliceSite.get(sliver.node.site.name,0) + 1
                 sliceImage = sliver.image.name
+                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})
+       userSliceInfo.append({'sliceName': sliceName,'sliceServiceClass': sliceServiceClass,'preferredImage':preferredImage,'numOfSites':numSites, 'sliceSite':sliceSite,'sliceImage':sliceImage,'numOfSlivers':numSliver,'sliceDataSet':sliceDataSet,'sliceNetwork':sliceNetwork, 'instanceNodePair':sliceNode})
     return userSliceInfo
 
 def getTenantSitesInfo():
@@ -134,16 +162,16 @@ def getServiceClassInfo(user):
     return sliceInfo
 
 def getImageInfo(user):
-    #imageList = Image.objects.all()
-    imageList = ['Fedora 16 LXC rev 1.3','Hadoop','MPI']
+    imageList = Image.objects.all()
+    #imageList = ['Fedora 16 LXC rev 1.3','Hadoop','MPI']
     imageInfo = []
     for imageEntry in imageList:
-          #imageInfo.append({'Image':imageEntry.name})
-          imageInfo.append({'Image':imageEntry})
+          imageInfo.append({'Image':imageEntry.name})
+          #imageInfo.append({'Image':imageEntry})
     return imageInfo
 
 def getMountDataSets():
-        dataSetList = ['GenBank','LSST','LHC','NOAA','Measurement Lab','Common Crawl']\r
+        dataSetList = ['------','GenBank','LSST','LHC','NOAA','Measurement Lab','Common Crawl']\r
         dataSetInfo = []\r
         for entry in dataSetList:\r
                 dataSetInfo.append({'DataSet':entry})\r
@@ -201,32 +229,81 @@ def getSliceInfo(user):
 
     return userSliceInfo
 
-def getCDNOperatorData(randomizeData = False):
+def getCDNContentProviderData():
+    cps = []
+    for dm_cp in ContentProvider.objects.all():
+        cp = {"name": dm_cp.name,
+              "account": dm_cp.account}
+        cps.append(cp)
+
+    return cps
+
+def getCDNOperatorData(randomizeData = False, wait=True):
+    HPC_SLICE_NAME = "HyperCache"
+
     bq = PlanetStackAnalytics()
 
-    #hpc_sliceNames = bq.service_to_sliceNames("HPC Service")
+    rows = bq.get_cached_query_results(bq.compose_cached_query(), wait)
 
-    rows = bq.get_cached_query_results(bq.compose_latest_query())
+    # wait=False on the first time the Dashboard is opened. This means we might
+    # not have any rows yet. The dashboard code polls every 30 seconds, so it
+    # will eventually pick them up.
 
-    #rows = [x for x in rows if x.get("slice","") in hpc_sliceNames]
+    if rows:
+        rows = bq.postprocess_results(rows, filter={"event": "hpc_heartbeat"}, maxi=["cpu"], count=["hostname"], computed=["bytes_sent/elapsed"], groupBy=["Time","site"], maxDeltaTime=80)
 
-    rows = bq.postprocess_results(rows, filter={"slice": "HyperCache"}, maxi=["cpu"], count=["hostname"], computed=["bytes_sent/elapsed"], groupBy=["Time","site"], maxDeltaTime=80)
+        # dictionaryize the statistics rows by site name
+        stats_rows = {}
+        for row in rows:
+            stats_rows[row["site"]] = row
+    else:
+        stats_rows = {}
 
-    bq.merge_datamodel_sites(rows, slice="HyperCache")
+    slice = Slice.objects.filter(name=HPC_SLICE_NAME)
+    if slice:
+        slice_slivers = list(slice[0].slivers.all())
+    else:
+        slice_slivers = []
 
     new_rows = {}
-    for row in rows:
-        new_row = {"lat": float(row.get("lat", 0)),
-               "long": float(row.get("long", 0)),
+    for site in Site.objects.all():
+        # compute number of slivers allocated in the data model
+        allocated_slivers = 0
+        for sliver in slice_slivers:
+            if sliver.node.site == site:
+                allocated_slivers = allocated_slivers + 1
+
+        stats_row = stats_rows.get(site.name,{})
+
+        max_cpu = stats_row.get("max_avg_cpu", stats_row.get("max_cpu",0))
+        cpu=float(max_cpu)/100.0
+        hotness = max(0.0, ((cpu*RED_LOAD) - BLUE_LOAD)/(RED_LOAD-BLUE_LOAD))
+
+        # format it to what that CDN Operations View is expecting
+        new_row = {"lat": float(site.location.longitude),
+               "long": float(site.location.longitude),
+               "lat": float(site.location.latitude),
                "health": 0,
-               "numNodes": int(row.get("numNodes",0)),
-               "activeHPCSlivers": int(row.get("count_hostname", 0)),
-               "numHPCSlivers": int(row.get("allocated_slivers", 0)),
-               "siteUrl": str(row.get("url", "")),
-               "hot": float(row.get("hotness", 0.0)),
-               "bandwidth": row.get("sum_computed_bytes_sent_div_elapsed",0),
-               "load": int(float(row.get("max_avg_cpu", row.get("max_cpu",0))))}
-        new_rows[str(row["site"])] = new_row
+               "numNodes": int(site.nodes.count()),
+               "activeHPCSlivers": int(stats_row.get("count_hostname", 0)),     # measured number of slivers, from bigquery statistics
+               "numHPCSlivers": allocated_slivers,                              # allocated number of slivers, from data model
+               "siteUrl": str(site.site_url),
+               "bandwidth": stats_row.get("sum_computed_bytes_sent_div_elapsed",0),
+               "load": max_cpu,
+               "hot": float(hotness)}
+        new_rows[str(site.name)] = new_row
+
+    # get rid of sites with 0 slivers that overlap other sites with >0 slivers
+    for (k,v) in new_rows.items():
+        bad=False
+        if v["numHPCSlivers"]==0:
+            for v2 in new_rows.values():
+                if (v!=v2) and (v2["numHPCSlivers"]>=0):
+                    d = haversine(v["lat"],v["long"],v2["lat"],v2["long"])
+                    if d<100:
+                         bad=True
+            if bad:
+                del new_rows[k]
 
     return new_rows
 
@@ -255,6 +332,20 @@ class TenantViewData(View):
     def get(self, request, **kwargs):
         return HttpResponse(json.dumps(getTenantSliceInfo(request.user, True)), mimetype='application/javascript')
 
+def haversine(site_lat, site_lon, lat, lon):
+    d=0
+    if lat and lon and site_lat and site_lon:
+        site_lat = float(site_lat)
+        site_lon = float(site_lon)
+        lat = float(lat)
+        lon = float(lon)
+        R = 6378.1
+        a = math.sin( math.radians((lat - site_lat)/2.0) )**2 + math.cos( math.radians(lat) )*math.cos( math.radians(site_lat) )*(math.sin( math.radians((lon - site_lon)/2.0 ) )**2)
+        c = 2 * math.atan2( math.sqrt(a), math.sqrt(1 - a) )
+        d = R * c
+
+    return d
+
 def siteSortKey(site, slice=None, count=None, lat=None, lon=None):
     # try to pick a site we're already using
     has_slivers_here=False
@@ -264,16 +355,7 @@ def siteSortKey(site, slice=None, count=None, lat=None, lon=None):
                 has_slivers_here=True
 
     # Haversine method
-    d = 0
-    site_lat = site.location.latitude
-    site_lon = site.location.longitude
-    if lat and lon and site_lat and site_lon:
-        site_lat = float(site_lat)
-        site_lon = float(site_lon)
-        R = 6378.1
-        a = math.sin( math.radians((lat - site_lat)/2.0) )**2 + math.cos( math.radians(lat) )*math.cos( math.radians(site_lat) )*(math.sin( math.radians((lon - site_lon)/2.0 ) )**2)
-        c = 2 * math.atan2( math.sqrt(a), math.sqrt(1 - a) )
-        d = R * c
+    d = haversine(site.location.latitude, site.location.longitude, lat, lon)
 
     return (-has_slivers_here, d)
 
@@ -367,6 +449,7 @@ class TenantDeleteSliceView(View):
                 print slice, slice.id\r
                 sliceToDel=Slice(name=sliceName, id=slice.id)\r
                 sliceToDel.delete()
+                return HttpResponse("Slice deleted")
 
 class TenantAddOrRemoveSliverView(View):
     """ Add or remove slivers from a Slice