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),
24 user.site=Site.objects.get(name=site)
25 user.save(update_fields=['site'])
26 sitePriv = SitePrivilege.objects.filter(site=user.site)
28 userUrl = "http://"+request.get_host()+"/admin/core/user/"+str(userId)
30 subject, from_email, to = 'Authorize OpenCloud User Account', 'support@opencloud.us', str(sp.user)
31 text_content = 'This is an important message.'
32 html_content = """<p>Please authorize the following user on site """+site+""": <br><br>User: """+firstname+""" """+lastname+"""<br>Email: """+email+"""<br><br>
33 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>"""
34 msg = EmailMultiAlternatives(subject,text_content, from_email, [to])
35 msg.attach_alternative(html_content, "text/html")
37 return HttpResponse(serializers.serialize("json",[user,]), content_type='application/javascript')
39 class TenantCreateSlice(View):
40 def post(self, request, *args, **kwargs):
41 if request.user.isReadOnlyUser():
42 return HttpResponseForbidden("User is in read-only mode")
44 sliceName = request.POST.get("sliceName", "0")
45 serviceClass = request.POST.get("serviceClass", "0")
46 imageName = request.POST.get("imageName", "0")
47 actionToDo = request.POST.get("actionToDo", "0")
48 networkPorts = request.POST.get("network","0")
49 mountDataSets = request.POST.get("mountDataSets","0")
50 privateVolume = request.POST.get("privateVolume","0")
51 userEmail = request.POST.get("userEmail","0")
52 if (actionToDo == "add"):
53 serviceClass = ServiceClass.objects.get(name=serviceClass)
54 site = request.user.site
55 image = Image.objects.get(name=imageName)
56 newSlice = Slice(name=sliceName,serviceClass=serviceClass,site=site,image_preference=image,mount_data_sets=mountDataSets)
58 privateTemplate="Private"
59 publicTemplate="Public shared IPv4"
\r
60 privateNetworkName = sliceName+"-"+privateTemplate
\r
61 publicNetworkName = sliceName+"-"+publicTemplate
\r
62 slice=Slice.objects.get(name=sliceName)
\r
63 addNetwork(privateNetworkName,privateTemplate,slice)
\r
64 addNetwork(publicNetworkName,publicTemplate,slice)
\r
65 addOrModifyPorts(networkPorts,sliceName)
\r
66 if privateVolume=="true":
\r
67 privateVolForSlice(request.user,sliceName)
68 slicePrivs=SlicePrivilege(user=User.objects.get(email=userEmail),slice=Slice.objects.get(name=sliceName),role=SliceRole.objects.get(role="admin"))
70 return HttpResponse(json.dumps("Slice created"), content_type='application/javascript')
72 class TenantAddUser(View):
73 def post(self, request, *args, **kwargs):
74 if request.user.isReadOnlyUser():
75 return HttpResponseForbidden("User is in read-only mode")
77 sliceName = request.POST.get("sliceName", "0")
78 userEmail = request.POST.get("userEmail","0")
79 slicePrivs=SlicePrivilege(user=User.objects.get(email=userEmail),slice=Slice.objects.get(name=sliceName),role=SliceRole.objects.get(role="admin"))
81 return HttpResponse(json.dumps("Slice created"), content_type='application/javascript')
83 def privateVolForSlice(user,sliceName):
84 if not hasPrivateVolume(sliceName):
\r
85 volumeName=createPrivateVolume(user,sliceName)
\r
87 mountVolume(sliceName,volumeName,readWrite)
89 class TenantUpdateSlice(View):
90 def post(self, request, *args, **kwargs):
\r
91 if request.user.isReadOnlyUser():
\r
92 return HttpResponseForbidden("User is in read-only mode")
\r
94 sliceName = request.POST.get("sliceName", "0")
\r
95 serviceClass = request.POST.get("serviceClass", "0")
\r
96 imageName = request.POST.get("imageName", "0")
\r
97 actionToDo = request.POST.get("actionToDo", "0")
\r
98 networkPorts = request.POST.get("networkPorts","0")
\r
99 dataSet = request.POST.get("dataSet","0")
\r
100 privateVolume = request.POST.get("privateVolume","0")
\r
101 slice = Slice.objects.all()
\r
102 for entry in slice:
\r
103 serviceClass = ServiceClass.objects.get(name=serviceClass)
\r
104 if(entry.name==sliceName):
\r
105 if (actionToDo == "update"):
\r
106 setattr(entry,'serviceClass',serviceClass)
\r
107 setattr(entry,'image_preference',imageName)
\r
108 setattr(entry,'mount_data_sets',dataSet)
\r
111 addOrModifyPorts(networkPorts,sliceName)
\r
112 if privateVolume=="true":
\r
113 privateVolForSlice(request.user,sliceName)
\r
114 return HttpResponse(json.dumps("Slice updated"), content_type='application/javascript')
\r
116 def addNetwork(name,template,sliceName):
\r
117 networkTemplate=NetworkTemplate.objects.get(name=template)
\r
118 newNetwork = Network(name = name,
\r
119 template = networkTemplate,
\r
122 addNetworkSlice(newNetwork,sliceName)
\r
124 def addNetworkSlice(networkSlice,sliceName):
\r
125 newNetworkSlice=NetworkSlice(network =networkSlice,
\r
127 newNetworkSlice.save()
\r
129 def addOrModifyPorts(networkPorts,sliceName):
\r
130 networkList = Network.objects.all()
\r
133 for networkEntry in networkList:
\r
134 networkSlices = networkEntry.slices.all()
\r
135 for slice in networkSlices:
\r
136 if slice.name==sliceName:
\r
137 if networkEntry.template.name=="Public shared IPv4":
\r
138 setattr(networkEntry,'ports',networkPorts)
\r
139 networkEntry.save()
\r
141 def getTenantSliceInfo(user, tableFormat = False):
142 tenantSliceDetails = {}
143 tenantSliceData = getTenantInfo(user)
144 tenantServiceClassData = getServiceClassInfo(user)
146 tenantSliceDetails['userSliceInfo'] = userSliceTableFormatter(tenantSliceData)
147 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
149 tenantSliceDetails['userSliceInfo'] = tenantSliceData
150 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
151 tenantSliceDetails['image']=userSliceTableFormatter(getImageInfo(user))
152 tenantSliceDetails['deploymentSites']=userSliceTableFormatter(getDeploymentSites())
153 #tenantSliceDetails['sites'] = userSliceTableFormatter(getTenantSitesInfo())
154 tenantSliceDetails['mountDataSets'] = userSliceTableFormatter(getMountDataSets())
155 tenantSliceDetails['publicKey'] = getPublicKey(user)
156 tenantSliceDetails['availableSites']=userSliceTableFormatter(getAvailableSites())
157 tenantSliceDetails['role']=getUserRole(user)
158 tenantSliceDetails['siteUsers']=getSiteUsers(user)
159 return tenantSliceDetails
161 def getSiteUsers(user):
162 users = User.objects.filter(site=user.site)
165 siteUsers.append(str(entry))
169 def getUserRole(user):
170 sp=SitePrivilege.objects.filter(user=user)
172 return str(entry.role)
175 def getTenantInfo(user):
176 slices =Slice.objects.all()
179 if (entry.site == user.site):
180 sliceName = Slice.objects.get(id=entry.id).name
181 slice = Slice.objects.get(name=Slice.objects.get(id=entry.id).name)
182 sliceServiceClass = entry.serviceClass.name
183 preferredImage = entry.image_preference
184 #sliceDataSet = entry.mount_data_sets
191 #createPrivateVolume(user,sliceName)
192 available_sites = getAvailableSites()
193 for sliver in slice.slivers.all():
194 if sliver.node.site.name in available_sites:
195 sliceSite[sliver.node.site.name] = sliceSite.get(sliver.node.site.name,0) + 1
196 sliceImage = sliver.image.name
197 sliceNode[str(sliver)] = sliver.node.name
198 numSliver = sum(sliceSite.values())
199 numSites = len(sliceSite)
200 userSliceInfo.append({'sliceName': sliceName,'sliceServiceClass': sliceServiceClass,'preferredImage':preferredImage,'numOfSites':numSites, 'sliceSite':sliceSite,'sliceImage':sliceImage,'numOfSlivers':numSliver,'instanceNodePair':sliceNode})
203 def getTenantSitesInfo():
204 availableSites=getAvailableSites()
206 for entry in Site.objects.all():
207 if entry.name in availableSites:
208 tenantSiteInfo.append({'siteName':entry.name})
209 return tenantSiteInfo
211 def getPublicKey(user):
212 users=User.objects.all()
\r
214 if (str(key.email)==str(user)):
\r
215 sshKey = key.public_key
\r
218 def getServiceClassInfo(user):
219 serviceClassList = ServiceClass.objects.all()
221 for entry in serviceClassList:
222 sliceInfo.append({'serviceClass':entry.name})
225 def getImageInfo(user):
226 #imageList = Image.objects.all()
228 #for imageEntry in imageList:
229 #imageInfo.append({'Image':imageEntry.name})
232 length = len(BLESSED_DEPLOYMENTS)
233 for deployment in Deployment.objects.all():
234 if deployment.name in BLESSED_DEPLOYMENTS:
235 for x in deployment.imagedeployments.all():
236 tempImageInfo.append(x.image.name)
238 for i in set(tempImageInfo):
239 temp[i] = tempImageInfo.count(i)
242 imageInfo.append(key)
245 def createPrivateVolume(user, sliceName):
246 caps = Volume.CAP_READ_DATA | Volume.CAP_WRITE_DATA | Volume.CAP_HOST_DATA
247 getattr(Volume.default_gateway_caps,"read data") | \
248 getattr(Volume.default_gateway_caps,"write data") | \
249 getattr(Volume.default_gateway_caps,"host files")
250 v = Volume(name="private_" + sliceName, owner_id=user, description="private volume for %s" % sliceName, blocksize=61440, private=True, archive=False, default_gateway_caps = caps)
254 SYNDICATE_REPLICATE_PORTNUM = 1025
258 inuse[SYNDICATE_REPLICATE_PORTNUM] = True
259 for vs in VolumeSlice.objects.all():
260 inuse[vs.peer_portnum]=True
261 inuse[vs.replicate_portnum]=True
262 for network in Network.objects.all():
263 if not network.ports:
265 network_ports = [x.strip() for x in network.ports.split(",")]
266 for network_port in network_ports:
268 inuse[int(network_port)] = True
270 # in case someone has put a malformed port number in the list
272 for i in range(1025, 65535):
273 if not inuse.get(i,False):
277 def mountVolume(sliceName, volumeName, readWrite):
278 slice = Slice.objects.get(name=sliceName)
279 volume = Volume.objects.get(name=volumeName)
280 # choose some unused port numbers
281 flags = Volume.CAP_READ_DATA
283 flags = flags | Volume.CAP_WRITE_DATA
284 vs = VolumeSlice(volume_id = volume, slice_id = slice, gateway_caps=flags, peer_portnum = get_free_port(), replicate_portnum = SYNDICATE_REPLICATE_PORTNUM)
287 def hasPrivateVolume(sliceName):
288 slice = Slice.objects.get(name=sliceName)
289 for vs in VolumeSlice.objects.filter(slice_id=slice):
290 if vs.volume_id.private:
294 def getMountDataSets():
296 for volume in Volume.objects.all():
\r
297 if not volume.private:
\r
298 dataSetInfo.append({'DataSet': volume.name})
\r
302 def getDeploymentSites():
303 deploymentList = Deployment.objects.all()
305 for entry in deploymentList:
306 deploymentInfo.append({'DeploymentSite':entry.name})
307 return deploymentInfo
309 def getAvailableSites():
311 for deployment in Deployment.objects.all():
312 if deployment.name in BLESSED_DEPLOYMENTS:
313 for x in deployment.sitedeployments.all():
314 if x.site.nodes.all():
315 available_sites.append(x.site.name)
316 return list(set(available_sites))
318 class TenantDeleteSliceView(View):
319 def post(self,request):
\r
320 if request.user.isReadOnlyUser():
\r
321 return HttpResponseForbidden("User is in read-only mode")
\r
322 sliceName = request.POST.get("sliceName",None)
\r
323 slice = Slice.objects.get(name=sliceName)
\r
324 #print slice, slice.id
\r
325 sliceToDel=Slice(name=sliceName, id=slice.id)
\r
327 return HttpResponse(json.dumps("Slice deleted"), content_type='application/javascript')
329 class TenantAddOrRemoveSliverView(View):
330 """ Add or remove slivers from a Slice
333 siteName - name of site. If not specified, PlanetStack will pick the
335 actionToDo - [add | rem]
336 count - number of slivers to add or remove
337 sliceName - name of slice
338 noAct - if set, no changes will be made to db, but result will still
339 show which sites would have been modified.
342 Dictionary of sites that were modified, and the count of nodes
343 that were added or removed at each site.
345 def post(self, request, *args, **kwargs):
346 siteName = request.POST.get("siteName", None)
347 actionToDo = request.POST.get("actionToDo", None)
348 count = int(request.POST.get("count","0"))
349 sliceName = request.POST.get("slice", None)
350 imageName = request.POST.get("image",None)
351 noAct = request.POST.get("noAct", False)
354 return HttpResponseServerError("No slice name given")
356 slice = Slice.objects.get(name=sliceName)
357 image = Image.objects.get(name=imageName)
360 siteList = [Site.objects.get(name=siteName)]
364 if (actionToDo == "add"):
365 user_ip = request.GET.get("ip", get_ip(request))
366 if (siteList is None):
367 siteList = tenant_pick_sites(user, user_ip, slice, count)
369 sitesChanged = slice_increase_slivers(request.user, user_ip, siteList, slice, image, count, noAct)
370 elif (actionToDo == "rem"):
371 sitesChanged = slice_decrease_slivers(request.user, siteList, slice, count, noAct)
373 return HttpResponseServerError("Unknown actionToDo %s" % actionToDo)
375 return HttpResponse(json.dumps(sitesChanged), content_type='application/javascript')
377 def get(self, request, *args, **kwargs):
378 request.POST = request.GET
379 return self.post(request, *args, **kwargs) # for testing REST in browser
380 #return HttpResponseServerError("GET is not supported")
382 class TenantPickSitesView(View):
383 """ primarily just for testing purposes """
384 def get(self, request, *args, **kwargs):
385 count = request.GET.get("count","0")
386 slice = request.GET.get("slice",None)
388 slice = Slice.objects.get(name=slice)
389 ip = request.GET.get("ip", get_ip(request))
390 sites = tenant_pick_sites(request.user, user_ip=ip, count=0, slice=slice)
391 sites = [x.name for x in sites]
392 return HttpResponse(json.dumps(sites), content_type='application/javascript')
394 def siteSortKey(site, slice=None, count=None, lat=None, lon=None):
395 # try to pick a site we're already using
396 has_slivers_here=False
398 for sliver in slice.slivers.all():
399 if sliver.node.site.name == site.name:
400 has_slivers_here=True
403 d = haversine(site.location.latitude, site.location.longitude, lat, lon)
405 return (-has_slivers_here, d)
407 def tenant_pick_sites(user, user_ip=None, slice=None, count=None):
408 """ Returns list of sites, sorted from most favorable to least favorable """
412 client_geo = GeoIP().city(user_ip)
414 lat=float(client_geo["latitude"])
415 lon=float(client_geo["longitude"])
417 print "exception in geo code"
418 traceback.print_exc()
420 available_sites = getAvailableSites()
421 sites = Site.objects.all()
422 sites = [x for x in sites if x.name in available_sites]
423 sites = sorted(sites, key=functools.partial(siteSortKey, slice=slice, count=count, lat=lat, lon=lon))
427 class TenantViewData(View):
428 def get(self, request, **kwargs):
429 return HttpResponse(json.dumps(getTenantSliceInfo(request.user, True)), content_type='application/javascript')
431 class RequestAccountView(View):
432 def get(self, request, **kwargs):
433 return HttpResponse()