blob: 762409e735f57130acab60b568003ae3b0471b0c [file] [log] [blame]
Scott Bakerc7325a42014-05-30 16:06:46 -07001from view_common import *
Scott Baker866c5b32014-08-29 11:34:00 -07002from core.models import *
Scott Bakerc7325a42014-05-30 16:06:46 -07003import functools
Scott Baker866c5b32014-08-29 11:34:00 -07004from django.contrib.auth.models import BaseUserManager
5from django.core import serializers
6from django.core.mail import EmailMultiAlternatives
Scott Bakerc7325a42014-05-30 16:06:46 -07007
Scott Baker866c5b32014-08-29 11:34:00 -07008BLESSED_DEPLOYMENTS = ["US-MaxPlanck", "US-GeorgiaTech", "US-Princeton", "US-Washington", "US-Stanford"]
9
10class 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 )
22 user.save()
23 user.site=Site.objects.get(name=site)
24 user.save(update_fields=['site'])
25 sitePriv = SitePrivilege.objects.filter(site=user.site)
26 userId = user.id
27 userUrl = "http://"+request.get_host()+"/admin/core/user/"+str(userId)
28 for sp in sitePriv:
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>
32Check 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")
35 msg.send()
36 return HttpResponse(serializers.serialize("json",[user,]), content_type='application/javascript')
Scott Bakerc7325a42014-05-30 16:06:46 -070037
38class TenantCreateSlice(View):
39 def post(self, request, *args, **kwargs):
40 if request.user.isReadOnlyUser():
41 return HttpResponseForbidden("User is in read-only mode")
42
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")
Scott Baker866c5b32014-08-29 11:34:00 -070050 userEmail = request.POST.get("userEmail","0")
Scott Bakerc7325a42014-05-30 16:06:46 -070051 if (actionToDo == "add"):
52 serviceClass = ServiceClass.objects.get(name=serviceClass)
53 site = request.user.site
54 image = Image.objects.get(name=imageName)
Scott Bakerffce7852015-01-03 16:26:38 -080055 newSlice = Slice(name=sliceName,serviceClass=serviceClass,site=site,image_preference=image,mount_data_sets=mountDataSets)
Scott Bakerc7325a42014-05-30 16:06:46 -070056 newSlice.save()
57 privateTemplate="Private"
58 publicTemplate="Public shared IPv4"
59 privateNetworkName = sliceName+"-"+privateTemplate
60 publicNetworkName = sliceName+"-"+publicTemplate
61 slice=Slice.objects.get(name=sliceName)
62 addNetwork(privateNetworkName,privateTemplate,slice)
63 addNetwork(publicNetworkName,publicTemplate,slice)
64 addOrModifyPorts(networkPorts,sliceName)
65 if privateVolume=="true":
66 privateVolForSlice(request.user,sliceName)
Scott Baker866c5b32014-08-29 11:34:00 -070067 slicePrivs=SlicePrivilege(user=User.objects.get(email=userEmail),slice=Slice.objects.get(name=sliceName),role=SliceRole.objects.get(role="admin"))
68 slicePrivs.save()
69 return HttpResponse(json.dumps("Slice created"), content_type='application/javascript')
70
71class TenantAddUser(View):
72 def post(self, request, *args, **kwargs):
73 if request.user.isReadOnlyUser():
74 return HttpResponseForbidden("User is in read-only mode")
75
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"))
79 slicePrivs.save()
Scott Baker823b7212014-06-16 10:25:39 -070080 return HttpResponse(json.dumps("Slice created"), content_type='application/javascript')
Scott Bakerc7325a42014-05-30 16:06:46 -070081
82def privateVolForSlice(user,sliceName):
83 if not hasPrivateVolume(sliceName):
84 volumeName=createPrivateVolume(user,sliceName)
85 readWrite="true"
86 mountVolume(sliceName,volumeName,readWrite)
87
88class TenantUpdateSlice(View):
89 def post(self, request, *args, **kwargs):
90 if request.user.isReadOnlyUser():
91 return HttpResponseForbidden("User is in read-only mode")
92
93 sliceName = request.POST.get("sliceName", "0")
94 serviceClass = request.POST.get("serviceClass", "0")
95 imageName = request.POST.get("imageName", "0")
96 actionToDo = request.POST.get("actionToDo", "0")
97 networkPorts = request.POST.get("networkPorts","0")
98 dataSet = request.POST.get("dataSet","0")
99 privateVolume = request.POST.get("privateVolume","0")
100 slice = Slice.objects.all()
101 for entry in slice:
102 serviceClass = ServiceClass.objects.get(name=serviceClass)
103 if(entry.name==sliceName):
104 if (actionToDo == "update"):
105 setattr(entry,'serviceClass',serviceClass)
Scott Bakerffce7852015-01-03 16:26:38 -0800106 setattr(entry,'image_preference',imageName)
107 setattr(entry,'mount_data_sets',dataSet)
Scott Bakerc7325a42014-05-30 16:06:46 -0700108 entry.save()
109 break
110 addOrModifyPorts(networkPorts,sliceName)
111 if privateVolume=="true":
112 privateVolForSlice(request.user,sliceName)
Scott Baker823b7212014-06-16 10:25:39 -0700113 return HttpResponse(json.dumps("Slice updated"), content_type='application/javascript')
Scott Bakerc7325a42014-05-30 16:06:46 -0700114
115def addNetwork(name,template,sliceName):
116 networkTemplate=NetworkTemplate.objects.get(name=template)
117 newNetwork = Network(name = name,
118 template = networkTemplate,
119 owner = sliceName)
120 newNetwork.save()
121 addNetworkSlice(newNetwork,sliceName)
122
123def addNetworkSlice(networkSlice,sliceName):
124 newNetworkSlice=NetworkSlice(network =networkSlice,
125 slice=sliceName)
126 newNetworkSlice.save()
127
128def addOrModifyPorts(networkPorts,sliceName):
129 networkList = Network.objects.all()
130 networkInfo = []
131 if networkPorts:
132 for networkEntry in networkList:
133 networkSlices = networkEntry.slices.all()
134 for slice in networkSlices:
135 if slice.name==sliceName:
136 if networkEntry.template.name=="Public shared IPv4":
137 setattr(networkEntry,'ports',networkPorts)
138 networkEntry.save()
139
140def getTenantSliceInfo(user, tableFormat = False):
141 tenantSliceDetails = {}
142 tenantSliceData = getTenantInfo(user)
143 tenantServiceClassData = getServiceClassInfo(user)
144 if (tableFormat):
145 tenantSliceDetails['userSliceInfo'] = userSliceTableFormatter(tenantSliceData)
146 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
147 else:
148 tenantSliceDetails['userSliceInfo'] = tenantSliceData
149 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
150 tenantSliceDetails['image']=userSliceTableFormatter(getImageInfo(user))
151 tenantSliceDetails['deploymentSites']=userSliceTableFormatter(getDeploymentSites())
Scott Baker866c5b32014-08-29 11:34:00 -0700152 #tenantSliceDetails['sites'] = userSliceTableFormatter(getTenantSitesInfo())
Scott Bakerc7325a42014-05-30 16:06:46 -0700153 tenantSliceDetails['mountDataSets'] = userSliceTableFormatter(getMountDataSets())
154 tenantSliceDetails['publicKey'] = getPublicKey(user)
Scott Baker866c5b32014-08-29 11:34:00 -0700155 tenantSliceDetails['availableSites']=userSliceTableFormatter(getAvailableSites())
156 tenantSliceDetails['role']=getUserRole(user)
157 tenantSliceDetails['siteUsers']=getSiteUsers(user)
Scott Bakerc7325a42014-05-30 16:06:46 -0700158 return tenantSliceDetails
159
Scott Baker866c5b32014-08-29 11:34:00 -0700160def getSiteUsers(user):
161 users = User.objects.filter(site=user.site)
162 siteUsers=[]
163 for entry in users:
164 siteUsers.append(str(entry))
165 return siteUsers
166
167
168def getUserRole(user):
169 sp=SitePrivilege.objects.filter(user=user)
170 for entry in sp:
171 return str(entry.role)
172
173
Scott Bakerc7325a42014-05-30 16:06:46 -0700174def getTenantInfo(user):
175 slices =Slice.objects.all()
176 userSliceInfo = []
177 for entry in slices:
Scott Baker866c5b32014-08-29 11:34:00 -0700178 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
Scott Bakerffce7852015-01-03 16:26:38 -0800182 preferredImage = entry.image_preference
183 #sliceDataSet = entry.mount_data_sets
Scott Baker866c5b32014-08-29 11:34:00 -0700184 sliceNetwork = {}
185 numSliver = 0
186 sliceImage=""
187 sliceSite = {}
188 sliceNode = {}
189 sliceInstance= {}
190 #createPrivateVolume(user,sliceName)
Scott Baker01ef6492014-08-29 12:19:09 -0700191 available_sites = getAvailableSites()
Scott Baker866c5b32014-08-29 11:34:00 -0700192 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})
Scott Bakerc7325a42014-05-30 16:06:46 -0700200 return userSliceInfo
201
202def getTenantSitesInfo():
Scott Baker866c5b32014-08-29 11:34:00 -0700203 availableSites=getAvailableSites()
Scott Bakerc7325a42014-05-30 16:06:46 -0700204 tenantSiteInfo=[]
205 for entry in Site.objects.all():
Scott Baker866c5b32014-08-29 11:34:00 -0700206 if entry.name in availableSites:
Scott Bakerc7325a42014-05-30 16:06:46 -0700207 tenantSiteInfo.append({'siteName':entry.name})
208 return tenantSiteInfo
209
210def getPublicKey(user):
211 users=User.objects.all()
212 for key in users:
213 if (str(key.email)==str(user)):
214 sshKey = key.public_key
215 return sshKey
216
217def getServiceClassInfo(user):
218 serviceClassList = ServiceClass.objects.all()
219 sliceInfo = []
220 for entry in serviceClassList:
221 sliceInfo.append({'serviceClass':entry.name})
222 return sliceInfo
223
224def getImageInfo(user):
Scott Baker866c5b32014-08-29 11:34:00 -0700225 #imageList = Image.objects.all()
226 #imageInfo = []
227 #for imageEntry in imageList:
228 #imageInfo.append({'Image':imageEntry.name})
Scott Bakerc7325a42014-05-30 16:06:46 -0700229 imageInfo = []
Scott Baker866c5b32014-08-29 11:34:00 -0700230 tempImageInfo = []
231 length = len(BLESSED_DEPLOYMENTS)
232 for deployment in Deployment.objects.all():
233 if deployment.name in BLESSED_DEPLOYMENTS:
Sapan Bhatiaa0beef82014-11-20 15:08:18 -0500234 for x in deployment.imagedeployments.all():
Scott Baker866c5b32014-08-29 11:34:00 -0700235 tempImageInfo.append(x.image.name)
236 temp = {}
237 for i in set(tempImageInfo):
238 temp[i] = tempImageInfo.count(i)
239 for key in temp:
240 if temp[key]>1:
241 imageInfo.append(key)
Scott Bakerc7325a42014-05-30 16:06:46 -0700242 return imageInfo
243
244def 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)
250 v.save()
251 return v
252
253SYNDICATE_REPLICATE_PORTNUM = 1025
254
255def get_free_port():
256 inuse={}
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:
263 continue
264 network_ports = [x.strip() for x in network.ports.split(",")]
265 for network_port in network_ports:
266 try:
267 inuse[int(network_port)] = True
268 except:
269 # in case someone has put a malformed port number in the list
270 pass
271 for i in range(1025, 65535):
272 if not inuse.get(i,False):
273 return i
274 return False
275
276def 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
281 if readWrite:
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)
284 vs.save()
285
286def 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:
290 return True
291 return False
292
293def getMountDataSets():
294 dataSetInfo=[]
295 for volume in Volume.objects.all():
296 if not volume.private:
297 dataSetInfo.append({'DataSet': volume.name})
298
299 return dataSetInfo
300
301def getDeploymentSites():
302 deploymentList = Deployment.objects.all()
303 deploymentInfo = []
304 for entry in deploymentList:
305 deploymentInfo.append({'DeploymentSite':entry.name})
306 return deploymentInfo
307
Scott Baker866c5b32014-08-29 11:34:00 -0700308def getAvailableSites():
309 available_sites = []
310 for deployment in Deployment.objects.all():
311 if deployment.name in BLESSED_DEPLOYMENTS:
Sapan Bhatiaa0beef82014-11-20 15:08:18 -0500312 for x in deployment.sitedeployments.all():
Scott Baker866c5b32014-08-29 11:34:00 -0700313 if x.site.nodes.all():
314 available_sites.append(x.site.name)
315 return list(set(available_sites))
316
Scott Bakerc7325a42014-05-30 16:06:46 -0700317class TenantDeleteSliceView(View):
318 def post(self,request):
319 if request.user.isReadOnlyUser():
320 return HttpResponseForbidden("User is in read-only mode")
321 sliceName = request.POST.get("sliceName",None)
322 slice = Slice.objects.get(name=sliceName)
323 #print slice, slice.id
324 sliceToDel=Slice(name=sliceName, id=slice.id)
325 sliceToDel.delete()
Scott Baker823b7212014-06-16 10:25:39 -0700326 return HttpResponse(json.dumps("Slice deleted"), content_type='application/javascript')
Scott Bakerc7325a42014-05-30 16:06:46 -0700327
328class TenantAddOrRemoveSliverView(View):
329 """ Add or remove slivers from a Slice
330
331 Arguments:
332 siteName - name of site. If not specified, PlanetStack will pick the
333 best site.,
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.
339
340 Returns:
341 Dictionary of sites that were modified, and the count of nodes
342 that were added or removed at each site.
343 """
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)
Scott Baker866c5b32014-08-29 11:34:00 -0700349 imageName = request.POST.get("image",None)
Scott Bakerc7325a42014-05-30 16:06:46 -0700350 noAct = request.POST.get("noAct", False)
351
352 if not sliceName:
353 return HttpResponseServerError("No slice name given")
354
355 slice = Slice.objects.get(name=sliceName)
Scott Baker866c5b32014-08-29 11:34:00 -0700356 image = Image.objects.get(name=imageName)
Scott Bakerc7325a42014-05-30 16:06:46 -0700357
358 if siteName:
359 siteList = [Site.objects.get(name=siteName)]
360 else:
361 siteList = None
362
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)
367
Scott Baker866c5b32014-08-29 11:34:00 -0700368 sitesChanged = slice_increase_slivers(request.user, user_ip, siteList, slice, image, count, noAct)
Scott Bakerc7325a42014-05-30 16:06:46 -0700369 elif (actionToDo == "rem"):
370 sitesChanged = slice_decrease_slivers(request.user, siteList, slice, count, noAct)
371 else:
372 return HttpResponseServerError("Unknown actionToDo %s" % actionToDo)
373
Scott Baker823b7212014-06-16 10:25:39 -0700374 return HttpResponse(json.dumps(sitesChanged), content_type='application/javascript')
Scott Bakerc7325a42014-05-30 16:06:46 -0700375
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")
380
381class 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)
386 if slice:
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]
Scott Baker823b7212014-06-16 10:25:39 -0700391 return HttpResponse(json.dumps(sites), content_type='application/javascript')
Scott Bakerc7325a42014-05-30 16:06:46 -0700392
393def 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
396 if slice:
397 for sliver in slice.slivers.all():
398 if sliver.node.site.name == site.name:
399 has_slivers_here=True
400
401 # Haversine method
402 d = haversine(site.location.latitude, site.location.longitude, lat, lon)
403
404 return (-has_slivers_here, d)
405
406def tenant_pick_sites(user, user_ip=None, slice=None, count=None):
407 """ Returns list of sites, sorted from most favorable to least favorable """
408 lat=None
409 lon=None
410 try:
411 client_geo = GeoIP().city(user_ip)
412 if client_geo:
413 lat=float(client_geo["latitude"])
414 lon=float(client_geo["longitude"])
415 except:
416 print "exception in geo code"
417 traceback.print_exc()
418
Scott Baker01ef6492014-08-29 12:19:09 -0700419 available_sites = getAvailableSites()
Scott Bakerc7325a42014-05-30 16:06:46 -0700420 sites = Site.objects.all()
Scott Baker866c5b32014-08-29 11:34:00 -0700421 sites = [x for x in sites if x.name in available_sites]
Scott Bakerc7325a42014-05-30 16:06:46 -0700422 sites = sorted(sites, key=functools.partial(siteSortKey, slice=slice, count=count, lat=lat, lon=lon))
423
424 return sites
425
426class TenantViewData(View):
427 def get(self, request, **kwargs):
Scott Baker823b7212014-06-16 10:25:39 -0700428 return HttpResponse(json.dumps(getTenantSliceInfo(request.user, True)), content_type='application/javascript')
Scott Baker866c5b32014-08-29 11:34:00 -0700429
430class RequestAccountView(View):
431 def get(self, request, **kwargs):
432 return HttpResponse()