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')
30 email=BaseUserManager.normalize_email(email),
38 user.site=Site.objects.get(name=site)
39 user.save(update_fields=['site'])
40 sitePriv = SitePrivilege.objects.filter(site=user.site)
42 userUrl = "http://"+request.get_host()+"/admin/core/user/"+str(userId)
44 subject, from_email, to = 'Authorize OpenCloud User Account', 'support@opencloud.us', str(sp.user)
45 text_content = 'This is an important message.'
46 html_content = """<p>Please authorize the following user on site """+site+""": <br><br>User: """+firstname+""" """+lastname+"""<br>Email: """+email+"""<br><br>
47 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>"""
48 msg = EmailMultiAlternatives(subject,text_content, from_email, [to])
49 msg.attach_alternative(html_content, "text/html")
51 return HttpResponse(serializers.serialize("json",[user,]), content_type='application/javascript')
53 class TenantCreateSlice(View):
54 def post(self, request, *args, **kwargs):
55 if request.user.isReadOnlyUser():
56 return HttpResponseForbidden("User is in read-only mode")
58 sliceName = request.POST.get("sliceName", "0")
59 serviceClass = request.POST.get("serviceClass", "0")
60 imageName = request.POST.get("imageName", "0")
61 actionToDo = request.POST.get("actionToDo", "0")
62 networkPorts = request.POST.get("network","0")
63 mountDataSets = request.POST.get("mountDataSets","0")
64 privateVolume = request.POST.get("privateVolume","0")
65 userEmail = request.POST.get("userEmail","0")
66 if (actionToDo == "add"):
67 serviceClass = ServiceClass.objects.get(name=serviceClass)
68 site = request.user.site
69 image = Image.objects.get(name=imageName)
70 newSlice = Slice(name=sliceName,serviceClass=serviceClass,site=site,image_preference=image,mount_data_sets=mountDataSets)
72 privateTemplate="Private"
73 publicTemplate="Public shared IPv4"
\r
74 privateNetworkName = sliceName+"-"+privateTemplate
\r
75 publicNetworkName = sliceName+"-"+publicTemplate
\r
76 slice=Slice.objects.get(name=sliceName)
\r
77 addNetwork(privateNetworkName,privateTemplate,slice)
\r
78 addNetwork(publicNetworkName,publicTemplate,slice)
\r
79 addOrModifyPorts(networkPorts,sliceName)
\r
80 if privateVolume=="true":
\r
81 privateVolForSlice(request.user,sliceName)
82 slicePrivs=SlicePrivilege(user=User.objects.get(email=userEmail),slice=Slice.objects.get(name=sliceName),role=SliceRole.objects.get(role="admin"))
84 return HttpResponse(json.dumps("Slice created"), content_type='application/javascript')
86 class TenantAddUser(View):
87 def post(self, request, *args, **kwargs):
88 if request.user.isReadOnlyUser():
89 return HttpResponseForbidden("User is in read-only mode")
91 sliceName = request.POST.get("sliceName", "0")
92 userEmail = request.POST.get("userEmail","0")
93 slicePrivs=SlicePrivilege(user=User.objects.get(email=userEmail),slice=Slice.objects.get(name=sliceName),role=SliceRole.objects.get(role="admin"))
95 return HttpResponse(json.dumps("Slice created"), content_type='application/javascript')
97 def privateVolForSlice(user,sliceName):
98 if not hasPrivateVolume(sliceName):
\r
99 volumeName=createPrivateVolume(user,sliceName)
\r
101 mountVolume(sliceName,volumeName,readWrite)
103 class TenantUpdateSlice(View):
104 def post(self, request, *args, **kwargs):
\r
105 if request.user.isReadOnlyUser():
\r
106 return HttpResponseForbidden("User is in read-only mode")
\r
108 sliceName = request.POST.get("sliceName", "0")
\r
109 serviceClass = request.POST.get("serviceClass", "0")
\r
110 imageName = request.POST.get("imageName", "0")
\r
111 actionToDo = request.POST.get("actionToDo", "0")
\r
112 networkPorts = request.POST.get("networkPorts","0")
\r
113 dataSet = request.POST.get("dataSet","0")
\r
114 privateVolume = request.POST.get("privateVolume","0")
\r
115 slice = Slice.objects.all()
\r
116 for entry in slice:
\r
117 serviceClass = ServiceClass.objects.get(name=serviceClass)
\r
118 if(entry.name==sliceName):
\r
119 if (actionToDo == "update"):
\r
120 setattr(entry,'serviceClass',serviceClass)
\r
121 setattr(entry,'image_preference',imageName)
\r
122 setattr(entry,'mount_data_sets',dataSet)
\r
125 addOrModifyPorts(networkPorts,sliceName)
\r
126 if privateVolume=="true":
\r
127 privateVolForSlice(request.user,sliceName)
\r
128 return HttpResponse(json.dumps("Slice updated"), content_type='application/javascript')
\r
130 def addNetwork(name,template,sliceName):
\r
131 networkTemplate=NetworkTemplate.objects.get(name=template)
\r
132 newNetwork = Network(name = name,
\r
133 template = networkTemplate,
\r
136 addNetworkSlice(newNetwork,sliceName)
\r
138 def addNetworkSlice(networkSlice,sliceName):
\r
139 newNetworkSlice=NetworkSlice(network =networkSlice,
\r
141 newNetworkSlice.save()
\r
143 def addOrModifyPorts(networkPorts,sliceName):
\r
144 networkList = Network.objects.all()
\r
147 for networkEntry in networkList:
\r
148 networkSlices = networkEntry.slices.all()
\r
149 for slice in networkSlices:
\r
150 if slice.name==sliceName:
\r
151 if networkEntry.template.name=="Public shared IPv4":
\r
152 setattr(networkEntry,'ports',networkPorts)
\r
153 networkEntry.save()
\r
155 def getTenantSliceInfo(user, tableFormat = False):
156 tenantSliceDetails = {}
157 tenantSliceData = getTenantInfo(user)
158 tenantServiceClassData = getServiceClassInfo(user)
160 tenantSliceDetails['userSliceInfo'] = userSliceTableFormatter(tenantSliceData)
161 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
163 tenantSliceDetails['userSliceInfo'] = tenantSliceData
164 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
165 tenantSliceDetails['image']=userSliceTableFormatter(getImageInfo(user))
166 tenantSliceDetails['deploymentSites']=userSliceTableFormatter(getDeploymentSites())
167 #tenantSliceDetails['sites'] = userSliceTableFormatter(getTenantSitesInfo())
168 tenantSliceDetails['mountDataSets'] = userSliceTableFormatter(getMountDataSets())
169 tenantSliceDetails['publicKey'] = getPublicKey(user)
170 tenantSliceDetails['availableSites']=userSliceTableFormatter(getAvailableSites())
171 tenantSliceDetails['role']=getUserRole(user)
172 tenantSliceDetails['siteUsers']=getSiteUsers(user)
173 return tenantSliceDetails
175 def getSiteUsers(user):
176 users = User.objects.filter(site=user.site)
179 siteUsers.append(str(entry))
183 def getUserRole(user):
184 sp=SitePrivilege.objects.filter(user=user)
186 return str(entry.role)
189 def getTenantInfo(user):
190 slices =Slice.objects.all()
193 if (entry.site == user.site):
194 sliceName = Slice.objects.get(id=entry.id).name
195 slice = Slice.objects.get(name=Slice.objects.get(id=entry.id).name)
196 sliceServiceClass = entry.serviceClass.name
197 preferredImage = entry.image_preference
198 #sliceDataSet = entry.mount_data_sets
205 #createPrivateVolume(user,sliceName)
206 available_sites = getAvailableSites()
207 for sliver in slice.slivers.all():
208 if sliver.node.site.name in available_sites:
209 sliceSite[sliver.node.site.name] = sliceSite.get(sliver.node.site.name,0) + 1
210 sliceImage = sliver.image.name
211 sliceNode[str(sliver)] = sliver.node.name
212 numSliver = sum(sliceSite.values())
213 numSites = len(sliceSite)
214 userSliceInfo.append({'sliceName': sliceName,'sliceServiceClass': sliceServiceClass,'preferredImage':preferredImage,'numOfSites':numSites, 'sliceSite':sliceSite,'sliceImage':sliceImage,'numOfSlivers':numSliver,'instanceNodePair':sliceNode})
217 def getTenantSitesInfo():
218 availableSites=getAvailableSites()
220 for entry in Site.objects.all():
221 if entry.name in availableSites:
222 tenantSiteInfo.append({'siteName':entry.name})
223 return tenantSiteInfo
225 def getPublicKey(user):
226 users=User.objects.all()
\r
228 if (str(key.email)==str(user)):
\r
229 sshKey = key.public_key
\r
232 def getServiceClassInfo(user):
233 serviceClassList = ServiceClass.objects.all()
235 for entry in serviceClassList:
236 sliceInfo.append({'serviceClass':entry.name})
239 def getImageInfo(user):
240 #imageList = Image.objects.all()
242 #for imageEntry in imageList:
243 #imageInfo.append({'Image':imageEntry.name})
246 length = len(BLESSED_DEPLOYMENTS)
247 for deployment in Deployment.objects.all():
248 if deployment.name in BLESSED_DEPLOYMENTS:
249 for x in deployment.imagedeployments.all():
250 tempImageInfo.append(x.image.name)
252 for i in set(tempImageInfo):
253 temp[i] = tempImageInfo.count(i)
256 imageInfo.append(key)
259 def createPrivateVolume(user, sliceName):
260 caps = Volume.CAP_READ_DATA | Volume.CAP_WRITE_DATA | Volume.CAP_HOST_DATA
261 getattr(Volume.default_gateway_caps,"read data") | \
262 getattr(Volume.default_gateway_caps,"write data") | \
263 getattr(Volume.default_gateway_caps,"host files")
264 v = Volume(name="private_" + sliceName, owner_id=user, description="private volume for %s" % sliceName, blocksize=61440, private=True, archive=False, default_gateway_caps = caps)
268 SYNDICATE_REPLICATE_PORTNUM = 1025
272 inuse[SYNDICATE_REPLICATE_PORTNUM] = True
273 for vs in VolumeSlice.objects.all():
274 inuse[vs.peer_portnum]=True
275 inuse[vs.replicate_portnum]=True
276 for network in Network.objects.all():
277 if not network.ports:
279 network_ports = [x.strip() for x in network.ports.split(",")]
280 for network_port in network_ports:
282 inuse[int(network_port)] = True
284 # in case someone has put a malformed port number in the list
286 for i in range(1025, 65535):
287 if not inuse.get(i,False):
291 def mountVolume(sliceName, volumeName, readWrite):
292 slice = Slice.objects.get(name=sliceName)
293 volume = Volume.objects.get(name=volumeName)
294 # choose some unused port numbers
295 flags = Volume.CAP_READ_DATA
297 flags = flags | Volume.CAP_WRITE_DATA
298 vs = VolumeSlice(volume_id = volume, slice_id = slice, gateway_caps=flags, peer_portnum = get_free_port(), replicate_portnum = SYNDICATE_REPLICATE_PORTNUM)
301 def hasPrivateVolume(sliceName):
302 slice = Slice.objects.get(name=sliceName)
303 for vs in VolumeSlice.objects.filter(slice_id=slice):
304 if vs.volume_id.private:
308 def getMountDataSets():
310 for volume in Volume.objects.all():
\r
311 if not volume.private:
\r
312 dataSetInfo.append({'DataSet': volume.name})
\r
316 def getDeploymentSites():
317 deploymentList = Deployment.objects.all()
319 for entry in deploymentList:
320 deploymentInfo.append({'DeploymentSite':entry.name})
321 return deploymentInfo
323 def getAvailableSites():
325 for deployment in Deployment.objects.all():
326 if deployment.name in BLESSED_DEPLOYMENTS:
327 for x in deployment.sitedeployments.all():
328 if x.site.nodes.all():
329 available_sites.append(x.site.name)
330 return list(set(available_sites))
332 class TenantDeleteSliceView(View):
333 def post(self,request):
\r
334 if request.user.isReadOnlyUser():
\r
335 return HttpResponseForbidden("User is in read-only mode")
\r
336 sliceName = request.POST.get("sliceName",None)
\r
337 slice = Slice.objects.get(name=sliceName)
\r
338 #print slice, slice.id
\r
339 sliceToDel=Slice(name=sliceName, id=slice.id)
\r
341 return HttpResponse(json.dumps("Slice deleted"), content_type='application/javascript')
343 class TenantAddOrRemoveSliverView(View):
344 """ Add or remove slivers from a Slice
347 siteName - name of site. If not specified, PlanetStack will pick the
349 actionToDo - [add | rem]
350 count - number of slivers to add or remove
351 sliceName - name of slice
352 noAct - if set, no changes will be made to db, but result will still
353 show which sites would have been modified.
356 Dictionary of sites that were modified, and the count of nodes
357 that were added or removed at each site.
359 def post(self, request, *args, **kwargs):
360 siteName = request.POST.get("siteName", None)
361 actionToDo = request.POST.get("actionToDo", None)
362 count = int(request.POST.get("count","0"))
363 sliceName = request.POST.get("slice", None)
364 imageName = request.POST.get("image",None)
365 noAct = request.POST.get("noAct", False)
368 return HttpResponseServerError("No slice name given")
370 slice = Slice.objects.get(name=sliceName)
371 image = Image.objects.get(name=imageName)
374 siteList = [Site.objects.get(name=siteName)]
378 if (actionToDo == "add"):
379 user_ip = request.GET.get("ip", get_ip(request))
380 if (siteList is None):
381 siteList = tenant_pick_sites(user, user_ip, slice, count)
383 sitesChanged = slice_increase_slivers(request.user, user_ip, siteList, slice, image, count, noAct)
384 elif (actionToDo == "rem"):
385 sitesChanged = slice_decrease_slivers(request.user, siteList, slice, count, noAct)
387 return HttpResponseServerError("Unknown actionToDo %s" % actionToDo)
389 return HttpResponse(json.dumps(sitesChanged), content_type='application/javascript')
391 def get(self, request, *args, **kwargs):
392 request.POST = request.GET
393 return self.post(request, *args, **kwargs) # for testing REST in browser
394 #return HttpResponseServerError("GET is not supported")
396 class TenantPickSitesView(View):
397 """ primarily just for testing purposes """
398 def get(self, request, *args, **kwargs):
399 count = request.GET.get("count","0")
400 slice = request.GET.get("slice",None)
402 slice = Slice.objects.get(name=slice)
403 ip = request.GET.get("ip", get_ip(request))
404 sites = tenant_pick_sites(request.user, user_ip=ip, count=0, slice=slice)
405 sites = [x.name for x in sites]
406 return HttpResponse(json.dumps(sites), content_type='application/javascript')
408 def siteSortKey(site, slice=None, count=None, lat=None, lon=None):
409 # try to pick a site we're already using
410 has_slivers_here=False
412 for sliver in slice.slivers.all():
413 if sliver.node.site.name == site.name:
414 has_slivers_here=True
417 d = haversine(site.location.latitude, site.location.longitude, lat, lon)
419 return (-has_slivers_here, d)
421 def tenant_pick_sites(user, user_ip=None, slice=None, count=None):
422 """ Returns list of sites, sorted from most favorable to least favorable """
426 client_geo = GeoIP().city(user_ip)
428 lat=float(client_geo["latitude"])
429 lon=float(client_geo["longitude"])
431 print "exception in geo code"
432 traceback.print_exc()
434 available_sites = getAvailableSites()
435 sites = Site.objects.all()
436 sites = [x for x in sites if x.name in available_sites]
437 sites = sorted(sites, key=functools.partial(siteSortKey, slice=slice, count=count, lat=lat, lon=lon))
441 class TenantViewData(View):
442 def get(self, request, **kwargs):
443 return HttpResponse(json.dumps(getTenantSliceInfo(request.user, True)), content_type='application/javascript')
445 class RequestAccountView(View):
446 def get(self, request, **kwargs):
447 return HttpResponse()