blob: 6fcef5239df1498111810a1bb333d9291aaab55c [file] [log] [blame]
Scott Baker1b241612015-04-14 17:19:16 -07001from django.db import models
Scott Baker27fab492016-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 Baker1b241612015-04-14 17:19:16 -07003from core.models.plcorebase import StrippedCharField
4import os
Scott Baker1027cd62015-07-08 19:01:56 -07005from django.db import models, transaction
Scott Baker1b241612015-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 Baker87891952016-02-02 16:56:18 -08009from core.models import Tag
10from core.models.service import LeastLoadedNodeScheduler
Scott Baker697fb0b2015-04-20 09:16:17 -070011import traceback
Scott Bakerd921e1c2015-04-20 14:24:29 -070012from xos.exceptions import *
Scott Baker27fab492016-02-13 14:38:43 -080013from xos.config import Config
Scott Baker1b241612015-04-14 17:19:16 -070014
Scott Baker1b241612015-04-14 17:19:16 -070015class ConfigurationError(Exception):
16 pass
17
Scott Baker361c86c2015-07-06 14:38:02 -070018VOLT_KIND = "vOLT"
19VCPE_KIND = "vCPE"
20VBNG_KIND = "vBNG"
Scott Bakerf08823d2015-07-06 16:50:30 -070021CORD_SUBSCRIBER_KIND = "CordSubscriberRoot"
22
Scott Baker27fab492016-02-13 14:38:43 -080023CORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
24
Scott Bakerf08823d2015-07-06 16:50:30 -070025# -------------------------------------------
26# CordSubscriberRoot
27# -------------------------------------------
28
29class CordSubscriberRoot(Subscriber):
Scott Bakercd349182015-07-06 17:21:21 -070030 class Meta:
31 proxy = True
32
Scott Bakerf08823d2015-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 Bakerdb66fd32015-07-07 17:59:44 -070041 "users": [],
42 "is_demo_user": False }
Scott Bakerf08823d2015-07-06 16:50:30 -070043
Scott Baker7c7b6312015-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 Bakerf08823d2015-07-06 16:50:30 -070050 def __init__(self, *args, **kwargs):
51 super(CordSubscriberRoot, self).__init__(*args, **kwargs)
52 self.cached_volt = None
Scott Baker7c7b6312015-07-07 12:15:03 -070053 self._initial_url_filter_enable = self.url_filter_enable
Scott Bakerf08823d2015-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 Bakerdb66fd32015-07-07 17:59:44 -070063 return self.cached_volt
Scott Bakerf08823d2015-07-06 16:50:30 -070064
Scott Bakercd349182015-07-06 17:21:21 -070065 #volt.caller = self.creator
Scott Bakerf08823d2015-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 Baker361c86c2015-07-06 14:38:02 -0700187
Scott Baker7c7b6312015-07-07 12:15:03 -0700188 def save(self, *args, **kwargs):
Scott Bakereaee05e2015-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 Baker7c7b6312015-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 Bakerdb66fd32015-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 Baker39b0d892015-04-15 20:59:15 -0700206# -------------------------------------------
207# VOLT
208# -------------------------------------------
209
210class VOLTService(Service):
Scott Baker361c86c2015-07-06 14:38:02 -0700211 KIND = VOLT_KIND
Scott Baker39b0d892015-04-15 20:59:15 -0700212
Scott Baker1b241612015-04-14 17:19:16 -0700213 class Meta:
Scott Baker39b0d892015-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 Baker361c86c2015-07-06 14:38:02 -0700222 KIND = VOLT_KIND
Scott Baker39b0d892015-04-15 20:59:15 -0700223
Scott Baker89a338c2015-11-09 20:25:21 -0800224 default_attributes = {"vlan_id": None, "s_tag": None, "c_tag": None}
Scott Baker557aada2015-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 Baker361c86c2015-07-06 14:38:02 -0700230 self.cached_vcpe = None
Scott Baker5e76f802015-06-24 12:54:24 -0700231
Scott Baker39b0d892015-04-15 20:59:15 -0700232 @property
Scott Baker89a338c2015-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 Bakere744c7b2015-04-20 11:50:09 -0700251 def vlan_id(self):
Scott Baker89a338c2015-11-09 20:25:21 -0800252 return self.c_tag
Scott Bakere744c7b2015-04-20 11:50:09 -0700253
254 @vlan_id.setter
255 def vlan_id(self, value):
Scott Baker89a338c2015-11-09 20:25:21 -0800256 self.c_tag = value
Scott Bakere744c7b2015-04-20 11:50:09 -0700257
258 @property
Scott Baker39b0d892015-04-15 20:59:15 -0700259 def vcpe(self):
Scott Baker664bbe52016-02-11 11:08:42 -0800260 vcpe = self.get_newest_subscribed_tenant(VSGTenant)
Scott Baker361c86c2015-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 Baker697fb0b2015-04-20 09:16:17 -0700266 return self.cached_vcpe
Scott Baker361c86c2015-07-06 14:38:02 -0700267
Scott Baker9c8a2c72015-05-05 17:49:46 -0700268 vcpe.caller = self.creator
Scott Baker697fb0b2015-04-20 09:16:17 -0700269 self.cached_vcpe = vcpe
270 return vcpe
Scott Baker39b0d892015-04-15 20:59:15 -0700271
272 @vcpe.setter
273 def vcpe(self, value):
Scott Bakercd349182015-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 Baker39b0d892015-04-15 20:59:15 -0700284
Scott Baker9c8a2c72015-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 Baker39b0d892015-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 Baker4698b922016-02-11 12:07:10 -0800314 vsgServices = VSGService.get_service_objects().all()
315 if not vsgServices:
316 raise XOSConfigurationError("No VSG Services available")
Scott Baker39b0d892015-04-15 20:59:15 -0700317
Scott Baker4698b922016-02-11 12:07:10 -0800318 vcpe = VSGTenant(provider_service = vsgServices[0],
Scott Baker39b0d892015-04-15 20:59:15 -0700319 subscriber_tenant = self)
Scott Baker9c8a2c72015-05-05 17:49:46 -0700320 vcpe.caller = self.creator
Scott Baker39b0d892015-04-15 20:59:15 -0700321 vcpe.save()
322
Scott Baker7c7b6312015-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 Baker39b0d892015-04-15 20:59:15 -0700342 def cleanup_vcpe(self):
343 if self.vcpe:
Scott Baker361c86c2015-07-06 14:38:02 -0700344 # print "XXX cleanup vcpe", self.vcpe
Scott Baker39b0d892015-04-15 20:59:15 -0700345 self.vcpe.delete()
Scott Baker39b0d892015-04-15 20:59:15 -0700346
Scott Baker5e76f802015-06-24 12:54:24 -0700347 def cleanup_orphans(self):
Scott Baker361c86c2015-07-06 14:38:02 -0700348 # ensure vOLT only has one vCPE
349 cur_vcpe = self.vcpe
Scott Baker664bbe52016-02-11 11:08:42 -0800350 for vcpe in list(self.get_subscribed_tenants(VSGTenant)):
Scott Baker361c86c2015-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 Baker5e76f802015-06-24 12:54:24 -0700354
Scott Baker39b0d892015-04-15 20:59:15 -0700355 def save(self, *args, **kwargs):
Scott Bakerd921e1c2015-04-20 14:24:29 -0700356 self.validate_unique_service_specific_id()
357
Scott Baker7c7b6312015-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 Baker9c8a2c72015-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 Baker39b0d892015-04-15 20:59:15 -0700371 super(VOLTTenant, self).save(*args, **kwargs)
Scott Baker1027cd62015-07-08 19:01:56 -0700372 model_policy_volt(self.pk)
373 #self.manage_vcpe()
374 #self.manage_subscriber()
375 #self.cleanup_orphans()
Scott Baker39b0d892015-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 Baker1027cd62015-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 Baker39b0d892015-04-15 20:59:15 -0700392# -------------------------------------------
393# VCPE
394# -------------------------------------------
395
Scott Baker4698b922016-02-11 12:07:10 -0800396class VSGService(Service):
Scott Baker361c86c2015-07-06 14:38:02 -0700397 KIND = VCPE_KIND
Scott Baker39b0d892015-04-15 20:59:15 -0700398
Scott Bakera0886fb2015-07-15 18:08:06 -0700399 simple_attributes = ( ("bbs_api_hostname", None),
400 ("bbs_api_port", None),
Scott Baker3d2493b2015-07-15 17:42:43 -0700401 ("bbs_server", None),
Scott Bakercec6a702015-07-15 18:04:22 -0700402 ("backend_network_label", "hpc_client"), )
Scott Baker7ba272e2015-07-13 14:30:25 -0700403
404 def __init__(self, *args, **kwargs):
Scott Baker4698b922016-02-11 12:07:10 -0800405 super(VSGService, self).__init__(*args, **kwargs)
Scott Baker7ba272e2015-07-13 14:30:25 -0700406
Scott Baker39b0d892015-04-15 20:59:15 -0700407 class Meta:
408 app_label = "cord"
Scott Baker060f7352016-02-11 16:56:34 -0800409 verbose_name = "vSG Service"
Scott Baker39b0d892015-04-15 20:59:15 -0700410 proxy = True
Scott Baker1b241612015-04-14 17:19:16 -0700411
Scott Baker5c8abf82015-05-20 20:45:11 -0700412 def allocate_bbs_account(self):
Scott Baker664bbe52016-02-11 11:08:42 -0800413 vcpes = VSGTenant.get_tenant_objects().all()
Scott Baker5c8abf82015-05-20 20:45:11 -0700414 bbs_accounts = [vcpe.bbs_account for vcpe in vcpes]
415
416 # There's a bit of a race here; some other user could be trying to
417 # allocate a bbs_account at the same time we are.
418
Scott Bakerf4f61ca2015-06-09 12:03:56 -0700419 for i in range(2,21):
Scott Baker5c8abf82015-05-20 20:45:11 -0700420 account_name = "bbs%02d@onlab.us" % i
421 if (account_name not in bbs_accounts):
422 return account_name
423
424 raise XOSConfigurationError("We've run out of available broadbandshield accounts. Delete some vcpe and try again.")
425
Scott Baker3d2493b2015-07-15 17:42:43 -0700426 @property
427 def bbs_slice(self):
428 bbs_slice_id=self.get_attribute("bbs_slice_id")
429 if not bbs_slice_id:
430 return None
431 bbs_slices=Slice.objects.filter(id=bbs_slice_id)
432 if not bbs_slices:
433 return None
434 return bbs_slices[0]
435
436 @bbs_slice.setter
437 def bbs_slice(self, value):
438 if value:
439 value = value.id
440 self.set_attribute("bbs_slice_id", value)
441
Scott Baker4698b922016-02-11 12:07:10 -0800442VSGService.setup_simple_attributes()
Scott Baker7ba272e2015-07-13 14:30:25 -0700443
Scott Baker87891952016-02-02 16:56:18 -0800444#class STagBlock(PlCoreBase):
445# instance = models.ForeignKey(Instance, related_name="s_tags")
446# s_tag = models.CharField(null=false, blank=false, unique=true, max_length=10)
447# #c_tags = models.TextField(null=true, blank=true)
448#
449# def __unicode__(self): return u'%s' % (self.s_tag)
Scott Baker7ba272e2015-07-13 14:30:25 -0700450
Scott Baker664bbe52016-02-11 11:08:42 -0800451class VSGTenant(TenantWithContainer):
Scott Baker1b241612015-04-14 17:19:16 -0700452 class Meta:
453 proxy = True
454
Scott Baker361c86c2015-07-06 14:38:02 -0700455 KIND = VCPE_KIND
Scott Baker39b0d892015-04-15 20:59:15 -0700456
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700457 sync_attributes = ("nat_ip", "nat_mac",
458 "lan_ip", "lan_mac",
Scott Baker27fab492016-02-13 14:38:43 -0800459 "wan_ip", "wan_mac",
460 "wan_container_ip", "wan_container_mac",
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700461 "private_ip", "private_mac",
462 "hpc_client_ip", "hpc_client_mac")
Scott Baker9c8a2c72015-05-05 17:49:46 -0700463
Tony Mack3de59e32015-08-19 11:58:18 -0400464 default_attributes = {"instance_id": None,
Scott Bakerf7f7e572015-11-02 20:55:07 -0800465 "container_id": None,
Scott Baker5c8abf82015-05-20 20:45:11 -0700466 "users": [],
Scott Bakerd40a42d2015-06-09 12:22:29 -0700467 "bbs_account": None,
Scott Baker27fab492016-02-13 14:38:43 -0800468 "last_ansible_hash": None,
469 "wan_container_ip": None}
Scott Baker1b241612015-04-14 17:19:16 -0700470
Scott Baker697fb0b2015-04-20 09:16:17 -0700471 def __init__(self, *args, **kwargs):
Scott Baker664bbe52016-02-11 11:08:42 -0800472 super(VSGTenant, self).__init__(*args, **kwargs)
Scott Baker697fb0b2015-04-20 09:16:17 -0700473 self.cached_vbng=None
Scott Baker9c8a2c72015-05-05 17:49:46 -0700474
475 @property
Scott Baker697fb0b2015-04-20 09:16:17 -0700476 def vbng(self):
Scott Baker361c86c2015-07-06 14:38:02 -0700477 vbng = self.get_newest_subscribed_tenant(VBNGTenant)
478 if not vbng:
479 return None
480
481 # always return the same object when possible
482 if (self.cached_vbng) and (self.cached_vbng.id == vbng.id):
Scott Baker697fb0b2015-04-20 09:16:17 -0700483 return self.cached_vbng
Scott Baker361c86c2015-07-06 14:38:02 -0700484
Scott Baker9c8a2c72015-05-05 17:49:46 -0700485 vbng.caller = self.creator
Scott Baker697fb0b2015-04-20 09:16:17 -0700486 self.cached_vbng = vbng
487 return vbng
488
489 @vbng.setter
490 def vbng(self, value):
Scott Baker361c86c2015-07-06 14:38:02 -0700491 raise XOSConfigurationError("vCPE.vBNG cannot be set this way -- create a new vBNG object and set it's subscriber_tenant instead")
Scott Baker1b241612015-04-14 17:19:16 -0700492
Scott Bakercd349182015-07-06 17:21:21 -0700493 @property
494 def volt(self):
495 if not self.subscriber_tenant:
496 return None
497 volts = VOLTTenant.objects.filter(id=self.subscriber_tenant.id)
498 if not volts:
499 return None
500 return volts[0]
501
Scott Bakerf08823d2015-07-06 16:50:30 -0700502 @property
503 def bbs_account(self):
504 return self.get_attribute("bbs_account", self.default_attributes["bbs_account"])
505
506 @bbs_account.setter
507 def bbs_account(self, value):
508 return self.set_attribute("bbs_account", value)
509
510 @property
511 def last_ansible_hash(self):
512 return self.get_attribute("last_ansible_hash", self.default_attributes["last_ansible_hash"])
513
514 @last_ansible_hash.setter
515 def last_ansible_hash(self, value):
516 return self.set_attribute("last_ansible_hash", value)
517
518 @property
519 def ssh_command(self):
Tony Mack3de59e32015-08-19 11:58:18 -0400520 if self.instance:
521 return self.instance.get_ssh_command()
Scott Bakerf08823d2015-07-06 16:50:30 -0700522 else:
Tony Mack3de59e32015-08-19 11:58:18 -0400523 return "no-instance"
Scott Bakerf08823d2015-07-06 16:50:30 -0700524
525 @ssh_command.setter
526 def ssh_command(self, value):
527 pass
528
Scott Baker706bf972015-05-20 08:19:25 -0700529 @property
530 def addresses(self):
Scott Bakerf7f7e572015-11-02 20:55:07 -0800531 if self.instance:
532 ports = self.instance.ports.all()
533 elif self.container:
534 ports = self.container.ports.all()
535 else:
Scott Baker706bf972015-05-20 08:19:25 -0700536 return {}
537
538 addresses = {}
Scott Bakerf7f7e572015-11-02 20:55:07 -0800539 for ns in ports:
Scott Baker706bf972015-05-20 08:19:25 -0700540 if "lan" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700541 addresses["lan"] = (ns.ip, ns.mac)
Scott Baker706bf972015-05-20 08:19:25 -0700542 elif "wan" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700543 addresses["wan"] = (ns.ip, ns.mac)
Scott Baker706bf972015-05-20 08:19:25 -0700544 elif "private" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700545 addresses["private"] = (ns.ip, ns.mac)
Scott Baker706bf972015-05-20 08:19:25 -0700546 elif "nat" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700547 addresses["nat"] = (ns.ip, ns.mac)
Scott Baker710ad052015-06-04 10:26:44 -0700548 elif "hpc_client" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700549 addresses["hpc_client"] = (ns.ip, ns.mac)
Scott Baker706bf972015-05-20 08:19:25 -0700550 return addresses
551
Scott Baker27fab492016-02-13 14:38:43 -0800552 # ------------------------------------------------------------------------
553 # The following IP addresses all come from the VM
554 # Note: They might not be useful for the VTN-vSG
555
Scott Bakera6a7e032015-05-20 08:25:29 -0700556 @property
557 def nat_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700558 return self.addresses.get("nat", (None,None) )[0]
559
560 @property
561 def nat_mac(self):
562 return self.addresses.get("nat", (None,None) )[1]
Scott Bakera6a7e032015-05-20 08:25:29 -0700563
564 @property
565 def lan_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700566 return self.addresses.get("lan", (None, None) )[0]
567
568 @property
569 def lan_mac(self):
570 return self.addresses.get("lan", (None, None) )[1]
Scott Bakera6a7e032015-05-20 08:25:29 -0700571
572 @property
573 def wan_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700574 return self.addresses.get("wan", (None, None) )[0]
Scott Bakera6a7e032015-05-20 08:25:29 -0700575
576 @property
Scott Baker3f4fe402015-06-05 12:08:34 -0700577 def wan_mac(self):
Andy Baviera75c1192015-09-09 18:06:30 -0400578 return self.addresses.get("wan", (None, None) )[1]
Scott Baker3f4fe402015-06-05 12:08:34 -0700579
Scott Baker27fab492016-02-13 14:38:43 -0800580 # end of VM IP address stubs
581 # ------------------------------------------------------------------------
582
583 @property
584 def wan_container_ip(self):
585 if CORD_USE_VTN:
586 # When using VTN, wan_container_ip is stored and maintained inside
587 # of the vSG object.
588 return self.get_attribute("wan_container_ip", self.default_attributes["wan_container_ip"])
589 else:
590 # When not using VTN, wan_container_ip is the same as wan_ip.
591 # XXX Is this broken for multiple-containers-per-VM?
592 return self.wan_ip
593
594 @wan_container_ip.setter
595 def wan_container_ip(self, value):
596 if CORD_USE_VTN:
597 self.set_attribute("wan_container_ip", value)
598 else:
599 raise Exception("wan_container_ip.setter called on non-VTN CORD")
600
Andy Bavier0fa3dd62015-10-29 14:01:47 -0400601 # Generate the MAC for the container interface connected to WAN
602 @property
603 def wan_container_mac(self):
Scott Baker27fab492016-02-13 14:38:43 -0800604 if not self.wan_container_ip:
605 return None
606 (a, b, c, d) = self.wan_container_ip.split('.')
Andy Bavier0fa3dd62015-10-29 14:01:47 -0400607 return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
608
Scott Baker3f4fe402015-06-05 12:08:34 -0700609 @property
Scott Bakera6a7e032015-05-20 08:25:29 -0700610 def private_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700611 return self.addresses.get("private", (None, None) )[0]
612
613 @property
614 def private_mac(self):
615 return self.addresses.get("private", (None, None) )[1]
Scott Bakera6a7e032015-05-20 08:25:29 -0700616
Scott Baker710ad052015-06-04 10:26:44 -0700617 @property
618 def hpc_client_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700619 return self.addresses.get("hpc_client", (None, None) )[0]
620
621 @property
622 def hpc_client_mac(self):
623 return self.addresses.get("hpc_client", (None, None) )[1]
Scott Baker710ad052015-06-04 10:26:44 -0700624
Scott Baker7bbdd2f2015-06-09 12:30:30 -0700625 @property
626 def is_synced(self):
627 return (self.enacted is not None) and (self.enacted >= self.updated)
628
629 @is_synced.setter
630 def is_synced(self, value):
631 pass
632
Scott Baker697fb0b2015-04-20 09:16:17 -0700633 def manage_vbng(self):
634 # Each vCPE object owns exactly one vBNG object
635
636 if self.deleted:
637 return
638
639 if self.vbng is None:
640 vbngServices = VBNGService.get_service_objects().all()
641 if not vbngServices:
Scott Bakerd921e1c2015-04-20 14:24:29 -0700642 raise XOSConfigurationError("No VBNG Services available")
Scott Baker697fb0b2015-04-20 09:16:17 -0700643
644 vbng = VBNGTenant(provider_service = vbngServices[0],
645 subscriber_tenant = self)
Scott Baker9c8a2c72015-05-05 17:49:46 -0700646 vbng.caller = self.creator
Scott Baker697fb0b2015-04-20 09:16:17 -0700647 vbng.save()
648
Scott Baker697fb0b2015-04-20 09:16:17 -0700649 def cleanup_vbng(self):
650 if self.vbng:
Scott Baker361c86c2015-07-06 14:38:02 -0700651 # print "XXX cleanup vnbg", self.vbng
Scott Baker697fb0b2015-04-20 09:16:17 -0700652 self.vbng.delete()
Scott Baker697fb0b2015-04-20 09:16:17 -0700653
Scott Baker5e76f802015-06-24 12:54:24 -0700654 def cleanup_orphans(self):
Scott Baker361c86c2015-07-06 14:38:02 -0700655 # ensure vCPE only has one vBNG
656 cur_vbng = self.vbng
657 for vbng in list(self.get_subscribed_tenants(VBNGTenant)):
658 if (not cur_vbng) or (vbng.id != cur_vbng.id):
659 # print "XXX clean up orphaned vbng", vbng
660 vbng.delete()
Scott Baker5e76f802015-06-24 12:54:24 -0700661
Tony Mack3de59e32015-08-19 11:58:18 -0400662 if self.orig_instance_id and (self.orig_instance_id != self.get_attribute("instance_id")):
663 instances=Instance.objects.filter(id=self.orig_instance_id)
664 if instances:
665 # print "XXX clean up orphaned instance", instances[0]
666 instances[0].delete()
Scott Baker5e76f802015-06-24 12:54:24 -0700667
Scott Baker87891952016-02-02 16:56:18 -0800668 def get_slice(self):
669 if not self.provider_service.slices.count():
670 raise XOSConfigurationError("The service has no slices")
671 slice = self.provider_service.slices.all()[0]
672 return slice
673
674 def find_instance_for_s_tag(self, s_tag):
675 #s_tags = STagBlock.objects.find(s_s_tag)
676 #if s_tags:
677 # return s_tags[0].instance
678
679 tags = Tag.objects.filter(name="s_tag", value=s_tag)
680 if tags:
681 return tags[0].content_object
682
683 return None
684
685 def find_or_make_instance_for_s_tag(self, s_tag):
686 instance = self.find_instance_for_s_tag(self.volt.s_tag)
687 if instance:
688 return instance
689
690 flavors = Flavor.objects.filter(name="m1.small")
691 if not flavors:
692 raise XOSConfigurationError("No m1.small flavor")
693
694 slice = self.provider_service.slices.all()[0]
695
696 if slice.default_isolation == "container_vm":
697 (node, parent) = ContainerVmScheduler(slice).pick()
698 else:
699 (node, parent) = LeastLoadedNodeScheduler(slice).pick()
700
701 instance = Instance(slice = slice,
702 node = node,
703 image = self.image,
704 creator = self.creator,
705 deployment = node.site_deployment.deployment,
706 flavor = flavors[0],
707 isolation = slice.default_isolation,
708 parent = parent)
Scott Bakerda5e8872016-02-12 16:17:04 -0800709
Scott Baker87891952016-02-02 16:56:18 -0800710 self.save_instance(instance)
711
712 return instance
713
714 def manage_container(self):
715 from core.models import Instance, Flavor
716
717 if self.deleted:
718 return
719
720 # For container or container_vm isolation, use what TenantWithCotnainer
721 # provides us
722 slice = self.get_slice()
723 if slice.default_isolation in ["container_vm", "container"]:
Scott Baker664bbe52016-02-11 11:08:42 -0800724 super(VSGTenant,self).manage_container()
Scott Baker87891952016-02-02 16:56:18 -0800725 return
726
727 if not self.volt:
728 raise XOSConfigurationError("This vCPE container has no volt")
729
730 instance = self.find_or_make_instance_for_s_tag(self.volt.s_tag)
731 self.instance = instance
732 super(TenantWithContainer, self).save()
733
734 def cleanup_container(self):
735 if self.get_slice().default_isolation in ["container_vm", "container"]:
Scott Baker664bbe52016-02-11 11:08:42 -0800736 super(VSGTenant,self).cleanup_container()
Scott Baker87891952016-02-02 16:56:18 -0800737
738 # To-do: cleanup unused instances
739 pass
740
Scott Baker5c8abf82015-05-20 20:45:11 -0700741 def manage_bbs_account(self):
742 if self.deleted:
743 return
744
Scott Bakerdb66fd32015-07-07 17:59:44 -0700745 if self.volt and self.volt.subscriber and self.volt.subscriber.url_filter_enable:
Scott Bakere2570112015-05-20 20:57:28 -0700746 if not self.bbs_account:
Scott Baker4698b922016-02-11 12:07:10 -0800747 # make sure we use the proxied VSGService object, not the generic Service object
748 vcpe_service = VSGService.objects.get(id=self.provider_service.id)
Scott Baker7c7b6312015-07-07 12:15:03 -0700749 self.bbs_account = vcpe_service.allocate_bbs_account()
Scott Baker664bbe52016-02-11 11:08:42 -0800750 super(VSGTenant, self).save()
Scott Bakere2570112015-05-20 20:57:28 -0700751 else:
752 if self.bbs_account:
753 self.bbs_account = None
Scott Baker664bbe52016-02-11 11:08:42 -0800754 super(VSGTenant, self).save()
Scott Baker5c8abf82015-05-20 20:45:11 -0700755
Scott Baker27fab492016-02-13 14:38:43 -0800756 def manage_wan_container_ip(self):
757 if CORD_USE_VTN:
758 if not self.wan_container_ip:
759 ap = AddressPool.objects.filter("public_addresses")
760 if not ap:
761 raise Exception("AddressPool 'public_addresses' does not exist. Please configure it.")
762 ap = ap[0]
763
764 addr = ap.get_address()
765 if not addr:
766 raise Exception("AddressPool 'public_addresses' has run out of addresses.")
767
768 self.wan_container_ip = addr
769
770 def cleanup_wan_container_ip(self):
771 if CORD_USE_VTN and self.wan_container_ip:
772 AddressPool.objects.filter("public_addresses")[0].put_address(self.wan_container_ip)
773 self.wan_container_ip = None
774
Scott Baker0a17ccc2015-11-16 22:53:49 -0800775 def find_or_make_port(self, instance, network, **kwargs):
776 port = Port.objects.filter(instance=instance, network=network)
777 if port:
778 port = port[0]
779 else:
780 port = Port(instance=instance, network=network, **kwargs)
781 port.save()
782 return port
783
Scott Bakerda5e8872016-02-12 16:17:04 -0800784 def get_lan_network(self, instance):
Scott Bakerda5e8872016-02-12 16:17:04 -0800785 slice = self.provider_service.slices.all()[0]
Scott Baker27fab492016-02-13 14:38:43 -0800786 if CORD_USE_VTN:
787 # there should only be one network private network, and its template should not be the management template
788 lan_networks = [x for x in slice.networks.all() if x.template.visibility=="private" and (not "management" in x.template.name)]
789 if len(lan_networks)>1:
790 raise XOSProgrammingError("The vSG slice should only have one non-management private network")
791 else:
792 lan_networks = [x for x in slice.networks.all() if "lan" in x.name]
Scott Bakerda5e8872016-02-12 16:17:04 -0800793 if not lan_networks:
794 raise XOSProgrammingError("No lan_network")
795 return lan_networks[0]
796
Scott Baker0a17ccc2015-11-16 22:53:49 -0800797 def save_instance(self, instance):
798 with transaction.atomic():
Andy Bavierde1707d2015-12-16 14:10:01 -0500799 instance.volumes = "/etc/dnsmasq.d,/etc/ufw"
Scott Baker664bbe52016-02-11 11:08:42 -0800800 super(VSGTenant, self).save_instance(instance)
Scott Baker0a17ccc2015-11-16 22:53:49 -0800801
802 if instance.isolation in ["container", "container_vm"]:
Scott Bakerda5e8872016-02-12 16:17:04 -0800803 lan_network = self.get_lan_network(instance)
804 port = self.find_or_make_port(instance, lan_network, ip="192.168.0.1", port_id="unmanaged")
Scott Baker0a17ccc2015-11-16 22:53:49 -0800805 port.set_parameter("c_tag", self.volt.c_tag)
806 port.set_parameter("s_tag", self.volt.s_tag)
807 port.set_parameter("device", "eth1")
Scott Bakerc7711632015-11-19 16:51:27 -0800808 port.set_parameter("bridge", "br-lan")
Scott Baker0a17ccc2015-11-16 22:53:49 -0800809
810 wan_networks = [x for x in instance.slice.networks.all() if "wan" in x.name]
811 if not wan_networks:
812 raise XOSProgrammingError("No wan_network")
813 port = self.find_or_make_port(instance, wan_networks[0])
814 port.set_parameter("next_hop", value="10.0.1.253") # FIX ME
815 port.set_parameter("device", "eth0")
816
Scott Bakerda5e8872016-02-12 16:17:04 -0800817 if instance.isolation in ["vm"]:
818 lan_network = self.get_lan_network(instance)
819 port = self.find_or_make_port(instance, lan_network)
820 port.set_parameter("c_tag", self.volt.c_tag)
821 port.set_parameter("s_tag", self.volt.s_tag)
822 port.set_parameter("neutron_port_name", "stag-%s" % self.volt.s_tag)
823 port.save()
824
Scott Baker87891952016-02-02 16:56:18 -0800825 # tag the instance with the s-tag, so we can easily find the
826 # instance later
827 if self.volt and self.volt.s_tag:
828 tags = Tag.objects.filter(name="s_tag", value=self.volt.s_tag)
829 if not tags:
830 tag = Tag(service=self.provider_service, content_object=instance, name="s_tag", value=self.volt.s_tag)
831 tag.save()
832
Scott Baker1b241612015-04-14 17:19:16 -0700833 def save(self, *args, **kwargs):
Scott Baker9c8a2c72015-05-05 17:49:46 -0700834 if not self.creator:
835 if not getattr(self, "caller", None):
836 # caller must be set when creating a vCPE since it creates a slice
Scott Baker664bbe52016-02-11 11:08:42 -0800837 raise XOSProgrammingError("VSGTenant's self.caller was not set")
Scott Baker9c8a2c72015-05-05 17:49:46 -0700838 self.creator = self.caller
839 if not self.creator:
Scott Baker664bbe52016-02-11 11:08:42 -0800840 raise XOSProgrammingError("VSGTenant's self.creator was not set")
Scott Baker9c8a2c72015-05-05 17:49:46 -0700841
Scott Baker664bbe52016-02-11 11:08:42 -0800842 super(VSGTenant, self).save(*args, **kwargs)
Scott Baker1027cd62015-07-08 19:01:56 -0700843 model_policy_vcpe(self.pk)
Scott Baker1b241612015-04-14 17:19:16 -0700844
845 def delete(self, *args, **kwargs):
Scott Baker697fb0b2015-04-20 09:16:17 -0700846 self.cleanup_vbng()
Scott Bakere458afd2015-09-09 16:36:06 -0700847 self.cleanup_container()
Scott Baker27fab492016-02-13 14:38:43 -0800848 self.cleanup_wan_container_ip()
Scott Baker664bbe52016-02-11 11:08:42 -0800849 super(VSGTenant, self).delete(*args, **kwargs)
Scott Baker1b241612015-04-14 17:19:16 -0700850
Scott Baker1027cd62015-07-08 19:01:56 -0700851def model_policy_vcpe(pk):
852 # TODO: this should be made in to a real model_policy
853 with transaction.atomic():
Scott Baker664bbe52016-02-11 11:08:42 -0800854 vcpe = VSGTenant.objects.select_for_update().filter(pk=pk)
Scott Baker1027cd62015-07-08 19:01:56 -0700855 if not vcpe:
856 return
857 vcpe = vcpe[0]
Scott Baker27fab492016-02-13 14:38:43 -0800858 vcpe.manage_wan_container_ip()
Scott Bakere458afd2015-09-09 16:36:06 -0700859 vcpe.manage_container()
Scott Baker1027cd62015-07-08 19:01:56 -0700860 vcpe.manage_vbng()
861 vcpe.manage_bbs_account()
862 vcpe.cleanup_orphans()
863
Scott Baker697fb0b2015-04-20 09:16:17 -0700864#----------------------------------------------------------------------------
865# vBNG
866#----------------------------------------------------------------------------
867
868class VBNGService(Service):
Scott Baker361c86c2015-07-06 14:38:02 -0700869 KIND = VBNG_KIND
Scott Baker697fb0b2015-04-20 09:16:17 -0700870
Scott Bakere337e512015-10-20 22:12:51 -0700871 simple_attributes = ( ("vbng_url", ""), ) # "http://10.0.3.136:8181/onos/virtualbng/"
Scott Baker3d7ec1a2015-07-21 18:24:21 -0700872
Scott Baker697fb0b2015-04-20 09:16:17 -0700873 class Meta:
874 app_label = "cord"
875 verbose_name = "vBNG Service"
876 proxy = True
877
Scott Baker3d7ec1a2015-07-21 18:24:21 -0700878VBNGService.setup_simple_attributes()
879
Scott Baker697fb0b2015-04-20 09:16:17 -0700880class VBNGTenant(Tenant):
881 class Meta:
882 proxy = True
883
Scott Baker361c86c2015-07-06 14:38:02 -0700884 KIND = VBNG_KIND
Scott Baker697fb0b2015-04-20 09:16:17 -0700885
Scott Baker99ff5512015-06-02 14:34:04 -0700886 default_attributes = {"routeable_subnet": "",
Scott Baker790613c2015-06-08 19:09:53 -0700887 "mapped_ip": "",
888 "mapped_mac": "",
889 "mapped_hostname": ""}
Scott Baker697fb0b2015-04-20 09:16:17 -0700890
891 @property
892 def routeable_subnet(self):
893 return self.get_attribute("routeable_subnet", self.default_attributes["routeable_subnet"])
894
895 @routeable_subnet.setter
896 def routeable_subnet(self, value):
897 self.set_attribute("routeable_subnet", value)
Scott Baker99ff5512015-06-02 14:34:04 -0700898
899 @property
900 def mapped_ip(self):
901 return self.get_attribute("mapped_ip", self.default_attributes["mapped_ip"])
902
903 @mapped_ip.setter
904 def mapped_ip(self, value):
905 self.set_attribute("mapped_ip", value)
Scott Baker790613c2015-06-08 19:09:53 -0700906
907 @property
908 def mapped_mac(self):
909 return self.get_attribute("mapped_mac", self.default_attributes["mapped_mac"])
910
911 @mapped_mac.setter
912 def mapped_mac(self, value):
913 self.set_attribute("mapped_mac", value)
914
915 @property
916 def mapped_hostname(self):
917 return self.get_attribute("mapped_hostname", self.default_attributes["mapped_hostname"])
918
919 @mapped_hostname.setter
920 def mapped_hostname(self, value):
921 self.set_attribute("mapped_hostname", value)