from view_common import * from core.models import * import functools from django.contrib.auth.models import BaseUserManager from django.core import serializers from django.core.mail import EmailMultiAlternatives import json BLESSED_DEPLOYMENTS = ["US-MaxPlanck", "US-GeorgiaTech", "US-Princeton", "US-Washington", "US-Stanford"] class RequestAccessView(View): def post(self, request, *args, **kwargs): email = request.POST.get("email", "0") firstname = request.POST.get("firstname", "0") lastname = request.POST.get("lastname", "0") site = request.POST.get("site","0") # see if it already exists user=User.objects.filter(email=BaseUserManager.normalize_email(email)) if (user): user = user[0] if user.is_active: # force a new email to be sent user.is_registering=True user.save() return HttpResponse(json.dumps({"error": "already_approved"}), content_type='application/javascript') else: return HttpResponse(json.dumps({"error": "already_pending"}), content_type='application/javascript') user=User.deleted_objects.filter(email=BaseUserManager.normalize_email(email)) if (user): return HttpResponse(json.dumps({"error": "is_deleted"}), content_type='application/javascript') user = User( email=BaseUserManager.normalize_email(email), firstname=firstname, lastname=lastname, is_active=False, is_admin=False, is_registering=True ) user.save() user.site=Site.objects.get(name=site) user.save(update_fields=['site']) sitePriv = SitePrivilege.objects.filter(site=user.site) userId = user.id userUrl = "http://"+request.get_host()+"/admin/core/user/"+str(userId) for sp in sitePriv: subject, from_email, to = 'Authorize OpenCloud User Account', 'support@opencloud.us', str(sp.user) text_content = 'This is an important message.' html_content = """

Please authorize the following user on site """+site+""":

User: """+firstname+""" """+lastname+"""
Email: """+email+"""

Check the checkbox next to Is Active property at this link 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.

