blob: e37faef986fbf582694014fc70cfdaf523a9ef6e [file] [log] [blame]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001#views.py
Scott Bakerbd57a432014-04-14 16:12:15 -07002import functools
3import math
Scott Baker771819b2014-03-19 22:10:17 -07004import os
5import sys
Siobhan Tully06d2c032014-02-23 23:48:52 -05006from django.views.generic import TemplateView, View
7import datetime
Scott Baker771819b2014-03-19 22:10:17 -07008from pprint import pprint
9import json
Scott Baker537d8232014-04-08 23:40:56 -070010from core.models import *
Scott Bakerbd57a432014-04-14 16:12:15 -070011from operator import attrgetter
12from django.views.decorators.csrf import csrf_exempt
13from django.http import HttpResponse, HttpResponseServerError
Scott Baker537d8232014-04-08 23:40:56 -070014from django.core import urlresolvers
Scott Bakerbd57a432014-04-14 16:12:15 -070015from django.contrib.gis.geoip import GeoIP
16from ipware.ip import get_ip
Scott Baker771819b2014-03-19 22:10:17 -070017import traceback
Scott Baker6253dfc2014-04-14 09:29:30 -070018import socket
Siobhan Tully06d2c032014-02-23 23:48:52 -050019
Scott Bakera880bb52014-04-14 23:52:48 -070020BLESSED_SITES = ["Stanford", "Washington", "Princeton", "GeorgiaTech", "MaxPlanck"]
21
Scott Baker771819b2014-03-19 22:10:17 -070022if os.path.exists("/home/smbaker/projects/vicci/cdn/bigquery"):
23 sys.path.append("/home/smbaker/projects/vicci/cdn/bigquery")
24else:
25 sys.path.append("/opt/planetstack/hpc_wizard")
26import hpc_wizard
27from planetstack_analytics import DoPlanetStackAnalytics
Siobhan Tullycf04fb62014-01-11 11:25:57 -050028
29class DashboardWelcomeView(TemplateView):
30 template_name = 'admin/dashboard/welcome.html'
31
32 def get(self, request, *args, **kwargs):
33 context = self.get_context_data(**kwargs)
Scott Baker771819b2014-03-19 22:10:17 -070034 userDetails = getUserSliceInfo(request.user)
35 #context['site'] = userDetails['site']
Siobhan Tullycf04fb62014-01-11 11:25:57 -050036
Scott Baker771819b2014-03-19 22:10:17 -070037 context['userSliceInfo'] = userDetails['userSliceInfo']
38 context['cdnData'] = userDetails['cdnData']
Siobhan Tullycf04fb62014-01-11 11:25:57 -050039 return self.render_to_response(context=context)
Siobhan Tullye18b3442014-02-23 14:23:34 -050040
Scott Baker771819b2014-03-19 22:10:17 -070041def getUserSliceInfo(user, tableFormat = False):
42 userDetails = {}
43# try:
44# // site = Site.objects.filter(id=user.site.id)
45# // except:
46# // site = Site.objects.filter(name="Princeton")
47# // userDetails['sitename'] = site[0].name
48# // userDetails['siteid'] = site[0].id
49
50 userSliceData = getSliceInfo(user)
51 if (tableFormat):
52# pprint("******* GET USER SLICE INFO")
53 userDetails['userSliceInfo'] = userSliceTableFormatter(userSliceData)
54 else:
55 userDetails['userSliceInfo'] = userSliceData
56 userDetails['cdnData'] = getCDNOperatorData();
57# pprint( userDetails)
58 return userDetails
59
Scott Baker537d8232014-04-08 23:40:56 -070060class TenantCreateSlice(View):
61 def post(self, request, *args, **kwargs):
62 sliceName = request.POST.get("sliceName", "0")
63 serviceClass = request.POST.get("serviceClass", "0")
64 imageName = request.POST.get("imageName", "0")
65 actionToDo = request.POST.get("actionToDo", "0")
66 print sliceName
67 if (actionToDo == "add"):
68 serviceClass = ServiceClass.objects.get(name=serviceClass)
69 site = request.user.site
70 #image = Image.objects.get(name=imageName)
71 newSlice = Slice(name=sliceName,serviceClass=serviceClass,site=site,imagePreference=imageName)
72 newSlice.save()
73 return newSlice
74
75
76def getTenantSliceInfo(user, tableFormat = False):
77 tenantSliceDetails = {}
78 tenantSliceData = getTenantInfo(user)
79 tenantServiceClassData = getServiceClassInfo(user)
80 if (tableFormat):
81 tenantSliceDetails['userSliceInfo'] = userSliceTableFormatter(tenantSliceData)
82 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
83 else:
84 tenantSliceDetails['userSliceInfo'] = tenantSliceData
85 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
86 tenantSliceDetails['image']=userSliceTableFormatter(getImageInfo(user))
87 tenantSliceDetails['network']=userSliceTableFormatter(getNetworkInfo(user))
88 tenantSliceDetails['deploymentSites']=userSliceTableFormatter(getDeploymentSites())
89 tenantSliceDetails['sites'] = userSliceTableFormatter(getTenantSitesInfo());
90 return tenantSliceDetails
91
92
93def getTenantInfo(user):
94 slices =Slice.objects.all()
95 userSliceInfo = []
96 for entry in slices:
97 sliceName = Slice.objects.get(id=entry.id).name
98 slice = Slice.objects.get(name=Slice.objects.get(id=entry.id).name)
99 sliceServiceClass = entry.serviceClass.name
100 preferredImage = entry.imagePreference
101 numSliver = 0
102 sliceImage=""
103 sliceSite = {}
104 for sliver in slice.slivers.all():
105 numSliver +=sliver.numberCores
106 # sliceSite[sliver.deploymentNetwork.name] =sliceSite.get(sliver.deploymentNetwork.name,0) + 1
Scott Bakera880bb52014-04-14 23:52:48 -0700107 if sliver.node.site.name in BLESSED_SITES:
108 print "equal",sliver.node.site.name
109 sliceSite[sliver.node.site.name] = sliceSite.get(sliver.node.site.name,0) + 1
110 sliceImage = sliver.image.name
Scott Baker537d8232014-04-08 23:40:56 -0700111 userSliceInfo.append({'sliceName': sliceName,'sliceServiceClass': sliceServiceClass,'preferredImage':preferredImage, 'sliceSite':sliceSite,'sliceImage':sliceImage,'numOfSlivers':numSliver})
112 return userSliceInfo
113
114def getTenantSitesInfo():
115 tenantSiteInfo=[]
116 for entry in Site.objects.all():
Scott Bakera880bb52014-04-14 23:52:48 -0700117 if entry.name in BLESSED_SITES:
118 tenantSiteInfo.append({'siteName':entry.name})
Scott Baker537d8232014-04-08 23:40:56 -0700119 return tenantSiteInfo
120
Scott Baker771819b2014-03-19 22:10:17 -0700121def userSliceTableFormatter(data):
122# pprint(data)
123 formattedData = {
124 'rows' : data
125 }
126 return formattedData
127
Scott Baker537d8232014-04-08 23:40:56 -0700128def getServiceClassInfo(user):
129 serviceClassList = ServiceClass.objects.all()
130 sliceInfo = []
131 for entry in serviceClassList:
132 sliceInfo.append({'serviceClass':entry.name})
133 return sliceInfo
134
135def getImageInfo(user):
136 imageList = Image.objects.all()
137 imageInfo = []
138 for imageEntry in imageList:
139 imageInfo.append({'Image':imageEntry.name})
140 return imageInfo
141
142def getNetworkInfo(user):
143 #networkList = Network.objects.all()
144 networkList = ['Private Only','Private and Publicly Routable']
145 networkInfo = []
146 for networkEntry in networkList:
147 #networkInfo.append({'Network':networkEntry.name})
148 networkInfo.append({'Network':networkEntry})
149 return networkInfo
150
151def getDeploymentSites():
152 deploymentList = Deployment.objects.all()
153 deploymentInfo = []
154 for entry in deploymentList:
155 deploymentInfo.append({'DeploymentSite':entry.name})
156 return deploymentInfo
157
Scott Baker771819b2014-03-19 22:10:17 -0700158def getSliceInfo(user):
Siobhan Tully06d2c032014-02-23 23:48:52 -0500159 sliceList = Slice.objects.all()
Scott Baker771819b2014-03-19 22:10:17 -0700160 slicePrivs = SlicePrivilege.objects.filter(user=user)
Siobhan Tully06d2c032014-02-23 23:48:52 -0500161 userSliceInfo = []
162 for entry in slicePrivs:
Siobhan Tullye18b3442014-02-23 14:23:34 -0500163
Scott Baker771819b2014-03-19 22:10:17 -0700164 slicename = Slice.objects.get(id=entry.slice.id).name
Scott Baker537d8232014-04-08 23:40:56 -0700165 slice = Slice.objects.get(name=Slice.objects.get(id=entry.slice.id).name)
166 sliverList=Sliver.objects.all()
167 sites_used = {}
168 for sliver in slice.slivers.all():
169 #sites_used['deploymentSites'] = sliver.node.deployment.name
170 # sites_used[sliver.image.name] = sliver.image.name
171 sites_used[sliver.node.site.name] = sliver.numberCores
Scott Baker771819b2014-03-19 22:10:17 -0700172 sliceid = Slice.objects.get(id=entry.slice.id).id
Siobhan Tully06d2c032014-02-23 23:48:52 -0500173 try:
Scott Baker771819b2014-03-19 22:10:17 -0700174 sliverList = Sliver.objects.filter(slice=entry.slice.id)
175 siteList = {}
176 for x in sliverList:
177 if x.node.site not in siteList:
178 siteList[x.node.site] = 1
179 slivercount = len(sliverList)
180 sitecount = len(siteList)
Siobhan Tully06d2c032014-02-23 23:48:52 -0500181 except:
Scott Baker771819b2014-03-19 22:10:17 -0700182 traceback.print_exc()
183 slivercount = 0
184 sitecount = 0
Siobhan Tully06d2c032014-02-23 23:48:52 -0500185
Scott Baker771819b2014-03-19 22:10:17 -0700186 userSliceInfo.append({'slicename': slicename, 'sliceid':sliceid,
Scott Baker537d8232014-04-08 23:40:56 -0700187 'sitesUsed':sites_used,
188 'role': SliceRole.objects.get(id=entry.role.id).role,
189 'slivercount': slivercount,
190 'sitecount':sitecount})
Scott Baker771819b2014-03-19 22:10:17 -0700191
Siobhan Tully06d2c032014-02-23 23:48:52 -0500192 return userSliceInfo
193
Siobhan Tully06d2c032014-02-23 23:48:52 -0500194def getCDNOperatorData(randomizeData = False):
Scott Baker771819b2014-03-19 22:10:17 -0700195 return hpc_wizard.get_hpc_wizard().get_sites_for_view()
Siobhan Tully06d2c032014-02-23 23:48:52 -0500196
Scott Baker771819b2014-03-19 22:10:17 -0700197def getPageSummary(request):
198 slice = request.GET.get('slice', None)
199 site = request.GET.get('site', None)
200 node = request.GET.get('node', None)
Siobhan Tully06d2c032014-02-23 23:48:52 -0500201
202
Scott Baker771819b2014-03-19 22:10:17 -0700203class SimulatorView(View):
204 def get(self, request, **kwargs):
205 sim = json.loads(file("/tmp/simulator.json","r").read())
206 text = "<html><head></head><body>"
207 text += "Iteration: %d<br>" % sim["iteration"]
208 text += "Elapsed since report %d<br><br>" % sim["elapsed_since_report"]
209 text += "<table border=1>"
210 text += "<tr><th>site</th><th>trend</th><th>weight</th><th>bytes_sent</th><th>hot</th></tr>"
211 for site in sim["site_load"].values():
212 text += "<tr>"
213 text += "<td>%s</td><td>%0.2f</td><td>%0.2f</td><td>%d</td><td>%0.2f</td>" % \
214 (site["name"], site["trend"], site["weight"], site["bytes_sent"], site["load_frac"])
215 text += "</tr>"
216 text += "</table>"
217 text += "</body></html>"
218 return HttpResponse(text)
219
220class DashboardUserSiteView(View):
221 def get(self, request, **kwargs):
222 return HttpResponse(json.dumps(getUserSliceInfo(request.user, True)), mimetype='application/javascript')
223
Scott Baker537d8232014-04-08 23:40:56 -0700224class TenantViewData(View):
225 def get(self, request, **kwargs):
226 return HttpResponse(json.dumps(getTenantSliceInfo(request.user, True)), mimetype='application/javascript')
227
Scott Bakerbd57a432014-04-14 16:12:15 -0700228def siteSortKey(site, slice=None, count=None, lat=None, lon=None):
229 # try to pick a site we're already using
230 has_slivers_here=False
231 if slice:
232 for sliver in slice.slivers.all():
233 if sliver.node.site.name == site.name:
234 has_slivers_here=True
235
236 # Haversine method
237 d = 0
238 site_lat = site.location.latitude
239 site_lon = site.location.longitude
240 if lat and lon and site_lat and site_lon:
241 site_lat = float(site_lat)
242 site_lon = float(site_lon)
243 R = 6378.1
244 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)
245 c = 2 * math.atan2( math.sqrt(a), math.sqrt(1 - a) )
246 d = R * c
247
248 return (-has_slivers_here, d)
249
250def tenant_pick_sites(user, user_ip=None, slice=None, count=None):
251 """ Returns list of sites, sorted from most favorable to least favorable """
252 lat=None
253 lon=None
254 try:
255 client_geo = GeoIP().city(user_ip)
256 if client_geo:
257 lat=float(client_geo["latitude"])
258 lon=float(client_geo["longitude"])
259 except:
260 print "exception in geo code"
261 traceback.print_exc()
262
263 sites = Site.objects.all()
Scott Bakera880bb52014-04-14 23:52:48 -0700264 sites = [x for x in sites if x.name in BLESSED_SITES]
Scott Bakerbd57a432014-04-14 16:12:15 -0700265 sites = sorted(sites, key=functools.partial(siteSortKey, slice=slice, count=count, lat=lat, lon=lon))
266
267 return sites
268
Scott Bakerfe6a7e12014-04-15 10:58:31 -0700269def slice_increase_slivers(user, user_ip, siteList, slice, count, noAct=False):
Scott Bakere4deddd2014-04-14 16:48:05 -0700270 sitesChanged = {}
271
Scott Bakerbd57a432014-04-14 16:12:15 -0700272 # let's compute how many slivers are in use in each node of each site
273 for site in siteList:
274 site.nodeList = list(site.nodes.all())
275 for node in site.nodeList:
276 node.sliverCount = 0
Scott Baker6253dfc2014-04-14 09:29:30 -0700277 for sliver in node.slivers.all():
Scott Baker21511942014-04-15 17:44:27 -0700278 if sliver.slice.id == slice.id:
Scott Bakerbd57a432014-04-14 16:12:15 -0700279 node.sliverCount = node.sliverCount +1
Scott Baker6253dfc2014-04-14 09:29:30 -0700280
Scott Bakerbd57a432014-04-14 16:12:15 -0700281 # Allocate slivers to nodes
282 # for now, assume we want to allocate all slivers from the same site
283 nodes = siteList[0].nodeList
284 while (count>0):
285 # Sort the node list by number of slivers per node, then pick the
286 # node with the least number of slivers.
287 nodes = sorted(nodes, key=attrgetter("sliverCount"))
288 node = nodes[0]
Scott Baker6253dfc2014-04-14 09:29:30 -0700289
Scott Bakerbd57a432014-04-14 16:12:15 -0700290 print "adding sliver at node", node.name, "of site", node.site.name
Scott Baker6253dfc2014-04-14 09:29:30 -0700291
Scott Bakere4deddd2014-04-14 16:48:05 -0700292 if not noAct:
293 sliver = Sliver(name=node.name,
294 slice=slice,
295 node=node,
296 image = Image.objects.all()[0],
297 creator = User.objects.get(email=user),
298 deploymentNetwork=node.deployment,
299 numberCores =1 )
300 sliver.save()
Scott Bakerbd57a432014-04-14 16:12:15 -0700301
302 node.sliverCount = node.sliverCount + 1
303
304 count = count - 1
305
Scott Bakere4deddd2014-04-14 16:48:05 -0700306 sitesChanged[node.site.name] = sitesChanged.get(node.site.name,0) + 1
307
308 return sitesChanged
309
Scott Bakerfe6a7e12014-04-15 10:58:31 -0700310def slice_decrease_slivers(user, siteList, slice, count, noAct=False):
Scott Bakere4deddd2014-04-14 16:48:05 -0700311 sitesChanged = {}
312
Scott Bakerbd57a432014-04-14 16:12:15 -0700313 if siteList:
314 siteNames = [site.name for site in siteList]
315 else:
316 siteNames = None
317
318 for sliver in slice.slivers.all():
319 if (count <= 0):
320 break
321
322 node = sliver.node
323 if (not siteNames) or (node.site.name in siteNames):
324 print "deleting sliver", sliver, "at node", node.name, "of site", node.site.name
Scott Bakere4deddd2014-04-14 16:48:05 -0700325 if not noAct:
326 sliver.delete()
Scott Bakerbd57a432014-04-14 16:12:15 -0700327 count = count -1
Scott Baker6253dfc2014-04-14 09:29:30 -0700328
Scott Bakere4deddd2014-04-14 16:48:05 -0700329 sitesChanged[node.site.name] = sitesChanged.get(node.site.name,0) - 1
330
331 return sitesChanged
332
Scott Baker6253dfc2014-04-14 09:29:30 -0700333class TenantAddOrRemoveSliverView(View):
Scott Bakere4deddd2014-04-14 16:48:05 -0700334 """ Add or remove slivers from a Slice
335
336 Arguments:
337 siteName - name of site. If not specified, PlanetStack will pick the
338 best site.,
339 actionToDo - [add | rem]
340 count - number of slivers to add or remove
341 sliceName - name of slice
Scott Bakera880bb52014-04-14 23:52:48 -0700342 noAct - if set, no changes will be made to db, but result will still
343 show which sites would have been modified.
Scott Bakere4deddd2014-04-14 16:48:05 -0700344
345 Returns:
346 Dictionary of sites that were modified, and the count of nodes
347 that were added or removed at each site.
348 """
Scott Baker6253dfc2014-04-14 09:29:30 -0700349 def post(self, request, *args, **kwargs):
Scott Bakerbd57a432014-04-14 16:12:15 -0700350 siteName = request.POST.get("siteName", None)
351 actionToDo = request.POST.get("actionToDo", None)
352 count = int(request.POST.get("count","0"))
353 sliceName = request.POST.get("slice", None)
Scott Bakere4deddd2014-04-14 16:48:05 -0700354 noAct = request.POST.get("noAct", False)
Scott Bakerbd57a432014-04-14 16:12:15 -0700355
356 if not sliceName:
357 return HttpResponseServerError("No slice name given")
358
359 slice = Slice.objects.get(name=sliceName)
360
361 if siteName:
362 siteList = [Site.objects.get(name=siteName)]
363 else:
364 siteList = None
Scott Baker6253dfc2014-04-14 09:29:30 -0700365
366 if (actionToDo == "add"):
Scott Bakerbd57a432014-04-14 16:12:15 -0700367 user_ip = request.GET.get("ip", get_ip(request))
368 if (siteList is None):
369 siteList = tenant_pick_sites(user, user_ip, slice, count)
370
Scott Bakerfe6a7e12014-04-15 10:58:31 -0700371 sitesChanged = slice_increase_slivers(request.user, user_ip, siteList, slice, count, noAct)
Scott Baker6253dfc2014-04-14 09:29:30 -0700372 elif (actionToDo == "rem"):
Scott Bakerfe6a7e12014-04-15 10:58:31 -0700373 sitesChanged = slice_decrease_slivers(request.user, siteList, slice, count, noAct)
Scott Bakerbd57a432014-04-14 16:12:15 -0700374 else:
375 return HttpResponseServerError("Unknown actionToDo %s" % actionToDo)
376
Scott Bakere4deddd2014-04-14 16:48:05 -0700377 return HttpResponse(json.dumps(sitesChanged), mimetype='application/javascript')
Scott Bakerbd57a432014-04-14 16:12:15 -0700378
379 def get(self, request, *args, **kwargs):
380 request.POST = request.GET
381 return self.post(request, *args, **kwargs) # for testing REST in browser
382 #return HttpResponseServerError("GET is not supported")
383
384class TenantPickSitesView(View):
385 """ primarily just for testing purposes """
386 def get(self, request, *args, **kwargs):
387 count = request.GET.get("count","0")
388 slice = request.GET.get("slice",None)
389 if slice:
390 slice = Slice.objects.get(name=slice)
391 ip = request.GET.get("ip", get_ip(request))
392 sites = tenant_pick_sites(request.user, user_ip=ip, count=0, slice=slice)
393 sites = [x.name for x in sites]
394 return HttpResponse(json.dumps(sites), mimetype='application/javascript')
Scott Baker6253dfc2014-04-14 09:29:30 -0700395
Scott Baker771819b2014-03-19 22:10:17 -0700396class DashboardSummaryAjaxView(View):
397 def get(self, request, **kwargs):
398 return HttpResponse(json.dumps(hpc_wizard.get_hpc_wizard().get_summary_for_view()), mimetype='application/javascript')
399
400class DashboardAddOrRemoveSliverView(View):
Scott Bakerfe6a7e12014-04-15 10:58:31 -0700401 # TODO: deprecate this view in favor of using TenantAddOrRemoveSliverView
402
Scott Baker771819b2014-03-19 22:10:17 -0700403 def post(self, request, *args, **kwargs):
Scott Bakerfe6a7e12014-04-15 10:58:31 -0700404 siteName = request.POST.get("site", None)
Scott Baker771819b2014-03-19 22:10:17 -0700405 actionToDo = request.POST.get("actionToDo", "0")
406
Scott Bakerfe6a7e12014-04-15 10:58:31 -0700407 siteList = [Site.objects.get(name=siteName)]
408 slice = Slice.objects.get(name="HyperCache")
409
Scott Baker771819b2014-03-19 22:10:17 -0700410 if (actionToDo == "add"):
Scott Bakerfe6a7e12014-04-15 10:58:31 -0700411 user_ip = request.GET.get("ip", get_ip(request))
412 slice_increase_slivers(request.user, user_ip, siteList, slice, 1)
Scott Baker771819b2014-03-19 22:10:17 -0700413 elif (actionToDo == "rem"):
Scott Bakerfe6a7e12014-04-15 10:58:31 -0700414 slice_decrease_slivers(request.user, siteList, slice, 1)
Scott Baker771819b2014-03-19 22:10:17 -0700415
416 print '*' * 50
417 print 'Ask for site: ' + siteName + ' to ' + actionToDo + ' another HPC Sliver'
418 return HttpResponse('This is POST request ')
Siobhan Tully06d2c032014-02-23 23:48:52 -0500419
420class DashboardAjaxView(View):
421 def get(self, request, **kwargs):
422 return HttpResponse(json.dumps(getCDNOperatorData(True)), mimetype='application/javascript')
Scott Baker771819b2014-03-19 22:10:17 -0700423
424class DashboardAnalyticsAjaxView(View):
425 def get(self, request, name="hello_world", **kwargs):
426 if (name == "hpcSummary"):
427 return HttpResponse(json.dumps(hpc_wizard.get_hpc_wizard().get_summary_for_view()), mimetype='application/javascript')
428 elif (name == "hpcUserSite"):
429 return HttpResponse(json.dumps(getUserSliceInfo(request.user, True)), mimetype='application/javascript')
430 elif (name == "hpcMap"):
431 return HttpResponse(json.dumps(getCDNOperatorData(True)), mimetype='application/javascript')
432 elif (name == "bigquery"):
433 (mimetype, data) = DoPlanetStackAnalytics(request)
434 return HttpResponse(data, mimetype=mimetype)
435 else:
436 return HttpResponse(json.dumps("Unknown"), mimetype='application/javascript')
437