blob: 05aeebc1cdce8dc4cef98866513a49bfce215c40 [file] [log] [blame]
from header import *
#from core.models.service import Service
from core.models import Service
#from core.models.tenantwithcontainer import TenantWithContainer
from core.models import TenantWithContainer
class VSGService(Service):
KIND = "vCPE"
class Meta:
app_label = "vsg"
name = "vsg"
verbose_name = "vSG Service"
# Primitive Fields (Not Relations)
url_filter_kind = StrippedCharField( blank = True, max_length = 30, null = True, db_index = False, choices = ((None, 'None'), ('safebrowsing', 'Safe Browsing'), ('answerx', 'AnswerX')) )
dns_servers = StrippedCharField( default = "8.8.8.8", max_length = 255, null = False, db_index = False, blank = False )
node_label = StrippedCharField( db_index = False, max_length = 30, null = True, blank = True )
docker_image_name = StrippedCharField( default = "docker.io/xosproject/vsg", max_length = 255, null = False, db_index = False, blank = False )
docker_insecure_registry = BooleanField( default = False, null = False, blank = True, db_index = False )
# Relations
pass
class VSGTenant(TenantWithContainer):
KIND = "vCPE"
class Meta:
app_label = "vsg"
name = "vsg"
verbose_name = "vSG Service"
# Primitive Fields (Not Relations)
last_ansible_hash = StrippedCharField( db_index = False, max_length = 128, null = True, blank = True )
# Relations
sync_attributes = ("wan_container_ip", "wan_container_mac", "wan_container_netbits",
"wan_container_gateway_ip", "wan_container_gateway_mac",
"wan_vm_ip", "wan_vm_mac")
def __init__(self, *args, **kwargs):
super(VSGTenant, self).__init__(*args, **kwargs)
self.cached_vrouter=None
@property
def vrouter(self):
vrouter = self.get_newest_subscribed_tenant(VRouterTenant)
if not vrouter:
return None
# always return the same object when possible
if (self.cached_vrouter) and (self.cached_vrouter.id == vrouter.id):
return self.cached_vrouter
vrouter.caller = self.creator
self.cached_vrouter = vrouter
return vrouter
@vrouter.setter
def vrouter(self, value):
raise XOSConfigurationError("VSGTenant.vrouter setter is not implemented")
@property
def volt(self):
from services.volt.models import VOLTTenant
if not self.subscriber_tenant:
return None
volts = VOLTTenant.objects.filter(id=self.subscriber_tenant.id)
if not volts:
return None
return volts[0]
@volt.setter
def volt(self, value):
raise XOSConfigurationError("VSGTenant.volt setter is not implemented")
@property
def ssh_command(self):
if self.instance:
return self.instance.get_ssh_command()
else:
return "no-instance"
def get_vrouter_field(self, name, default=None):
if self.vrouter:
return getattr(self.vrouter, name, default)
else:
return default
@property
def wan_container_ip(self):
return self.get_vrouter_field("public_ip", None)
@property
def wan_container_mac(self):
return self.get_vrouter_field("public_mac", None)
@property
def wan_container_netbits(self):
return self.get_vrouter_field("netbits", None)
@property
def wan_container_gateway_ip(self):
return self.get_vrouter_field("gateway_ip", None)
@property
def wan_container_gateway_mac(self):
return self.get_vrouter_field("gateway_mac", None)
@property
def wan_vm_ip(self):
tags = Tag.select_by_content_object(self.instance).filter(name="vm_vrouter_tenant")
if tags:
tenant = VRouterTenant.objects.get(id=tags[0].value)
return tenant.public_ip
else:
raise Exception("no vm_vrouter_tenant tag for instance %s" % o.instance)
@property
def wan_vm_mac(self):
tags = Tag.select_by_content_object(self.instance).filter(name="vm_vrouter_tenant")
if tags:
tenant = VRouterTenant.objects.get(id=tags[0].value)
return tenant.public_mac
else:
raise Exception("no vm_vrouter_tenant tag for instance %s" % o.instance)
@property
def is_synced(self):
return (self.enacted is not None) and (self.enacted >= self.updated)
@is_synced.setter
def is_synced(self, value):
pass
def get_vrouter_service(self):
vrouterServices = VRouterService.get_service_objects().all()
if not vrouterServices:
raise XOSConfigurationError("No VROUTER Services available")
return vrouterServices[0]
def manage_vrouter(self):
# Each vCPE object owns exactly one vRouterTenant object
if self.deleted:
return
if self.vrouter is None:
vrouter = self.get_vrouter_service().get_tenant(address_pool_name="addresses_vsg", subscriber_tenant = self)
vrouter.caller = self.creator
vrouter.save()
def cleanup_vrouter(self):
if self.vrouter:
# print "XXX cleanup vrouter", self.vrouter
self.vrouter.delete()
def cleanup_orphans(self):
# ensure vCPE only has one vRouter
cur_vrouter = self.vrouter
for vrouter in list(self.get_subscribed_tenants(VRouterTenant)):
if (not cur_vrouter) or (vrouter.id != cur_vrouter.id):
# print "XXX clean up orphaned vrouter", vrouter
vrouter.delete()
if self.orig_instance_id and (self.orig_instance_id != self.get_attribute("instance_id")):
instances=Instance.objects.filter(id=self.orig_instance_id)
if instances:
# print "XXX clean up orphaned instance", instances[0]
instances[0].delete()
def get_slice(self):
if not self.provider_service.slices.count():
print self, "dio porco"
raise XOSConfigurationError("The service has no slices")
slice = self.provider_service.slices.all()[0]
return slice
def get_vsg_service(self):
return VSGService.get_service_objects().get(id=self.provider_service.id)
def find_instance_for_s_tag(self, s_tag):
#s_tags = STagBlock.objects.find(s_s_tag)
#if s_tags:
# return s_tags[0].instance
tags = Tag.objects.filter(name="s_tag", value=s_tag)
if tags:
return tags[0].content_object
return None
def find_or_make_instance_for_s_tag(self, s_tag):
instance = self.find_instance_for_s_tag(self.volt.s_tag)
if instance:
return instance
flavors = Flavor.objects.filter(name="m1.small")
if not flavors:
raise XOSConfigurationError("No m1.small flavor")
slice = self.provider_service.slices.all()[0]
if slice.default_isolation == "container_vm":
(node, parent) = ContainerVmScheduler(slice).pick()
else:
(node, parent) = LeastLoadedNodeScheduler(slice, label=self.get_vsg_service().node_label).pick()
instance = Instance(slice = slice,
node = node,
image = self.image,
creator = self.creator,
deployment = node.site_deployment.deployment,
flavor = flavors[0],
isolation = slice.default_isolation,
parent = parent)
self.save_instance(instance)
return instance
def manage_container(self):
from core.models import Instance, Flavor
if self.deleted:
return
# For container or container_vm isolation, use what TenantWithCotnainer
# provides us
slice = self.get_slice()
if slice.default_isolation in ["container_vm", "container"]:
super(VSGTenant,self).manage_container()
return
if not self.volt:
raise XOSConfigurationError("This vCPE container has no volt")
if self.instance:
# We're good.
return
instance = self.find_or_make_instance_for_s_tag(self.volt.s_tag)
self.instance = instance
super(TenantWithContainer, self).save()
def cleanup_container(self):
if self.get_slice().default_isolation in ["container_vm", "container"]:
super(VSGTenant,self).cleanup_container()
# To-do: cleanup unused instances
pass
def find_or_make_port(self, instance, network, **kwargs):
port = Port.objects.filter(instance=instance, network=network)
if port:
port = port[0]
else:
port = Port(instance=instance, network=network, **kwargs)
port.save()
return port
def get_lan_network(self, instance):
slice = self.provider_service.slices.all()[0]
# there should only be one network private network, and its template should not be the management template
lan_networks = [x for x in slice.networks.all() if x.template.visibility=="private" and (not "management" in x.template.name)]
if len(lan_networks)>1:
raise XOSProgrammingError("The vSG slice should only have one non-management private network")
if not lan_networks:
raise XOSProgrammingError("No lan_network")
return lan_networks[0]
def save_instance(self, instance):
with transaction.atomic():
instance.volumes = "/etc/dnsmasq.d,/etc/ufw"
super(VSGTenant, self).save_instance(instance)
if instance.isolation in ["container", "container_vm"]:
lan_network = self.get_lan_network(instance)
port = self.find_or_make_port(instance, lan_network, ip="192.168.0.1", port_id="unmanaged")
port.set_parameter("c_tag", self.volt.c_tag)
port.set_parameter("s_tag", self.volt.s_tag)
port.set_parameter("device", "eth1")
port.set_parameter("bridge", "br-lan")
wan_networks = [x for x in instance.slice.networks.all() if "wan" in x.name]
if not wan_networks:
raise XOSProgrammingError("No wan_network")
port = self.find_or_make_port(instance, wan_networks[0])
port.set_parameter("next_hop", value="10.0.1.253") # FIX ME
port.set_parameter("device", "eth0")
if instance.isolation in ["vm"]:
lan_network = self.get_lan_network(instance)
port = self.find_or_make_port(instance, lan_network)
port.set_parameter("c_tag", self.volt.c_tag)
port.set_parameter("s_tag", self.volt.s_tag)
port.set_parameter("neutron_port_name", "stag-%s" % self.volt.s_tag)
port.save()
# tag the instance with the s-tag, so we can easily find the
# instance later
if self.volt and self.volt.s_tag:
tags = Tag.objects.filter(name="s_tag", value=self.volt.s_tag)
if not tags:
tag = Tag(service=self.provider_service, content_object=instance, name="s_tag", value=self.volt.s_tag)
tag.save()
# VTN-CORD needs a WAN address for the VM, so that the VM can
# be configured.
tags = Tag.select_by_content_object(instance).filter(name="vm_vrouter_tenant")
if not tags:
vrouter = self.get_vrouter_service().get_tenant(address_pool_name="addresses_vsg", subscriber_service = self.provider_service)
vrouter.set_attribute("tenant_for_instance_id", instance.id)
vrouter.save()
tag = Tag(service=self.provider_service, content_object=instance, name="vm_vrouter_tenant", value="%d" % vrouter.id)
tag.save()
def save(self, *args, **kwargs):
if not self.creator:
if not getattr(self, "caller", None):
# caller must be set when creating a vCPE since it creates a slice
raise XOSProgrammingError("VSGTenant's self.caller was not set")
self.creator = self.caller
if not self.creator:
raise XOSProgrammingError("VSGTenant's self.creator was not set")
super(VSGTenant, self).save(*args, **kwargs)
model_policy_vcpe(self.pk)
def delete(self, *args, **kwargs):
self.cleanup_vrouter()
self.cleanup_container()
super(VSGTenant, self).delete(*args, **kwargs)
pass
def model_policy_vcpe(pk):
# TODO: this should be made in to a real model_policy
with transaction.atomic():
vcpe = VSGTenant.objects.select_for_update().filter(pk=pk)
if not vcpe:
return
vcpe = vcpe[0]
vcpe.manage_container()
vcpe.manage_vrouter()
vcpe.cleanup_orphans()