blob: 48c959742bb93371cb0d18594de3d83175c24aaf [file] [log] [blame]
Scott Bakere9ff7ce2015-04-14 17:19:16 -07001from django.db import models
Scott Bakere6780e92016-02-13 14:38:43 -08002from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, Port, AddressPool
Scott Bakere9ff7ce2015-04-14 17:19:16 -07003from core.models.plcorebase import StrippedCharField
4import os
Scott Baker192da222015-07-08 19:01:56 -07005from django.db import models, transaction
Scott Bakere9ff7ce2015-04-14 17:19:16 -07006from django.forms.models import model_to_dict
7from django.db.models import Q
8from operator import itemgetter, attrgetter, methodcaller
Scott Baker5b8eb412016-02-02 16:56:18 -08009from core.models import Tag
10from core.models.service import LeastLoadedNodeScheduler
Scott Bakerdf6375e2016-04-13 11:25:52 -070011from services.vrouter.models import VRouterService, VRouterTenant
Scott Bakerfa5cdd62015-04-20 09:16:17 -070012import traceback
Scott Baker7f8ef8f2015-04-20 14:24:29 -070013from xos.exceptions import *
Scott Bakere6780e92016-02-13 14:38:43 -080014from xos.config import Config
Scott Bakere9ff7ce2015-04-14 17:19:16 -070015
Scott Bakere9ff7ce2015-04-14 17:19:16 -070016class ConfigurationError(Exception):
17 pass
18
Scott Bakerd9133342015-07-06 14:38:02 -070019VOLT_KIND = "vOLT"
20VCPE_KIND = "vCPE"
21VBNG_KIND = "vBNG"
Scott Baker81de6402015-07-06 16:50:30 -070022CORD_SUBSCRIBER_KIND = "CordSubscriberRoot"
23
Scott Bakere6780e92016-02-13 14:38:43 -080024CORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
25
Scott Baker81de6402015-07-06 16:50:30 -070026# -------------------------------------------
27# CordSubscriberRoot
28# -------------------------------------------
29
30class CordSubscriberRoot(Subscriber):
Scott Bakere4364d12015-07-06 17:21:21 -070031 class Meta:
32 proxy = True
33
Scott Baker81de6402015-07-06 16:50:30 -070034 KIND = CORD_SUBSCRIBER_KIND
35
Scott Baker669212f2016-03-01 20:14:35 -080036 status_choices = (("enabled", "Enabled"),
37 ("suspended", "Suspended"),
38 ("delinquent", "Delinquent"),
39 ("copyrightviolation", "Copyright Violation"))
40
41 # 'simple_attributes' will be expanded into properties and setters that
42 # store the attribute using self.set_attribute / self.get_attribute.
43
44 simple_attributes = ( ("firewall_enable", False),
45 ("firewall_rules", "accept all anywhere anywhere"),
46 ("url_filter_enable", False),
47 ("url_filter_rules", "allow all"),
48 ("url_filter_level", "PG"),
49 ("cdn_enable", False),
Scott Bakera40f9a32016-05-05 14:58:41 -070050 ("devices", []),
Scott Baker669212f2016-03-01 20:14:35 -080051 ("is_demo_user", False),
52
Matteoc1018542016-03-02 09:33:20 -080053 ("uplink_speed", 1000000000), # 1 gigabit, a reasonable default?
54 ("downlink_speed", 1000000000),
Scott Baker669212f2016-03-01 20:14:35 -080055 ("enable_uverse", True) )
56
57 default_attributes = {"status": "enabled"}
Scott Baker81de6402015-07-06 16:50:30 -070058
Scott Baker050f8b32015-07-07 12:15:03 -070059 sync_attributes = ("firewall_enable",
60 "firewall_rules",
61 "url_filter_enable",
62 "url_filter_rules",
Scott Bakere98eea82016-03-01 20:44:40 -080063 "cdn_enable",
64 "uplink_speed",
65 "downlink_speed",
66 "enable_uverse",
67 "status")
Scott Baker050f8b32015-07-07 12:15:03 -070068
Scott Baker81de6402015-07-06 16:50:30 -070069 def __init__(self, *args, **kwargs):
70 super(CordSubscriberRoot, self).__init__(*args, **kwargs)
71 self.cached_volt = None
Scott Baker050f8b32015-07-07 12:15:03 -070072 self._initial_url_filter_enable = self.url_filter_enable
Scott Baker81de6402015-07-06 16:50:30 -070073
74 @property
75 def volt(self):
76 volt = self.get_newest_subscribed_tenant(VOLTTenant)
77 if not volt:
78 return None
79
80 # always return the same object when possible
81 if (self.cached_volt) and (self.cached_volt.id == volt.id):
Scott Baker126ad472015-07-07 17:59:44 -070082 return self.cached_volt
Scott Baker81de6402015-07-06 16:50:30 -070083
Scott Bakere4364d12015-07-06 17:21:21 -070084 #volt.caller = self.creator
Scott Baker81de6402015-07-06 16:50:30 -070085 self.cached_volt = volt
86 return volt
87
88 @property
Scott Baker669212f2016-03-01 20:14:35 -080089 def status(self):
90 return self.get_attribute("status", self.default_attributes["status"])
Scott Baker81de6402015-07-06 16:50:30 -070091
Scott Baker669212f2016-03-01 20:14:35 -080092 @status.setter
93 def status(self, value):
94 if not value in [x[0] for x in self.status_choices]:
95 raise Exception("invalid status %s" % value)
96 self.set_attribute("status", value)
Scott Baker81de6402015-07-06 16:50:30 -070097
Scott Bakera40f9a32016-05-05 14:58:41 -070098 def find_device(self, mac):
99 for device in self.devices:
100 if device["mac"] == mac:
101 return device
Scott Baker81de6402015-07-06 16:50:30 -0700102 return None
103
Scott Bakera40f9a32016-05-05 14:58:41 -0700104 def update_device(self, mac, **kwargs):
Scott Baker81de6402015-07-06 16:50:30 -0700105 # kwargs may be "level" or "mac"
106 # Setting one of these to None will cause None to be stored in the db
Scott Bakera40f9a32016-05-05 14:58:41 -0700107 devices = self.devices
108 for device in devices:
109 if device["mac"] == mac:
Scott Baker81de6402015-07-06 16:50:30 -0700110 for arg in kwargs.keys():
Scott Bakera40f9a32016-05-05 14:58:41 -0700111 device[arg] = kwargs[arg]
112 self.devices = devices
113 return device
114 raise ValueError("Device with mac %s not found" % mac)
Scott Baker81de6402015-07-06 16:50:30 -0700115
Scott Bakera40f9a32016-05-05 14:58:41 -0700116 def create_device(self, **kwargs):
117 if "mac" not in kwargs:
118 raise XOSMissingField("The mac field is required")
Scott Baker81de6402015-07-06 16:50:30 -0700119
Scott Bakera40f9a32016-05-05 14:58:41 -0700120 if self.find_device(kwargs['mac']):
121 raise XOSDuplicateKey("Device with mac %s already exists" % kwargs["mac"])
Scott Baker81de6402015-07-06 16:50:30 -0700122
Scott Bakera40f9a32016-05-05 14:58:41 -0700123 device = kwargs.copy()
Scott Baker81de6402015-07-06 16:50:30 -0700124
Scott Bakera40f9a32016-05-05 14:58:41 -0700125 devices = self.devices
126 devices.append(device)
127 self.devices = devices
Scott Baker81de6402015-07-06 16:50:30 -0700128
Scott Bakera40f9a32016-05-05 14:58:41 -0700129 return device
Scott Baker81de6402015-07-06 16:50:30 -0700130
Scott Bakera40f9a32016-05-05 14:58:41 -0700131 def delete_device(self, mac):
132 devices = self.devices
133 for device in devices:
134 if device["mac"]==mac:
135 devices.remove(device)
136 self.devices = devices
Scott Baker81de6402015-07-06 16:50:30 -0700137 return
138
Scott Bakera40f9a32016-05-05 14:58:41 -0700139 raise ValueError("Device with mac %s not found" % mac)
140
141 #--------------------------------------------------------------------------
142 # Deprecated -- devices used to be called users
143
144 def find_user(self, uid):
145 return self.find_device(uid)
146
147 def update_user(self, uid, **kwargs):
148 return self.update_device(uid, **kwargs)
149
150 def create_user(self, **kwargs):
151 return self.create_device(**kwargs)
152
153 def delete_user(self, uid):
154 return self.delete_user(uid)
155
156 # ------------------------------------------------------------------------
Scott Baker81de6402015-07-06 16:50:30 -0700157
158 @property
159 def services(self):
160 return {"cdn": self.cdn_enable,
161 "url_filter": self.url_filter_enable,
162 "firewall": self.firewall_enable}
163
164 @services.setter
165 def services(self, value):
166 pass
Scott Bakerd9133342015-07-06 14:38:02 -0700167
Scott Baker050f8b32015-07-07 12:15:03 -0700168 def save(self, *args, **kwargs):
Scott Bakerf3735762016-03-31 14:45:31 -0700169 self.validate_unique_service_specific_id(none_okay=True)
Scott Bakerfe38c2a2015-07-24 18:56:57 -0700170 if (not hasattr(self, 'caller') or not self.caller.is_admin):
171 if (self.has_field_changed("service_specific_id")):
172 raise XOSPermissionDenied("You do not have permission to change service_specific_id")
Scott Baker050f8b32015-07-07 12:15:03 -0700173 super(CordSubscriberRoot, self).save(*args, **kwargs)
174 if (self.volt) and (self.volt.vcpe): # and (self._initial_url_filter_enabled != self.url_filter_enable):
175 # 1) trigger manage_bbs_account to run
176 # 2) trigger vcpe observer to wake up
177 self.volt.vcpe.save()
178
Scott Baker669212f2016-03-01 20:14:35 -0800179CordSubscriberRoot.setup_simple_attributes()
Scott Baker126ad472015-07-07 17:59:44 -0700180
Scott Bakerd4b48c02015-04-15 20:59:15 -0700181# -------------------------------------------
182# VOLT
183# -------------------------------------------
184
185class VOLTService(Service):
Scott Bakerd9133342015-07-06 14:38:02 -0700186 KIND = VOLT_KIND
Scott Bakerd4b48c02015-04-15 20:59:15 -0700187
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700188 class Meta:
Scott Bakerd4b48c02015-04-15 20:59:15 -0700189 app_label = "cord"
190 verbose_name = "vOLT Service"
191 proxy = True
192
193class VOLTTenant(Tenant):
194 class Meta:
195 proxy = True
196
Scott Bakerd9133342015-07-06 14:38:02 -0700197 KIND = VOLT_KIND
Scott Bakerd4b48c02015-04-15 20:59:15 -0700198
Scott Bakerf1527cd2015-11-09 20:25:21 -0800199 default_attributes = {"vlan_id": None, "s_tag": None, "c_tag": None}
Scott Baker323eca92015-04-20 09:48:34 -0700200 def __init__(self, *args, **kwargs):
201 volt_services = VOLTService.get_service_objects().all()
202 if volt_services:
203 self._meta.get_field("provider_service").default = volt_services[0].id
204 super(VOLTTenant, self).__init__(*args, **kwargs)
Scott Bakerd9133342015-07-06 14:38:02 -0700205 self.cached_vcpe = None
Scott Baker868dabf2015-06-24 12:54:24 -0700206
Scott Bakerd4b48c02015-04-15 20:59:15 -0700207 @property
Scott Bakerf1527cd2015-11-09 20:25:21 -0800208 def s_tag(self):
209 return self.get_attribute("s_tag", self.default_attributes["s_tag"])
210
211 @s_tag.setter
212 def s_tag(self, value):
213 self.set_attribute("s_tag", value)
214
215 @property
216 def c_tag(self):
217 return self.get_attribute("c_tag", self.default_attributes["c_tag"])
218
219 @c_tag.setter
220 def c_tag(self, value):
221 self.set_attribute("c_tag", value)
222
223 # for now, vlan_id is a synonym for c_tag
224
225 @property
Scott Baker679f7022015-04-20 11:50:09 -0700226 def vlan_id(self):
Scott Bakerf1527cd2015-11-09 20:25:21 -0800227 return self.c_tag
Scott Baker679f7022015-04-20 11:50:09 -0700228
229 @vlan_id.setter
230 def vlan_id(self, value):
Scott Bakerf1527cd2015-11-09 20:25:21 -0800231 self.c_tag = value
Scott Baker679f7022015-04-20 11:50:09 -0700232
233 @property
Scott Bakerd4b48c02015-04-15 20:59:15 -0700234 def vcpe(self):
Scott Baker4c052b22016-02-11 11:08:42 -0800235 vcpe = self.get_newest_subscribed_tenant(VSGTenant)
Scott Bakerd9133342015-07-06 14:38:02 -0700236 if not vcpe:
237 return None
238
239 # always return the same object when possible
240 if (self.cached_vcpe) and (self.cached_vcpe.id == vcpe.id):
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700241 return self.cached_vcpe
Scott Bakerd9133342015-07-06 14:38:02 -0700242
Scott Bakerc633dc92015-05-05 17:49:46 -0700243 vcpe.caller = self.creator
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700244 self.cached_vcpe = vcpe
245 return vcpe
Scott Bakerd4b48c02015-04-15 20:59:15 -0700246
247 @vcpe.setter
248 def vcpe(self, value):
Scott Bakere4364d12015-07-06 17:21:21 -0700249 raise XOSConfigurationError("vOLT.vCPE cannot be set this way -- create a new vCPE object and set its subscriber_tenant instead")
250
251 @property
252 def subscriber(self):
253 if not self.subscriber_root:
254 return None
255 subs = CordSubscriberRoot.objects.filter(id=self.subscriber_root.id)
256 if not subs:
257 return None
258 return subs[0]
Scott Bakerd4b48c02015-04-15 20:59:15 -0700259
Scott Bakerc633dc92015-05-05 17:49:46 -0700260 @property
261 def creator(self):
262 if getattr(self, "cached_creator", None):
263 return self.cached_creator
264 creator_id=self.get_attribute("creator_id")
265 if not creator_id:
266 return None
267 users=User.objects.filter(id=creator_id)
268 if not users:
269 return None
270 user=users[0]
271 self.cached_creator = users[0]
272 return user
273
274 @creator.setter
275 def creator(self, value):
276 if value:
277 value = value.id
278 if (value != self.get_attribute("creator_id", None)):
279 self.cached_creator=None
280 self.set_attribute("creator_id", value)
281
Scott Bakerd4b48c02015-04-15 20:59:15 -0700282 def manage_vcpe(self):
283 # Each VOLT object owns exactly one VCPE object
284
285 if self.deleted:
286 return
287
288 if self.vcpe is None:
Scott Bakerf91e6152016-02-11 12:07:10 -0800289 vsgServices = VSGService.get_service_objects().all()
290 if not vsgServices:
291 raise XOSConfigurationError("No VSG Services available")
Scott Bakerd4b48c02015-04-15 20:59:15 -0700292
Scott Bakerf91e6152016-02-11 12:07:10 -0800293 vcpe = VSGTenant(provider_service = vsgServices[0],
Scott Bakerd4b48c02015-04-15 20:59:15 -0700294 subscriber_tenant = self)
Scott Bakerc633dc92015-05-05 17:49:46 -0700295 vcpe.caller = self.creator
Scott Bakerd4b48c02015-04-15 20:59:15 -0700296 vcpe.save()
297
Scott Baker050f8b32015-07-07 12:15:03 -0700298 def manage_subscriber(self):
299 if (self.subscriber_root is None):
300 # The vOLT is not connected to a Subscriber, so either find an
301 # existing subscriber with the same SSID, or autogenerate a new
302 # subscriber.
303 #
304 # TODO: This probably goes away when we rethink the ONOS-to-XOS
305 # vOLT API.
306
307 subs = CordSubscriberRoot.get_tenant_objects().filter(service_specific_id = self.service_specific_id)
308 if subs:
309 sub = subs[0]
310 else:
311 sub = CordSubscriberRoot(service_specific_id = self.service_specific_id,
312 name = "autogenerated-for-vOLT-%s" % self.id)
313 sub.save()
314 self.subscriber_root = sub
315 self.save()
316
Scott Bakerd4b48c02015-04-15 20:59:15 -0700317 def cleanup_vcpe(self):
318 if self.vcpe:
Scott Bakerd9133342015-07-06 14:38:02 -0700319 # print "XXX cleanup vcpe", self.vcpe
Scott Bakerd4b48c02015-04-15 20:59:15 -0700320 self.vcpe.delete()
Scott Bakerd4b48c02015-04-15 20:59:15 -0700321
Scott Baker868dabf2015-06-24 12:54:24 -0700322 def cleanup_orphans(self):
Scott Bakerd9133342015-07-06 14:38:02 -0700323 # ensure vOLT only has one vCPE
324 cur_vcpe = self.vcpe
Scott Baker4c052b22016-02-11 11:08:42 -0800325 for vcpe in list(self.get_subscribed_tenants(VSGTenant)):
Scott Bakerd9133342015-07-06 14:38:02 -0700326 if (not cur_vcpe) or (vcpe.id != cur_vcpe.id):
327 # print "XXX clean up orphaned vcpe", vcpe
328 vcpe.delete()
Scott Baker868dabf2015-06-24 12:54:24 -0700329
Scott Bakerd4b48c02015-04-15 20:59:15 -0700330 def save(self, *args, **kwargs):
Scott Baker6292f342016-03-30 09:39:59 -0700331 # VOLTTenant probably doesn't need a SSID anymore; that will be handled
332 # by CORDSubscriberRoot...
333 # self.validate_unique_service_specific_id()
Scott Baker7f8ef8f2015-04-20 14:24:29 -0700334
Scott Baker050f8b32015-07-07 12:15:03 -0700335 if (self.subscriber_root is not None):
336 subs = self.subscriber_root.get_subscribed_tenants(VOLTTenant)
337 if (subs) and (self not in subs):
338 raise XOSDuplicateKey("Subscriber should only be linked to one vOLT")
339
Scott Bakerc633dc92015-05-05 17:49:46 -0700340 if not self.creator:
341 if not getattr(self, "caller", None):
342 # caller must be set when creating a vCPE since it creates a slice
343 raise XOSProgrammingError("VOLTTenant's self.caller was not set")
344 self.creator = self.caller
345 if not self.creator:
346 raise XOSProgrammingError("VOLTTenant's self.creator was not set")
347
Scott Bakerd4b48c02015-04-15 20:59:15 -0700348 super(VOLTTenant, self).save(*args, **kwargs)
Scott Baker192da222015-07-08 19:01:56 -0700349 model_policy_volt(self.pk)
350 #self.manage_vcpe()
351 #self.manage_subscriber()
352 #self.cleanup_orphans()
Scott Bakerd4b48c02015-04-15 20:59:15 -0700353
354 def delete(self, *args, **kwargs):
355 self.cleanup_vcpe()
356 super(VOLTTenant, self).delete(*args, **kwargs)
357
Scott Baker192da222015-07-08 19:01:56 -0700358def model_policy_volt(pk):
359 # TODO: this should be made in to a real model_policy
360 with transaction.atomic():
361 volt = VOLTTenant.objects.select_for_update().filter(pk=pk)
362 if not volt:
363 return
364 volt = volt[0]
365 volt.manage_vcpe()
366 volt.manage_subscriber()
367 volt.cleanup_orphans()
368
Scott Bakerd4b48c02015-04-15 20:59:15 -0700369# -------------------------------------------
370# VCPE
371# -------------------------------------------
372
Scott Bakerf91e6152016-02-11 12:07:10 -0800373class VSGService(Service):
Scott Bakerd9133342015-07-06 14:38:02 -0700374 KIND = VCPE_KIND
Scott Bakerd4b48c02015-04-15 20:59:15 -0700375
Scott Bakerbe2bc9a2016-03-10 20:12:15 -0800376 URL_FILTER_KIND_CHOICES = ( (None, "None"), ("safebrowsing", "Safe Browsing"), ("answerx", "AnswerX") )
Scott Baker9b870572016-03-10 19:23:10 -0800377
Scott Baker2b56cf42015-07-15 18:08:06 -0700378 simple_attributes = ( ("bbs_api_hostname", None),
379 ("bbs_api_port", None),
Scott Baker80157da2015-07-15 17:42:43 -0700380 ("bbs_server", None),
Scott Bakercf6eb992016-02-18 06:43:02 -0800381 ("backend_network_label", "hpc_client"),
Scott Baker67074ab2016-03-04 11:29:02 -0800382 ("dns_servers", "8.8.8.8"),
Scott Baker9b870572016-03-10 19:23:10 -0800383 ("url_filter_kind", None),
Scott Baker67074ab2016-03-04 11:29:02 -0800384 ("node_label", None) )
Scott Baker62312972015-07-13 14:30:25 -0700385
386 def __init__(self, *args, **kwargs):
Scott Bakerf91e6152016-02-11 12:07:10 -0800387 super(VSGService, self).__init__(*args, **kwargs)
Scott Baker62312972015-07-13 14:30:25 -0700388
Scott Bakerd4b48c02015-04-15 20:59:15 -0700389 class Meta:
390 app_label = "cord"
Scott Baker2516ce32016-02-11 16:56:34 -0800391 verbose_name = "vSG Service"
Scott Bakerd4b48c02015-04-15 20:59:15 -0700392 proxy = True
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700393
Scott Baker4e6d60d2015-05-20 20:45:11 -0700394 def allocate_bbs_account(self):
Scott Baker4c052b22016-02-11 11:08:42 -0800395 vcpes = VSGTenant.get_tenant_objects().all()
Scott Baker4e6d60d2015-05-20 20:45:11 -0700396 bbs_accounts = [vcpe.bbs_account for vcpe in vcpes]
397
398 # There's a bit of a race here; some other user could be trying to
399 # allocate a bbs_account at the same time we are.
400
Scott Baker2f0b3462015-06-09 12:03:56 -0700401 for i in range(2,21):
Scott Baker4e6d60d2015-05-20 20:45:11 -0700402 account_name = "bbs%02d@onlab.us" % i
403 if (account_name not in bbs_accounts):
404 return account_name
405
406 raise XOSConfigurationError("We've run out of available broadbandshield accounts. Delete some vcpe and try again.")
407
Scott Baker80157da2015-07-15 17:42:43 -0700408 @property
409 def bbs_slice(self):
410 bbs_slice_id=self.get_attribute("bbs_slice_id")
411 if not bbs_slice_id:
412 return None
413 bbs_slices=Slice.objects.filter(id=bbs_slice_id)
414 if not bbs_slices:
415 return None
416 return bbs_slices[0]
417
418 @bbs_slice.setter
419 def bbs_slice(self, value):
420 if value:
421 value = value.id
422 self.set_attribute("bbs_slice_id", value)
423
Scott Bakerf91e6152016-02-11 12:07:10 -0800424VSGService.setup_simple_attributes()
Scott Baker62312972015-07-13 14:30:25 -0700425
Scott Baker4c052b22016-02-11 11:08:42 -0800426class VSGTenant(TenantWithContainer):
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700427 class Meta:
428 proxy = True
429
Scott Bakerd9133342015-07-06 14:38:02 -0700430 KIND = VCPE_KIND
Scott Bakerd4b48c02015-04-15 20:59:15 -0700431
Scott Baker451050f2016-04-19 17:55:56 -0700432 sync_attributes = ("wan_container_ip", "wan_container_mac", "wan_container_netbits",
433 "wan_container_gateway_ip", "wan_container_gateway_mac",
434 "wan_vm_ip", "wan_vm_mac")
Scott Bakerc633dc92015-05-05 17:49:46 -0700435
Tony Mackd8515472015-08-19 11:58:18 -0400436 default_attributes = {"instance_id": None,
Scott Baker4b206972015-11-02 20:55:07 -0800437 "container_id": None,
Scott Baker4e6d60d2015-05-20 20:45:11 -0700438 "users": [],
Scott Baker4aa660e2015-06-09 12:22:29 -0700439 "bbs_account": None,
Scott Bakere6780e92016-02-13 14:38:43 -0800440 "last_ansible_hash": None,
441 "wan_container_ip": None}
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700442
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700443 def __init__(self, *args, **kwargs):
Scott Baker4c052b22016-02-11 11:08:42 -0800444 super(VSGTenant, self).__init__(*args, **kwargs)
Scott Baker47075932016-04-13 09:12:45 -0700445 self.cached_vrouter=None
Scott Bakerc633dc92015-05-05 17:49:46 -0700446
447 @property
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700448 def vbng(self):
Scott Baker451050f2016-04-19 17:55:56 -0700449 # not supported
450 return None
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700451
452 @vbng.setter
453 def vbng(self, value):
Scott Bakerd9133342015-07-06 14:38:02 -0700454 raise XOSConfigurationError("vCPE.vBNG cannot be set this way -- create a new vBNG object and set it's subscriber_tenant instead")
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700455
Scott Bakere4364d12015-07-06 17:21:21 -0700456 @property
Scott Baker47075932016-04-13 09:12:45 -0700457 def vrouter(self):
458 vrouter = self.get_newest_subscribed_tenant(VRouterTenant)
459 if not vrouter:
460 return None
461
462 # always return the same object when possible
463 if (self.cached_vrouter) and (self.cached_vrouter.id == vrouter.id):
464 return self.cached_vrouter
465
466 vrouter.caller = self.creator
467 self.cached_vrouter = vrouter
468 return vrouter
469
470 @vrouter.setter
471 def vrouter(self, value):
472 raise XOSConfigurationError("vCPE.vRouter cannot be set this way -- create a new vRuter object and set its subscriber_tenant instead")
473
474 @property
Scott Bakere4364d12015-07-06 17:21:21 -0700475 def volt(self):
476 if not self.subscriber_tenant:
477 return None
478 volts = VOLTTenant.objects.filter(id=self.subscriber_tenant.id)
479 if not volts:
480 return None
481 return volts[0]
482
Scott Baker81de6402015-07-06 16:50:30 -0700483 @property
484 def bbs_account(self):
485 return self.get_attribute("bbs_account", self.default_attributes["bbs_account"])
486
487 @bbs_account.setter
488 def bbs_account(self, value):
489 return self.set_attribute("bbs_account", value)
490
491 @property
492 def last_ansible_hash(self):
493 return self.get_attribute("last_ansible_hash", self.default_attributes["last_ansible_hash"])
494
495 @last_ansible_hash.setter
496 def last_ansible_hash(self, value):
497 return self.set_attribute("last_ansible_hash", value)
498
499 @property
500 def ssh_command(self):
Tony Mackd8515472015-08-19 11:58:18 -0400501 if self.instance:
502 return self.instance.get_ssh_command()
Scott Baker81de6402015-07-06 16:50:30 -0700503 else:
Tony Mackd8515472015-08-19 11:58:18 -0400504 return "no-instance"
Scott Baker81de6402015-07-06 16:50:30 -0700505
506 @ssh_command.setter
507 def ssh_command(self, value):
508 pass
509
Scott Baker451050f2016-04-19 17:55:56 -0700510 def get_vrouter_field(self, name, default=None):
511 if self.vrouter:
512 return getattr(self.vrouter, name, default)
Scott Baker4b206972015-11-02 20:55:07 -0800513 else:
Scott Baker451050f2016-04-19 17:55:56 -0700514 return default
Scott Bakere6780e92016-02-13 14:38:43 -0800515
516 @property
517 def wan_container_ip(self):
Scott Baker451050f2016-04-19 17:55:56 -0700518 return self.get_vrouter_field("public_ip", None)
Scott Bakere6780e92016-02-13 14:38:43 -0800519
Andy Bavier733733d2015-10-29 14:01:47 -0400520 @property
521 def wan_container_mac(self):
Scott Baker451050f2016-04-19 17:55:56 -0700522 return self.get_vrouter_field("public_mac", None)
523
524 @property
525 def wan_container_netbits(self):
526 return self.get_vrouter_field("netbits", None)
527
528 @property
529 def wan_container_gateway_ip(self):
530 return self.get_vrouter_field("gateway_ip", None)
531
532 @property
533 def wan_container_gateway_mac(self):
534 return self.get_vrouter_field("gateway_mac", None)
Andy Bavier733733d2015-10-29 14:01:47 -0400535
Scott Baker4f972592015-06-05 12:08:34 -0700536 @property
Scott Bakerb24c8f22016-04-14 17:19:01 -0700537 def wan_vm_ip(self):
538 tags = Tag.select_by_content_object(self.instance).filter(name="vm_vrouter_tenant")
539 if tags:
540 tenant = VRouterTenant.objects.get(id=tags[0].value)
541 return tenant.public_ip
542 else:
Scott Baker451050f2016-04-19 17:55:56 -0700543 raise Exception("no vm_vrouter_tenant tag for instance %s" % o.instance)
Scott Bakerb24c8f22016-04-14 17:19:01 -0700544
545 @property
546 def wan_vm_mac(self):
547 tags = Tag.select_by_content_object(self.instance).filter(name="vm_vrouter_tenant")
548 if tags:
549 tenant = VRouterTenant.objects.get(id=tags[0].value)
550 return tenant.public_mac
551 else:
Scott Baker451050f2016-04-19 17:55:56 -0700552 raise Exception("no vm_vrouter_tenant tag for instance %s" % o.instance)
Scott Baker281b1af2015-06-04 10:26:44 -0700553
Scott Bakerd517a102015-06-09 12:30:30 -0700554 @property
555 def is_synced(self):
556 return (self.enacted is not None) and (self.enacted >= self.updated)
557
558 @is_synced.setter
559 def is_synced(self, value):
560 pass
561
Scott Bakereb43b2b2016-04-13 15:00:13 -0700562 def get_vrouter_service(self):
563 vrouterServices = VRouterService.get_service_objects().all()
564 if not vrouterServices:
565 raise XOSConfigurationError("No VROUTER Services available")
566 return vrouterServices[0]
567
Scott Baker47075932016-04-13 09:12:45 -0700568 def manage_vrouter(self):
569 # Each vCPE object owns exactly one vRouterTenant object
570
571 if self.deleted:
572 return
573
574 if self.vrouter is None:
Scott Bakereb43b2b2016-04-13 15:00:13 -0700575 vrouter = self.get_vrouter_service().get_tenant(address_pool_name="addresses_vsg", subscriber_tenant = self)
Scott Baker47075932016-04-13 09:12:45 -0700576 vrouter.caller = self.creator
577 vrouter.save()
578
579 def cleanup_vrouter(self):
580 if self.vrouter:
581 # print "XXX cleanup vrouter", self.vrouter
582 self.vrouter.delete()
583
Scott Baker868dabf2015-06-24 12:54:24 -0700584 def cleanup_orphans(self):
Scott Baker47075932016-04-13 09:12:45 -0700585 # ensure vCPE only has one vRouter
586 cur_vrouter = self.vrouter
587 for vrouter in list(self.get_subscribed_tenants(VRouterTenant)):
588 if (not cur_vrouter) or (vrouter.id != cur_vrouter.id):
589 # print "XXX clean up orphaned vrouter", vrouter
590 vrouter.delete()
591
Tony Mackd8515472015-08-19 11:58:18 -0400592 if self.orig_instance_id and (self.orig_instance_id != self.get_attribute("instance_id")):
593 instances=Instance.objects.filter(id=self.orig_instance_id)
594 if instances:
595 # print "XXX clean up orphaned instance", instances[0]
596 instances[0].delete()
Scott Baker868dabf2015-06-24 12:54:24 -0700597
Scott Baker5b8eb412016-02-02 16:56:18 -0800598 def get_slice(self):
599 if not self.provider_service.slices.count():
Matteo Scandoloc6103aa2016-04-07 14:38:54 -0700600 print self, "dio porco"
Scott Baker5b8eb412016-02-02 16:56:18 -0800601 raise XOSConfigurationError("The service has no slices")
602 slice = self.provider_service.slices.all()[0]
603 return slice
604
Scott Baker67074ab2016-03-04 11:29:02 -0800605 def get_vsg_service(self):
606 return VSGService.get_service_objects().get(id=self.provider_service.id)
607
Scott Baker5b8eb412016-02-02 16:56:18 -0800608 def find_instance_for_s_tag(self, s_tag):
609 #s_tags = STagBlock.objects.find(s_s_tag)
610 #if s_tags:
611 # return s_tags[0].instance
612
613 tags = Tag.objects.filter(name="s_tag", value=s_tag)
614 if tags:
615 return tags[0].content_object
616
617 return None
618
619 def find_or_make_instance_for_s_tag(self, s_tag):
620 instance = self.find_instance_for_s_tag(self.volt.s_tag)
621 if instance:
622 return instance
623
624 flavors = Flavor.objects.filter(name="m1.small")
625 if not flavors:
626 raise XOSConfigurationError("No m1.small flavor")
627
628 slice = self.provider_service.slices.all()[0]
629
630 if slice.default_isolation == "container_vm":
631 (node, parent) = ContainerVmScheduler(slice).pick()
632 else:
Scott Baker67074ab2016-03-04 11:29:02 -0800633 (node, parent) = LeastLoadedNodeScheduler(slice, label=self.get_vsg_service().node_label).pick()
Scott Baker5b8eb412016-02-02 16:56:18 -0800634
635 instance = Instance(slice = slice,
636 node = node,
637 image = self.image,
638 creator = self.creator,
639 deployment = node.site_deployment.deployment,
640 flavor = flavors[0],
641 isolation = slice.default_isolation,
642 parent = parent)
Scott Baker63c8b372016-02-12 16:17:04 -0800643
Scott Baker5b8eb412016-02-02 16:56:18 -0800644 self.save_instance(instance)
645
646 return instance
647
648 def manage_container(self):
649 from core.models import Instance, Flavor
650
651 if self.deleted:
652 return
653
654 # For container or container_vm isolation, use what TenantWithCotnainer
655 # provides us
656 slice = self.get_slice()
657 if slice.default_isolation in ["container_vm", "container"]:
Scott Baker4c052b22016-02-11 11:08:42 -0800658 super(VSGTenant,self).manage_container()
Scott Baker5b8eb412016-02-02 16:56:18 -0800659 return
660
661 if not self.volt:
662 raise XOSConfigurationError("This vCPE container has no volt")
663
Scott Bakercf6eb992016-02-18 06:43:02 -0800664 if self.instance:
665 # We're good.
666 return
667
Scott Baker5b8eb412016-02-02 16:56:18 -0800668 instance = self.find_or_make_instance_for_s_tag(self.volt.s_tag)
669 self.instance = instance
670 super(TenantWithContainer, self).save()
671
672 def cleanup_container(self):
673 if self.get_slice().default_isolation in ["container_vm", "container"]:
Scott Baker4c052b22016-02-11 11:08:42 -0800674 super(VSGTenant,self).cleanup_container()
Scott Baker5b8eb412016-02-02 16:56:18 -0800675
676 # To-do: cleanup unused instances
677 pass
678
Scott Baker4e6d60d2015-05-20 20:45:11 -0700679 def manage_bbs_account(self):
680 if self.deleted:
681 return
682
Scott Baker126ad472015-07-07 17:59:44 -0700683 if self.volt and self.volt.subscriber and self.volt.subscriber.url_filter_enable:
Scott Baker642126f2015-05-20 20:57:28 -0700684 if not self.bbs_account:
Scott Bakerf91e6152016-02-11 12:07:10 -0800685 # make sure we use the proxied VSGService object, not the generic Service object
686 vcpe_service = VSGService.objects.get(id=self.provider_service.id)
Scott Baker050f8b32015-07-07 12:15:03 -0700687 self.bbs_account = vcpe_service.allocate_bbs_account()
Scott Baker4c052b22016-02-11 11:08:42 -0800688 super(VSGTenant, self).save()
Scott Baker642126f2015-05-20 20:57:28 -0700689 else:
690 if self.bbs_account:
691 self.bbs_account = None
Scott Baker4c052b22016-02-11 11:08:42 -0800692 super(VSGTenant, self).save()
Scott Baker4e6d60d2015-05-20 20:45:11 -0700693
Scott Baker839daa82015-11-16 22:53:49 -0800694 def find_or_make_port(self, instance, network, **kwargs):
695 port = Port.objects.filter(instance=instance, network=network)
696 if port:
697 port = port[0]
698 else:
699 port = Port(instance=instance, network=network, **kwargs)
700 port.save()
701 return port
702
Scott Baker63c8b372016-02-12 16:17:04 -0800703 def get_lan_network(self, instance):
Scott Baker63c8b372016-02-12 16:17:04 -0800704 slice = self.provider_service.slices.all()[0]
Scott Bakere6780e92016-02-13 14:38:43 -0800705 if CORD_USE_VTN:
706 # there should only be one network private network, and its template should not be the management template
707 lan_networks = [x for x in slice.networks.all() if x.template.visibility=="private" and (not "management" in x.template.name)]
708 if len(lan_networks)>1:
709 raise XOSProgrammingError("The vSG slice should only have one non-management private network")
710 else:
711 lan_networks = [x for x in slice.networks.all() if "lan" in x.name]
Scott Baker63c8b372016-02-12 16:17:04 -0800712 if not lan_networks:
713 raise XOSProgrammingError("No lan_network")
714 return lan_networks[0]
715
Scott Baker839daa82015-11-16 22:53:49 -0800716 def save_instance(self, instance):
717 with transaction.atomic():
Andy Bavier1aa49af2015-12-16 14:10:01 -0500718 instance.volumes = "/etc/dnsmasq.d,/etc/ufw"
Scott Baker4c052b22016-02-11 11:08:42 -0800719 super(VSGTenant, self).save_instance(instance)
Scott Baker839daa82015-11-16 22:53:49 -0800720
721 if instance.isolation in ["container", "container_vm"]:
Scott Baker63c8b372016-02-12 16:17:04 -0800722 lan_network = self.get_lan_network(instance)
723 port = self.find_or_make_port(instance, lan_network, ip="192.168.0.1", port_id="unmanaged")
Scott Baker839daa82015-11-16 22:53:49 -0800724 port.set_parameter("c_tag", self.volt.c_tag)
725 port.set_parameter("s_tag", self.volt.s_tag)
726 port.set_parameter("device", "eth1")
Scott Bakere2920b22015-11-19 16:51:27 -0800727 port.set_parameter("bridge", "br-lan")
Scott Baker839daa82015-11-16 22:53:49 -0800728
729 wan_networks = [x for x in instance.slice.networks.all() if "wan" in x.name]
730 if not wan_networks:
731 raise XOSProgrammingError("No wan_network")
732 port = self.find_or_make_port(instance, wan_networks[0])
733 port.set_parameter("next_hop", value="10.0.1.253") # FIX ME
734 port.set_parameter("device", "eth0")
735
Scott Baker63c8b372016-02-12 16:17:04 -0800736 if instance.isolation in ["vm"]:
737 lan_network = self.get_lan_network(instance)
738 port = self.find_or_make_port(instance, lan_network)
739 port.set_parameter("c_tag", self.volt.c_tag)
740 port.set_parameter("s_tag", self.volt.s_tag)
741 port.set_parameter("neutron_port_name", "stag-%s" % self.volt.s_tag)
742 port.save()
743
Scott Baker5b8eb412016-02-02 16:56:18 -0800744 # tag the instance with the s-tag, so we can easily find the
745 # instance later
746 if self.volt and self.volt.s_tag:
747 tags = Tag.objects.filter(name="s_tag", value=self.volt.s_tag)
748 if not tags:
749 tag = Tag(service=self.provider_service, content_object=instance, name="s_tag", value=self.volt.s_tag)
750 tag.save()
751
Scott Baker6fd78342016-02-18 15:18:47 -0800752 # VTN-CORD needs a WAN address for the VM, so that the VM can
753 # be configured.
754 if CORD_USE_VTN:
Scott Bakereb43b2b2016-04-13 15:00:13 -0700755 tags = Tag.select_by_content_object(instance).filter(name="vm_vrouter_tenant")
Scott Baker6fd78342016-02-18 15:18:47 -0800756 if not tags:
Scott Bakereb43b2b2016-04-13 15:00:13 -0700757 vrouter = self.get_vrouter_service().get_tenant(address_pool_name="addresses_vsg", subscriber_service = self.provider_service)
758 vrouter.set_attribute("tenant_for_instance_id", instance.id)
759 vrouter.save()
760 tag = Tag(service=self.provider_service, content_object=instance, name="vm_vrouter_tenant", value="%d" % vrouter.id)
Scott Baker6fd78342016-02-18 15:18:47 -0800761 tag.save()
762
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700763 def save(self, *args, **kwargs):
Scott Bakerc633dc92015-05-05 17:49:46 -0700764 if not self.creator:
765 if not getattr(self, "caller", None):
766 # caller must be set when creating a vCPE since it creates a slice
Scott Baker4c052b22016-02-11 11:08:42 -0800767 raise XOSProgrammingError("VSGTenant's self.caller was not set")
Scott Bakerc633dc92015-05-05 17:49:46 -0700768 self.creator = self.caller
769 if not self.creator:
Scott Baker4c052b22016-02-11 11:08:42 -0800770 raise XOSProgrammingError("VSGTenant's self.creator was not set")
Scott Bakerc633dc92015-05-05 17:49:46 -0700771
Scott Baker4c052b22016-02-11 11:08:42 -0800772 super(VSGTenant, self).save(*args, **kwargs)
Scott Baker192da222015-07-08 19:01:56 -0700773 model_policy_vcpe(self.pk)
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700774
775 def delete(self, *args, **kwargs):
Scott Baker520a9b62016-04-13 15:44:42 -0700776 self.cleanup_vrouter()
Scott Bakerc1584b82015-09-09 16:36:06 -0700777 self.cleanup_container()
Scott Baker4c052b22016-02-11 11:08:42 -0800778 super(VSGTenant, self).delete(*args, **kwargs)
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700779
Scott Baker192da222015-07-08 19:01:56 -0700780def model_policy_vcpe(pk):
781 # TODO: this should be made in to a real model_policy
782 with transaction.atomic():
Scott Baker4c052b22016-02-11 11:08:42 -0800783 vcpe = VSGTenant.objects.select_for_update().filter(pk=pk)
Scott Baker192da222015-07-08 19:01:56 -0700784 if not vcpe:
785 return
786 vcpe = vcpe[0]
Scott Bakerc1584b82015-09-09 16:36:06 -0700787 vcpe.manage_container()
Scott Baker47075932016-04-13 09:12:45 -0700788 vcpe.manage_vrouter()
Scott Baker192da222015-07-08 19:01:56 -0700789 vcpe.manage_bbs_account()
790 vcpe.cleanup_orphans()
791
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700792#----------------------------------------------------------------------------
793# vBNG
794#----------------------------------------------------------------------------
795
796class VBNGService(Service):
Scott Bakerd9133342015-07-06 14:38:02 -0700797 KIND = VBNG_KIND
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700798
Scott Baker6667ddc2015-10-20 22:12:51 -0700799 simple_attributes = ( ("vbng_url", ""), ) # "http://10.0.3.136:8181/onos/virtualbng/"
Scott Baker21d18932015-07-21 18:24:21 -0700800
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700801 class Meta:
802 app_label = "cord"
803 verbose_name = "vBNG Service"
804 proxy = True
805
Scott Baker21d18932015-07-21 18:24:21 -0700806VBNGService.setup_simple_attributes()
807
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700808class VBNGTenant(Tenant):
809 class Meta:
810 proxy = True
811
Scott Bakerd9133342015-07-06 14:38:02 -0700812 KIND = VBNG_KIND
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700813
Scott Baker61c8e8d2015-06-02 14:34:04 -0700814 default_attributes = {"routeable_subnet": "",
Scott Bakercf155f42015-06-08 19:09:53 -0700815 "mapped_ip": "",
816 "mapped_mac": "",
817 "mapped_hostname": ""}
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700818
819 @property
820 def routeable_subnet(self):
821 return self.get_attribute("routeable_subnet", self.default_attributes["routeable_subnet"])
822
823 @routeable_subnet.setter
824 def routeable_subnet(self, value):
825 self.set_attribute("routeable_subnet", value)
Scott Baker61c8e8d2015-06-02 14:34:04 -0700826
827 @property
828 def mapped_ip(self):
829 return self.get_attribute("mapped_ip", self.default_attributes["mapped_ip"])
830
831 @mapped_ip.setter
832 def mapped_ip(self, value):
833 self.set_attribute("mapped_ip", value)
Scott Bakercf155f42015-06-08 19:09:53 -0700834
835 @property
836 def mapped_mac(self):
837 return self.get_attribute("mapped_mac", self.default_attributes["mapped_mac"])
838
839 @mapped_mac.setter
840 def mapped_mac(self, value):
841 self.set_attribute("mapped_mac", value)
842
843 @property
844 def mapped_hostname(self):
845 return self.get_attribute("mapped_hostname", self.default_attributes["mapped_hostname"])
846
847 @mapped_hostname.setter
848 def mapped_hostname(self, value):
849 self.set_attribute("mapped_hostname", value)