blob: 37ee78e51ba2d228ab3a9df4ee282266568d6422 [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 Baker4d3cc752016-02-18 06:43:02 -0800402 ("backend_network_label", "hpc_client"),
403 ("wan_container_gateway_ip", ""),
404 ("wan_container_gateway_mac", ""),
Scott Bakerda3122a2016-02-23 14:58:49 -0800405 ("wan_container_netbits", "24"),
406 ("dns_servers", "8.8.8.8") )
Scott Baker7ba272e2015-07-13 14:30:25 -0700407
408 def __init__(self, *args, **kwargs):
Scott Baker4698b922016-02-11 12:07:10 -0800409 super(VSGService, self).__init__(*args, **kwargs)
Scott Baker7ba272e2015-07-13 14:30:25 -0700410
Scott Baker39b0d892015-04-15 20:59:15 -0700411 class Meta:
412 app_label = "cord"
Scott Baker060f7352016-02-11 16:56:34 -0800413 verbose_name = "vSG Service"
Scott Baker39b0d892015-04-15 20:59:15 -0700414 proxy = True
Scott Baker1b241612015-04-14 17:19:16 -0700415
Scott Baker5c8abf82015-05-20 20:45:11 -0700416 def allocate_bbs_account(self):
Scott Baker664bbe52016-02-11 11:08:42 -0800417 vcpes = VSGTenant.get_tenant_objects().all()
Scott Baker5c8abf82015-05-20 20:45:11 -0700418 bbs_accounts = [vcpe.bbs_account for vcpe in vcpes]
419
420 # There's a bit of a race here; some other user could be trying to
421 # allocate a bbs_account at the same time we are.
422
Scott Bakerf4f61ca2015-06-09 12:03:56 -0700423 for i in range(2,21):
Scott Baker5c8abf82015-05-20 20:45:11 -0700424 account_name = "bbs%02d@onlab.us" % i
425 if (account_name not in bbs_accounts):
426 return account_name
427
428 raise XOSConfigurationError("We've run out of available broadbandshield accounts. Delete some vcpe and try again.")
429
Scott Baker3d2493b2015-07-15 17:42:43 -0700430 @property
431 def bbs_slice(self):
432 bbs_slice_id=self.get_attribute("bbs_slice_id")
433 if not bbs_slice_id:
434 return None
435 bbs_slices=Slice.objects.filter(id=bbs_slice_id)
436 if not bbs_slices:
437 return None
438 return bbs_slices[0]
439
440 @bbs_slice.setter
441 def bbs_slice(self, value):
442 if value:
443 value = value.id
444 self.set_attribute("bbs_slice_id", value)
445
Scott Baker4698b922016-02-11 12:07:10 -0800446VSGService.setup_simple_attributes()
Scott Baker7ba272e2015-07-13 14:30:25 -0700447
Scott Baker87891952016-02-02 16:56:18 -0800448#class STagBlock(PlCoreBase):
449# instance = models.ForeignKey(Instance, related_name="s_tags")
450# s_tag = models.CharField(null=false, blank=false, unique=true, max_length=10)
451# #c_tags = models.TextField(null=true, blank=true)
452#
453# def __unicode__(self): return u'%s' % (self.s_tag)
Scott Baker7ba272e2015-07-13 14:30:25 -0700454
Scott Baker664bbe52016-02-11 11:08:42 -0800455class VSGTenant(TenantWithContainer):
Scott Baker1b241612015-04-14 17:19:16 -0700456 class Meta:
457 proxy = True
458
Scott Baker361c86c2015-07-06 14:38:02 -0700459 KIND = VCPE_KIND
Scott Baker39b0d892015-04-15 20:59:15 -0700460
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700461 sync_attributes = ("nat_ip", "nat_mac",
462 "lan_ip", "lan_mac",
Scott Baker27fab492016-02-13 14:38:43 -0800463 "wan_ip", "wan_mac",
464 "wan_container_ip", "wan_container_mac",
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700465 "private_ip", "private_mac",
466 "hpc_client_ip", "hpc_client_mac")
Scott Baker9c8a2c72015-05-05 17:49:46 -0700467
Tony Mack3de59e32015-08-19 11:58:18 -0400468 default_attributes = {"instance_id": None,
Scott Bakerf7f7e572015-11-02 20:55:07 -0800469 "container_id": None,
Scott Baker5c8abf82015-05-20 20:45:11 -0700470 "users": [],
Scott Bakerd40a42d2015-06-09 12:22:29 -0700471 "bbs_account": None,
Scott Baker27fab492016-02-13 14:38:43 -0800472 "last_ansible_hash": None,
473 "wan_container_ip": None}
Scott Baker1b241612015-04-14 17:19:16 -0700474
Scott Baker697fb0b2015-04-20 09:16:17 -0700475 def __init__(self, *args, **kwargs):
Scott Baker664bbe52016-02-11 11:08:42 -0800476 super(VSGTenant, self).__init__(*args, **kwargs)
Scott Baker697fb0b2015-04-20 09:16:17 -0700477 self.cached_vbng=None
Scott Baker9c8a2c72015-05-05 17:49:46 -0700478
479 @property
Scott Baker697fb0b2015-04-20 09:16:17 -0700480 def vbng(self):
Scott Baker361c86c2015-07-06 14:38:02 -0700481 vbng = self.get_newest_subscribed_tenant(VBNGTenant)
482 if not vbng:
483 return None
484
485 # always return the same object when possible
486 if (self.cached_vbng) and (self.cached_vbng.id == vbng.id):
Scott Baker697fb0b2015-04-20 09:16:17 -0700487 return self.cached_vbng
Scott Baker361c86c2015-07-06 14:38:02 -0700488
Scott Baker9c8a2c72015-05-05 17:49:46 -0700489 vbng.caller = self.creator
Scott Baker697fb0b2015-04-20 09:16:17 -0700490 self.cached_vbng = vbng
491 return vbng
492
493 @vbng.setter
494 def vbng(self, value):
Scott Baker361c86c2015-07-06 14:38:02 -0700495 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 -0700496
Scott Bakercd349182015-07-06 17:21:21 -0700497 @property
498 def volt(self):
499 if not self.subscriber_tenant:
500 return None
501 volts = VOLTTenant.objects.filter(id=self.subscriber_tenant.id)
502 if not volts:
503 return None
504 return volts[0]
505
Scott Bakerf08823d2015-07-06 16:50:30 -0700506 @property
507 def bbs_account(self):
508 return self.get_attribute("bbs_account", self.default_attributes["bbs_account"])
509
510 @bbs_account.setter
511 def bbs_account(self, value):
512 return self.set_attribute("bbs_account", value)
513
514 @property
515 def last_ansible_hash(self):
516 return self.get_attribute("last_ansible_hash", self.default_attributes["last_ansible_hash"])
517
518 @last_ansible_hash.setter
519 def last_ansible_hash(self, value):
520 return self.set_attribute("last_ansible_hash", value)
521
522 @property
523 def ssh_command(self):
Tony Mack3de59e32015-08-19 11:58:18 -0400524 if self.instance:
525 return self.instance.get_ssh_command()
Scott Bakerf08823d2015-07-06 16:50:30 -0700526 else:
Tony Mack3de59e32015-08-19 11:58:18 -0400527 return "no-instance"
Scott Bakerf08823d2015-07-06 16:50:30 -0700528
529 @ssh_command.setter
530 def ssh_command(self, value):
531 pass
532
Scott Baker706bf972015-05-20 08:19:25 -0700533 @property
534 def addresses(self):
Scott Bakerf7f7e572015-11-02 20:55:07 -0800535 if self.instance:
536 ports = self.instance.ports.all()
537 elif self.container:
538 ports = self.container.ports.all()
539 else:
Scott Baker706bf972015-05-20 08:19:25 -0700540 return {}
541
542 addresses = {}
Scott Bakerf7f7e572015-11-02 20:55:07 -0800543 for ns in ports:
Scott Baker706bf972015-05-20 08:19:25 -0700544 if "lan" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700545 addresses["lan"] = (ns.ip, ns.mac)
Scott Baker706bf972015-05-20 08:19:25 -0700546 elif "wan" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700547 addresses["wan"] = (ns.ip, ns.mac)
Scott Baker706bf972015-05-20 08:19:25 -0700548 elif "private" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700549 addresses["private"] = (ns.ip, ns.mac)
Scott Baker706bf972015-05-20 08:19:25 -0700550 elif "nat" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700551 addresses["nat"] = (ns.ip, ns.mac)
Scott Baker710ad052015-06-04 10:26:44 -0700552 elif "hpc_client" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700553 addresses["hpc_client"] = (ns.ip, ns.mac)
Scott Baker706bf972015-05-20 08:19:25 -0700554 return addresses
555
Scott Baker27fab492016-02-13 14:38:43 -0800556 # ------------------------------------------------------------------------
557 # The following IP addresses all come from the VM
558 # Note: They might not be useful for the VTN-vSG
559
Scott Bakera6a7e032015-05-20 08:25:29 -0700560 @property
561 def nat_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700562 return self.addresses.get("nat", (None,None) )[0]
563
564 @property
565 def nat_mac(self):
566 return self.addresses.get("nat", (None,None) )[1]
Scott Bakera6a7e032015-05-20 08:25:29 -0700567
568 @property
569 def lan_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700570 return self.addresses.get("lan", (None, None) )[0]
571
572 @property
573 def lan_mac(self):
574 return self.addresses.get("lan", (None, None) )[1]
Scott Bakera6a7e032015-05-20 08:25:29 -0700575
576 @property
577 def wan_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700578 return self.addresses.get("wan", (None, None) )[0]
Scott Bakera6a7e032015-05-20 08:25:29 -0700579
580 @property
Scott Baker3f4fe402015-06-05 12:08:34 -0700581 def wan_mac(self):
Andy Baviera75c1192015-09-09 18:06:30 -0400582 return self.addresses.get("wan", (None, None) )[1]
Scott Baker3f4fe402015-06-05 12:08:34 -0700583
Scott Baker27fab492016-02-13 14:38:43 -0800584 # end of VM IP address stubs
585 # ------------------------------------------------------------------------
586
587 @property
588 def wan_container_ip(self):
589 if CORD_USE_VTN:
590 # When using VTN, wan_container_ip is stored and maintained inside
591 # of the vSG object.
592 return self.get_attribute("wan_container_ip", self.default_attributes["wan_container_ip"])
593 else:
594 # When not using VTN, wan_container_ip is the same as wan_ip.
595 # XXX Is this broken for multiple-containers-per-VM?
596 return self.wan_ip
597
598 @wan_container_ip.setter
599 def wan_container_ip(self, value):
600 if CORD_USE_VTN:
601 self.set_attribute("wan_container_ip", value)
602 else:
603 raise Exception("wan_container_ip.setter called on non-VTN CORD")
604
Scott Baker7aa5e992016-02-18 15:18:47 -0800605 def ip_to_mac(self, ip):
606 (a, b, c, d) = ip.split('.')
607 return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
608
Andy Bavier0fa3dd62015-10-29 14:01:47 -0400609 # Generate the MAC for the container interface connected to WAN
610 @property
611 def wan_container_mac(self):
Scott Baker27fab492016-02-13 14:38:43 -0800612 if not self.wan_container_ip:
613 return None
Scott Baker7aa5e992016-02-18 15:18:47 -0800614 return self.ip_to_mac(self.wan_container_ip)
Andy Bavier0fa3dd62015-10-29 14:01:47 -0400615
Scott Baker3f4fe402015-06-05 12:08:34 -0700616 @property
Scott Bakera6a7e032015-05-20 08:25:29 -0700617 def private_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700618 return self.addresses.get("private", (None, None) )[0]
619
620 @property
621 def private_mac(self):
622 return self.addresses.get("private", (None, None) )[1]
Scott Bakera6a7e032015-05-20 08:25:29 -0700623
Scott Baker710ad052015-06-04 10:26:44 -0700624 @property
625 def hpc_client_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700626 return self.addresses.get("hpc_client", (None, None) )[0]
627
628 @property
629 def hpc_client_mac(self):
630 return self.addresses.get("hpc_client", (None, None) )[1]
Scott Baker710ad052015-06-04 10:26:44 -0700631
Scott Baker7bbdd2f2015-06-09 12:30:30 -0700632 @property
633 def is_synced(self):
634 return (self.enacted is not None) and (self.enacted >= self.updated)
635
636 @is_synced.setter
637 def is_synced(self, value):
638 pass
639
Scott Baker697fb0b2015-04-20 09:16:17 -0700640 def manage_vbng(self):
641 # Each vCPE object owns exactly one vBNG object
642
643 if self.deleted:
644 return
645
646 if self.vbng is None:
647 vbngServices = VBNGService.get_service_objects().all()
648 if not vbngServices:
Scott Bakerd921e1c2015-04-20 14:24:29 -0700649 raise XOSConfigurationError("No VBNG Services available")
Scott Baker697fb0b2015-04-20 09:16:17 -0700650
651 vbng = VBNGTenant(provider_service = vbngServices[0],
652 subscriber_tenant = self)
Scott Baker9c8a2c72015-05-05 17:49:46 -0700653 vbng.caller = self.creator
Scott Baker697fb0b2015-04-20 09:16:17 -0700654 vbng.save()
655
Scott Baker697fb0b2015-04-20 09:16:17 -0700656 def cleanup_vbng(self):
657 if self.vbng:
Scott Baker361c86c2015-07-06 14:38:02 -0700658 # print "XXX cleanup vnbg", self.vbng
Scott Baker697fb0b2015-04-20 09:16:17 -0700659 self.vbng.delete()
Scott Baker697fb0b2015-04-20 09:16:17 -0700660
Scott Baker5e76f802015-06-24 12:54:24 -0700661 def cleanup_orphans(self):
Scott Baker361c86c2015-07-06 14:38:02 -0700662 # ensure vCPE only has one vBNG
663 cur_vbng = self.vbng
664 for vbng in list(self.get_subscribed_tenants(VBNGTenant)):
665 if (not cur_vbng) or (vbng.id != cur_vbng.id):
666 # print "XXX clean up orphaned vbng", vbng
667 vbng.delete()
Scott Baker5e76f802015-06-24 12:54:24 -0700668
Tony Mack3de59e32015-08-19 11:58:18 -0400669 if self.orig_instance_id and (self.orig_instance_id != self.get_attribute("instance_id")):
670 instances=Instance.objects.filter(id=self.orig_instance_id)
671 if instances:
672 # print "XXX clean up orphaned instance", instances[0]
673 instances[0].delete()
Scott Baker5e76f802015-06-24 12:54:24 -0700674
Scott Baker87891952016-02-02 16:56:18 -0800675 def get_slice(self):
676 if not self.provider_service.slices.count():
677 raise XOSConfigurationError("The service has no slices")
678 slice = self.provider_service.slices.all()[0]
679 return slice
680
681 def find_instance_for_s_tag(self, s_tag):
682 #s_tags = STagBlock.objects.find(s_s_tag)
683 #if s_tags:
684 # return s_tags[0].instance
685
686 tags = Tag.objects.filter(name="s_tag", value=s_tag)
687 if tags:
688 return tags[0].content_object
689
690 return None
691
692 def find_or_make_instance_for_s_tag(self, s_tag):
693 instance = self.find_instance_for_s_tag(self.volt.s_tag)
694 if instance:
695 return instance
696
697 flavors = Flavor.objects.filter(name="m1.small")
698 if not flavors:
699 raise XOSConfigurationError("No m1.small flavor")
700
701 slice = self.provider_service.slices.all()[0]
702
703 if slice.default_isolation == "container_vm":
704 (node, parent) = ContainerVmScheduler(slice).pick()
705 else:
706 (node, parent) = LeastLoadedNodeScheduler(slice).pick()
707
708 instance = Instance(slice = slice,
709 node = node,
710 image = self.image,
711 creator = self.creator,
712 deployment = node.site_deployment.deployment,
713 flavor = flavors[0],
714 isolation = slice.default_isolation,
715 parent = parent)
Scott Bakerda5e8872016-02-12 16:17:04 -0800716
Scott Baker87891952016-02-02 16:56:18 -0800717 self.save_instance(instance)
718
719 return instance
720
721 def manage_container(self):
722 from core.models import Instance, Flavor
723
724 if self.deleted:
725 return
726
727 # For container or container_vm isolation, use what TenantWithCotnainer
728 # provides us
729 slice = self.get_slice()
730 if slice.default_isolation in ["container_vm", "container"]:
Scott Baker664bbe52016-02-11 11:08:42 -0800731 super(VSGTenant,self).manage_container()
Scott Baker87891952016-02-02 16:56:18 -0800732 return
733
734 if not self.volt:
735 raise XOSConfigurationError("This vCPE container has no volt")
736
Scott Baker4d3cc752016-02-18 06:43:02 -0800737 if self.instance:
738 # We're good.
739 return
740
Scott Baker87891952016-02-02 16:56:18 -0800741 instance = self.find_or_make_instance_for_s_tag(self.volt.s_tag)
742 self.instance = instance
743 super(TenantWithContainer, self).save()
744
745 def cleanup_container(self):
746 if self.get_slice().default_isolation in ["container_vm", "container"]:
Scott Baker664bbe52016-02-11 11:08:42 -0800747 super(VSGTenant,self).cleanup_container()
Scott Baker87891952016-02-02 16:56:18 -0800748
749 # To-do: cleanup unused instances
750 pass
751
Scott Baker5c8abf82015-05-20 20:45:11 -0700752 def manage_bbs_account(self):
753 if self.deleted:
754 return
755
Scott Bakerdb66fd32015-07-07 17:59:44 -0700756 if self.volt and self.volt.subscriber and self.volt.subscriber.url_filter_enable:
Scott Bakere2570112015-05-20 20:57:28 -0700757 if not self.bbs_account:
Scott Baker4698b922016-02-11 12:07:10 -0800758 # make sure we use the proxied VSGService object, not the generic Service object
759 vcpe_service = VSGService.objects.get(id=self.provider_service.id)
Scott Baker7c7b6312015-07-07 12:15:03 -0700760 self.bbs_account = vcpe_service.allocate_bbs_account()
Scott Baker664bbe52016-02-11 11:08:42 -0800761 super(VSGTenant, self).save()
Scott Bakere2570112015-05-20 20:57:28 -0700762 else:
763 if self.bbs_account:
764 self.bbs_account = None
Scott Baker664bbe52016-02-11 11:08:42 -0800765 super(VSGTenant, self).save()
Scott Baker5c8abf82015-05-20 20:45:11 -0700766
Scott Baker7aa5e992016-02-18 15:18:47 -0800767 def get_wan_address_from_pool(self):
768 ap = AddressPool.objects.filter(name="public_addresses")
769 if not ap:
770 raise Exception("AddressPool 'public_addresses' does not exist. Please configure it.")
771 ap = ap[0]
772
773 addr = ap.get_address()
774 if not addr:
775 raise Exception("AddressPool 'public_addresses' has run out of addresses.")
776 return addr
777
778 def put_wan_address_to_pool(self, addr):
779 AddressPool.objects.filter(name="public_addresses")[0].put_address(addr)
780
Scott Baker27fab492016-02-13 14:38:43 -0800781 def manage_wan_container_ip(self):
782 if CORD_USE_VTN:
783 if not self.wan_container_ip:
Scott Baker7aa5e992016-02-18 15:18:47 -0800784 addr = self.get_wan_address_from_pool()
Scott Baker27fab492016-02-13 14:38:43 -0800785
786 self.wan_container_ip = addr
Scott Baker4d3cc752016-02-18 06:43:02 -0800787 super(TenantWithContainer, self).save()
Scott Baker27fab492016-02-13 14:38:43 -0800788
789 def cleanup_wan_container_ip(self):
790 if CORD_USE_VTN and self.wan_container_ip:
Scott Baker7aa5e992016-02-18 15:18:47 -0800791 self.put_wan_address_to_pool(self.wan_container_ip)
Scott Baker27fab492016-02-13 14:38:43 -0800792 self.wan_container_ip = None
793
Scott Baker0a17ccc2015-11-16 22:53:49 -0800794 def find_or_make_port(self, instance, network, **kwargs):
795 port = Port.objects.filter(instance=instance, network=network)
796 if port:
797 port = port[0]
798 else:
799 port = Port(instance=instance, network=network, **kwargs)
800 port.save()
801 return port
802
Scott Bakerda5e8872016-02-12 16:17:04 -0800803 def get_lan_network(self, instance):
Scott Bakerda5e8872016-02-12 16:17:04 -0800804 slice = self.provider_service.slices.all()[0]
Scott Baker27fab492016-02-13 14:38:43 -0800805 if CORD_USE_VTN:
806 # there should only be one network private network, and its template should not be the management template
807 lan_networks = [x for x in slice.networks.all() if x.template.visibility=="private" and (not "management" in x.template.name)]
808 if len(lan_networks)>1:
809 raise XOSProgrammingError("The vSG slice should only have one non-management private network")
810 else:
811 lan_networks = [x for x in slice.networks.all() if "lan" in x.name]
Scott Bakerda5e8872016-02-12 16:17:04 -0800812 if not lan_networks:
813 raise XOSProgrammingError("No lan_network")
814 return lan_networks[0]
815
Scott Baker0a17ccc2015-11-16 22:53:49 -0800816 def save_instance(self, instance):
817 with transaction.atomic():
Andy Bavierde1707d2015-12-16 14:10:01 -0500818 instance.volumes = "/etc/dnsmasq.d,/etc/ufw"
Scott Baker664bbe52016-02-11 11:08:42 -0800819 super(VSGTenant, self).save_instance(instance)
Scott Baker0a17ccc2015-11-16 22:53:49 -0800820
821 if instance.isolation in ["container", "container_vm"]:
Scott Bakerda5e8872016-02-12 16:17:04 -0800822 lan_network = self.get_lan_network(instance)
823 port = self.find_or_make_port(instance, lan_network, ip="192.168.0.1", port_id="unmanaged")
Scott Baker0a17ccc2015-11-16 22:53:49 -0800824 port.set_parameter("c_tag", self.volt.c_tag)
825 port.set_parameter("s_tag", self.volt.s_tag)
826 port.set_parameter("device", "eth1")
Scott Bakerc7711632015-11-19 16:51:27 -0800827 port.set_parameter("bridge", "br-lan")
Scott Baker0a17ccc2015-11-16 22:53:49 -0800828
829 wan_networks = [x for x in instance.slice.networks.all() if "wan" in x.name]
830 if not wan_networks:
831 raise XOSProgrammingError("No wan_network")
832 port = self.find_or_make_port(instance, wan_networks[0])
833 port.set_parameter("next_hop", value="10.0.1.253") # FIX ME
834 port.set_parameter("device", "eth0")
835
Scott Bakerda5e8872016-02-12 16:17:04 -0800836 if instance.isolation in ["vm"]:
837 lan_network = self.get_lan_network(instance)
838 port = self.find_or_make_port(instance, lan_network)
839 port.set_parameter("c_tag", self.volt.c_tag)
840 port.set_parameter("s_tag", self.volt.s_tag)
841 port.set_parameter("neutron_port_name", "stag-%s" % self.volt.s_tag)
842 port.save()
843
Scott Baker87891952016-02-02 16:56:18 -0800844 # tag the instance with the s-tag, so we can easily find the
845 # instance later
846 if self.volt and self.volt.s_tag:
847 tags = Tag.objects.filter(name="s_tag", value=self.volt.s_tag)
848 if not tags:
849 tag = Tag(service=self.provider_service, content_object=instance, name="s_tag", value=self.volt.s_tag)
850 tag.save()
851
Scott Baker7aa5e992016-02-18 15:18:47 -0800852 # VTN-CORD needs a WAN address for the VM, so that the VM can
853 # be configured.
854 if CORD_USE_VTN:
855 tags = Tag.select_by_content_object(instance).filter(name="vm_wan_addr")
856 if not tags:
857 address = self.get_wan_address_from_pool()
858 tag = Tag(service=self.provider_service, content_object=instance, name="vm_wan_addr", value="%s,%s,%s" % ("public_addresses", address, self.ip_to_mac(address)))
859 tag.save()
860
Scott Baker1b241612015-04-14 17:19:16 -0700861 def save(self, *args, **kwargs):
Scott Baker9c8a2c72015-05-05 17:49:46 -0700862 if not self.creator:
863 if not getattr(self, "caller", None):
864 # caller must be set when creating a vCPE since it creates a slice
Scott Baker664bbe52016-02-11 11:08:42 -0800865 raise XOSProgrammingError("VSGTenant's self.caller was not set")
Scott Baker9c8a2c72015-05-05 17:49:46 -0700866 self.creator = self.caller
867 if not self.creator:
Scott Baker664bbe52016-02-11 11:08:42 -0800868 raise XOSProgrammingError("VSGTenant's self.creator was not set")
Scott Baker9c8a2c72015-05-05 17:49:46 -0700869
Scott Baker664bbe52016-02-11 11:08:42 -0800870 super(VSGTenant, self).save(*args, **kwargs)
Scott Baker1027cd62015-07-08 19:01:56 -0700871 model_policy_vcpe(self.pk)
Scott Baker1b241612015-04-14 17:19:16 -0700872
873 def delete(self, *args, **kwargs):
Scott Baker697fb0b2015-04-20 09:16:17 -0700874 self.cleanup_vbng()
Scott Bakere458afd2015-09-09 16:36:06 -0700875 self.cleanup_container()
Scott Baker27fab492016-02-13 14:38:43 -0800876 self.cleanup_wan_container_ip()
Scott Baker664bbe52016-02-11 11:08:42 -0800877 super(VSGTenant, self).delete(*args, **kwargs)
Scott Baker1b241612015-04-14 17:19:16 -0700878
Scott Baker1027cd62015-07-08 19:01:56 -0700879def model_policy_vcpe(pk):
880 # TODO: this should be made in to a real model_policy
881 with transaction.atomic():
Scott Baker664bbe52016-02-11 11:08:42 -0800882 vcpe = VSGTenant.objects.select_for_update().filter(pk=pk)
Scott Baker1027cd62015-07-08 19:01:56 -0700883 if not vcpe:
884 return
885 vcpe = vcpe[0]
Scott Baker27fab492016-02-13 14:38:43 -0800886 vcpe.manage_wan_container_ip()
Scott Bakere458afd2015-09-09 16:36:06 -0700887 vcpe.manage_container()
Scott Baker1027cd62015-07-08 19:01:56 -0700888 vcpe.manage_vbng()
889 vcpe.manage_bbs_account()
890 vcpe.cleanup_orphans()
891
Scott Baker697fb0b2015-04-20 09:16:17 -0700892#----------------------------------------------------------------------------
893# vBNG
894#----------------------------------------------------------------------------
895
896class VBNGService(Service):
Scott Baker361c86c2015-07-06 14:38:02 -0700897 KIND = VBNG_KIND
Scott Baker697fb0b2015-04-20 09:16:17 -0700898
Scott Bakere337e512015-10-20 22:12:51 -0700899 simple_attributes = ( ("vbng_url", ""), ) # "http://10.0.3.136:8181/onos/virtualbng/"
Scott Baker3d7ec1a2015-07-21 18:24:21 -0700900
Scott Baker697fb0b2015-04-20 09:16:17 -0700901 class Meta:
902 app_label = "cord"
903 verbose_name = "vBNG Service"
904 proxy = True
905
Scott Baker3d7ec1a2015-07-21 18:24:21 -0700906VBNGService.setup_simple_attributes()
907
Scott Baker697fb0b2015-04-20 09:16:17 -0700908class VBNGTenant(Tenant):
909 class Meta:
910 proxy = True
911
Scott Baker361c86c2015-07-06 14:38:02 -0700912 KIND = VBNG_KIND
Scott Baker697fb0b2015-04-20 09:16:17 -0700913
Scott Baker99ff5512015-06-02 14:34:04 -0700914 default_attributes = {"routeable_subnet": "",
Scott Baker790613c2015-06-08 19:09:53 -0700915 "mapped_ip": "",
916 "mapped_mac": "",
917 "mapped_hostname": ""}
Scott Baker697fb0b2015-04-20 09:16:17 -0700918
919 @property
920 def routeable_subnet(self):
921 return self.get_attribute("routeable_subnet", self.default_attributes["routeable_subnet"])
922
923 @routeable_subnet.setter
924 def routeable_subnet(self, value):
925 self.set_attribute("routeable_subnet", value)
Scott Baker99ff5512015-06-02 14:34:04 -0700926
927 @property
928 def mapped_ip(self):
929 return self.get_attribute("mapped_ip", self.default_attributes["mapped_ip"])
930
931 @mapped_ip.setter
932 def mapped_ip(self, value):
933 self.set_attribute("mapped_ip", value)
Scott Baker790613c2015-06-08 19:09:53 -0700934
935 @property
936 def mapped_mac(self):
937 return self.get_attribute("mapped_mac", self.default_attributes["mapped_mac"])
938
939 @mapped_mac.setter
940 def mapped_mac(self, value):
941 self.set_attribute("mapped_mac", value)
942
943 @property
944 def mapped_hostname(self):
945 return self.get_attribute("mapped_hostname", self.default_attributes["mapped_hostname"])
946
947 @mapped_hostname.setter
948 def mapped_hostname(self, value):
949 self.set_attribute("mapped_hostname", value)