blob: 8f3cc1fcd2ae0c26d963b8be41477df6abaee053 [file] [log] [blame]
Scott Baker8e6647a2016-06-20 17:16:20 -07001from django.db import models
2from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, Port, AddressPool, User
3from core.models.plcorebase import StrippedCharField
4import os
5from django.db import models, transaction
6from django.forms.models import model_to_dict
7from django.db.models import Q
8from operator import itemgetter, attrgetter, methodcaller
9from core.models import Tag
10from core.models.service import LeastLoadedNodeScheduler
11from services.vrouter.models import VRouterService, VRouterTenant
12import traceback
13from xos.exceptions import *
14from xos.config import Config
15
16class ConfigurationError(Exception):
17 pass
18
19VOLT_KIND = "vOLT"
20CORD_SUBSCRIBER_KIND = "CordSubscriberRoot"
21
22CORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
23
24# -------------------------------------------
25# CordSubscriberRoot
26# -------------------------------------------
27
28class CordSubscriberRoot(Subscriber):
29 class Meta:
30 proxy = True
31
32 KIND = CORD_SUBSCRIBER_KIND
33
34 status_choices = (("enabled", "Enabled"),
35 ("suspended", "Suspended"),
36 ("delinquent", "Delinquent"),
37 ("copyrightviolation", "Copyright Violation"))
38
39 # 'simple_attributes' will be expanded into properties and setters that
40 # store the attribute using self.set_attribute / self.get_attribute.
41
42 simple_attributes = ( ("firewall_enable", False),
43 ("firewall_rules", "accept all anywhere anywhere"),
44 ("url_filter_enable", False),
45 ("url_filter_rules", "allow all"),
46 ("url_filter_level", "PG"),
47 ("cdn_enable", False),
48 ("devices", []),
49 ("is_demo_user", False),
50
51 ("uplink_speed", 1000000000), # 1 gigabit, a reasonable default?
52 ("downlink_speed", 1000000000),
53 ("enable_uverse", True) )
54
55 default_attributes = {"status": "enabled"}
56
57 sync_attributes = ("firewall_enable",
58 "firewall_rules",
59 "url_filter_enable",
60 "url_filter_rules",
61 "cdn_enable",
62 "uplink_speed",
63 "downlink_speed",
64 "enable_uverse",
65 "status")
66
67 def __init__(self, *args, **kwargs):
68 super(CordSubscriberRoot, self).__init__(*args, **kwargs)
69 self.cached_volt = None
70 self._initial_url_filter_enable = self.url_filter_enable
71
72 @property
73 def volt(self):
74 volt = self.get_newest_subscribed_tenant(VOLTTenant)
75 if not volt:
76 return None
77
78 # always return the same object when possible
79 if (self.cached_volt) and (self.cached_volt.id == volt.id):
80 return self.cached_volt
81
82 #volt.caller = self.creator
83 self.cached_volt = volt
84 return volt
85
86 @property
87 def status(self):
88 return self.get_attribute("status", self.default_attributes["status"])
89
90 @status.setter
91 def status(self, value):
92 if not value in [x[0] for x in self.status_choices]:
93 raise Exception("invalid status %s" % value)
94 self.set_attribute("status", value)
95
96 def find_device(self, mac):
97 for device in self.devices:
98 if device["mac"] == mac:
99 return device
100 return None
101
102 def update_device(self, mac, **kwargs):
103 # kwargs may be "level" or "mac"
104 # Setting one of these to None will cause None to be stored in the db
105 devices = self.devices
106 for device in devices:
107 if device["mac"] == mac:
108 for arg in kwargs.keys():
109 device[arg] = kwargs[arg]
110 self.devices = devices
111 return device
112 raise ValueError("Device with mac %s not found" % mac)
113
114 def create_device(self, **kwargs):
115 if "mac" not in kwargs:
116 raise XOSMissingField("The mac field is required")
117
118 if self.find_device(kwargs['mac']):
119 raise XOSDuplicateKey("Device with mac %s already exists" % kwargs["mac"])
120
121 device = kwargs.copy()
122
123 devices = self.devices
124 devices.append(device)
125 self.devices = devices
126
127 return device
128
129 def delete_device(self, mac):
130 devices = self.devices
131 for device in devices:
132 if device["mac"]==mac:
133 devices.remove(device)
134 self.devices = devices
135 return
136
137 raise ValueError("Device with mac %s not found" % mac)
138
139 #--------------------------------------------------------------------------
140 # Deprecated -- devices used to be called users
141
142 def find_user(self, uid):
143 return self.find_device(uid)
144
145 def update_user(self, uid, **kwargs):
146 return self.update_device(uid, **kwargs)
147
148 def create_user(self, **kwargs):
149 return self.create_device(**kwargs)
150
151 def delete_user(self, uid):
152 return self.delete_user(uid)
153
154 # ------------------------------------------------------------------------
155
156 @property
157 def services(self):
158 return {"cdn": self.cdn_enable,
159 "url_filter": self.url_filter_enable,
160 "firewall": self.firewall_enable}
161
162 @services.setter
163 def services(self, value):
164 pass
165
166 def save(self, *args, **kwargs):
167 self.validate_unique_service_specific_id(none_okay=True)
168 if (not hasattr(self, 'caller') or not self.caller.is_admin):
169 if (self.has_field_changed("service_specific_id")):
170 raise XOSPermissionDenied("You do not have permission to change service_specific_id")
171 super(CordSubscriberRoot, self).save(*args, **kwargs)
172 if (self.volt) and (self.volt.vcpe): # and (self._initial_url_filter_enabled != self.url_filter_enable):
173 # 1) trigger manage_bbs_account to run
174 # 2) trigger vcpe observer to wake up
175 self.volt.vcpe.save()
176
177CordSubscriberRoot.setup_simple_attributes()
178
179# -------------------------------------------
180# VOLT
181# -------------------------------------------
182
183class VOLTService(Service):
184 KIND = VOLT_KIND
185
186 class Meta:
187 app_label = "volt"
188 verbose_name = "vOLT Service"
189
190class VOLTTenant(Tenant):
191 KIND = VOLT_KIND
192
193 class Meta:
194 app_label = "volt"
195 verbose_name = "vOLT Tenant"
196
197 s_tag = models.IntegerField(null=True, blank=True, help_text="s-tag")
198 c_tag = models.IntegerField(null=True, blank=True, help_text="c-tag")
199
200 # at some point, this should probably end up part of Tenant.
201 creator = models.ForeignKey(User, related_name='created_volts', blank=True, null=True)
202
203 def __init__(self, *args, **kwargs):
204 volt_services = VOLTService.get_service_objects().all()
205 if volt_services:
206 self._meta.get_field("provider_service").default = volt_services[0].id
207 super(VOLTTenant, self).__init__(*args, **kwargs)
208 self.cached_vcpe = None
209
210 @property
211 def vcpe(self):
212 from services.vsg.models import VSGTenant
213 vcpe = self.get_newest_subscribed_tenant(VSGTenant)
214 if not vcpe:
215 return None
216
217 # always return the same object when possible
218 if (self.cached_vcpe) and (self.cached_vcpe.id == vcpe.id):
219 return self.cached_vcpe
220
221 vcpe.caller = self.creator
222 self.cached_vcpe = vcpe
223 return vcpe
224
225 @vcpe.setter
226 def vcpe(self, value):
227 raise XOSConfigurationError("vOLT.vCPE cannot be set this way -- create a new vCPE object and set its subscriber_tenant instead")
228
229 @property
230 def subscriber(self):
231 if not self.subscriber_root:
232 return None
233 subs = CordSubscriberRoot.objects.filter(id=self.subscriber_root.id)
234 if not subs:
235 return None
236 return subs[0]
237
238 def manage_vcpe(self):
239 # Each VOLT object owns exactly one VCPE object
240
241 if self.deleted:
242 return
243
244 if self.vcpe is None:
245 from services.vsg.models import VSGService, VSGTenant
246 vsgServices = VSGService.get_service_objects().all()
247 if not vsgServices:
248 raise XOSConfigurationError("No VSG Services available")
249
250 vcpe = VSGTenant(provider_service = vsgServices[0],
251 subscriber_tenant = self)
252 vcpe.caller = self.creator
253 vcpe.save()
254
255 def manage_subscriber(self):
256 if (self.subscriber_root is None):
257 # The vOLT is not connected to a Subscriber, so either find an
258 # existing subscriber with the same SSID, or autogenerate a new
259 # subscriber.
260 #
261 # TODO: This probably goes away when we rethink the ONOS-to-XOS
262 # vOLT API.
263
264 subs = CordSubscriberRoot.get_tenant_objects().filter(service_specific_id = self.service_specific_id)
265 if subs:
266 sub = subs[0]
267 else:
268 sub = CordSubscriberRoot(service_specific_id = self.service_specific_id,
269 name = "autogenerated-for-vOLT-%s" % self.id)
270 sub.save()
271 self.subscriber_root = sub
272 self.save()
273
274 def cleanup_vcpe(self):
275 if self.vcpe:
276 # print "XXX cleanup vcpe", self.vcpe
277 self.vcpe.delete()
278
279 def cleanup_orphans(self):
280 from services.vsg.models import VSGTenant
281 # ensure vOLT only has one vCPE
282 cur_vcpe = self.vcpe
283 for vcpe in list(self.get_subscribed_tenants(VSGTenant)):
284 if (not cur_vcpe) or (vcpe.id != cur_vcpe.id):
285 # print "XXX clean up orphaned vcpe", vcpe
286 vcpe.delete()
287
288 def save(self, *args, **kwargs):
289 # VOLTTenant probably doesn't need a SSID anymore; that will be handled
290 # by CORDSubscriberRoot...
291 # self.validate_unique_service_specific_id()
292
293 if (self.subscriber_root is not None):
294 subs = self.subscriber_root.get_subscribed_tenants(VOLTTenant)
295 if (subs) and (self not in subs):
296 raise XOSDuplicateKey("Subscriber should only be linked to one vOLT")
297
298 if not self.creator:
299 if not getattr(self, "caller", None):
300 # caller must be set when creating a vCPE since it creates a slice
301 raise XOSProgrammingError("VOLTTenant's self.caller was not set")
302 self.creator = self.caller
303 if not self.creator:
304 raise XOSProgrammingError("VOLTTenant's self.creator was not set")
305
306 super(VOLTTenant, self).save(*args, **kwargs)
307 model_policy_volt(self.pk)
308
309 def delete(self, *args, **kwargs):
310 self.cleanup_vcpe()
311 super(VOLTTenant, self).delete(*args, **kwargs)
312
313def model_policy_volt(pk):
314 # TODO: this should be made in to a real model_policy
315 with transaction.atomic():
316 volt = VOLTTenant.objects.select_for_update().filter(pk=pk)
317 if not volt:
318 return
319 volt = volt[0]
320 volt.manage_vcpe()
321 volt.manage_subscriber()
322 volt.cleanup_orphans()
323
324class VOLTDevice(PlCoreBase):
325 class Meta:
326 app_label = "volt"
327
328 name = models.CharField(max_length=254, help_text="name of device", null=False, blank=False)
329 volt_service = models.ForeignKey(VOLTService, related_name='volt_devices')
330 openflow_id = models.CharField(max_length=254, help_text="OpenFlow ID", null=True, blank=True)
331 driver = models.CharField(max_length=254, help_text="driver", null=True, blank=True)
332 access_agent = models.ForeignKey("AccessAgent", related_name='volt_devices', blank=True, null=True)
333
334 def __unicode__(self): return u'%s' % (self.name)
335
336class AccessDevice(PlCoreBase):
337 class Meta:
338 app_label = "volt"
339
340 volt_device = models.ForeignKey(VOLTDevice, related_name='access_devices')
341 uplink = models.IntegerField(null=True, blank=True)
342 vlan = models.IntegerField(null=True, blank=True)
343
344 def __unicode__(self): return u'%s-%d:%d' % (self.volt_device.name,self.uplink,self.vlan)
345
346class AccessAgent(PlCoreBase):
347 class Meta:
348 app_label = "volt"
349
350 name = models.CharField(max_length=254, help_text="name of agent", null=False, blank=False)
351 volt_service = models.ForeignKey(VOLTService, related_name='access_agents')
352 mac = models.CharField(max_length=32, help_text="MAC Address or Access Agent", null=True, blank=True)
353
354 def __unicode__(self): return u'%s' % (self.name)
355
356class AgentPortMapping(PlCoreBase):
357 class Meta:
358 app_label = "volt"
359
360 access_agent = models.ForeignKey(AccessAgent, related_name='port_mappings')
361 mac = models.CharField(max_length=32, help_text="MAC Address", null=True, blank=True)
362 port = models.CharField(max_length=32, help_text="Openflow port ID", null=True, blank=True)
363
364 def __unicode__(self): return u'%s-%s-%s' % (self.access_agent.name, self.port, self.mac)
365
366
367