blob: 8f3cc1fcd2ae0c26d963b8be41477df6abaee053 [file] [log] [blame]
from django.db import models
from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, Port, AddressPool, User
from core.models.plcorebase import StrippedCharField
import os
from django.db import models, transaction
from django.forms.models import model_to_dict
from django.db.models import Q
from operator import itemgetter, attrgetter, methodcaller
from core.models import Tag
from core.models.service import LeastLoadedNodeScheduler
from services.vrouter.models import VRouterService, VRouterTenant
import traceback
from xos.exceptions import *
from xos.config import Config
class ConfigurationError(Exception):
CORD_SUBSCRIBER_KIND = "CordSubscriberRoot"
CORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
# -------------------------------------------
# CordSubscriberRoot
# -------------------------------------------
class CordSubscriberRoot(Subscriber):
class Meta:
proxy = True
status_choices = (("enabled", "Enabled"),
("suspended", "Suspended"),
("delinquent", "Delinquent"),
("copyrightviolation", "Copyright Violation"))
# 'simple_attributes' will be expanded into properties and setters that
# store the attribute using self.set_attribute / self.get_attribute.
simple_attributes = ( ("firewall_enable", False),
("firewall_rules", "accept all anywhere anywhere"),
("url_filter_enable", False),
("url_filter_rules", "allow all"),
("url_filter_level", "PG"),
("cdn_enable", False),
("devices", []),
("is_demo_user", False),
("uplink_speed", 1000000000), # 1 gigabit, a reasonable default?
("downlink_speed", 1000000000),
("enable_uverse", True) )
default_attributes = {"status": "enabled"}
sync_attributes = ("firewall_enable",
def __init__(self, *args, **kwargs):
super(CordSubscriberRoot, self).__init__(*args, **kwargs)
self.cached_volt = None
self._initial_url_filter_enable = self.url_filter_enable
def volt(self):
volt = self.get_newest_subscribed_tenant(VOLTTenant)
if not volt:
return None
# always return the same object when possible
if (self.cached_volt) and ( ==
return self.cached_volt
#volt.caller = self.creator
self.cached_volt = volt
return volt
def status(self):
return self.get_attribute("status", self.default_attributes["status"])
def status(self, value):
if not value in [x[0] for x in self.status_choices]:
raise Exception("invalid status %s" % value)
self.set_attribute("status", value)
def find_device(self, mac):
for device in self.devices:
if device["mac"] == mac:
return device
return None
def update_device(self, mac, **kwargs):
# kwargs may be "level" or "mac"
# Setting one of these to None will cause None to be stored in the db
devices = self.devices
for device in devices:
if device["mac"] == mac:
for arg in kwargs.keys():
device[arg] = kwargs[arg]
self.devices = devices
return device
raise ValueError("Device with mac %s not found" % mac)
def create_device(self, **kwargs):
if "mac" not in kwargs:
raise XOSMissingField("The mac field is required")
if self.find_device(kwargs['mac']):
raise XOSDuplicateKey("Device with mac %s already exists" % kwargs["mac"])
device = kwargs.copy()
devices = self.devices
self.devices = devices
return device
def delete_device(self, mac):
devices = self.devices
for device in devices:
if device["mac"]==mac:
self.devices = devices
raise ValueError("Device with mac %s not found" % mac)
# Deprecated -- devices used to be called users
def find_user(self, uid):
return self.find_device(uid)
def update_user(self, uid, **kwargs):
return self.update_device(uid, **kwargs)
def create_user(self, **kwargs):
return self.create_device(**kwargs)
def delete_user(self, uid):
return self.delete_user(uid)
# ------------------------------------------------------------------------
def services(self):
return {"cdn": self.cdn_enable,
"url_filter": self.url_filter_enable,
"firewall": self.firewall_enable}
def services(self, value):
def save(self, *args, **kwargs):
if (not hasattr(self, 'caller') or not self.caller.is_admin):
if (self.has_field_changed("service_specific_id")):
raise XOSPermissionDenied("You do not have permission to change service_specific_id")
super(CordSubscriberRoot, self).save(*args, **kwargs)
if (self.volt) and (self.volt.vcpe): # and (self._initial_url_filter_enabled != self.url_filter_enable):
# 1) trigger manage_bbs_account to run
# 2) trigger vcpe observer to wake up
# -------------------------------------------
# -------------------------------------------
class VOLTService(Service):
class Meta:
app_label = "volt"
verbose_name = "vOLT Service"
class VOLTTenant(Tenant):
class Meta:
app_label = "volt"
verbose_name = "vOLT Tenant"
s_tag = models.IntegerField(null=True, blank=True, help_text="s-tag")
c_tag = models.IntegerField(null=True, blank=True, help_text="c-tag")
# at some point, this should probably end up part of Tenant.
creator = models.ForeignKey(User, related_name='created_volts', blank=True, null=True)
def __init__(self, *args, **kwargs):
volt_services = VOLTService.get_service_objects().all()
if volt_services:
self._meta.get_field("provider_service").default = volt_services[0].id
super(VOLTTenant, self).__init__(*args, **kwargs)
self.cached_vcpe = None
def vcpe(self):
from services.vsg.models import VSGTenant
vcpe = self.get_newest_subscribed_tenant(VSGTenant)
if not vcpe:
return None
# always return the same object when possible
if (self.cached_vcpe) and ( ==
return self.cached_vcpe
vcpe.caller = self.creator
self.cached_vcpe = vcpe
return vcpe
def vcpe(self, value):
raise XOSConfigurationError("vOLT.vCPE cannot be set this way -- create a new vCPE object and set its subscriber_tenant instead")
def subscriber(self):
if not self.subscriber_root:
return None
subs = CordSubscriberRoot.objects.filter(
if not subs:
return None
return subs[0]
def manage_vcpe(self):
# Each VOLT object owns exactly one VCPE object
if self.deleted:
if self.vcpe is None:
from services.vsg.models import VSGService, VSGTenant
vsgServices = VSGService.get_service_objects().all()
if not vsgServices:
raise XOSConfigurationError("No VSG Services available")
vcpe = VSGTenant(provider_service = vsgServices[0],
subscriber_tenant = self)
vcpe.caller = self.creator
def manage_subscriber(self):
if (self.subscriber_root is None):
# The vOLT is not connected to a Subscriber, so either find an
# existing subscriber with the same SSID, or autogenerate a new
# subscriber.
# TODO: This probably goes away when we rethink the ONOS-to-XOS
subs = CordSubscriberRoot.get_tenant_objects().filter(service_specific_id = self.service_specific_id)
if subs:
sub = subs[0]
sub = CordSubscriberRoot(service_specific_id = self.service_specific_id,
name = "autogenerated-for-vOLT-%s" %
self.subscriber_root = sub
def cleanup_vcpe(self):
if self.vcpe:
# print "XXX cleanup vcpe", self.vcpe
def cleanup_orphans(self):
from services.vsg.models import VSGTenant
# ensure vOLT only has one vCPE
cur_vcpe = self.vcpe
for vcpe in list(self.get_subscribed_tenants(VSGTenant)):
if (not cur_vcpe) or ( !=
# print "XXX clean up orphaned vcpe", vcpe
def save(self, *args, **kwargs):
# VOLTTenant probably doesn't need a SSID anymore; that will be handled
# by CORDSubscriberRoot...
# self.validate_unique_service_specific_id()
if (self.subscriber_root is not None):
subs = self.subscriber_root.get_subscribed_tenants(VOLTTenant)
if (subs) and (self not in subs):
raise XOSDuplicateKey("Subscriber should only be linked to one vOLT")
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("VOLTTenant's self.caller was not set")
self.creator = self.caller
if not self.creator:
raise XOSProgrammingError("VOLTTenant's self.creator was not set")
super(VOLTTenant, self).save(*args, **kwargs)
def delete(self, *args, **kwargs):
super(VOLTTenant, self).delete(*args, **kwargs)
def model_policy_volt(pk):
# TODO: this should be made in to a real model_policy
with transaction.atomic():
volt = VOLTTenant.objects.select_for_update().filter(pk=pk)
if not volt:
volt = volt[0]
class VOLTDevice(PlCoreBase):
class Meta:
app_label = "volt"
name = models.CharField(max_length=254, help_text="name of device", null=False, blank=False)
volt_service = models.ForeignKey(VOLTService, related_name='volt_devices')
openflow_id = models.CharField(max_length=254, help_text="OpenFlow ID", null=True, blank=True)
driver = models.CharField(max_length=254, help_text="driver", null=True, blank=True)
access_agent = models.ForeignKey("AccessAgent", related_name='volt_devices', blank=True, null=True)
def __unicode__(self): return u'%s' % (
class AccessDevice(PlCoreBase):
class Meta:
app_label = "volt"
volt_device = models.ForeignKey(VOLTDevice, related_name='access_devices')
uplink = models.IntegerField(null=True, blank=True)
vlan = models.IntegerField(null=True, blank=True)
def __unicode__(self): return u'%s-%d:%d' % (,self.uplink,self.vlan)
class AccessAgent(PlCoreBase):
class Meta:
app_label = "volt"
name = models.CharField(max_length=254, help_text="name of agent", null=False, blank=False)
volt_service = models.ForeignKey(VOLTService, related_name='access_agents')
mac = models.CharField(max_length=32, help_text="MAC Address or Access Agent", null=True, blank=True)
def __unicode__(self): return u'%s' % (
class AgentPortMapping(PlCoreBase):
class Meta:
app_label = "volt"
access_agent = models.ForeignKey(AccessAgent, related_name='port_mappings')
mac = models.CharField(max_length=32, help_text="MAC Address", null=True, blank=True)
port = models.CharField(max_length=32, help_text="Openflow port ID", null=True, blank=True)
def __unicode__(self): return u'%s-%s-%s' % (, self.port, self.mac)