Scott Baker | c7325a4 | 2014-05-30 16:06:46 -0700 | [diff] [blame] | 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"
|
| 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) |
Scott Baker | 823b721 | 2014-06-16 10:25:39 -0700 | [diff] [blame] | 34 | return HttpResponse(json.dumps("Slice created"), content_type='application/javascript') |
Scott Baker | c7325a4 | 2014-05-30 16:06:46 -0700 | [diff] [blame] | 35 | |
| 36 | def privateVolForSlice(user,sliceName): |
| 37 | if not hasPrivateVolume(sliceName):
|
| 38 | volumeName=createPrivateVolume(user,sliceName)
|
| 39 | readWrite="true"
|
| 40 | mountVolume(sliceName,volumeName,readWrite) |
| 41 | |
| 42 | class 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)
|
Scott Baker | 823b721 | 2014-06-16 10:25:39 -0700 | [diff] [blame] | 67 | return HttpResponse(json.dumps("Slice updated"), content_type='application/javascript')
|
Scott Baker | c7325a4 | 2014-05-30 16:06:46 -0700 | [diff] [blame] | 68 |
|
| 69 | def 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 |
|
| 77 | def addNetworkSlice(networkSlice,sliceName):
|
| 78 | newNetworkSlice=NetworkSlice(network =networkSlice,
|
| 79 | slice=sliceName)
|
| 80 | newNetworkSlice.save()
|
| 81 |
|
| 82 | def 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 |
|
| 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()
|
| 146 | for key in users:
|
| 147 | if (str(key.email)==str(user)):
|
| 148 | sshKey = key.public_key
|
| 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=[]
|
| 218 | for volume in Volume.objects.all():
|
| 219 | if not volume.private:
|
| 220 | dataSetInfo.append({'DataSet': volume.name})
|
| 221 |
|
| 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):
|
| 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() |
Scott Baker | 823b721 | 2014-06-16 10:25:39 -0700 | [diff] [blame] | 240 | return HttpResponse(json.dumps("Slice deleted"), content_type='application/javascript') |
Scott Baker | c7325a4 | 2014-05-30 16:06:46 -0700 | [diff] [blame] | 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 | |
Scott Baker | 823b721 | 2014-06-16 10:25:39 -0700 | [diff] [blame] | 286 | return HttpResponse(json.dumps(sitesChanged), content_type='application/javascript') |
Scott Baker | c7325a4 | 2014-05-30 16:06:46 -0700 | [diff] [blame] | 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] |
Scott Baker | 823b721 | 2014-06-16 10:25:39 -0700 | [diff] [blame] | 303 | return HttpResponse(json.dumps(sites), content_type='application/javascript') |
Scott Baker | c7325a4 | 2014-05-30 16:06:46 -0700 | [diff] [blame] | 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): |
Scott Baker | 823b721 | 2014-06-16 10:25:39 -0700 | [diff] [blame] | 339 | return HttpResponse(json.dumps(getTenantSliceInfo(request.user, True)), content_type='application/javascript') |