blob: fe978c91bd108986b1facc4003fbb69d85e57246 [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 Bakerfa5cdd62015-04-20 09:16:17 -070011import traceback
Scott Baker7f8ef8f2015-04-20 14:24:29 -070012from xos.exceptions import *
Scott Bakere6780e92016-02-13 14:38:43 -080013from xos.config import Config
Scott Bakere9ff7ce2015-04-14 17:19:16 -070014
Scott Bakere9ff7ce2015-04-14 17:19:16 -070015class ConfigurationError(Exception):
16 pass
17
Scott Bakerd9133342015-07-06 14:38:02 -070018VOLT_KIND = "vOLT"
19VCPE_KIND = "vCPE"
20VBNG_KIND = "vBNG"
Scott Baker81de6402015-07-06 16:50:30 -070021CORD_SUBSCRIBER_KIND = "CordSubscriberRoot"
22
Scott Bakere6780e92016-02-13 14:38:43 -080023CORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
24
Scott Baker81de6402015-07-06 16:50:30 -070025# -------------------------------------------
26# CordSubscriberRoot
27# -------------------------------------------
28
29class CordSubscriberRoot(Subscriber):
Scott Bakere4364d12015-07-06 17:21:21 -070030 class Meta:
31 proxy = True
32
Scott Baker81de6402015-07-06 16:50:30 -070033 KIND = CORD_SUBSCRIBER_KIND
34
35 default_attributes = {"firewall_enable": False,
36 "firewall_rules": "accept all anywhere anywhere",
37 "url_filter_enable": False,
38 "url_filter_rules": "allow all",
39 "url_filter_level": "PG",
40 "cdn_enable": False,
Scott Baker126ad472015-07-07 17:59:44 -070041 "users": [],
42 "is_demo_user": False }
Scott Baker81de6402015-07-06 16:50:30 -070043
Scott Baker050f8b32015-07-07 12:15:03 -070044 sync_attributes = ("firewall_enable",
45 "firewall_rules",
46 "url_filter_enable",
47 "url_filter_rules",
48 "cdn_enable",)
49
Scott Baker81de6402015-07-06 16:50:30 -070050 def __init__(self, *args, **kwargs):
51 super(CordSubscriberRoot, self).__init__(*args, **kwargs)
52 self.cached_volt = None
Scott Baker050f8b32015-07-07 12:15:03 -070053 self._initial_url_filter_enable = self.url_filter_enable
Scott Baker81de6402015-07-06 16:50:30 -070054
55 @property
56 def volt(self):
57 volt = self.get_newest_subscribed_tenant(VOLTTenant)
58 if not volt:
59 return None
60
61 # always return the same object when possible
62 if (self.cached_volt) and (self.cached_volt.id == volt.id):
Scott Baker126ad472015-07-07 17:59:44 -070063 return self.cached_volt
Scott Baker81de6402015-07-06 16:50:30 -070064
Scott Bakere4364d12015-07-06 17:21:21 -070065 #volt.caller = self.creator
Scott Baker81de6402015-07-06 16:50:30 -070066 self.cached_volt = volt
67 return volt
68
69 @property
70 def firewall_enable(self):
71 return self.get_attribute("firewall_enable", self.default_attributes["firewall_enable"])
72
73 @firewall_enable.setter
74 def firewall_enable(self, value):
75 self.set_attribute("firewall_enable", value)
76
77 @property
78 def firewall_rules(self):
79 return self.get_attribute("firewall_rules", self.default_attributes["firewall_rules"])
80
81 @firewall_rules.setter
82 def firewall_rules(self, value):
83 self.set_attribute("firewall_rules", value)
84
85 @property
86 def url_filter_enable(self):
87 return self.get_attribute("url_filter_enable", self.default_attributes["url_filter_enable"])
88
89 @url_filter_enable.setter
90 def url_filter_enable(self, value):
91 self.set_attribute("url_filter_enable", value)
92
93 @property
94 def url_filter_level(self):
95 return self.get_attribute("url_filter_level", self.default_attributes["url_filter_level"])
96
97 @url_filter_level.setter
98 def url_filter_level(self, value):
99 self.set_attribute("url_filter_level", value)
100
101 @property
102 def url_filter_rules(self):
103 return self.get_attribute("url_filter_rules", self.default_attributes["url_filter_rules"])
104
105 @url_filter_rules.setter
106 def url_filter_rules(self, value):
107 self.set_attribute("url_filter_rules", value)
108
109 @property
110 def cdn_enable(self):
111 return self.get_attribute("cdn_enable", self.default_attributes["cdn_enable"])
112
113 @cdn_enable.setter
114 def cdn_enable(self, value):
115 self.set_attribute("cdn_enable", value)
116
117 @property
118 def users(self):
119 return self.get_attribute("users", self.default_attributes["users"])
120
121 @users.setter
122 def users(self, value):
123 self.set_attribute("users", value)
124
125 def find_user(self, uid):
126 uid = int(uid)
127 for user in self.users:
128 if user["id"] == uid:
129 return user
130 return None
131
132 def update_user(self, uid, **kwargs):
133 # kwargs may be "level" or "mac"
134 # Setting one of these to None will cause None to be stored in the db
135 uid = int(uid)
136 users = self.users
137 for user in users:
138 if user["id"] == uid:
139 for arg in kwargs.keys():
140 user[arg] = kwargs[arg]
141 self.users = users
142 return user
143 raise ValueError("User %d not found" % uid)
144
145 def create_user(self, **kwargs):
146 if "name" not in kwargs:
147 raise XOSMissingField("The name field is required")
148
149 for user in self.users:
150 if kwargs["name"] == user["name"]:
151 raise XOSDuplicateKey("User %s already exists" % kwargs["name"])
152
153 uids = [x["id"] for x in self.users]
154 if uids:
155 uid = max(uids)+1
156 else:
157 uid = 0
158 newuser = kwargs.copy()
159 newuser["id"] = uid
160
161 users = self.users
162 users.append(newuser)
163 self.users = users
164
165 return newuser
166
167 def delete_user(self, uid):
168 uid = int(uid)
169 users = self.users
170 for user in users:
171 if user["id"]==uid:
172 users.remove(user)
173 self.users = users
174 return
175
176 raise ValueError("Users %d not found" % uid)
177
178 @property
179 def services(self):
180 return {"cdn": self.cdn_enable,
181 "url_filter": self.url_filter_enable,
182 "firewall": self.firewall_enable}
183
184 @services.setter
185 def services(self, value):
186 pass
Scott Bakerd9133342015-07-06 14:38:02 -0700187
Scott Baker050f8b32015-07-07 12:15:03 -0700188 def save(self, *args, **kwargs):
Scott Bakerfe38c2a2015-07-24 18:56:57 -0700189 if (not hasattr(self, 'caller') or not self.caller.is_admin):
190 if (self.has_field_changed("service_specific_id")):
191 raise XOSPermissionDenied("You do not have permission to change service_specific_id")
Scott Baker050f8b32015-07-07 12:15:03 -0700192 super(CordSubscriberRoot, self).save(*args, **kwargs)
193 if (self.volt) and (self.volt.vcpe): # and (self._initial_url_filter_enabled != self.url_filter_enable):
194 # 1) trigger manage_bbs_account to run
195 # 2) trigger vcpe observer to wake up
196 self.volt.vcpe.save()
197
Scott Baker126ad472015-07-07 17:59:44 -0700198 @property
199 def is_demo_user(self):
200 return self.get_attribute("is_demo_user", self.default_attributes["is_demo_user"])
201
202 @is_demo_user.setter
203 def is_demo_user(self, value):
204 self.set_attribute("is_demo_user", value)
205
Scott Bakerd4b48c02015-04-15 20:59:15 -0700206# -------------------------------------------
207# VOLT
208# -------------------------------------------
209
210class VOLTService(Service):
Scott Bakerd9133342015-07-06 14:38:02 -0700211 KIND = VOLT_KIND
Scott Bakerd4b48c02015-04-15 20:59:15 -0700212
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700213 class Meta:
Scott Bakerd4b48c02015-04-15 20:59:15 -0700214 app_label = "cord"
215 verbose_name = "vOLT Service"
216 proxy = True
217
218class VOLTTenant(Tenant):
219 class Meta:
220 proxy = True
221
Scott Bakerd9133342015-07-06 14:38:02 -0700222 KIND = VOLT_KIND
Scott Bakerd4b48c02015-04-15 20:59:15 -0700223
Scott Bakerf1527cd2015-11-09 20:25:21 -0800224 default_attributes = {"vlan_id": None, "s_tag": None, "c_tag": None}
Scott Baker323eca92015-04-20 09:48:34 -0700225 def __init__(self, *args, **kwargs):
226 volt_services = VOLTService.get_service_objects().all()
227 if volt_services:
228 self._meta.get_field("provider_service").default = volt_services[0].id
229 super(VOLTTenant, self).__init__(*args, **kwargs)
Scott Bakerd9133342015-07-06 14:38:02 -0700230 self.cached_vcpe = None
Scott Baker868dabf2015-06-24 12:54:24 -0700231
Scott Bakerd4b48c02015-04-15 20:59:15 -0700232 @property
Scott Bakerf1527cd2015-11-09 20:25:21 -0800233 def s_tag(self):
234 return self.get_attribute("s_tag", self.default_attributes["s_tag"])
235
236 @s_tag.setter
237 def s_tag(self, value):
238 self.set_attribute("s_tag", value)
239
240 @property
241 def c_tag(self):
242 return self.get_attribute("c_tag", self.default_attributes["c_tag"])
243
244 @c_tag.setter
245 def c_tag(self, value):
246 self.set_attribute("c_tag", value)
247
248 # for now, vlan_id is a synonym for c_tag
249
250 @property
Scott Baker679f7022015-04-20 11:50:09 -0700251 def vlan_id(self):
Scott Bakerf1527cd2015-11-09 20:25:21 -0800252 return self.c_tag
Scott Baker679f7022015-04-20 11:50:09 -0700253
254 @vlan_id.setter
255 def vlan_id(self, value):
Scott Bakerf1527cd2015-11-09 20:25:21 -0800256 self.c_tag = value
Scott Baker679f7022015-04-20 11:50:09 -0700257
258 @property
Scott Bakerd4b48c02015-04-15 20:59:15 -0700259 def vcpe(self):
Scott Baker4c052b22016-02-11 11:08:42 -0800260 vcpe = self.get_newest_subscribed_tenant(VSGTenant)
Scott Bakerd9133342015-07-06 14:38:02 -0700261 if not vcpe:
262 return None
263
264 # always return the same object when possible
265 if (self.cached_vcpe) and (self.cached_vcpe.id == vcpe.id):
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700266 return self.cached_vcpe
Scott Bakerd9133342015-07-06 14:38:02 -0700267
Scott Bakerc633dc92015-05-05 17:49:46 -0700268 vcpe.caller = self.creator
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700269 self.cached_vcpe = vcpe
270 return vcpe
Scott Bakerd4b48c02015-04-15 20:59:15 -0700271
272 @vcpe.setter
273 def vcpe(self, value):
Scott Bakere4364d12015-07-06 17:21:21 -0700274 raise XOSConfigurationError("vOLT.vCPE cannot be set this way -- create a new vCPE object and set its subscriber_tenant instead")
275
276 @property
277 def subscriber(self):
278 if not self.subscriber_root:
279 return None
280 subs = CordSubscriberRoot.objects.filter(id=self.subscriber_root.id)
281 if not subs:
282 return None
283 return subs[0]
Scott Bakerd4b48c02015-04-15 20:59:15 -0700284
Scott Bakerc633dc92015-05-05 17:49:46 -0700285 @property
286 def creator(self):
287 if getattr(self, "cached_creator", None):
288 return self.cached_creator
289 creator_id=self.get_attribute("creator_id")
290 if not creator_id:
291 return None
292 users=User.objects.filter(id=creator_id)
293 if not users:
294 return None
295 user=users[0]
296 self.cached_creator = users[0]
297 return user
298
299 @creator.setter
300 def creator(self, value):
301 if value:
302 value = value.id
303 if (value != self.get_attribute("creator_id", None)):
304 self.cached_creator=None
305 self.set_attribute("creator_id", value)
306
Scott Bakerd4b48c02015-04-15 20:59:15 -0700307 def manage_vcpe(self):
308 # Each VOLT object owns exactly one VCPE object
309
310 if self.deleted:
311 return
312
313 if self.vcpe is None:
Scott Bakerf91e6152016-02-11 12:07:10 -0800314 vsgServices = VSGService.get_service_objects().all()
315 if not vsgServices:
316 raise XOSConfigurationError("No VSG Services available")
Scott Bakerd4b48c02015-04-15 20:59:15 -0700317
Scott Bakerf91e6152016-02-11 12:07:10 -0800318 vcpe = VSGTenant(provider_service = vsgServices[0],
Scott Bakerd4b48c02015-04-15 20:59:15 -0700319 subscriber_tenant = self)
Scott Bakerc633dc92015-05-05 17:49:46 -0700320 vcpe.caller = self.creator
Scott Bakerd4b48c02015-04-15 20:59:15 -0700321 vcpe.save()
322
Scott Baker050f8b32015-07-07 12:15:03 -0700323 def manage_subscriber(self):
324 if (self.subscriber_root is None):
325 # The vOLT is not connected to a Subscriber, so either find an
326 # existing subscriber with the same SSID, or autogenerate a new
327 # subscriber.
328 #
329 # TODO: This probably goes away when we rethink the ONOS-to-XOS
330 # vOLT API.
331
332 subs = CordSubscriberRoot.get_tenant_objects().filter(service_specific_id = self.service_specific_id)
333 if subs:
334 sub = subs[0]
335 else:
336 sub = CordSubscriberRoot(service_specific_id = self.service_specific_id,
337 name = "autogenerated-for-vOLT-%s" % self.id)
338 sub.save()
339 self.subscriber_root = sub
340 self.save()
341
Scott Bakerd4b48c02015-04-15 20:59:15 -0700342 def cleanup_vcpe(self):
343 if self.vcpe:
Scott Bakerd9133342015-07-06 14:38:02 -0700344 # print "XXX cleanup vcpe", self.vcpe
Scott Bakerd4b48c02015-04-15 20:59:15 -0700345 self.vcpe.delete()
Scott Bakerd4b48c02015-04-15 20:59:15 -0700346
Scott Baker868dabf2015-06-24 12:54:24 -0700347 def cleanup_orphans(self):
Scott Bakerd9133342015-07-06 14:38:02 -0700348 # ensure vOLT only has one vCPE
349 cur_vcpe = self.vcpe
Scott Baker4c052b22016-02-11 11:08:42 -0800350 for vcpe in list(self.get_subscribed_tenants(VSGTenant)):
Scott Bakerd9133342015-07-06 14:38:02 -0700351 if (not cur_vcpe) or (vcpe.id != cur_vcpe.id):
352 # print "XXX clean up orphaned vcpe", vcpe
353 vcpe.delete()
Scott Baker868dabf2015-06-24 12:54:24 -0700354
Scott Bakerd4b48c02015-04-15 20:59:15 -0700355 def save(self, *args, **kwargs):
Scott Baker7f8ef8f2015-04-20 14:24:29 -0700356 self.validate_unique_service_specific_id()
357
Scott Baker050f8b32015-07-07 12:15:03 -0700358 if (self.subscriber_root is not None):
359 subs = self.subscriber_root.get_subscribed_tenants(VOLTTenant)
360 if (subs) and (self not in subs):
361 raise XOSDuplicateKey("Subscriber should only be linked to one vOLT")
362
Scott Bakerc633dc92015-05-05 17:49:46 -0700363 if not self.creator:
364 if not getattr(self, "caller", None):
365 # caller must be set when creating a vCPE since it creates a slice
366 raise XOSProgrammingError("VOLTTenant's self.caller was not set")
367 self.creator = self.caller
368 if not self.creator:
369 raise XOSProgrammingError("VOLTTenant's self.creator was not set")
370
Scott Bakerd4b48c02015-04-15 20:59:15 -0700371 super(VOLTTenant, self).save(*args, **kwargs)
Scott Baker192da222015-07-08 19:01:56 -0700372 model_policy_volt(self.pk)
373 #self.manage_vcpe()
374 #self.manage_subscriber()
375 #self.cleanup_orphans()
Scott Bakerd4b48c02015-04-15 20:59:15 -0700376
377 def delete(self, *args, **kwargs):
378 self.cleanup_vcpe()
379 super(VOLTTenant, self).delete(*args, **kwargs)
380
Scott Baker192da222015-07-08 19:01:56 -0700381def model_policy_volt(pk):
382 # TODO: this should be made in to a real model_policy
383 with transaction.atomic():
384 volt = VOLTTenant.objects.select_for_update().filter(pk=pk)
385 if not volt:
386 return
387 volt = volt[0]
388 volt.manage_vcpe()
389 volt.manage_subscriber()
390 volt.cleanup_orphans()
391
Scott Bakerd4b48c02015-04-15 20:59:15 -0700392# -------------------------------------------
393# VCPE
394# -------------------------------------------
395
Scott Bakerf91e6152016-02-11 12:07:10 -0800396class VSGService(Service):
Scott Bakerd9133342015-07-06 14:38:02 -0700397 KIND = VCPE_KIND
Scott Bakerd4b48c02015-04-15 20:59:15 -0700398
Scott Baker2b56cf42015-07-15 18:08:06 -0700399 simple_attributes = ( ("bbs_api_hostname", None),
400 ("bbs_api_port", None),
Scott Baker80157da2015-07-15 17:42:43 -0700401 ("bbs_server", None),
Scott Bakercf6eb992016-02-18 06:43:02 -0800402 ("backend_network_label", "hpc_client"),
403 ("wan_container_gateway_ip", ""),
404 ("wan_container_gateway_mac", ""),
405 ("wan_container_netbits", "24") )
Scott Baker62312972015-07-13 14:30:25 -0700406
407 def __init__(self, *args, **kwargs):
Scott Bakerf91e6152016-02-11 12:07:10 -0800408 super(VSGService, self).__init__(*args, **kwargs)
Scott Baker62312972015-07-13 14:30:25 -0700409
Scott Bakerd4b48c02015-04-15 20:59:15 -0700410 class Meta:
411 app_label = "cord"
Scott Baker2516ce32016-02-11 16:56:34 -0800412 verbose_name = "vSG Service"
Scott Bakerd4b48c02015-04-15 20:59:15 -0700413 proxy = True
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700414
Scott Baker4e6d60d2015-05-20 20:45:11 -0700415 def allocate_bbs_account(self):
Scott Baker4c052b22016-02-11 11:08:42 -0800416 vcpes = VSGTenant.get_tenant_objects().all()
Scott Baker4e6d60d2015-05-20 20:45:11 -0700417 bbs_accounts = [vcpe.bbs_account for vcpe in vcpes]
418
419 # There's a bit of a race here; some other user could be trying to
420 # allocate a bbs_account at the same time we are.
421
Scott Baker2f0b3462015-06-09 12:03:56 -0700422 for i in range(2,21):
Scott Baker4e6d60d2015-05-20 20:45:11 -0700423 account_name = "bbs%02d@onlab.us" % i
424 if (account_name not in bbs_accounts):
425 return account_name
426
427 raise XOSConfigurationError("We've run out of available broadbandshield accounts. Delete some vcpe and try again.")
428
Scott Baker80157da2015-07-15 17:42:43 -0700429 @property
430 def bbs_slice(self):
431 bbs_slice_id=self.get_attribute("bbs_slice_id")
432 if not bbs_slice_id:
433 return None
434 bbs_slices=Slice.objects.filter(id=bbs_slice_id)
435 if not bbs_slices:
436 return None
437 return bbs_slices[0]
438
439 @bbs_slice.setter
440 def bbs_slice(self, value):
441 if value:
442 value = value.id
443 self.set_attribute("bbs_slice_id", value)
444
Scott Bakerf91e6152016-02-11 12:07:10 -0800445VSGService.setup_simple_attributes()
Scott Baker62312972015-07-13 14:30:25 -0700446
Scott Baker5b8eb412016-02-02 16:56:18 -0800447#class STagBlock(PlCoreBase):
448# instance = models.ForeignKey(Instance, related_name="s_tags")
449# s_tag = models.CharField(null=false, blank=false, unique=true, max_length=10)
450# #c_tags = models.TextField(null=true, blank=true)
451#
452# def __unicode__(self): return u'%s' % (self.s_tag)
Scott Baker62312972015-07-13 14:30:25 -0700453
Scott Baker4c052b22016-02-11 11:08:42 -0800454class VSGTenant(TenantWithContainer):
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700455 class Meta:
456 proxy = True
457
Scott Bakerd9133342015-07-06 14:38:02 -0700458 KIND = VCPE_KIND
Scott Bakerd4b48c02015-04-15 20:59:15 -0700459
Scott Baker92a81d42015-09-09 10:57:16 -0700460 sync_attributes = ("nat_ip", "nat_mac",
461 "lan_ip", "lan_mac",
Scott Bakere6780e92016-02-13 14:38:43 -0800462 "wan_ip", "wan_mac",
463 "wan_container_ip", "wan_container_mac",
Scott Baker92a81d42015-09-09 10:57:16 -0700464 "private_ip", "private_mac",
465 "hpc_client_ip", "hpc_client_mac")
Scott Bakerc633dc92015-05-05 17:49:46 -0700466
Tony Mackd8515472015-08-19 11:58:18 -0400467 default_attributes = {"instance_id": None,
Scott Baker4b206972015-11-02 20:55:07 -0800468 "container_id": None,
Scott Baker4e6d60d2015-05-20 20:45:11 -0700469 "users": [],
Scott Baker4aa660e2015-06-09 12:22:29 -0700470 "bbs_account": None,
Scott Bakere6780e92016-02-13 14:38:43 -0800471 "last_ansible_hash": None,
472 "wan_container_ip": None}
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700473
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700474 def __init__(self, *args, **kwargs):
Scott Baker4c052b22016-02-11 11:08:42 -0800475 super(VSGTenant, self).__init__(*args, **kwargs)
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700476 self.cached_vbng=None
Scott Bakerc633dc92015-05-05 17:49:46 -0700477
478 @property
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700479 def vbng(self):
Scott Bakerd9133342015-07-06 14:38:02 -0700480 vbng = self.get_newest_subscribed_tenant(VBNGTenant)
481 if not vbng:
482 return None
483
484 # always return the same object when possible
485 if (self.cached_vbng) and (self.cached_vbng.id == vbng.id):
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700486 return self.cached_vbng
Scott Bakerd9133342015-07-06 14:38:02 -0700487
Scott Bakerc633dc92015-05-05 17:49:46 -0700488 vbng.caller = self.creator
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700489 self.cached_vbng = vbng
490 return vbng
491
492 @vbng.setter
493 def vbng(self, value):
Scott Bakerd9133342015-07-06 14:38:02 -0700494 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 -0700495
Scott Bakere4364d12015-07-06 17:21:21 -0700496 @property
497 def volt(self):
498 if not self.subscriber_tenant:
499 return None
500 volts = VOLTTenant.objects.filter(id=self.subscriber_tenant.id)
501 if not volts:
502 return None
503 return volts[0]
504
Scott Baker81de6402015-07-06 16:50:30 -0700505 @property
506 def bbs_account(self):
507 return self.get_attribute("bbs_account", self.default_attributes["bbs_account"])
508
509 @bbs_account.setter
510 def bbs_account(self, value):
511 return self.set_attribute("bbs_account", value)
512
513 @property
514 def last_ansible_hash(self):
515 return self.get_attribute("last_ansible_hash", self.default_attributes["last_ansible_hash"])
516
517 @last_ansible_hash.setter
518 def last_ansible_hash(self, value):
519 return self.set_attribute("last_ansible_hash", value)
520
521 @property
522 def ssh_command(self):
Tony Mackd8515472015-08-19 11:58:18 -0400523 if self.instance:
524 return self.instance.get_ssh_command()
Scott Baker81de6402015-07-06 16:50:30 -0700525 else:
Tony Mackd8515472015-08-19 11:58:18 -0400526 return "no-instance"
Scott Baker81de6402015-07-06 16:50:30 -0700527
528 @ssh_command.setter
529 def ssh_command(self, value):
530 pass
531
Scott Baker5571c692015-05-20 08:19:25 -0700532 @property
533 def addresses(self):
Scott Baker4b206972015-11-02 20:55:07 -0800534 if self.instance:
535 ports = self.instance.ports.all()
536 elif self.container:
537 ports = self.container.ports.all()
538 else:
Scott Baker5571c692015-05-20 08:19:25 -0700539 return {}
540
541 addresses = {}
Scott Baker4b206972015-11-02 20:55:07 -0800542 for ns in ports:
Scott Baker5571c692015-05-20 08:19:25 -0700543 if "lan" in ns.network.name.lower():
Scott Baker92a81d42015-09-09 10:57:16 -0700544 addresses["lan"] = (ns.ip, ns.mac)
Scott Baker5571c692015-05-20 08:19:25 -0700545 elif "wan" in ns.network.name.lower():
Scott Baker92a81d42015-09-09 10:57:16 -0700546 addresses["wan"] = (ns.ip, ns.mac)
Scott Baker5571c692015-05-20 08:19:25 -0700547 elif "private" in ns.network.name.lower():
Scott Baker92a81d42015-09-09 10:57:16 -0700548 addresses["private"] = (ns.ip, ns.mac)
Scott Baker5571c692015-05-20 08:19:25 -0700549 elif "nat" in ns.network.name.lower():
Scott Baker92a81d42015-09-09 10:57:16 -0700550 addresses["nat"] = (ns.ip, ns.mac)
Scott Baker281b1af2015-06-04 10:26:44 -0700551 elif "hpc_client" in ns.network.name.lower():
Scott Baker92a81d42015-09-09 10:57:16 -0700552 addresses["hpc_client"] = (ns.ip, ns.mac)
Scott Baker5571c692015-05-20 08:19:25 -0700553 return addresses
554
Scott Bakere6780e92016-02-13 14:38:43 -0800555 # ------------------------------------------------------------------------
556 # The following IP addresses all come from the VM
557 # Note: They might not be useful for the VTN-vSG
558
Scott Baker432d1402015-05-20 08:25:29 -0700559 @property
560 def nat_ip(self):
Scott Baker92a81d42015-09-09 10:57:16 -0700561 return self.addresses.get("nat", (None,None) )[0]
562
563 @property
564 def nat_mac(self):
565 return self.addresses.get("nat", (None,None) )[1]
Scott Baker432d1402015-05-20 08:25:29 -0700566
567 @property
568 def lan_ip(self):
Scott Baker92a81d42015-09-09 10:57:16 -0700569 return self.addresses.get("lan", (None, None) )[0]
570
571 @property
572 def lan_mac(self):
573 return self.addresses.get("lan", (None, None) )[1]
Scott Baker432d1402015-05-20 08:25:29 -0700574
575 @property
576 def wan_ip(self):
Scott Baker92a81d42015-09-09 10:57:16 -0700577 return self.addresses.get("wan", (None, None) )[0]
Scott Baker432d1402015-05-20 08:25:29 -0700578
579 @property
Scott Baker4f972592015-06-05 12:08:34 -0700580 def wan_mac(self):
Andy Bavier4c7e76d2015-09-09 18:06:30 -0400581 return self.addresses.get("wan", (None, None) )[1]
Scott Baker4f972592015-06-05 12:08:34 -0700582
Scott Bakere6780e92016-02-13 14:38:43 -0800583 # end of VM IP address stubs
584 # ------------------------------------------------------------------------
585
586 @property
587 def wan_container_ip(self):
588 if CORD_USE_VTN:
589 # When using VTN, wan_container_ip is stored and maintained inside
590 # of the vSG object.
591 return self.get_attribute("wan_container_ip", self.default_attributes["wan_container_ip"])
592 else:
593 # When not using VTN, wan_container_ip is the same as wan_ip.
594 # XXX Is this broken for multiple-containers-per-VM?
595 return self.wan_ip
596
597 @wan_container_ip.setter
598 def wan_container_ip(self, value):
599 if CORD_USE_VTN:
600 self.set_attribute("wan_container_ip", value)
601 else:
602 raise Exception("wan_container_ip.setter called on non-VTN CORD")
603
Andy Bavier733733d2015-10-29 14:01:47 -0400604 # Generate the MAC for the container interface connected to WAN
605 @property
606 def wan_container_mac(self):
Scott Bakere6780e92016-02-13 14:38:43 -0800607 if not self.wan_container_ip:
608 return None
609 (a, b, c, d) = self.wan_container_ip.split('.')
Andy Bavier733733d2015-10-29 14:01:47 -0400610 return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
611
Scott Baker4f972592015-06-05 12:08:34 -0700612 @property
Scott Baker432d1402015-05-20 08:25:29 -0700613 def private_ip(self):
Scott Baker92a81d42015-09-09 10:57:16 -0700614 return self.addresses.get("private", (None, None) )[0]
615
616 @property
617 def private_mac(self):
618 return self.addresses.get("private", (None, None) )[1]
Scott Baker432d1402015-05-20 08:25:29 -0700619
Scott Baker281b1af2015-06-04 10:26:44 -0700620 @property
621 def hpc_client_ip(self):
Scott Baker92a81d42015-09-09 10:57:16 -0700622 return self.addresses.get("hpc_client", (None, None) )[0]
623
624 @property
625 def hpc_client_mac(self):
626 return self.addresses.get("hpc_client", (None, None) )[1]
Scott Baker281b1af2015-06-04 10:26:44 -0700627
Scott Bakerd517a102015-06-09 12:30:30 -0700628 @property
629 def is_synced(self):
630 return (self.enacted is not None) and (self.enacted >= self.updated)
631
632 @is_synced.setter
633 def is_synced(self, value):
634 pass
635
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700636 def manage_vbng(self):
637 # Each vCPE object owns exactly one vBNG object
638
639 if self.deleted:
640 return
641
642 if self.vbng is None:
643 vbngServices = VBNGService.get_service_objects().all()
644 if not vbngServices:
Scott Baker7f8ef8f2015-04-20 14:24:29 -0700645 raise XOSConfigurationError("No VBNG Services available")
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700646
647 vbng = VBNGTenant(provider_service = vbngServices[0],
648 subscriber_tenant = self)
Scott Bakerc633dc92015-05-05 17:49:46 -0700649 vbng.caller = self.creator
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700650 vbng.save()
651
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700652 def cleanup_vbng(self):
653 if self.vbng:
Scott Bakerd9133342015-07-06 14:38:02 -0700654 # print "XXX cleanup vnbg", self.vbng
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700655 self.vbng.delete()
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700656
Scott Baker868dabf2015-06-24 12:54:24 -0700657 def cleanup_orphans(self):
Scott Bakerd9133342015-07-06 14:38:02 -0700658 # ensure vCPE only has one vBNG
659 cur_vbng = self.vbng
660 for vbng in list(self.get_subscribed_tenants(VBNGTenant)):
661 if (not cur_vbng) or (vbng.id != cur_vbng.id):
662 # print "XXX clean up orphaned vbng", vbng
663 vbng.delete()
Scott Baker868dabf2015-06-24 12:54:24 -0700664
Tony Mackd8515472015-08-19 11:58:18 -0400665 if self.orig_instance_id and (self.orig_instance_id != self.get_attribute("instance_id")):
666 instances=Instance.objects.filter(id=self.orig_instance_id)
667 if instances:
668 # print "XXX clean up orphaned instance", instances[0]
669 instances[0].delete()
Scott Baker868dabf2015-06-24 12:54:24 -0700670
Scott Baker5b8eb412016-02-02 16:56:18 -0800671 def get_slice(self):
672 if not self.provider_service.slices.count():
673 raise XOSConfigurationError("The service has no slices")
674 slice = self.provider_service.slices.all()[0]
675 return slice
676
677 def find_instance_for_s_tag(self, s_tag):
678 #s_tags = STagBlock.objects.find(s_s_tag)
679 #if s_tags:
680 # return s_tags[0].instance
681
682 tags = Tag.objects.filter(name="s_tag", value=s_tag)
683 if tags:
684 return tags[0].content_object
685
686 return None
687
688 def find_or_make_instance_for_s_tag(self, s_tag):
689 instance = self.find_instance_for_s_tag(self.volt.s_tag)
690 if instance:
691 return instance
692
693 flavors = Flavor.objects.filter(name="m1.small")
694 if not flavors:
695 raise XOSConfigurationError("No m1.small flavor")
696
697 slice = self.provider_service.slices.all()[0]
698
699 if slice.default_isolation == "container_vm":
700 (node, parent) = ContainerVmScheduler(slice).pick()
701 else:
702 (node, parent) = LeastLoadedNodeScheduler(slice).pick()
703
704 instance = Instance(slice = slice,
705 node = node,
706 image = self.image,
707 creator = self.creator,
708 deployment = node.site_deployment.deployment,
709 flavor = flavors[0],
710 isolation = slice.default_isolation,
711 parent = parent)
Scott Baker63c8b372016-02-12 16:17:04 -0800712
Scott Baker5b8eb412016-02-02 16:56:18 -0800713 self.save_instance(instance)
714
715 return instance
716
717 def manage_container(self):
718 from core.models import Instance, Flavor
719
720 if self.deleted:
721 return
722
723 # For container or container_vm isolation, use what TenantWithCotnainer
724 # provides us
725 slice = self.get_slice()
726 if slice.default_isolation in ["container_vm", "container"]:
Scott Baker4c052b22016-02-11 11:08:42 -0800727 super(VSGTenant,self).manage_container()
Scott Baker5b8eb412016-02-02 16:56:18 -0800728 return
729
730 if not self.volt:
731 raise XOSConfigurationError("This vCPE container has no volt")
732
Scott Bakercf6eb992016-02-18 06:43:02 -0800733 if self.instance:
734 # We're good.
735 return
736
Scott Baker5b8eb412016-02-02 16:56:18 -0800737 instance = self.find_or_make_instance_for_s_tag(self.volt.s_tag)
738 self.instance = instance
739 super(TenantWithContainer, self).save()
740
741 def cleanup_container(self):
742 if self.get_slice().default_isolation in ["container_vm", "container"]:
Scott Baker4c052b22016-02-11 11:08:42 -0800743 super(VSGTenant,self).cleanup_container()
Scott Baker5b8eb412016-02-02 16:56:18 -0800744
745 # To-do: cleanup unused instances
746 pass
747
Scott Baker4e6d60d2015-05-20 20:45:11 -0700748 def manage_bbs_account(self):
749 if self.deleted:
750 return
751
Scott Baker126ad472015-07-07 17:59:44 -0700752 if self.volt and self.volt.subscriber and self.volt.subscriber.url_filter_enable:
Scott Baker642126f2015-05-20 20:57:28 -0700753 if not self.bbs_account:
Scott Bakerf91e6152016-02-11 12:07:10 -0800754 # make sure we use the proxied VSGService object, not the generic Service object
755 vcpe_service = VSGService.objects.get(id=self.provider_service.id)
Scott Baker050f8b32015-07-07 12:15:03 -0700756 self.bbs_account = vcpe_service.allocate_bbs_account()
Scott Baker4c052b22016-02-11 11:08:42 -0800757 super(VSGTenant, self).save()
Scott Baker642126f2015-05-20 20:57:28 -0700758 else:
759 if self.bbs_account:
760 self.bbs_account = None
Scott Baker4c052b22016-02-11 11:08:42 -0800761 super(VSGTenant, self).save()
Scott Baker4e6d60d2015-05-20 20:45:11 -0700762
Scott Bakere6780e92016-02-13 14:38:43 -0800763 def manage_wan_container_ip(self):
764 if CORD_USE_VTN:
765 if not self.wan_container_ip:
Scott Bakerda2982d2016-02-13 14:59:47 -0800766 ap = AddressPool.objects.filter(name="public_addresses")
Scott Bakere6780e92016-02-13 14:38:43 -0800767 if not ap:
768 raise Exception("AddressPool 'public_addresses' does not exist. Please configure it.")
769 ap = ap[0]
770
771 addr = ap.get_address()
772 if not addr:
773 raise Exception("AddressPool 'public_addresses' has run out of addresses.")
774
775 self.wan_container_ip = addr
Scott Bakercf6eb992016-02-18 06:43:02 -0800776 super(TenantWithContainer, self).save()
Scott Bakere6780e92016-02-13 14:38:43 -0800777
778 def cleanup_wan_container_ip(self):
779 if CORD_USE_VTN and self.wan_container_ip:
Scott Baker2c336f22016-02-13 16:08:50 -0800780 AddressPool.objects.filter(name="public_addresses")[0].put_address(self.wan_container_ip)
Scott Bakere6780e92016-02-13 14:38:43 -0800781 self.wan_container_ip = None
782
Scott Baker839daa82015-11-16 22:53:49 -0800783 def find_or_make_port(self, instance, network, **kwargs):
784 port = Port.objects.filter(instance=instance, network=network)
785 if port:
786 port = port[0]
787 else:
788 port = Port(instance=instance, network=network, **kwargs)
789 port.save()
790 return port
791
Scott Baker63c8b372016-02-12 16:17:04 -0800792 def get_lan_network(self, instance):
Scott Baker63c8b372016-02-12 16:17:04 -0800793 slice = self.provider_service.slices.all()[0]
Scott Bakere6780e92016-02-13 14:38:43 -0800794 if CORD_USE_VTN:
795 # there should only be one network private network, and its template should not be the management template
796 lan_networks = [x for x in slice.networks.all() if x.template.visibility=="private" and (not "management" in x.template.name)]
797 if len(lan_networks)>1:
798 raise XOSProgrammingError("The vSG slice should only have one non-management private network")
799 else:
800 lan_networks = [x for x in slice.networks.all() if "lan" in x.name]
Scott Baker63c8b372016-02-12 16:17:04 -0800801 if not lan_networks:
802 raise XOSProgrammingError("No lan_network")
803 return lan_networks[0]
804
Scott Baker839daa82015-11-16 22:53:49 -0800805 def save_instance(self, instance):
806 with transaction.atomic():
Andy Bavier1aa49af2015-12-16 14:10:01 -0500807 instance.volumes = "/etc/dnsmasq.d,/etc/ufw"
Scott Baker4c052b22016-02-11 11:08:42 -0800808 super(VSGTenant, self).save_instance(instance)
Scott Baker839daa82015-11-16 22:53:49 -0800809
810 if instance.isolation in ["container", "container_vm"]:
Scott Baker63c8b372016-02-12 16:17:04 -0800811 lan_network = self.get_lan_network(instance)
812 port = self.find_or_make_port(instance, lan_network, ip="192.168.0.1", port_id="unmanaged")
Scott Baker839daa82015-11-16 22:53:49 -0800813 port.set_parameter("c_tag", self.volt.c_tag)
814 port.set_parameter("s_tag", self.volt.s_tag)
815 port.set_parameter("device", "eth1")
Scott Bakere2920b22015-11-19 16:51:27 -0800816 port.set_parameter("bridge", "br-lan")
Scott Baker839daa82015-11-16 22:53:49 -0800817
818 wan_networks = [x for x in instance.slice.networks.all() if "wan" in x.name]
819 if not wan_networks:
820 raise XOSProgrammingError("No wan_network")
821 port = self.find_or_make_port(instance, wan_networks[0])
822 port.set_parameter("next_hop", value="10.0.1.253") # FIX ME
823 port.set_parameter("device", "eth0")
824
Scott Baker63c8b372016-02-12 16:17:04 -0800825 if instance.isolation in ["vm"]:
826 lan_network = self.get_lan_network(instance)
827 port = self.find_or_make_port(instance, lan_network)
828 port.set_parameter("c_tag", self.volt.c_tag)
829 port.set_parameter("s_tag", self.volt.s_tag)
830 port.set_parameter("neutron_port_name", "stag-%s" % self.volt.s_tag)
831 port.save()
832
Scott Baker5b8eb412016-02-02 16:56:18 -0800833 # tag the instance with the s-tag, so we can easily find the
834 # instance later
835 if self.volt and self.volt.s_tag:
836 tags = Tag.objects.filter(name="s_tag", value=self.volt.s_tag)
837 if not tags:
838 tag = Tag(service=self.provider_service, content_object=instance, name="s_tag", value=self.volt.s_tag)
839 tag.save()
840
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700841 def save(self, *args, **kwargs):
Scott Bakerc633dc92015-05-05 17:49:46 -0700842 if not self.creator:
843 if not getattr(self, "caller", None):
844 # caller must be set when creating a vCPE since it creates a slice
Scott Baker4c052b22016-02-11 11:08:42 -0800845 raise XOSProgrammingError("VSGTenant's self.caller was not set")
Scott Bakerc633dc92015-05-05 17:49:46 -0700846 self.creator = self.caller
847 if not self.creator:
Scott Baker4c052b22016-02-11 11:08:42 -0800848 raise XOSProgrammingError("VSGTenant's self.creator was not set")
Scott Bakerc633dc92015-05-05 17:49:46 -0700849
Scott Baker4c052b22016-02-11 11:08:42 -0800850 super(VSGTenant, self).save(*args, **kwargs)
Scott Baker192da222015-07-08 19:01:56 -0700851 model_policy_vcpe(self.pk)
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700852
853 def delete(self, *args, **kwargs):
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700854 self.cleanup_vbng()
Scott Bakerc1584b82015-09-09 16:36:06 -0700855 self.cleanup_container()
Scott Bakere6780e92016-02-13 14:38:43 -0800856 self.cleanup_wan_container_ip()
Scott Baker4c052b22016-02-11 11:08:42 -0800857 super(VSGTenant, self).delete(*args, **kwargs)
Scott Bakere9ff7ce2015-04-14 17:19:16 -0700858
Scott Baker192da222015-07-08 19:01:56 -0700859def model_policy_vcpe(pk):
860 # TODO: this should be made in to a real model_policy
861 with transaction.atomic():
Scott Baker4c052b22016-02-11 11:08:42 -0800862 vcpe = VSGTenant.objects.select_for_update().filter(pk=pk)
Scott Baker192da222015-07-08 19:01:56 -0700863 if not vcpe:
864 return
865 vcpe = vcpe[0]
Scott Bakere6780e92016-02-13 14:38:43 -0800866 vcpe.manage_wan_container_ip()
Scott Bakerc1584b82015-09-09 16:36:06 -0700867 vcpe.manage_container()
Scott Baker192da222015-07-08 19:01:56 -0700868 vcpe.manage_vbng()
869 vcpe.manage_bbs_account()
870 vcpe.cleanup_orphans()
871
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700872#----------------------------------------------------------------------------
873# vBNG
874#----------------------------------------------------------------------------
875
876class VBNGService(Service):
Scott Bakerd9133342015-07-06 14:38:02 -0700877 KIND = VBNG_KIND
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700878
Scott Baker6667ddc2015-10-20 22:12:51 -0700879 simple_attributes = ( ("vbng_url", ""), ) # "http://10.0.3.136:8181/onos/virtualbng/"
Scott Baker21d18932015-07-21 18:24:21 -0700880
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700881 class Meta:
882 app_label = "cord"
883 verbose_name = "vBNG Service"
884 proxy = True
885
Scott Baker21d18932015-07-21 18:24:21 -0700886VBNGService.setup_simple_attributes()
887
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700888class VBNGTenant(Tenant):
889 class Meta:
890 proxy = True
891
Scott Bakerd9133342015-07-06 14:38:02 -0700892 KIND = VBNG_KIND
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700893
Scott Baker61c8e8d2015-06-02 14:34:04 -0700894 default_attributes = {"routeable_subnet": "",
Scott Bakercf155f42015-06-08 19:09:53 -0700895 "mapped_ip": "",
896 "mapped_mac": "",
897 "mapped_hostname": ""}
Scott Bakerfa5cdd62015-04-20 09:16:17 -0700898
899 @property
900 def routeable_subnet(self):
901 return self.get_attribute("routeable_subnet", self.default_attributes["routeable_subnet"])
902
903 @routeable_subnet.setter
904 def routeable_subnet(self, value):
905 self.set_attribute("routeable_subnet", value)
Scott Baker61c8e8d2015-06-02 14:34:04 -0700906
907 @property
908 def mapped_ip(self):
909 return self.get_attribute("mapped_ip", self.default_attributes["mapped_ip"])
910
911 @mapped_ip.setter
912 def mapped_ip(self, value):
913 self.set_attribute("mapped_ip", value)
Scott Bakercf155f42015-06-08 19:09:53 -0700914
915 @property
916 def mapped_mac(self):
917 return self.get_attribute("mapped_mac", self.default_attributes["mapped_mac"])
918
919 @mapped_mac.setter
920 def mapped_mac(self, value):
921 self.set_attribute("mapped_mac", value)
922
923 @property
924 def mapped_hostname(self):
925 return self.get_attribute("mapped_hostname", self.default_attributes["mapped_hostname"])
926
927 @mapped_hostname.setter
928 def mapped_hostname(self, value):
929 self.set_attribute("mapped_hostname", value)