make sure users created through tenant view are not admins
[plstackapi.git] / planetstack / core / dashboard / views / tenant.py
1 from view_common import *
2 from core.models import *
3 import functools
4 from django.contrib.auth.models import BaseUserManager
5 from django.core import serializers
6 from django.core.mail import EmailMultiAlternatives
7
8 BLESSED_DEPLOYMENTS = ["US-MaxPlanck", "US-GeorgiaTech", "US-Princeton", "US-Washington", "US-Stanford"]
9
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")
16         user = User(
17             email=BaseUserManager.normalize_email(email),
18             firstname=firstname,
19             lastname=lastname,
20             is_active=False,
21             is_admin=False
22         )
23         user.save()
24         user.site=Site.objects.get(name=site)
25         user.save(update_fields=['site'])
26         sitePriv = SitePrivilege.objects.filter(site=user.site)
27         userId = user.id
28         userUrl = "http://"+request.get_host()+"/admin/core/user/"+str(userId)
29         for sp in sitePriv:
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")
36                 msg.send()
37         return HttpResponse(serializers.serialize("json",[user,]), content_type='application/javascript')
38
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")
43
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)
57            newSlice.save()
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"))
69            slicePrivs.save()
70         return HttpResponse(json.dumps("Slice created"), content_type='application/javascript')
71
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")
76
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"))
80         slicePrivs.save()
81         return HttpResponse(json.dumps("Slice created"), content_type='application/javascript')
82
83 def privateVolForSlice(user,sliceName):
84         if not hasPrivateVolume(sliceName):\r
85            volumeName=createPrivateVolume(user,sliceName)\r
86            readWrite="true"\r
87            mountVolume(sliceName,volumeName,readWrite)
88
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
93 \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
109                                 entry.save()\r
110                                 break\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
115 \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
120                               owner = sliceName)\r
121         newNetwork.save()\r
122         addNetworkSlice(newNetwork,sliceName)\r
123 \r
124 def addNetworkSlice(networkSlice,sliceName):\r
125         newNetworkSlice=NetworkSlice(network =networkSlice,\r
126                                      slice=sliceName)\r
127         newNetworkSlice.save()\r
128 \r
129 def addOrModifyPorts(networkPorts,sliceName):\r
130         networkList = Network.objects.all()\r
131         networkInfo = []\r
132         if networkPorts:\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
140 \r
141 def getTenantSliceInfo(user, tableFormat = False):
142     tenantSliceDetails = {}
143     tenantSliceData = getTenantInfo(user)
144     tenantServiceClassData = getServiceClassInfo(user)
145     if (tableFormat):
146        tenantSliceDetails['userSliceInfo'] = userSliceTableFormatter(tenantSliceData)
147        tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
148     else:
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
160
161 def getSiteUsers(user):
162         users = User.objects.filter(site=user.site)
163         siteUsers=[]
164         for entry in users:
165                 siteUsers.append(str(entry))
166         return siteUsers
167
168
169 def getUserRole(user):
170         sp=SitePrivilege.objects.filter(user=user)
171         for entry in sp:
172                 return str(entry.role)
173
174
175 def getTenantInfo(user):
176     slices =Slice.objects.all()
177     userSliceInfo = []
178     for entry in slices:
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
185            sliceNetwork = {}
186            numSliver = 0
187            sliceImage=""
188            sliceSite = {}
189            sliceNode = {}
190            sliceInstance= {}
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})
201     return userSliceInfo
202
203 def getTenantSitesInfo():
204         availableSites=getAvailableSites()
205         tenantSiteInfo=[]
206         for entry in Site.objects.all():
207             if entry.name in availableSites:
208                  tenantSiteInfo.append({'siteName':entry.name})
209         return tenantSiteInfo
210
211 def getPublicKey(user):
212         users=User.objects.all()\r
213         for key in users:\r
214                 if (str(key.email)==str(user)):\r
215                         sshKey = key.public_key\r
216         return sshKey
217
218 def getServiceClassInfo(user):
219     serviceClassList = ServiceClass.objects.all()
220     sliceInfo = []
221     for entry in serviceClassList:
222           sliceInfo.append({'serviceClass':entry.name})
223     return sliceInfo
224
225 def getImageInfo(user):
226     #imageList = Image.objects.all()
227     #imageInfo = []
228     #for imageEntry in imageList:
229           #imageInfo.append({'Image':imageEntry.name})
230     imageInfo = []
231     tempImageInfo = []
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)
237     temp = {}
238     for i in set(tempImageInfo):
239         temp[i] = tempImageInfo.count(i)
240     for key in temp:
241         if temp[key]>1:
242                 imageInfo.append(key)
243     return imageInfo
244
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)
251     v.save()
252     return v
253
254 SYNDICATE_REPLICATE_PORTNUM = 1025
255
256 def get_free_port():
257     inuse={}
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:
264             continue
265         network_ports = [x.strip() for x in network.ports.split(",")]
266         for network_port in network_ports:
267             try:
268                 inuse[int(network_port)] = True
269             except:
270                 # in case someone has put a malformed port number in the list
271                 pass
272     for i in range(1025, 65535):
273         if not inuse.get(i,False):
274             return i
275     return False
276
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
282     if readWrite:
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)
285     vs.save()
286
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:
291              return True
292      return False
293
294 def getMountDataSets():
295         dataSetInfo=[]\r
296         for volume in Volume.objects.all():\r
297             if not volume.private:\r
298                 dataSetInfo.append({'DataSet': volume.name})\r
299 \r
300         return dataSetInfo
301
302 def getDeploymentSites():
303     deploymentList = Deployment.objects.all()
304     deploymentInfo = []
305     for entry in deploymentList:
306         deploymentInfo.append({'DeploymentSite':entry.name})
307     return deploymentInfo
308
309 def getAvailableSites():
310     available_sites = []
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))
317
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
326                 sliceToDel.delete()
327                 return HttpResponse(json.dumps("Slice deleted"), content_type='application/javascript')
328
329 class TenantAddOrRemoveSliverView(View):
330     """ Add or remove slivers from a Slice
331
332         Arguments:
333             siteName - name of site. If not specified, PlanetStack will pick the
334                        best site.,
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.
340
341         Returns:
342             Dictionary of sites that were modified, and the count of nodes
343             that were added or removed at each site.
344     """
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)
352
353         if not sliceName:
354             return HttpResponseServerError("No slice name given")
355
356         slice = Slice.objects.get(name=sliceName)
357         image = Image.objects.get(name=imageName)
358
359         if siteName:
360             siteList = [Site.objects.get(name=siteName)]
361         else:
362             siteList = None
363
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)
368
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)
372         else:
373             return HttpResponseServerError("Unknown actionToDo %s" % actionToDo)
374
375         return HttpResponse(json.dumps(sitesChanged), content_type='application/javascript')
376
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")
381
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)
387         if slice:
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')
393
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
397     if slice:
398         for sliver in slice.slivers.all():
399             if sliver.node.site.name == site.name:
400                 has_slivers_here=True
401
402     # Haversine method
403     d = haversine(site.location.latitude, site.location.longitude, lat, lon)
404
405     return (-has_slivers_here, d)
406
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 """
409     lat=None
410     lon=None
411     try:
412         client_geo = GeoIP().city(user_ip)
413         if client_geo:
414             lat=float(client_geo["latitude"])
415             lon=float(client_geo["longitude"])
416     except:
417         print "exception in geo code"
418         traceback.print_exc()
419
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))
424
425     return sites
426
427 class TenantViewData(View):
428     def get(self, request, **kwargs):
429         return HttpResponse(json.dumps(getTenantSliceInfo(request.user, True)), content_type='application/javascript')
430
431 class RequestAccountView(View):
432     def get(self, request, **kwargs):
433         return HttpResponse()