blob: a4b8e993d38a330284b6399792694b9cf6377c22 [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,
Scott Bakerbb2fb7e2015-01-27 23:07:51 -080020 is_active=False,
21 is_admin=False
Scott Baker866c5b32014-08-29 11:34:00 -070022 )
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>
33Check 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')
Scott Bakerc7325a42014-05-30 16:06:46 -070038
39class 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")
Scott Baker866c5b32014-08-29 11:34:00 -070051 userEmail = request.POST.get("userEmail","0")
Scott Bakerc7325a42014-05-30 16:06:46 -070052 if (actionToDo == "add"):
53 serviceClass = ServiceClass.objects.get(name=serviceClass)
54 site = request.user.site
55 image = Image.objects.get(name=imageName)
Scott Bakeree9736d2015-01-03 16:26:38 -080056 newSlice = Slice(name=sliceName,serviceClass=serviceClass,site=site,image_preference=image,mount_data_sets=mountDataSets)
Scott Bakerc7325a42014-05-30 16:06:46 -070057 newSlice.save()
58 privateTemplate="Private"
59 publicTemplate="Public shared IPv4"
60 privateNetworkName = sliceName+"-"+privateTemplate
61 publicNetworkName = sliceName+"-"+publicTemplate
62 slice=Slice.objects.get(name=sliceName)
63 addNetwork(privateNetworkName,privateTemplate,slice)
64 addNetwork(publicNetworkName,publicTemplate,slice)
65 addOrModifyPorts(networkPorts,sliceName)
66 if privateVolume=="true":
67 privateVolForSlice(request.user,sliceName)
Scott Baker866c5b32014-08-29 11:34:00 -070068 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
72class 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()
Scott Baker823b7212014-06-16 10:25:39 -070081 return HttpResponse(json.dumps("Slice created"), content_type='application/javascript')
Scott Bakerc7325a42014-05-30 16:06:46 -070082
83def privateVolForSlice(user,sliceName):
84 if not hasPrivateVolume(sliceName):
85 volumeName=createPrivateVolume(user,sliceName)
86 readWrite="true"
87 mountVolume(sliceName,volumeName,readWrite)
88
89class TenantUpdateSlice(View):
90 def post(self, request, *args, **kwargs):
91 if request.user.isReadOnlyUser():
92 return HttpResponseForbidden("User is in read-only mode")
93
94 sliceName = request.POST.get("sliceName", "0")
95 serviceClass = request.POST.get("serviceClass", "0")
96 imageName = request.POST.get("imageName", "0")
97 actionToDo = request.POST.get("actionToDo", "0")
98 networkPorts = request.POST.get("networkPorts","0")
99 dataSet = request.POST.get("dataSet","0")
100 privateVolume = request.POST.get("privateVolume","0")
101 slice = Slice.objects.all()
102 for entry in slice:
103 serviceClass = ServiceClass.objects.get(name=serviceClass)
104 if(entry.name==sliceName):
105 if (actionToDo == "update"):
106 setattr(entry,'serviceClass',serviceClass)
Scott Bakeree9736d2015-01-03 16:26:38 -0800107 setattr(entry,'image_preference',imageName)
108 setattr(entry,'mount_data_sets',dataSet)
Scott Bakerc7325a42014-05-30 16:06:46 -0700109 entry.save()
110 break
111 addOrModifyPorts(networkPorts,sliceName)
112 if privateVolume=="true":
113 privateVolForSlice(request.user,sliceName)
Scott Baker823b7212014-06-16 10:25:39 -0700114 return HttpResponse(json.dumps("Slice updated"), content_type='application/javascript')
Scott Bakerc7325a42014-05-30 16:06:46 -0700115
116def addNetwork(name,template,sliceName):
117 networkTemplate=NetworkTemplate.objects.get(name=template)
118 newNetwork = Network(name = name,
119 template = networkTemplate,
120 owner = sliceName)
121 newNetwork.save()
122 addNetworkSlice(newNetwork,sliceName)
123
124def addNetworkSlice(networkSlice,sliceName):
125 newNetworkSlice=NetworkSlice(network =networkSlice,
126 slice=sliceName)
127 newNetworkSlice.save()
128
129def addOrModifyPorts(networkPorts,sliceName):
130 networkList = Network.objects.all()
131 networkInfo = []
132 if networkPorts:
133 for networkEntry in networkList:
134 networkSlices = networkEntry.slices.all()
135 for slice in networkSlices:
136 if slice.name==sliceName:
137 if networkEntry.template.name=="Public shared IPv4":
138 setattr(networkEntry,'ports',networkPorts)
139 networkEntry.save()
140
141def 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())
Scott Baker866c5b32014-08-29 11:34:00 -0700153 #tenantSliceDetails['sites'] = userSliceTableFormatter(getTenantSitesInfo())
Scott Bakerc7325a42014-05-30 16:06:46 -0700154 tenantSliceDetails['mountDataSets'] = userSliceTableFormatter(getMountDataSets())
155 tenantSliceDetails['publicKey'] = getPublicKey(user)
Scott Baker866c5b32014-08-29 11:34:00 -0700156 tenantSliceDetails['availableSites']=userSliceTableFormatter(getAvailableSites())
157 tenantSliceDetails['role']=getUserRole(user)
158 tenantSliceDetails['siteUsers']=getSiteUsers(user)
Scott Bakerc7325a42014-05-30 16:06:46 -0700159 return tenantSliceDetails
160
Scott Baker866c5b32014-08-29 11:34:00 -0700161def 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
169def getUserRole(user):
170 sp=SitePrivilege.objects.filter(user=user)
171 for entry in sp:
172 return str(entry.role)
173
174
Scott Bakerc7325a42014-05-30 16:06:46 -0700175def getTenantInfo(user):
176 slices =Slice.objects.all()
177 userSliceInfo = []
178 for entry in slices:
Scott Baker866c5b32014-08-29 11:34:00 -0700179 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
Scott Bakeree9736d2015-01-03 16:26:38 -0800183 preferredImage = entry.image_preference
184 #sliceDataSet = entry.mount_data_sets
Scott Baker866c5b32014-08-29 11:34:00 -0700185 sliceNetwork = {}
186 numSliver = 0
187 sliceImage=""
188 sliceSite = {}
189 sliceNode = {}
190 sliceInstance= {}
191 #createPrivateVolume(user,sliceName)
Scott Baker01ef6492014-08-29 12:19:09 -0700192 available_sites = getAvailableSites()
Scott Baker866c5b32014-08-29 11:34:00 -0700193 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})
Scott Bakerc7325a42014-05-30 16:06:46 -0700201 return userSliceInfo
202
203def getTenantSitesInfo():
Scott Baker866c5b32014-08-29 11:34:00 -0700204 availableSites=getAvailableSites()
Scott Bakerc7325a42014-05-30 16:06:46 -0700205 tenantSiteInfo=[]
206 for entry in Site.objects.all():
Scott Baker866c5b32014-08-29 11:34:00 -0700207 if entry.name in availableSites:
Scott Bakerc7325a42014-05-30 16:06:46 -0700208 tenantSiteInfo.append({'siteName':entry.name})
209 return tenantSiteInfo
210
211def getPublicKey(user):
212 users=User.objects.all()
213 for key in users:
214 if (str(key.email)==str(user)):
215 sshKey = key.public_key
216 return sshKey
217
218def getServiceClassInfo(user):
219 serviceClassList = ServiceClass.objects.all()
220 sliceInfo = []
221 for entry in serviceClassList:
222 sliceInfo.append({'serviceClass':entry.name})
223 return sliceInfo
224
225def getImageInfo(user):
Scott Baker866c5b32014-08-29 11:34:00 -0700226 #imageList = Image.objects.all()
227 #imageInfo = []
228 #for imageEntry in imageList:
229 #imageInfo.append({'Image':imageEntry.name})
Scott Bakerc7325a42014-05-30 16:06:46 -0700230 imageInfo = []
Scott Baker866c5b32014-08-29 11:34:00 -0700231 tempImageInfo = []
232 length = len(BLESSED_DEPLOYMENTS)
233 for deployment in Deployment.objects.all():
234 if deployment.name in BLESSED_DEPLOYMENTS:
Sapan Bhatia400f5b52014-11-20 15:08:18 -0500235 for x in deployment.imagedeployments.all():
Scott Baker866c5b32014-08-29 11:34:00 -0700236 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)
Scott Bakerc7325a42014-05-30 16:06:46 -0700243 return imageInfo
244
245def 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
254SYNDICATE_REPLICATE_PORTNUM = 1025
255
256def 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
277def 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
287def 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
294def getMountDataSets():
295 dataSetInfo=[]
296 for volume in Volume.objects.all():
297 if not volume.private:
298 dataSetInfo.append({'DataSet': volume.name})
299
300 return dataSetInfo
301
302def getDeploymentSites():
303 deploymentList = Deployment.objects.all()
304 deploymentInfo = []
305 for entry in deploymentList:
306 deploymentInfo.append({'DeploymentSite':entry.name})
307 return deploymentInfo
308
Scott Baker866c5b32014-08-29 11:34:00 -0700309def getAvailableSites():
310 available_sites = []
311 for deployment in Deployment.objects.all():
312 if deployment.name in BLESSED_DEPLOYMENTS:
Sapan Bhatia400f5b52014-11-20 15:08:18 -0500313 for x in deployment.sitedeployments.all():
Scott Baker866c5b32014-08-29 11:34:00 -0700314 if x.site.nodes.all():
315 available_sites.append(x.site.name)
316 return list(set(available_sites))
317
Scott Bakerc7325a42014-05-30 16:06:46 -0700318class TenantDeleteSliceView(View):
319 def post(self,request):
320 if request.user.isReadOnlyUser():
321 return HttpResponseForbidden("User is in read-only mode")
322 sliceName = request.POST.get("sliceName",None)
323 slice = Slice.objects.get(name=sliceName)
324 #print slice, slice.id
325 sliceToDel=Slice(name=sliceName, id=slice.id)
326 sliceToDel.delete()
Scott Baker823b7212014-06-16 10:25:39 -0700327 return HttpResponse(json.dumps("Slice deleted"), content_type='application/javascript')
Scott Bakerc7325a42014-05-30 16:06:46 -0700328
329class 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)
Scott Baker866c5b32014-08-29 11:34:00 -0700350 imageName = request.POST.get("image",None)
Scott Bakerc7325a42014-05-30 16:06:46 -0700351 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)
Scott Baker866c5b32014-08-29 11:34:00 -0700357 image = Image.objects.get(name=imageName)
Scott Bakerc7325a42014-05-30 16:06:46 -0700358
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
Scott Baker866c5b32014-08-29 11:34:00 -0700369 sitesChanged = slice_increase_slivers(request.user, user_ip, siteList, slice, image, count, noAct)
Scott Bakerc7325a42014-05-30 16:06:46 -0700370 elif (actionToDo == "rem"):
371 sitesChanged = slice_decrease_slivers(request.user, siteList, slice, count, noAct)
372 else:
373 return HttpResponseServerError("Unknown actionToDo %s" % actionToDo)
374
Scott Baker823b7212014-06-16 10:25:39 -0700375 return HttpResponse(json.dumps(sitesChanged), content_type='application/javascript')
Scott Bakerc7325a42014-05-30 16:06:46 -0700376
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
382class 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]
Scott Baker823b7212014-06-16 10:25:39 -0700392 return HttpResponse(json.dumps(sites), content_type='application/javascript')
Scott Bakerc7325a42014-05-30 16:06:46 -0700393
394def 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
407def 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
Scott Baker01ef6492014-08-29 12:19:09 -0700420 available_sites = getAvailableSites()
Scott Bakerc7325a42014-05-30 16:06:46 -0700421 sites = Site.objects.all()
Scott Baker866c5b32014-08-29 11:34:00 -0700422 sites = [x for x in sites if x.name in available_sites]
Scott Bakerc7325a42014-05-30 16:06:46 -0700423 sites = sorted(sites, key=functools.partial(siteSortKey, slice=slice, count=count, lat=lat, lon=lon))
424
425 return sites
426
427class TenantViewData(View):
428 def get(self, request, **kwargs):
Scott Baker823b7212014-06-16 10:25:39 -0700429 return HttpResponse(json.dumps(getTenantSliceInfo(request.user, True)), content_type='application/javascript')
Scott Baker866c5b32014-08-29 11:34:00 -0700430
431class RequestAccountView(View):
432 def get(self, request, **kwargs):
433 return HttpResponse()