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
9 BLESSED_DEPLOYMENTS = ["US-MaxPlanck", "US-GeorgiaTech", "US-Princeton", "US-Washington", "US-Stanford"]
11 class RequestAccessView(View):
12 def post(self, request, *args, **kwargs):
13 email = request.POST.get("email", "0")
14 firstname = request.POST.get("firstname", "0")
15 lastname = request.POST.get("lastname", "0")
16 site = request.POST.get("site","0")
17 # see if it already exists
18 user=User.objects.filter(email=BaseUserManager.normalize_email(email))
22 # force a new email to be sent
23 user.is_registering=True
25 return HttpResponse(json.dumps({"error": "already_approved"}), content_type='application/javascript')
27 return HttpResponse(json.dumps({"error": "already_pending"}), content_type='application/javascript')
29 user=User.deleted_objects.filter(email=BaseUserManager.normalize_email(email))
31 return HttpResponse(json.dumps({"error": "is_deleted"}), content_type='application/javascript')
34 email=BaseUserManager.normalize_email(email),
42 user.site=Site.objects.get(name=site)
43 user.save(update_fields=['site'])
44 sitePriv = SitePrivilege.objects.filter(site=user.site)
46 userUrl = "http://"+request.get_host()+"/admin/core/user/"+str(userId)
48 subject, from_email, to = 'Authorize OpenCloud User Account', 'support@opencloud.us', str(sp.user)
49 text_content = 'This is an important message.'
50 html_content = """<p>Please authorize the following user on site """+site+""": <br><br>User: """+firstname+""" """+lastname+"""<br>Email: """+email+"""<br><br>
51 Check the checkbox next to Is Active property at <a href="""+userUrl+"""> this link</a> to authorize the user, and then click the Save button. 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>"""
52 msg = EmailMultiAlternatives(subject,text_content, from_email, [to])
53 msg.attach_alternative(html_content, "text/html")
55 return HttpResponse(serializers.serialize("json",[user,]), content_type='application/javascript')
57 class TenantCreateSlice(View):
58 def post(self, request, *args, **kwargs):
59 if request.user.isReadOnlyUser():
60 return HttpResponseForbidden("User is in read-only mode")
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 networkPorts = request.POST.get("network","0")
67 mountDataSets = request.POST.get("mountDataSets","0")
68 privateVolume = request.POST.get("privateVolume","0")
69 userEmail = request.POST.get("userEmail","0")
70 if (actionToDo == "add"):
71 serviceClass = ServiceClass.objects.get(name=serviceClass)
72 site = request.user.site
73 image = Image.objects.get(name=imageName)
74 newSlice = Slice(name=sliceName,serviceClass=serviceClass,site=site,image_preference=image,mount_data_sets=mountDataSets)
76 privateTemplate="Private"
77 publicTemplate="Public shared IPv4"
\r
78 privateNetworkName = sliceName+"-"+privateTemplate
\r
79 publicNetworkName = sliceName+"-"+publicTemplate
\r
80 slice=Slice.objects.get(name=sliceName)
\r
81 addNetwork(privateNetworkName,privateTemplate,slice)
\r
82 addNetwork(publicNetworkName,publicTemplate,slice)
\r
83 addOrModifyPorts(networkPorts,sliceName)
\r
84 if privateVolume=="true":
\r
85 privateVolForSlice(request.user,sliceName)
86 slicePrivs=SlicePrivilege(user=User.objects.get(email=userEmail),slice=Slice.objects.get(name=sliceName),role=SliceRole.objects.get(role="admin"))
88 return HttpResponse(json.dumps("Slice created"), content_type='application/javascript')
90 class TenantAddUser(View):
91 def post(self, request, *args, **kwargs):
92 if request.user.isReadOnlyUser():
93 return HttpResponseForbidden("User is in read-only mode")
95 sliceName = request.POST.get("sliceName", "0")
96 userEmail = request.POST.get("userEmail","0")
97 slicePrivs=SlicePrivilege(user=User.objects.get(email=userEmail),slice=Slice.objects.get(name=sliceName),role=SliceRole.objects.get(role="admin"))
99 return HttpResponse(json.dumps("Slice created"), content_type='application/javascript')
101 def privateVolForSlice(user,sliceName):
102 if not hasPrivateVolume(sliceName):
\r
103 volumeName=createPrivateVolume(user,sliceName)
\r
105 mountVolume(sliceName,volumeName,readWrite)
107 class TenantUpdateSlice(View):
108 def post(self, request, *args, **kwargs):
\r
109 if request.user.isReadOnlyUser():
\r
110 return HttpResponseForbidden("User is in read-only mode")
\r
112 sliceName = request.POST.get("sliceName", "0")
\r
113 serviceClass = request.POST.get("serviceClass", "0")
\r
114 imageName = request.POST.get("imageName", "0")
\r
115 actionToDo = request.POST.get("actionToDo", "0")
\r
116 networkPorts = request.POST.get("networkPorts","0")
\r
117 dataSet = request.POST.get("dataSet","0")
\r
118 privateVolume = request.POST.get("privateVolume","0")
\r
119 slice = Slice.objects.all()
\r
120 for entry in slice:
\r
121 serviceClass = ServiceClass.objects.get(name=serviceClass)
\r
122 if(entry.name==sliceName):
\r
123 if (actionToDo == "update"):
\r
124 setattr(entry,'serviceClass',serviceClass)
\r
125 setattr(entry,'image_preference',imageName)
\r
126 setattr(entry,'mount_data_sets',dataSet)
\r
129 addOrModifyPorts(networkPorts,sliceName)
\r
130 if privateVolume=="true":
\r
131 privateVolForSlice(request.user,sliceName)
\r
132 return HttpResponse(json.dumps("Slice updated"), content_type='application/javascript')
\r
134 def addNetwork(name,template,sliceName):
\r
135 networkTemplate=NetworkTemplate.objects.get(name=template)
\r
136 newNetwork = Network(name = name,
\r
137 template = networkTemplate,
\r
140 addNetworkSlice(newNetwork,sliceName)
\r
142 def addNetworkSlice(networkSlice,sliceName):
\r
143 newNetworkSlice=NetworkSlice(network =networkSlice,
\r
145 newNetworkSlice.save()
\r
147 def addOrModifyPorts(networkPorts,sliceName):
\r
148 networkList = Network.objects.all()
\r
151 for networkEntry in networkList:
\r
152 networkSlices = networkEntry.slices.all()
\r
153 for slice in networkSlices:
\r
154 if slice.name==sliceName:
\r
155 if networkEntry.template.name=="Public shared IPv4":
\r
156 setattr(networkEntry,'ports',networkPorts)
\r
157 networkEntry.save()
\r
159 def getTenantSliceInfo(user, tableFormat = False):
160 tenantSliceDetails = {}
161 tenantSliceData = getTenantInfo(user)
162 tenantServiceClassData = getServiceClassInfo(user)
164 tenantSliceDetails['userSliceInfo'] = userSliceTableFormatter(tenantSliceData)
165 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
167 tenantSliceDetails['userSliceInfo'] = tenantSliceData
168 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
169 tenantSliceDetails['image']=userSliceTableFormatter(getImageInfo(user))
170 tenantSliceDetails['deploymentSites']=userSliceTableFormatter(getDeploymentSites())
171 #tenantSliceDetails['sites'] = userSliceTableFormatter(getTenantSitesInfo())
172 tenantSliceDetails['mountDataSets'] = userSliceTableFormatter(getMountDataSets())
173 tenantSliceDetails['publicKey'] = getPublicKey(user)
174 tenantSliceDetails['availableSites']=userSliceTableFormatter(getAvailableSites())
175 tenantSliceDetails['role']=getUserRole(user)
176 tenantSliceDetails['siteUsers']=getSiteUsers(user)
177 return tenantSliceDetails
179 def getSiteUsers(user):
180 users = User.objects.filter(site=user.site)
183 siteUsers.append(str(entry))
187 def getUserRole(user):
188 sp=SitePrivilege.objects.filter(user=user)
190 return str(entry.role)
193 def getTenantInfo(user):
194 slices =Slice.objects.all()
197 if (entry.site == user.site):
198 sliceName = Slice.objects.get(id=entry.id).name
199 slice = Slice.objects.get(name=Slice.objects.get(id=entry.id).name)
200 sliceServiceClass = entry.serviceClass.name
201 preferredImage = entry.image_preference
202 #sliceDataSet = entry.mount_data_sets
209 #createPrivateVolume(user,sliceName)
210 available_sites = getAvailableSites()
211 for sliver in slice.slivers.all():
212 if sliver.node.site.name in available_sites:
213 sliceSite[sliver.node.site.name] = sliceSite.get(sliver.node.site.name,0) + 1
214 sliceImage = sliver.image.name
215 sliceNode[str(sliver)] = sliver.node.name
216 numSliver = sum(sliceSite.values())
217 numSites = len(sliceSite)
218 userSliceInfo.append({'sliceName': sliceName,'sliceServiceClass': sliceServiceClass,'preferredImage':preferredImage,'numOfSites':numSites, 'sliceSite':sliceSite,'sliceImage':sliceImage,'numOfSlivers':numSliver,'instanceNodePair':sliceNode})
221 def getTenantSitesInfo():
222 availableSites=getAvailableSites()
224 for entry in Site.objects.all():
225 if entry.name in availableSites:
226 tenantSiteInfo.append({'siteName':entry.name})
227 return tenantSiteInfo
229 def getPublicKey(user):
230 users=User.objects.all()
\r
232 if (str(key.email)==str(user)):
\r
233 sshKey = key.public_key
\r
236 def getServiceClassInfo(user):
237 serviceClassList = ServiceClass.objects.all()
239 for entry in serviceClassList:
240 sliceInfo.append({'serviceClass':entry.name})
243 def getImageInfo(user):
244 #imageList = Image.objects.all()
246 #for imageEntry in imageList:
247 #imageInfo.append({'Image':imageEntry.name})
250 length = len(BLESSED_DEPLOYMENTS)
251 for deployment in Deployment.objects.all():
252 if deployment.name in BLESSED_DEPLOYMENTS:
253 for x in deployment.imagedeployments.all():
254 tempImageInfo.append(x.image.name)
256 for i in set(tempImageInfo):
257 temp[i] = tempImageInfo.count(i)
260 imageInfo.append(key)
263 def createPrivateVolume(user, sliceName):
264 caps = Volume.CAP_READ_DATA | Volume.CAP_WRITE_DATA | Volume.CAP_HOST_DATA
265 getattr(Volume.default_gateway_caps,"read data") | \
266 getattr(Volume.default_gateway_caps,"write data") | \
267 getattr(Volume.default_gateway_caps,"host files")
268 v = Volume(name="private_" + sliceName, owner_id=user, description="private volume for %s" % sliceName, blocksize=61440, private=True, archive=False, default_gateway_caps = caps)
272 SYNDICATE_REPLICATE_PORTNUM = 1025
276 inuse[SYNDICATE_REPLICATE_PORTNUM] = True
277 for vs in VolumeSlice.objects.all():
278 inuse[vs.peer_portnum]=True
279 inuse[vs.replicate_portnum]=True
280 for network in Network.objects.all():
281 if not network.ports:
283 network_ports = [x.strip() for x in network.ports.split(",")]
284 for network_port in network_ports:
286 inuse[int(network_port)] = True
288 # in case someone has put a malformed port number in the list
290 for i in range(1025, 65535):
291 if not inuse.get(i,False):
295 def mountVolume(sliceName, volumeName, readWrite):
296 slice = Slice.objects.get(name=sliceName)
297 volume = Volume.objects.get(name=volumeName)
298 # choose some unused port numbers
299 flags = Volume.CAP_READ_DATA
301 flags = flags | Volume.CAP_WRITE_DATA
302 vs = VolumeSlice(volume_id = volume, slice_id = slice, gateway_caps=flags, peer_portnum = get_free_port(), replicate_portnum = SYNDICATE_REPLICATE_PORTNUM)
305 def hasPrivateVolume(sliceName):
306 slice = Slice.objects.get(name=sliceName)
307 for vs in VolumeSlice.objects.filter(slice_id=slice):
308 if vs.volume_id.private:
312 def getMountDataSets():
314 for volume in Volume.objects.all():
\r
315 if not volume.private:
\r
316 dataSetInfo.append({'DataSet': volume.name})
\r
320 def getDeploymentSites():
321 deploymentList = Deployment.objects.all()
323 for entry in deploymentList:
324 deploymentInfo.append({'DeploymentSite':entry.name})
325 return deploymentInfo
327 def getAvailableSites():
329 for deployment in Deployment.objects.all():
330 if deployment.name in BLESSED_DEPLOYMENTS:
331 for x in deployment.sitedeployments.all():
332 if x.site.nodes.all():
333 available_sites.append(x.site.name)
334 return list(set(available_sites))
336 class TenantDeleteSliceView(View):
337 def post(self,request):
\r
338 if request.user.isReadOnlyUser():
\r
339 return HttpResponseForbidden("User is in read-only mode")
\r
340 sliceName = request.POST.get("sliceName",None)
\r
341 slice = Slice.objects.get(name=sliceName)
\r
342 #print slice, slice.id
\r
343 sliceToDel=Slice(name=sliceName, id=slice.id)
\r
345 return HttpResponse(json.dumps("Slice deleted"), content_type='application/javascript')
347 class TenantAddOrRemoveSliverView(View):
348 """ Add or remove slivers from a Slice
351 siteName - name of site. If not specified, PlanetStack will pick the
353 actionToDo - [add | rem]
354 count - number of slivers to add or remove
355 sliceName - name of slice
356 noAct - if set, no changes will be made to db, but result will still
357 show which sites would have been modified.
360 Dictionary of sites that were modified, and the count of nodes
361 that were added or removed at each site.
363 def post(self, request, *args, **kwargs):
364 siteName = request.POST.get("siteName", None)
365 actionToDo = request.POST.get("actionToDo", None)
366 count = int(request.POST.get("count","0"))
367 sliceName = request.POST.get("slice", None)
368 imageName = request.POST.get("image",None)
369 noAct = request.POST.get("noAct", False)
372 return HttpResponseServerError("No slice name given")
374 slice = Slice.objects.get(name=sliceName)
375 image = Image.objects.get(name=imageName)
378 siteList = [Site.objects.get(name=siteName)]
382 if (actionToDo == "add"):
383 user_ip = request.GET.get("ip", get_ip(request))
384 if (siteList is None):
385 siteList = tenant_pick_sites(user, user_ip, slice, count)
387 sitesChanged = slice_increase_slivers(request.user, user_ip, siteList, slice, image, count, noAct)
388 elif (actionToDo == "rem"):
389 sitesChanged = slice_decrease_slivers(request.user, siteList, slice, count, noAct)
391 return HttpResponseServerError("Unknown actionToDo %s" % actionToDo)
393 return HttpResponse(json.dumps(sitesChanged), content_type='application/javascript')
395 def get(self, request, *args, **kwargs):
396 request.POST = request.GET
397 return self.post(request, *args, **kwargs) # for testing REST in browser
398 #return HttpResponseServerError("GET is not supported")
400 class TenantPickSitesView(View):
401 """ primarily just for testing purposes """
402 def get(self, request, *args, **kwargs):
403 count = request.GET.get("count","0")
404 slice = request.GET.get("slice",None)
406 slice = Slice.objects.get(name=slice)
407 ip = request.GET.get("ip", get_ip(request))
408 sites = tenant_pick_sites(request.user, user_ip=ip, count=0, slice=slice)
409 sites = [x.name for x in sites]
410 return HttpResponse(json.dumps(sites), content_type='application/javascript')
412 def siteSortKey(site, slice=None, count=None, lat=None, lon=None):
413 # try to pick a site we're already using
414 has_slivers_here=False
416 for sliver in slice.slivers.all():
417 if sliver.node.site.name == site.name:
418 has_slivers_here=True
421 d = haversine(site.location.latitude, site.location.longitude, lat, lon)
423 return (-has_slivers_here, d)
425 def tenant_pick_sites(user, user_ip=None, slice=None, count=None):
426 """ Returns list of sites, sorted from most favorable to least favorable """
430 client_geo = GeoIP().city(user_ip)
432 lat=float(client_geo["latitude"])
433 lon=float(client_geo["longitude"])
435 print "exception in geo code"
436 traceback.print_exc()
438 available_sites = getAvailableSites()
439 sites = Site.objects.all()
440 sites = [x for x in sites if x.name in available_sites]
441 sites = sorted(sites, key=functools.partial(siteSortKey, slice=slice, count=count, lat=lat, lon=lon))
445 class TenantViewData(View):
446 def get(self, request, **kwargs):
447 return HttpResponse(json.dumps(getTenantSliceInfo(request.user, True)), content_type='application/javascript')
449 class RequestAccountView(View):
450 def get(self, request, **kwargs):
451 return HttpResponse()