blob: a4b8e993d38a330284b6399792694b9cf6377c22 [file] [log] [blame]
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
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")
user = User(
email=BaseUserManager.normalize_email(email),
firstname=firstname,
lastname=lastname,
is_active=False,
is_admin=False
)
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 = """<p>Please authorize the following user on site """+site+""": <br><br>User: """+firstname+""" """+lastname+"""<br>Email: """+email+"""<br><br>
Check 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>"""
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()