split views.py into individual per-dashboard files
[plstackapi.git] / planetstack / core / dashboard / views / tenant.py
1 from view_common import *
2 import functools
3
4 BLESSED_SITES = ["Stanford", "Washington", "Princeton", "GeorgiaTech", "MaxPlanck"]
5
6 class 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"\r
26            privateNetworkName = sliceName+"-"+privateTemplate\r
27            publicNetworkName = sliceName+"-"+publicTemplate\r
28            slice=Slice.objects.get(name=sliceName)\r
29            addNetwork(privateNetworkName,privateTemplate,slice)\r
30            addNetwork(publicNetworkName,publicTemplate,slice)\r
31            addOrModifyPorts(networkPorts,sliceName)\r
32            if privateVolume=="true":\r
33                 privateVolForSlice(request.user,sliceName)
34         return HttpResponse("Slice created")
35
36 def privateVolForSlice(user,sliceName):
37         if not hasPrivateVolume(sliceName):\r
38            volumeName=createPrivateVolume(user,sliceName)\r
39            readWrite="true"\r
40            mountVolume(sliceName,volumeName,readWrite)
41
42 class TenantUpdateSlice(View):
43     def post(self, request, *args, **kwargs):\r
44         if request.user.isReadOnlyUser():\r
45             return HttpResponseForbidden("User is in read-only mode")\r
46 \r
47         sliceName = request.POST.get("sliceName", "0")\r
48         serviceClass = request.POST.get("serviceClass", "0")\r
49         imageName = request.POST.get("imageName", "0")\r
50         actionToDo = request.POST.get("actionToDo", "0")\r
51         networkPorts = request.POST.get("networkPorts","0")\r
52         dataSet = request.POST.get("dataSet","0")\r
53         privateVolume = request.POST.get("privateVolume","0")\r
54         slice = Slice.objects.all()\r
55         for entry in slice:\r
56                 serviceClass = ServiceClass.objects.get(name=serviceClass)\r
57                 if(entry.name==sliceName):\r
58                          if (actionToDo == "update"):\r
59                                 setattr(entry,'serviceClass',serviceClass)\r
60                                 setattr(entry,'imagePreference',imageName)\r
61                                 setattr(entry,'mountDataSets',dataSet)\r
62                                 entry.save()\r
63                                 break\r
64         addOrModifyPorts(networkPorts,sliceName)\r
65         if privateVolume=="true":\r
66                 privateVolForSlice(request.user,sliceName)\r
67         return HttpResponse("Slice updated")\r
68 \r
69 def addNetwork(name,template,sliceName):\r
70         networkTemplate=NetworkTemplate.objects.get(name=template)\r
71         newNetwork = Network(name = name,\r
72                               template = networkTemplate,\r
73                               owner = sliceName)\r
74         newNetwork.save()\r
75         addNetworkSlice(newNetwork,sliceName)\r
76 \r
77 def addNetworkSlice(networkSlice,sliceName):\r
78         newNetworkSlice=NetworkSlice(network =networkSlice,\r
79                                      slice=sliceName)\r
80         newNetworkSlice.save()\r
81 \r
82 def addOrModifyPorts(networkPorts,sliceName):\r
83         networkList = Network.objects.all()\r
84         networkInfo = []\r
85         if networkPorts:\r
86            for networkEntry in networkList:\r
87                networkSlices = networkEntry.slices.all()\r
88                for slice in networkSlices:\r
89                    if slice.name==sliceName:\r
90                           if networkEntry.template.name=="Public shared IPv4":\r
91                              setattr(networkEntry,'ports',networkPorts)\r
92                              networkEntry.save()\r
93 \r
94 def 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
111 def 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
137 def 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
144 def getPublicKey(user):
145         users=User.objects.all()\r
146         for key in users:\r
147                 if (str(key.email)==str(user)):\r
148                         sshKey = key.public_key\r
149         return sshKey
150
151 def getServiceClassInfo(user):
152     serviceClassList = ServiceClass.objects.all()
153     sliceInfo = []
154     for entry in serviceClassList:
155           sliceInfo.append({'serviceClass':entry.name})
156     return sliceInfo
157
158 def 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
167 def 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
176 SYNDICATE_REPLICATE_PORTNUM = 1025
177
178 def 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
199 def 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
209 def 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
216 def getMountDataSets():
217         dataSetInfo=[]\r
218         for volume in Volume.objects.all():\r
219             if not volume.private:\r
220                 dataSetInfo.append({'DataSet': volume.name})\r
221 \r
222         return dataSetInfo
223
224 def getDeploymentSites():
225     deploymentList = Deployment.objects.all()
226     deploymentInfo = []
227     for entry in deploymentList:
228         deploymentInfo.append({'DeploymentSite':entry.name})
229     return deploymentInfo
230
231 class TenantDeleteSliceView(View):
232         def post(self,request):\r
233                 if request.user.isReadOnlyUser():\r
234                     return HttpResponseForbidden("User is in read-only mode")\r
235                 sliceName = request.POST.get("sliceName",None)\r
236                 slice = Slice.objects.get(name=sliceName)\r
237                 #print slice, slice.id\r
238                 sliceToDel=Slice(name=sliceName, id=slice.id)\r
239                 sliceToDel.delete()
240                 return HttpResponse("Slice deleted")
241
242 class 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
293 class 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
305 def 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
318 def 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
337 class TenantViewData(View):
338     def get(self, request, **kwargs):
339         return HttpResponse(json.dumps(getTenantSliceInfo(request.user, True)), mimetype='application/javascript')