1 from view_common import *
2 from core.models import *
4 from django.contrib.auth.models import BaseUserManager
5 from django.core import serializers
6 from django.core.mail import EmailMultiAlternatives
8 BLESSED_DEPLOYMENTS = ["US-MaxPlanck", "US-GeorgiaTech", "US-Princeton", "US-Washington", "US-Stanford"]
10 class RequestAccessView(View):
11 def post(self, request, *args, **kwargs):
12 email = request.POST.get("email", "0")
13 firstname = request.POST.get("firstname", "0")
14 lastname = request.POST.get("lastname", "0")
15 site = request.POST.get("site","0")
17 email=BaseUserManager.normalize_email(email),
23 user.site=Site.objects.get(name=site)
24 user.save(update_fields=['site'])
25 sitePriv = SitePrivilege.objects.filter(site=user.site)
27 userUrl = "http://"+request.get_host()+"/admin/core/user/"+str(userId)
29 subject, from_email, to = 'Authorize OpenCloud User Account', 'support@opencloud.us', str(sp.user)
30 text_content = 'This is an important message.'
31 html_content = """<p>Please authorize the following user on site """+site+""": <br><br>User: """+firstname+""" """+lastname+"""<br>Email: """+email+"""<br><br>
32 Check the checkbox next to Is Active property at <a href="""+userUrl+"""> this link</a> to authorize the user. If you do not recognize this individual, or otherwise do not want to approve this account, please ignore this email. If you do not approve this request in 48 hours, the account will automatically be deleted.</p>"""
33 msg = EmailMultiAlternatives(subject,text_content, from_email, [to])
34 msg.attach_alternative(html_content, "text/html")
36 return HttpResponse(serializers.serialize("json",[user,]), content_type='application/javascript')
38 class TenantCreateSlice(View):
39 def post(self, request, *args, **kwargs):
40 if request.user.isReadOnlyUser():
41 return HttpResponseForbidden("User is in read-only mode")
43 sliceName = request.POST.get("sliceName", "0")
44 serviceClass = request.POST.get("serviceClass", "0")
45 imageName = request.POST.get("imageName", "0")
46 actionToDo = request.POST.get("actionToDo", "0")
47 networkPorts = request.POST.get("network","0")
48 mountDataSets = request.POST.get("mountDataSets","0")
49 privateVolume = request.POST.get("privateVolume","0")
50 userEmail = request.POST.get("userEmail","0")
51 if (actionToDo == "add"):
52 serviceClass = ServiceClass.objects.get(name=serviceClass)
53 site = request.user.site
54 image = Image.objects.get(name=imageName)
55 newSlice = Slice(name=sliceName,serviceClass=serviceClass,site=site,imagePreference=image,mountDataSets=mountDataSets)
57 privateTemplate="Private"
58 publicTemplate="Public shared IPv4"
\r
59 privateNetworkName = sliceName+"-"+privateTemplate
\r
60 publicNetworkName = sliceName+"-"+publicTemplate
\r
61 slice=Slice.objects.get(name=sliceName)
\r
62 addNetwork(privateNetworkName,privateTemplate,slice)
\r
63 addNetwork(publicNetworkName,publicTemplate,slice)
\r
64 addOrModifyPorts(networkPorts,sliceName)
\r
65 if privateVolume=="true":
\r
66 privateVolForSlice(request.user,sliceName)
67 slicePrivs=SlicePrivilege(user=User.objects.get(email=userEmail),slice=Slice.objects.get(name=sliceName),role=SliceRole.objects.get(role="admin"))
69 return HttpResponse(json.dumps("Slice created"), content_type='application/javascript')
71 class TenantAddUser(View):
72 def post(self, request, *args, **kwargs):
73 if request.user.isReadOnlyUser():
74 return HttpResponseForbidden("User is in read-only mode")
76 sliceName = request.POST.get("sliceName", "0")
77 userEmail = request.POST.get("userEmail","0")
78 slicePrivs=SlicePrivilege(user=User.objects.get(email=userEmail),slice=Slice.objects.get(name=sliceName),role=SliceRole.objects.get(role="admin"))
80 return HttpResponse(json.dumps("Slice created"), content_type='application/javascript')
82 def privateVolForSlice(user,sliceName):
83 if not hasPrivateVolume(sliceName):
\r
84 volumeName=createPrivateVolume(user,sliceName)
\r
86 mountVolume(sliceName,volumeName,readWrite)
88 class TenantUpdateSlice(View):
89 def post(self, request, *args, **kwargs):
\r
90 if request.user.isReadOnlyUser():
\r
91 return HttpResponseForbidden("User is in read-only mode")
\r
93 sliceName = request.POST.get("sliceName", "0")
\r
94 serviceClass = request.POST.get("serviceClass", "0")
\r
95 imageName = request.POST.get("imageName", "0")
\r
96 actionToDo = request.POST.get("actionToDo", "0")
\r
97 networkPorts = request.POST.get("networkPorts","0")
\r
98 dataSet = request.POST.get("dataSet","0")
\r
99 privateVolume = request.POST.get("privateVolume","0")
\r
100 slice = Slice.objects.all()
\r
101 for entry in slice:
\r
102 serviceClass = ServiceClass.objects.get(name=serviceClass)
\r
103 if(entry.name==sliceName):
\r
104 if (actionToDo == "update"):
\r
105 setattr(entry,'serviceClass',serviceClass)
\r
106 setattr(entry,'imagePreference',imageName)
\r
107 setattr(entry,'mountDataSets',dataSet)
\r
110 addOrModifyPorts(networkPorts,sliceName)
\r
111 if privateVolume=="true":
\r
112 privateVolForSlice(request.user,sliceName)
\r
113 return HttpResponse(json.dumps("Slice updated"), content_type='application/javascript')
\r
115 def addNetwork(name,template,sliceName):
\r
116 networkTemplate=NetworkTemplate.objects.get(name=template)
\r
117 newNetwork = Network(name = name,
\r
118 template = networkTemplate,
\r
121 addNetworkSlice(newNetwork,sliceName)
\r
123 def addNetworkSlice(networkSlice,sliceName):
\r
124 newNetworkSlice=NetworkSlice(network =networkSlice,
\r
126 newNetworkSlice.save()
\r
128 def addOrModifyPorts(networkPorts,sliceName):
\r
129 networkList = Network.objects.all()
\r
132 for networkEntry in networkList:
\r
133 networkSlices = networkEntry.slices.all()
\r
134 for slice in networkSlices:
\r
135 if slice.name==sliceName:
\r
136 if networkEntry.template.name=="Public shared IPv4":
\r
137 setattr(networkEntry,'ports',networkPorts)
\r
138 networkEntry.save()
\r
140 def getTenantSliceInfo(user, tableFormat = False):
141 tenantSliceDetails = {}
142 tenantSliceData = getTenantInfo(user)
143 tenantServiceClassData = getServiceClassInfo(user)
145 tenantSliceDetails['userSliceInfo'] = userSliceTableFormatter(tenantSliceData)
146 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
148 tenantSliceDetails['userSliceInfo'] = tenantSliceData
149 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
150 tenantSliceDetails['image']=userSliceTableFormatter(getImageInfo(user))
151 tenantSliceDetails['deploymentSites']=userSliceTableFormatter(getDeploymentSites())
152 #tenantSliceDetails['sites'] = userSliceTableFormatter(getTenantSitesInfo())
153 tenantSliceDetails['mountDataSets'] = userSliceTableFormatter(getMountDataSets())
154 tenantSliceDetails['publicKey'] = getPublicKey(user)
155 tenantSliceDetails['availableSites']=userSliceTableFormatter(getAvailableSites())
156 tenantSliceDetails['role']=getUserRole(user)
157 tenantSliceDetails['siteUsers']=getSiteUsers(user)
158 return tenantSliceDetails
160 def getSiteUsers(user):
161 users = User.objects.filter(site=user.site)
164 siteUsers.append(str(entry))
168 def getUserRole(user):
169 sp=SitePrivilege.objects.filter(user=user)
171 return str(entry.role)
174 def getTenantInfo(user):
175 slices =Slice.objects.all()
178 if (entry.site == user.site):
179 sliceName = Slice.objects.get(id=entry.id).name
180 slice = Slice.objects.get(name=Slice.objects.get(id=entry.id).name)
181 sliceServiceClass = entry.serviceClass.name
182 preferredImage = entry.imagePreference
183 #sliceDataSet = entry.mountDataSets
190 #createPrivateVolume(user,sliceName)
191 available_sites = getAvailableSites()
192 for sliver in slice.slivers.all():
193 if sliver.node.site.name in available_sites:
194 sliceSite[sliver.node.site.name] = sliceSite.get(sliver.node.site.name,0) + 1
195 sliceImage = sliver.image.name
196 sliceNode[str(sliver)] = sliver.node.name
197 numSliver = sum(sliceSite.values())
198 numSites = len(sliceSite)
199 userSliceInfo.append({'sliceName': sliceName,'sliceServiceClass': sliceServiceClass,'preferredImage':preferredImage,'numOfSites':numSites, 'sliceSite':sliceSite,'sliceImage':sliceImage,'numOfSlivers':numSliver,'instanceNodePair':sliceNode})
202 def getTenantSitesInfo():
203 availableSites=getAvailableSites()
205 for entry in Site.objects.all():
206 if entry.name in availableSites:
207 tenantSiteInfo.append({'siteName':entry.name})
208 return tenantSiteInfo
210 def getPublicKey(user):
211 users=User.objects.all()
\r
213 if (str(key.email)==str(user)):
\r
214 sshKey = key.public_key
\r
217 def getServiceClassInfo(user):
218 serviceClassList = ServiceClass.objects.all()
220 for entry in serviceClassList:
221 sliceInfo.append({'serviceClass':entry.name})
224 def getImageInfo(user):
225 #imageList = Image.objects.all()
227 #for imageEntry in imageList:
228 #imageInfo.append({'Image':imageEntry.name})
231 length = len(BLESSED_DEPLOYMENTS)
232 for deployment in Deployment.objects.all():
233 if deployment.name in BLESSED_DEPLOYMENTS:
234 for x in deployment.imagedeployments.all():
235 tempImageInfo.append(x.image.name)
237 for i in set(tempImageInfo):
238 temp[i] = tempImageInfo.count(i)
241 imageInfo.append(key)
244 def createPrivateVolume(user, sliceName):
245 caps = Volume.CAP_READ_DATA | Volume.CAP_WRITE_DATA | Volume.CAP_HOST_DATA
246 getattr(Volume.default_gateway_caps,"read data") | \
247 getattr(Volume.default_gateway_caps,"write data") | \
248 getattr(Volume.default_gateway_caps,"host files")
249 v = Volume(name="private_" + sliceName, owner_id=user, description="private volume for %s" % sliceName, blocksize=61440, private=True, archive=False, default_gateway_caps = caps)
253 SYNDICATE_REPLICATE_PORTNUM = 1025
257 inuse[SYNDICATE_REPLICATE_PORTNUM] = True
258 for vs in VolumeSlice.objects.all():
259 inuse[vs.peer_portnum]=True
260 inuse[vs.replicate_portnum]=True
261 for network in Network.objects.all():
262 if not network.ports:
264 network_ports = [x.strip() for x in network.ports.split(",")]
265 for network_port in network_ports:
267 inuse[int(network_port)] = True
269 # in case someone has put a malformed port number in the list
271 for i in range(1025, 65535):
272 if not inuse.get(i,False):
276 def mountVolume(sliceName, volumeName, readWrite):
277 slice = Slice.objects.get(name=sliceName)
278 volume = Volume.objects.get(name=volumeName)
279 # choose some unused port numbers
280 flags = Volume.CAP_READ_DATA
282 flags = flags | Volume.CAP_WRITE_DATA
283 vs = VolumeSlice(volume_id = volume, slice_id = slice, gateway_caps=flags, peer_portnum = get_free_port(), replicate_portnum = SYNDICATE_REPLICATE_PORTNUM)
286 def hasPrivateVolume(sliceName):
287 slice = Slice.objects.get(name=sliceName)
288 for vs in VolumeSlice.objects.filter(slice_id=slice):
289 if vs.volume_id.private:
293 def getMountDataSets():
295 for volume in Volume.objects.all():
\r
296 if not volume.private:
\r
297 dataSetInfo.append({'DataSet': volume.name})
\r
301 def getDeploymentSites():
302 deploymentList = Deployment.objects.all()
304 for entry in deploymentList:
305 deploymentInfo.append({'DeploymentSite':entry.name})
306 return deploymentInfo
308 def getAvailableSites():
310 for deployment in Deployment.objects.all():
311 if deployment.name in BLESSED_DEPLOYMENTS:
312 for x in deployment.sitedeployments.all():
313 if x.site.nodes.all():
314 available_sites.append(x.site.name)
315 return list(set(available_sites))
317 class TenantDeleteSliceView(View):
318 def post(self,request):
\r
319 if request.user.isReadOnlyUser():
\r
320 return HttpResponseForbidden("User is in read-only mode")
\r
321 sliceName = request.POST.get("sliceName",None)
\r
322 slice = Slice.objects.get(name=sliceName)
\r
323 #print slice, slice.id
\r
324 sliceToDel=Slice(name=sliceName, id=slice.id)
\r
326 return HttpResponse(json.dumps("Slice deleted"), content_type='application/javascript')
328 class TenantAddOrRemoveSliverView(View):
329 """ Add or remove slivers from a Slice
332 siteName - name of site. If not specified, PlanetStack will pick the
334 actionToDo - [add | rem]
335 count - number of slivers to add or remove
336 sliceName - name of slice
337 noAct - if set, no changes will be made to db, but result will still
338 show which sites would have been modified.
341 Dictionary of sites that were modified, and the count of nodes
342 that were added or removed at each site.
344 def post(self, request, *args, **kwargs):
345 siteName = request.POST.get("siteName", None)
346 actionToDo = request.POST.get("actionToDo", None)
347 count = int(request.POST.get("count","0"))
348 sliceName = request.POST.get("slice", None)
349 imageName = request.POST.get("image",None)
350 noAct = request.POST.get("noAct", False)
353 return HttpResponseServerError("No slice name given")
355 slice = Slice.objects.get(name=sliceName)
356 image = Image.objects.get(name=imageName)
359 siteList = [Site.objects.get(name=siteName)]
363 if (actionToDo == "add"):
364 user_ip = request.GET.get("ip", get_ip(request))
365 if (siteList is None):
366 siteList = tenant_pick_sites(user, user_ip, slice, count)
368 sitesChanged = slice_increase_slivers(request.user, user_ip, siteList, slice, image, count, noAct)
369 elif (actionToDo == "rem"):
370 sitesChanged = slice_decrease_slivers(request.user, siteList, slice, count, noAct)
372 return HttpResponseServerError("Unknown actionToDo %s" % actionToDo)
374 return HttpResponse(json.dumps(sitesChanged), content_type='application/javascript')
376 def get(self, request, *args, **kwargs):
377 request.POST = request.GET
378 return self.post(request, *args, **kwargs) # for testing REST in browser
379 #return HttpResponseServerError("GET is not supported")
381 class TenantPickSitesView(View):
382 """ primarily just for testing purposes """
383 def get(self, request, *args, **kwargs):
384 count = request.GET.get("count","0")
385 slice = request.GET.get("slice",None)
387 slice = Slice.objects.get(name=slice)
388 ip = request.GET.get("ip", get_ip(request))
389 sites = tenant_pick_sites(request.user, user_ip=ip, count=0, slice=slice)
390 sites = [x.name for x in sites]
391 return HttpResponse(json.dumps(sites), content_type='application/javascript')
393 def siteSortKey(site, slice=None, count=None, lat=None, lon=None):
394 # try to pick a site we're already using
395 has_slivers_here=False
397 for sliver in slice.slivers.all():
398 if sliver.node.site.name == site.name:
399 has_slivers_here=True
402 d = haversine(site.location.latitude, site.location.longitude, lat, lon)
404 return (-has_slivers_here, d)
406 def tenant_pick_sites(user, user_ip=None, slice=None, count=None):
407 """ Returns list of sites, sorted from most favorable to least favorable """
411 client_geo = GeoIP().city(user_ip)
413 lat=float(client_geo["latitude"])
414 lon=float(client_geo["longitude"])
416 print "exception in geo code"
417 traceback.print_exc()
419 available_sites = getAvailableSites()
420 sites = Site.objects.all()
421 sites = [x for x in sites if x.name in available_sites]
422 sites = sorted(sites, key=functools.partial(siteSortKey, slice=slice, count=count, lat=lat, lon=lon))
426 class TenantViewData(View):
427 def get(self, request, **kwargs):
428 return HttpResponse(json.dumps(getTenantSliceInfo(request.user, True)), content_type='application/javascript')
430 class RequestAccountView(View):
431 def get(self, request, **kwargs):
432 return HttpResponse()