""" msg = EmailMultiAlternatives(subject,text_content, from_email, [to]) msg.attach_alternative(html_content, "text/html") msg.send() return HttpResponse(serializers.serialize("json",[user,]), content_type='application/javascript') class TenantCreateSlice(View): def post(self, request, *args, **kwargs): if request.user.isReadOnlyUser(): return HttpResponseForbidden("User is in read-only mode") sliceName = request.POST.get("sliceName", "0") serviceClass = request.POST.get("serviceClass", "0") imageName = request.POST.get("imageName", "0") actionToDo = request.POST.get("actionToDo", "0") networkPorts = request.POST.get("network","0") mountDataSets = request.POST.get("mountDataSets","0") privateVolume = request.POST.get("privateVolume","0") userEmail = request.POST.get("userEmail","0") if (actionToDo == "add"): serviceClass = ServiceClass.objects.get(name=serviceClass) site = request.user.site image = Image.objects.get(name=imageName) newSlice = Slice(name=sliceName,serviceClass=serviceClass,site=site,image_preference=image,mount_data_sets=mountDataSets) newSlice.save() privateTemplate="Private" publicTemplate="Public shared IPv4" privateNetworkName = sliceName+"-"+privateTemplate publicNetworkName = sliceName+"-"+publicTemplate slice=Slice.objects.get(name=sliceName) addNetwork(privateNetworkName,privateTemplate,slice) addNetwork(publicNetworkName,publicTemplate,slice) addOrModifyPorts(networkPorts,sliceName) if privateVolume=="true": privateVolForSlice(request.user,sliceName) slicePrivs=SlicePrivilege(user=User.objects.get(email=userEmail),slice=Slice.objects.get(name=sliceName),role=SliceRole.objects.get(role="admin")) slicePrivs.save() return HttpResponse(json.dumps("Slice created"), content_type='application/javascript') class TenantAddUser(View): def post(self, request, *args, **kwargs): if request.user.isReadOnlyUser(): return HttpResponseForbidden("User is in read-only mode") sliceName = request.POST.get("sliceName", "0") userEmail = request.POST.get("userEmail","0") slicePrivs=SlicePrivilege(user=User.objects.get(email=userEmail),slice=Slice.objects.get(name=sliceName),role=SliceRole.objects.get(role="admin")) slicePrivs.save() return HttpResponse(json.dumps("Slice created"), content_type='application/javascript') def privateVolForSlice(user,sliceName): if not hasPrivateVolume(sliceName): volumeName=createPrivateVolume(user,sliceName) readWrite="true" mountVolume(sliceName,volumeName,readWrite) class TenantUpdateSlice(View): def post(self, request, *args, **kwargs): if request.user.isReadOnlyUser(): return HttpResponseForbidden("User is in read-only mode") sliceName = request.POST.get("sliceName", "0") serviceClass = request.POST.get("serviceClass", "0") imageName = request.POST.get("imageName", "0") actionToDo = request.POST.get("actionToDo", "0") networkPorts = request.POST.get("networkPorts","0") dataSet = request.POST.get("dataSet","0") privateVolume = request.POST.get("privateVolume","0") slice = Slice.objects.all() for entry in slice: serviceClass = ServiceClass.objects.get(name=serviceClass) if(entry.name==sliceName): if (actionToDo == "update"): setattr(entry,'serviceClass',serviceClass) setattr(entry,'image_preference',imageName) setattr(entry,'mount_data_sets',dataSet) entry.save() break addOrModifyPorts(networkPorts,sliceName) if privateVolume=="true": privateVolForSlice(request.user,sliceName) return HttpResponse(json.dumps("Slice updated"), content_type='application/javascript') def addNetwork(name,template,sliceName): networkTemplate=NetworkTemplate.objects.get(name=template) newNetwork = Network(name = name, template = networkTemplate, owner = sliceName) newNetwork.save() addNetworkSlice(newNetwork,sliceName) def addNetworkSlice(networkSlice,sliceName): newNetworkSlice=NetworkSlice(network =networkSlice, slice=sliceName) newNetworkSlice.save() def addOrModifyPorts(networkPorts,sliceName): networkList = Network.objects.all() networkInfo = [] if networkPorts: for networkEntry in networkList: networkSlices = networkEntry.slices.all() for slice in networkSlices: if slice.name==sliceName: if networkEntry.template.name=="Public shared IPv4": setattr(networkEntry,'ports',networkPorts) networkEntry.save() def getTenantSliceInfo(user, tableFormat = False): tenantSliceDetails = {} tenantSliceData = getTenantInfo(user) tenantServiceClassData = getServiceClassInfo(user) if (tableFormat): tenantSliceDetails['userSliceInfo'] = userSliceTableFormatter(tenantSliceData) tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData) else: tenantSliceDetails['userSliceInfo'] = tenantSliceData tenantSliceDetails['sliceServiceClass']=userSliceTableFormatter(tenantServiceClassData) tenantSliceDetails['image']=userSliceTableFormatter(getImageInfo(user)) tenantSliceDetails['deploymentSites']=userSliceTableFormatter(getDeploymentSites()) #tenantSliceDetails['sites'] = userSliceTableFormatter(getTenantSitesInfo()) tenantSliceDetails['mountDataSets'] = userSliceTableFormatter(getMountDataSets()) tenantSliceDetails['publicKey'] = getPublicKey(user) tenantSliceDetails['availableSites']=userSliceTableFormatter(getAvailableSites()) tenantSliceDetails['role']=getUserRole(user) tenantSliceDetails['siteUsers']=getSiteUsers(user) return tenantSliceDetails def getSiteUsers(user): users = User.objects.filter(site=user.site) siteUsers=[] for entry in users: siteUsers.append(str(entry)) return siteUsers def getUserRole(user): sp=SitePrivilege.objects.filter(user=user) for entry in sp: return str(entry.role) def getTenantInfo(user): slices =Slice.objects.all() userSliceInfo = [] for entry in slices: if (entry.site == user.site): sliceName = Slice.objects.get(id=entry.id).name slice = Slice.objects.get(name=Slice.objects.get(id=entry.id).name) sliceServiceClass = entry.serviceClass.name preferredImage = entry.image_preference #sliceDataSet = entry.mount_data_sets sliceNetwork = {} numSliver = 0 sliceImage="" sliceSite = {} sliceNode = {} sliceInstance= {} #createPrivateVolume(user,sliceName) available_sites = getAvailableSites() for sliver in slice.slivers.all(): if sliver.node.site.name in available_sites: sliceSite[sliver.node.site.name] = sliceSite.get(sliver.node.site.name,0) + 1 sliceImage = sliver.image.name sliceNode[str(sliver)] = sliver.node.name numSliver = sum(sliceSite.values()) numSites = len(sliceSite) userSliceInfo.append({'sliceName': sliceName,'sliceServiceClass': sliceServiceClass,'preferredImage':preferredImage,'numOfSites':numSites, 'sliceSite':sliceSite,'sliceImage':sliceImage,'numOfSlivers':numSliver,'instanceNodePair':sliceNode}) return userSliceInfo def getTenantSitesInfo(): availableSites=getAvailableSites() tenantSiteInfo=[] for entry in Site.objects.all(): if entry.name in availableSites: tenantSiteInfo.append({'siteName':entry.name}) return tenantSiteInfo def getPublicKey(user): users=User.objects.all() for key in users: if (str(key.email)==str(user)): sshKey = key.public_key return sshKey def getServiceClassInfo(user): serviceClassList = ServiceClass.objects.all() sliceInfo = [] for entry in serviceClassList: sliceInfo.append({'serviceClass':entry.name}) return sliceInfo def getImageInfo(user): #imageList = Image.objects.all() #imageInfo = [] #for imageEntry in imageList: #imageInfo.append({'Image':imageEntry.name}) imageInfo = [] tempImageInfo = [] length = len(BLESSED_DEPLOYMENTS) for deployment in Deployment.objects.all(): if deployment.name in BLESSED_DEPLOYMENTS: for x in deployment.imagedeployments.all(): tempImageInfo.append(x.image.name) temp = {} for i in set(tempImageInfo): temp[i] = tempImageInfo.count(i) for key in temp: if temp[key]>1: imageInfo.append(key) return imageInfo def createPrivateVolume(user, sliceName): caps = Volume.CAP_READ_DATA | Volume.CAP_WRITE_DATA | Volume.CAP_HOST_DATA getattr(Volume.default_gateway_caps,"read data") | \ getattr(Volume.default_gateway_caps,"write data") | \ getattr(Volume.default_gateway_caps,"host files") v = Volume(name="private_" + sliceName, owner_id=user, description="private volume for %s" % sliceName, blocksize=61440, private=True, archive=False, default_gateway_caps = caps) v.save() return v SYNDICATE_REPLICATE_PORTNUM = 1025 def get_free_port(): inuse={} inuse[SYNDICATE_REPLICATE_PORTNUM] = True for vs in VolumeSlice.objects.all(): inuse[vs.peer_portnum]=True inuse[vs.replicate_portnum]=True for network in Network.objects.all(): if not network.ports: continue network_ports = [x.strip() for x in network.ports.split(",")] for network_port in network_ports: try: inuse[int(network_port)] = True except: # in case someone has put a malformed port number in the list pass for i in range(1025, 65535): if not inuse.get(i,False): return i return False def mountVolume(sliceName, volumeName, readWrite): slice = Slice.objects.get(name=sliceName) volume = Volume.objects.get(name=volumeName) # choose some unused port numbers flags = Volume.CAP_READ_DATA if readWrite: flags = flags | Volume.CAP_WRITE_DATA vs = VolumeSlice(volume_id = volume, slice_id = slice, gateway_caps=flags, peer_portnum = get_free_port(), replicate_portnum = SYNDICATE_REPLICATE_PORTNUM) vs.save() def hasPrivateVolume(sliceName): slice = Slice.objects.get(name=sliceName) for vs in VolumeSlice.objects.filter(slice_id=slice): if vs.volume_id.private: return True return False def getMountDataSets(): dataSetInfo=[] for volume in Volume.objects.all(): if not volume.private: dataSetInfo.append({'DataSet': volume.name}) return dataSetInfo def getDeploymentSites(): deploymentList = Deployment.objects.all() deploymentInfo = [] for entry in deploymentList: deploymentInfo.append({'DeploymentSite':entry.name}) return deploymentInfo def getAvailableSites(): available_sites = [] for deployment in Deployment.objects.all(): if deployment.name in BLESSED_DEPLOYMENTS: for x in deployment.sitedeployments.all(): if x.site.nodes.all(): available_sites.append(x.site.name) return list(set(available_sites)) class TenantDeleteSliceView(View): def post(self,request): if request.user.isReadOnlyUser(): return HttpResponseForbidden("User is in read-only mode") sliceName = request.POST.get("sliceName",None) slice = Slice.objects.get(name=sliceName) #print slice, slice.id sliceToDel=Slice(name=sliceName, id=slice.id) sliceToDel.delete() return HttpResponse(json.dumps("Slice deleted"), content_type='application/javascript') class TenantAddOrRemoveSliverView(View): """ Add or remove slivers from a Slice Arguments: siteName - name of site. If not specified, PlanetStack will pick the best site., actionToDo - [add | rem] count - number of slivers to add or remove sliceName - name of slice noAct - if set, no changes will be made to db, but result will still show which sites would have been modified. Returns: Dictionary of sites that were modified, and the count of nodes that were added or removed at each site. """ def post(self, request, *args, **kwargs): siteName = request.POST.get("siteName", None) actionToDo = request.POST.get("actionToDo", None) count = int(request.POST.get("count","0")) sliceName = request.POST.get("slice", None) imageName = request.POST.get("image",None) noAct = request.POST.get("noAct", False) if not sliceName: return HttpResponseServerError("No slice name given") slice = Slice.objects.get(name=sliceName) image = Image.objects.get(name=imageName) if siteName: siteList = [Site.objects.get(name=siteName)] else: siteList = None if (actionToDo == "add"): user_ip = request.GET.get("ip", get_ip(request)) if (siteList is None): siteList = tenant_pick_sites(user, user_ip, slice, count) sitesChanged = slice_increase_slivers(request.user, user_ip, siteList, slice, image, count, noAct) elif (actionToDo == "rem"): sitesChanged = slice_decrease_slivers(request.user, siteList, slice, count, noAct) else: return HttpResponseServerError("Unknown actionToDo %s" % actionToDo) return HttpResponse(json.dumps(sitesChanged), content_type='application/javascript') def get(self, request, *args, **kwargs): request.POST = request.GET return self.post(request, *args, **kwargs) # for testing REST in browser #return HttpResponseServerError("GET is not supported") class TenantPickSitesView(View): """ primarily just for testing purposes """ def get(self, request, *args, **kwargs): count = request.GET.get("count","0") slice = request.GET.get("slice",None) if slice: slice = Slice.objects.get(name=slice) ip = request.GET.get("ip", get_ip(request)) sites = tenant_pick_sites(request.user, user_ip=ip, count=0, slice=slice) sites = [x.name for x in sites] return HttpResponse(json.dumps(sites), content_type='application/javascript') def siteSortKey(site, slice=None, count=None, lat=None, lon=None): # try to pick a site we're already using has_slivers_here=False if slice: for sliver in slice.slivers.all(): if sliver.node.site.name == site.name: has_slivers_here=True # Haversine method d = haversine(site.location.latitude, site.location.longitude, lat, lon) return (-has_slivers_here, d) def tenant_pick_sites(user, user_ip=None, slice=None, count=None): """ Returns list of sites, sorted from most favorable to least favorable """ lat=None lon=None try: client_geo = GeoIP().city(user_ip) if client_geo: lat=float(client_geo["latitude"]) lon=float(client_geo["longitude"]) except: print "exception in geo code" traceback.print_exc() available_sites = getAvailableSites() sites = Site.objects.all() sites = [x for x in sites if x.name in available_sites] sites = sorted(sites, key=functools.partial(siteSortKey, slice=slice, count=count, lat=lat, lon=lon)) return sites class TenantViewData(View): def get(self, request, **kwargs): return HttpResponse(json.dumps(getTenantSliceInfo(request.user, True)), content_type='application/javascript') class RequestAccountView(View): def get(self, request, **kwargs): return HttpResponse()