blob: f6b73b4815b30a989b2a524bcd4734e238b8446c [file] [log] [blame]
# Copyright 2017-present Open Networking Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from xos.exceptions import *
from tenantwithcontainer_decl import *
class TenantWithContainer(TenantWithContainer_decl):
class Meta:
proxy = True
def __init__(self, *args, **kwargs):
super(TenantWithContainer, self).__init__(*args, **kwargs)
# vSG service relies on knowing when instance id has changed
self.orig_instance_id = self.get_attribute("instance_id")
# vSG service relies on instance_id attribute
def get_attribute(self, name, default=None):
if name=="instance_id":
if self.instance:
return self.instance.id
else:
return None
else:
return super(TenantWithContainer, self).get_attribute(name, default)
# Services may wish to override the image() function to return different
# images based on criteria in the tenant object. For example,
# if (self.has_feature_A):
# return Instance.object.get(name="image_with_feature_a")
# elif (self.has_feature_B):
# return Instance.object.get(name="image_with_feature_b")
# else:
# return super(MyTenantClass,self).image()
@property
def image(self):
from core.models import Image
# Implement the logic here to pick the image that should be used when
# instantiating the VM that will hold the container.
slice = self.provider_service.slices.all()
if not slice:
raise XOSProgrammingError("provider service has no slice")
slice = slice[0]
# If slice has default_image set then use it
if slice.default_image:
return slice.default_image
raise XOSProgrammingError("Please set a default image for %s" % self.slice.name)
def save_instance(self, instance):
# Override this function to do custom pre-save or post-save processing,
# such as creating ports for containers.
instance.save()
def pick_least_loaded_instance_in_slice(self, slices, image):
for slice in slices:
if slice.instances.all().count() > 0:
for instance in slice.instances.all():
if instance.image != image:
continue
# Pick the first instance that has lesser than 5 tenants
if self.count_of_tenants_of_an_instance(instance) < 5:
return instance
return None
# TODO: Ideally the tenant count for an instance should be maintained using a
# many-to-one relationship attribute, however this model being proxy, it does
# not permit any new attributes to be defined. Find if any better solutions
def count_of_tenants_of_an_instance(self, instance):
tenant_count = 0
for tenant in self.__class__.objects.all():
if tenant.get_attribute("instance_id", None) == instance.id:
tenant_count += 1
return tenant_count
def manage_container(self):
from core.models import Instance, Flavor
if self.deleted:
return
if (self.instance is not None) and (self.instance.image != self.image):
self.instance.delete()
self.instance = None
if self.instance is None:
if not self.provider_service.slices.count():
raise XOSConfigurationError("The service has no slices")
new_instance_created = False
instance = None
if self.get_attribute("use_same_instance_for_multiple_tenants", default=False):
# Find if any existing instances can be used for this tenant
slices = self.provider_service.slices.all()
instance = self.pick_least_loaded_instance_in_slice(slices, self.image)
if not instance:
slice = self.provider_service.slices.all()[0]
flavor = slice.default_flavor
if not flavor:
flavors = Flavor.objects.filter(name="m1.small")
if not flavors:
raise XOSConfigurationError("No m1.small flavor")
flavor = flavors[0]
if slice.default_isolation == "container_vm":
(node, parent) = ContainerVmScheduler(slice).pick()
else:
(node, parent) = LeastLoadedNodeScheduler(slice).pick()
instance = Instance(slice=slice,
node=node,
image=self.image,
creator=self.creator,
deployment=node.site_deployment.deployment,
flavor=flavor,
isolation=slice.default_isolation,
parent=parent)
self.save_instance(instance)
new_instance_created = True
try:
self.instance = instance
super(TenantWithContainer, self).save()
except:
if new_instance_created:
instance.delete()
raise
def cleanup_container(self):
if self.instance:
if self.get_attribute("use_same_instance_for_multiple_tenants", default=False):
# Delete the instance only if this is last tenant in that
# instance
tenant_count = self.count_of_tenants_of_an_instance(
self.instance)
if tenant_count == 0:
self.instance.delete()
else:
self.instance.delete()
self.instance = None
def save(self, *args, **kwargs):
if (not self.creator) and (hasattr(self, "caller")) and (self.caller):
self.creator = self.caller
super(TenantWithContainer, self).save(*args, **kwargs)