blob: a541ed91a30f5ab0226018e67339e8ea1e186020 [file] [log] [blame]
Scott Bakerc7325a42014-05-30 16:06:46 -07001from view_common import *
2import functools
3
4BLESSED_SITES = ["Stanford", "Washington", "Princeton", "GeorgiaTech", "MaxPlanck"]
5
6class TenantCreateSlice(View):
7 def post(self, request, *args, **kwargs):
8 if request.user.isReadOnlyUser():
9 return HttpResponseForbidden("User is in read-only mode")
10
11 sliceName = request.POST.get("sliceName", "0")
12 serviceClass = request.POST.get("serviceClass", "0")
13 imageName = request.POST.get("imageName", "0")
14 actionToDo = request.POST.get("actionToDo", "0")
15 networkPorts = request.POST.get("network","0")
16 mountDataSets = request.POST.get("mountDataSets","0")
17 privateVolume = request.POST.get("privateVolume","0")
18 if (actionToDo == "add"):
19 serviceClass = ServiceClass.objects.get(name=serviceClass)
20 site = request.user.site
21 image = Image.objects.get(name=imageName)
22 newSlice = Slice(name=sliceName,serviceClass=serviceClass,site=site,imagePreference=image,mountDataSets=mountDataSets)
23 newSlice.save()
24 privateTemplate="Private"
25 publicTemplate="Public shared IPv4"
26 privateNetworkName = sliceName+"-"+privateTemplate
27 publicNetworkName = sliceName+"-"+publicTemplate
28 slice=Slice.objects.get(name=sliceName)
29 addNetwork(privateNetworkName,privateTemplate,slice)
30 addNetwork(publicNetworkName,publicTemplate,slice)
31 addOrModifyPorts(networkPorts,sliceName)
32 if privateVolume=="true":
33 privateVolForSlice(request.user,sliceName)
34 return HttpResponse("Slice created")
35
36def privateVolForSlice(user,sliceName):
37 if not hasPrivateVolume(sliceName):
38 volumeName=createPrivateVolume(user,sliceName)
39 readWrite="true"
40 mountVolume(sliceName,volumeName,readWrite)
41
42class TenantUpdateSlice(View):
43 def post(self, request, *args, **kwargs):
44 if request.user.isReadOnlyUser():
45 return HttpResponseForbidden("User is in read-only mode")
46
47 sliceName = request.POST.get("sliceName", "0")
48 serviceClass = request.POST.get("serviceClass", "0")
49 imageName = request.POST.get("imageName", "0")
50 actionToDo = request.POST.get("actionToDo", "0")
51 networkPorts = request.POST.get("networkPorts","0")
52 dataSet = request.POST.get("dataSet","0")
53 privateVolume = request.POST.get("privateVolume","0")
54 slice = Slice.objects.all()
55 for entry in slice:
56 serviceClass = ServiceClass.objects.get(name=serviceClass)
57 if(entry.name==sliceName):
58 if (actionToDo == "update"):
59 setattr(entry,'serviceClass',serviceClass)
60 setattr(entry,'imagePreference',imageName)
61 setattr(entry,'mountDataSets',dataSet)
62 entry.save()
63 break
64 addOrModifyPorts(networkPorts,sliceName)
65 if privateVolume=="true":
66 privateVolForSlice(request.user,sliceName)
67 return HttpResponse("Slice updated")
68
69def addNetwork(name,template,sliceName):
70 networkTemplate=NetworkTemplate.objects.get(name=template)
71 newNetwork = Network(name = name,
72 template = networkTemplate,
73 owner = sliceName)
74 newNetwork.save()
75 addNetworkSlice(newNetwork,sliceName)
76
77def addNetworkSlice(networkSlice,sliceName):
78 newNetworkSlice=NetworkSlice(network =networkSlice,
79 slice=sliceName)
80 newNetworkSlice.save()
81
82def addOrModifyPorts(networkPorts,sliceName):
83 networkList = Network.objects.all()
84 networkInfo = []
85 if networkPorts:
86 for networkEntry in networkList:
87 networkSlices = networkEntry.slices.all()
88 for slice in networkSlices:
89 if slice.name==sliceName:
90 if networkEntry.template.name=="Public shared IPv4":
91 setattr(networkEntry,'ports',networkPorts)
92 networkEntry.save()
93
94def getTenantSliceInfo(user, tableFormat = False):
95 tenantSliceDetails = {}
96 tenantSliceData = getTenantInfo(user)
97 tenantServiceClassData = getServiceClassInfo(user)
98 if (tableFormat):
99 tenantSliceDetails['userSliceInfo'] = userSliceTableFormatter(tenantSliceData)
100 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
101 else:
102 tenantSliceDetails['userSliceInfo'] = tenantSliceData
103 tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData)
104 tenantSliceDetails['image']=userSliceTableFormatter(getImageInfo(user))
105 tenantSliceDetails['deploymentSites']=userSliceTableFormatter(getDeploymentSites())
106 tenantSliceDetails['sites'] = userSliceTableFormatter(getTenantSitesInfo())
107 tenantSliceDetails['mountDataSets'] = userSliceTableFormatter(getMountDataSets())
108 tenantSliceDetails['publicKey'] = getPublicKey(user)
109 return tenantSliceDetails
110
111def getTenantInfo(user):
112 slices =Slice.objects.all()
113 userSliceInfo = []
114 for entry in slices:
115 sliceName = Slice.objects.get(id=entry.id).name
116 slice = Slice.objects.get(name=Slice.objects.get(id=entry.id).name)
117 sliceServiceClass = entry.serviceClass.name
118 preferredImage = entry.imagePreference
119 #sliceDataSet = entry.mountDataSets
120 sliceNetwork = {}
121 numSliver = 0
122 sliceImage=""
123 sliceSite = {}
124 sliceNode = {}
125 sliceInstance= {}
126 #createPrivateVolume(user,sliceName)
127 for sliver in slice.slivers.all():
128 if sliver.node.site.name in BLESSED_SITES:
129 sliceSite[sliver.node.site.name] = sliceSite.get(sliver.node.site.name,0) + 1
130 sliceImage = sliver.image.name
131 sliceNode[str(sliver)] = sliver.node.name
132 numSliver = sum(sliceSite.values())
133 numSites = len(sliceSite)
134 userSliceInfo.append({'sliceName': sliceName,'sliceServiceClass': sliceServiceClass,'preferredImage':preferredImage,'numOfSites':numSites, 'sliceSite':sliceSite,'sliceImage':sliceImage,'numOfSlivers':numSliver,'instanceNodePair':sliceNode})
135 return userSliceInfo
136
137def getTenantSitesInfo():
138 tenantSiteInfo=[]
139 for entry in Site.objects.all():
140 if entry.name in BLESSED_SITES:
141 tenantSiteInfo.append({'siteName':entry.name})
142 return tenantSiteInfo
143
144def getPublicKey(user):
145 users=User.objects.all()
146 for key in users:
147 if (str(key.email)==str(user)):
148 sshKey = key.public_key
149 return sshKey
150
151def getServiceClassInfo(user):
152 serviceClassList = ServiceClass.objects.all()
153 sliceInfo = []
154 for entry in serviceClassList:
155 sliceInfo.append({'serviceClass':entry.name})
156 return sliceInfo
157
158def getImageInfo(user):
159 imageList = Image.objects.all()
160 #imageList = ['Fedora 16 LXC rev 1.3','Hadoop','MPI']
161 imageInfo = []
162 for imageEntry in imageList:
163 imageInfo.append({'Image':imageEntry.name})
164 #imageInfo.append({'Image':imageEntry})
165 return imageInfo
166
167def createPrivateVolume(user, sliceName):
168 caps = Volume.CAP_READ_DATA | Volume.CAP_WRITE_DATA | Volume.CAP_HOST_DATA
169 getattr(Volume.default_gateway_caps,"read data") | \
170 getattr(Volume.default_gateway_caps,"write data") | \
171 getattr(Volume.default_gateway_caps,"host files")
172 v = Volume(name="private_" + sliceName, owner_id=user, description="private volume for %s" % sliceName, blocksize=61440, private=True, archive=False, default_gateway_caps = caps)
173 v.save()
174 return v
175
176SYNDICATE_REPLICATE_PORTNUM = 1025
177
178def get_free_port():
179 inuse={}
180 inuse[SYNDICATE_REPLICATE_PORTNUM] = True
181 for vs in VolumeSlice.objects.all():
182 inuse[vs.peer_portnum]=True
183 inuse[vs.replicate_portnum]=True
184 for network in Network.objects.all():
185 if not network.ports:
186 continue
187 network_ports = [x.strip() for x in network.ports.split(",")]
188 for network_port in network_ports:
189 try:
190 inuse[int(network_port)] = True
191 except:
192 # in case someone has put a malformed port number in the list
193 pass
194 for i in range(1025, 65535):
195 if not inuse.get(i,False):
196 return i
197 return False
198
199def mountVolume(sliceName, volumeName, readWrite):
200 slice = Slice.objects.get(name=sliceName)
201 volume = Volume.objects.get(name=volumeName)
202 # choose some unused port numbers
203 flags = Volume.CAP_READ_DATA
204 if readWrite:
205 flags = flags | Volume.CAP_WRITE_DATA
206 vs = VolumeSlice(volume_id = volume, slice_id = slice, gateway_caps=flags, peer_portnum = get_free_port(), replicate_portnum = SYNDICATE_REPLICATE_PORTNUM)
207 vs.save()
208
209def hasPrivateVolume(sliceName):
210 slice = Slice.objects.get(name=sliceName)
211 for vs in VolumeSlice.objects.filter(slice_id=slice):
212 if vs.volume_id.private:
213 return True
214 return False
215
216def getMountDataSets():
217 dataSetInfo=[]
218 for volume in Volume.objects.all():
219 if not volume.private:
220 dataSetInfo.append({'DataSet': volume.name})
221
222 return dataSetInfo
223
224def getDeploymentSites():
225 deploymentList = Deployment.objects.all()
226 deploymentInfo = []
227 for entry in deploymentList:
228 deploymentInfo.append({'DeploymentSite':entry.name})
229 return deploymentInfo
230
231class TenantDeleteSliceView(View):
232 def post(self,request):
233 if request.user.isReadOnlyUser():
234 return HttpResponseForbidden("User is in read-only mode")
235 sliceName = request.POST.get("sliceName",None)
236 slice = Slice.objects.get(name=sliceName)
237 #print slice, slice.id
238 sliceToDel=Slice(name=sliceName, id=slice.id)
239 sliceToDel.delete()
240 return HttpResponse("Slice deleted")
241
242class TenantAddOrRemoveSliverView(View):
243 """ Add or remove slivers from a Slice
244
245 Arguments:
246 siteName - name of site. If not specified, PlanetStack will pick the
247 best site.,
248 actionToDo - [add | rem]
249 count - number of slivers to add or remove
250 sliceName - name of slice
251 noAct - if set, no changes will be made to db, but result will still
252 show which sites would have been modified.
253
254 Returns:
255 Dictionary of sites that were modified, and the count of nodes
256 that were added or removed at each site.
257 """
258 def post(self, request, *args, **kwargs):
259 siteName = request.POST.get("siteName", None)
260 actionToDo = request.POST.get("actionToDo", None)
261 count = int(request.POST.get("count","0"))
262 sliceName = request.POST.get("slice", None)
263 noAct = request.POST.get("noAct", False)
264
265 if not sliceName:
266 return HttpResponseServerError("No slice name given")
267
268 slice = Slice.objects.get(name=sliceName)
269
270 if siteName:
271 siteList = [Site.objects.get(name=siteName)]
272 else:
273 siteList = None
274
275 if (actionToDo == "add"):
276 user_ip = request.GET.get("ip", get_ip(request))
277 if (siteList is None):
278 siteList = tenant_pick_sites(user, user_ip, slice, count)
279
280 sitesChanged = slice_increase_slivers(request.user, user_ip, siteList, slice, count, noAct)
281 elif (actionToDo == "rem"):
282 sitesChanged = slice_decrease_slivers(request.user, siteList, slice, count, noAct)
283 else:
284 return HttpResponseServerError("Unknown actionToDo %s" % actionToDo)
285
286 return HttpResponse(json.dumps(sitesChanged), mimetype='application/javascript')
287
288 def get(self, request, *args, **kwargs):
289 request.POST = request.GET
290 return self.post(request, *args, **kwargs) # for testing REST in browser
291 #return HttpResponseServerError("GET is not supported")
292
293class TenantPickSitesView(View):
294 """ primarily just for testing purposes """
295 def get(self, request, *args, **kwargs):
296 count = request.GET.get("count","0")
297 slice = request.GET.get("slice",None)
298 if slice:
299 slice = Slice.objects.get(name=slice)
300 ip = request.GET.get("ip", get_ip(request))
301 sites = tenant_pick_sites(request.user, user_ip=ip, count=0, slice=slice)
302 sites = [x.name for x in sites]
303 return HttpResponse(json.dumps(sites), mimetype='application/javascript')
304
305def siteSortKey(site, slice=None, count=None, lat=None, lon=None):
306 # try to pick a site we're already using
307 has_slivers_here=False
308 if slice:
309 for sliver in slice.slivers.all():
310 if sliver.node.site.name == site.name:
311 has_slivers_here=True
312
313 # Haversine method
314 d = haversine(site.location.latitude, site.location.longitude, lat, lon)
315
316 return (-has_slivers_here, d)
317
318def tenant_pick_sites(user, user_ip=None, slice=None, count=None):
319 """ Returns list of sites, sorted from most favorable to least favorable """
320 lat=None
321 lon=None
322 try:
323 client_geo = GeoIP().city(user_ip)
324 if client_geo:
325 lat=float(client_geo["latitude"])
326 lon=float(client_geo["longitude"])
327 except:
328 print "exception in geo code"
329 traceback.print_exc()
330
331 sites = Site.objects.all()
332 sites = [x for x in sites if x.name in BLESSED_SITES]
333 sites = sorted(sites, key=functools.partial(siteSortKey, slice=slice, count=count, lat=lat, lon=lon))
334
335 return sites
336
337class TenantViewData(View):
338 def get(self, request, **kwargs):
339 return HttpResponse(json.dumps(getTenantSliceInfo(request.user, True)), mimetype='application/javascript')