blob: 9ff2dcbc6c7079be2cb5f226ccf4d06e060515b2 [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
Scott Baker95e38ae2016-03-01 20:14:35 -080035 status_choices = (("enabled", "Enabled"),
36 ("suspended", "Suspended"),
37 ("delinquent", "Delinquent"),
38 ("copyrightviolation", "Copyright Violation"))
39
40 # 'simple_attributes' will be expanded into properties and setters that
41 # store the attribute using self.set_attribute / self.get_attribute.
42
43 simple_attributes = ( ("firewall_enable", False),
44 ("firewall_rules", "accept all anywhere anywhere"),
45 ("url_filter_enable", False),
46 ("url_filter_rules", "allow all"),
47 ("url_filter_level", "PG"),
48 ("cdn_enable", False),
49 ("users", []),
50 ("is_demo_user", False),
51
Matteod86563d2016-03-02 09:33:20 -080052 ("uplink_speed", 1000000000), # 1 gigabit, a reasonable default?
53 ("downlink_speed", 1000000000),
Scott Baker95e38ae2016-03-01 20:14:35 -080054 ("enable_uverse", True) )
55
56 default_attributes = {"status": "enabled"}
Scott Bakerf08823d2015-07-06 16:50:30 -070057
Scott Baker7c7b6312015-07-07 12:15:03 -070058 sync_attributes = ("firewall_enable",
59 "firewall_rules",
60 "url_filter_enable",
61 "url_filter_rules",
Scott Bakere059cf72016-03-01 20:44:40 -080062 "cdn_enable",
63 "uplink_speed",
64 "downlink_speed",
65 "enable_uverse",
66 "status")
Scott Baker7c7b6312015-07-07 12:15:03 -070067
Scott Bakerf08823d2015-07-06 16:50:30 -070068 def __init__(self, *args, **kwargs):
69 super(CordSubscriberRoot, self).__init__(*args, **kwargs)
70 self.cached_volt = None
Scott Baker7c7b6312015-07-07 12:15:03 -070071 self._initial_url_filter_enable = self.url_filter_enable
Scott Bakerf08823d2015-07-06 16:50:30 -070072
73 @property
74 def volt(self):
75 volt = self.get_newest_subscribed_tenant(VOLTTenant)
76 if not volt:
77 return None
78
79 # always return the same object when possible
80 if (self.cached_volt) and (self.cached_volt.id == volt.id):
Scott Bakerdb66fd32015-07-07 17:59:44 -070081 return self.cached_volt
Scott Bakerf08823d2015-07-06 16:50:30 -070082
Scott Bakercd349182015-07-06 17:21:21 -070083 #volt.caller = self.creator
Scott Bakerf08823d2015-07-06 16:50:30 -070084 self.cached_volt = volt
85 return volt
86
87 @property
Scott Baker95e38ae2016-03-01 20:14:35 -080088 def status(self):
89 return self.get_attribute("status", self.default_attributes["status"])
Scott Bakerf08823d2015-07-06 16:50:30 -070090
Scott Baker95e38ae2016-03-01 20:14:35 -080091 @status.setter
92 def status(self, value):
93 if not value in [x[0] for x in self.status_choices]:
94 raise Exception("invalid status %s" % value)
95 self.set_attribute("status", value)
Scott Bakerf08823d2015-07-06 16:50:30 -070096
97 def find_user(self, uid):
98 uid = int(uid)
99 for user in self.users:
100 if user["id"] == uid:
101 return user
102 return None
103
104 def update_user(self, uid, **kwargs):
105 # kwargs may be "level" or "mac"
106 # Setting one of these to None will cause None to be stored in the db
107 uid = int(uid)
108 users = self.users
109 for user in users:
110 if user["id"] == uid:
111 for arg in kwargs.keys():
112 user[arg] = kwargs[arg]
113 self.users = users
114 return user
115 raise ValueError("User %d not found" % uid)
116
117 def create_user(self, **kwargs):
118 if "name" not in kwargs:
119 raise XOSMissingField("The name field is required")
120
121 for user in self.users:
122 if kwargs["name"] == user["name"]:
123 raise XOSDuplicateKey("User %s already exists" % kwargs["name"])
124
125 uids = [x["id"] for x in self.users]
126 if uids:
127 uid = max(uids)+1
128 else:
129 uid = 0
130 newuser = kwargs.copy()
131 newuser["id"] = uid
132
133 users = self.users
134 users.append(newuser)
135 self.users = users
136
137 return newuser
138
139 def delete_user(self, uid):
140 uid = int(uid)
141 users = self.users
142 for user in users:
143 if user["id"]==uid:
144 users.remove(user)
145 self.users = users
146 return
147
148 raise ValueError("Users %d not found" % uid)
149
150 @property
151 def services(self):
152 return {"cdn": self.cdn_enable,
153 "url_filter": self.url_filter_enable,
154 "firewall": self.firewall_enable}
155
156 @services.setter
157 def services(self, value):
158 pass
Scott Baker361c86c2015-07-06 14:38:02 -0700159
Scott Baker7c7b6312015-07-07 12:15:03 -0700160 def save(self, *args, **kwargs):
Scott Bakereaee05e2015-07-24 18:56:57 -0700161 if (not hasattr(self, 'caller') or not self.caller.is_admin):
162 if (self.has_field_changed("service_specific_id")):
163 raise XOSPermissionDenied("You do not have permission to change service_specific_id")
Scott Baker7c7b6312015-07-07 12:15:03 -0700164 super(CordSubscriberRoot, self).save(*args, **kwargs)
165 if (self.volt) and (self.volt.vcpe): # and (self._initial_url_filter_enabled != self.url_filter_enable):
166 # 1) trigger manage_bbs_account to run
167 # 2) trigger vcpe observer to wake up
168 self.volt.vcpe.save()
169
Scott Baker95e38ae2016-03-01 20:14:35 -0800170CordSubscriberRoot.setup_simple_attributes()
Scott Bakerdb66fd32015-07-07 17:59:44 -0700171
Scott Baker39b0d892015-04-15 20:59:15 -0700172# -------------------------------------------
173# VOLT
174# -------------------------------------------
175
176class VOLTService(Service):
Scott Baker361c86c2015-07-06 14:38:02 -0700177 KIND = VOLT_KIND
Scott Baker39b0d892015-04-15 20:59:15 -0700178
Scott Baker1b241612015-04-14 17:19:16 -0700179 class Meta:
Scott Baker39b0d892015-04-15 20:59:15 -0700180 app_label = "cord"
181 verbose_name = "vOLT Service"
182 proxy = True
183
184class VOLTTenant(Tenant):
185 class Meta:
186 proxy = True
187
Scott Baker361c86c2015-07-06 14:38:02 -0700188 KIND = VOLT_KIND
Scott Baker39b0d892015-04-15 20:59:15 -0700189
Scott Baker89a338c2015-11-09 20:25:21 -0800190 default_attributes = {"vlan_id": None, "s_tag": None, "c_tag": None}
Scott Baker557aada2015-04-20 09:48:34 -0700191 def __init__(self, *args, **kwargs):
192 volt_services = VOLTService.get_service_objects().all()
193 if volt_services:
194 self._meta.get_field("provider_service").default = volt_services[0].id
195 super(VOLTTenant, self).__init__(*args, **kwargs)
Scott Baker361c86c2015-07-06 14:38:02 -0700196 self.cached_vcpe = None
Scott Baker5e76f802015-06-24 12:54:24 -0700197
Scott Baker39b0d892015-04-15 20:59:15 -0700198 @property
Scott Baker89a338c2015-11-09 20:25:21 -0800199 def s_tag(self):
200 return self.get_attribute("s_tag", self.default_attributes["s_tag"])
201
202 @s_tag.setter
203 def s_tag(self, value):
204 self.set_attribute("s_tag", value)
205
206 @property
207 def c_tag(self):
208 return self.get_attribute("c_tag", self.default_attributes["c_tag"])
209
210 @c_tag.setter
211 def c_tag(self, value):
212 self.set_attribute("c_tag", value)
213
214 # for now, vlan_id is a synonym for c_tag
215
216 @property
Scott Bakere744c7b2015-04-20 11:50:09 -0700217 def vlan_id(self):
Scott Baker89a338c2015-11-09 20:25:21 -0800218 return self.c_tag
Scott Bakere744c7b2015-04-20 11:50:09 -0700219
220 @vlan_id.setter
221 def vlan_id(self, value):
Scott Baker89a338c2015-11-09 20:25:21 -0800222 self.c_tag = value
Scott Bakere744c7b2015-04-20 11:50:09 -0700223
224 @property
Scott Baker39b0d892015-04-15 20:59:15 -0700225 def vcpe(self):
Scott Baker664bbe52016-02-11 11:08:42 -0800226 vcpe = self.get_newest_subscribed_tenant(VSGTenant)
Scott Baker361c86c2015-07-06 14:38:02 -0700227 if not vcpe:
228 return None
229
230 # always return the same object when possible
231 if (self.cached_vcpe) and (self.cached_vcpe.id == vcpe.id):
Scott Baker697fb0b2015-04-20 09:16:17 -0700232 return self.cached_vcpe
Scott Baker361c86c2015-07-06 14:38:02 -0700233
Scott Baker9c8a2c72015-05-05 17:49:46 -0700234 vcpe.caller = self.creator
Scott Baker697fb0b2015-04-20 09:16:17 -0700235 self.cached_vcpe = vcpe
236 return vcpe
Scott Baker39b0d892015-04-15 20:59:15 -0700237
238 @vcpe.setter
239 def vcpe(self, value):
Scott Bakercd349182015-07-06 17:21:21 -0700240 raise XOSConfigurationError("vOLT.vCPE cannot be set this way -- create a new vCPE object and set its subscriber_tenant instead")
241
242 @property
243 def subscriber(self):
244 if not self.subscriber_root:
245 return None
246 subs = CordSubscriberRoot.objects.filter(id=self.subscriber_root.id)
247 if not subs:
248 return None
249 return subs[0]
Scott Baker39b0d892015-04-15 20:59:15 -0700250
Scott Baker9c8a2c72015-05-05 17:49:46 -0700251 @property
252 def creator(self):
253 if getattr(self, "cached_creator", None):
254 return self.cached_creator
255 creator_id=self.get_attribute("creator_id")
256 if not creator_id:
257 return None
258 users=User.objects.filter(id=creator_id)
259 if not users:
260 return None
261 user=users[0]
262 self.cached_creator = users[0]
263 return user
264
265 @creator.setter
266 def creator(self, value):
267 if value:
268 value = value.id
269 if (value != self.get_attribute("creator_id", None)):
270 self.cached_creator=None
271 self.set_attribute("creator_id", value)
272
Scott Baker39b0d892015-04-15 20:59:15 -0700273 def manage_vcpe(self):
274 # Each VOLT object owns exactly one VCPE object
275
276 if self.deleted:
277 return
278
279 if self.vcpe is None:
Scott Baker4698b922016-02-11 12:07:10 -0800280 vsgServices = VSGService.get_service_objects().all()
281 if not vsgServices:
282 raise XOSConfigurationError("No VSG Services available")
Scott Baker39b0d892015-04-15 20:59:15 -0700283
Scott Baker4698b922016-02-11 12:07:10 -0800284 vcpe = VSGTenant(provider_service = vsgServices[0],
Scott Baker39b0d892015-04-15 20:59:15 -0700285 subscriber_tenant = self)
Scott Baker9c8a2c72015-05-05 17:49:46 -0700286 vcpe.caller = self.creator
Scott Baker39b0d892015-04-15 20:59:15 -0700287 vcpe.save()
288
Scott Baker7c7b6312015-07-07 12:15:03 -0700289 def manage_subscriber(self):
290 if (self.subscriber_root is None):
291 # The vOLT is not connected to a Subscriber, so either find an
292 # existing subscriber with the same SSID, or autogenerate a new
293 # subscriber.
294 #
295 # TODO: This probably goes away when we rethink the ONOS-to-XOS
296 # vOLT API.
297
298 subs = CordSubscriberRoot.get_tenant_objects().filter(service_specific_id = self.service_specific_id)
299 if subs:
300 sub = subs[0]
301 else:
302 sub = CordSubscriberRoot(service_specific_id = self.service_specific_id,
303 name = "autogenerated-for-vOLT-%s" % self.id)
304 sub.save()
305 self.subscriber_root = sub
306 self.save()
307
Scott Baker39b0d892015-04-15 20:59:15 -0700308 def cleanup_vcpe(self):
309 if self.vcpe:
Scott Baker361c86c2015-07-06 14:38:02 -0700310 # print "XXX cleanup vcpe", self.vcpe
Scott Baker39b0d892015-04-15 20:59:15 -0700311 self.vcpe.delete()
Scott Baker39b0d892015-04-15 20:59:15 -0700312
Scott Baker5e76f802015-06-24 12:54:24 -0700313 def cleanup_orphans(self):
Scott Baker361c86c2015-07-06 14:38:02 -0700314 # ensure vOLT only has one vCPE
315 cur_vcpe = self.vcpe
Scott Baker664bbe52016-02-11 11:08:42 -0800316 for vcpe in list(self.get_subscribed_tenants(VSGTenant)):
Scott Baker361c86c2015-07-06 14:38:02 -0700317 if (not cur_vcpe) or (vcpe.id != cur_vcpe.id):
318 # print "XXX clean up orphaned vcpe", vcpe
319 vcpe.delete()
Scott Baker5e76f802015-06-24 12:54:24 -0700320
Scott Baker39b0d892015-04-15 20:59:15 -0700321 def save(self, *args, **kwargs):
Scott Bakerd921e1c2015-04-20 14:24:29 -0700322 self.validate_unique_service_specific_id()
323
Scott Baker7c7b6312015-07-07 12:15:03 -0700324 if (self.subscriber_root is not None):
325 subs = self.subscriber_root.get_subscribed_tenants(VOLTTenant)
326 if (subs) and (self not in subs):
327 raise XOSDuplicateKey("Subscriber should only be linked to one vOLT")
328
Scott Baker9c8a2c72015-05-05 17:49:46 -0700329 if not self.creator:
330 if not getattr(self, "caller", None):
331 # caller must be set when creating a vCPE since it creates a slice
332 raise XOSProgrammingError("VOLTTenant's self.caller was not set")
333 self.creator = self.caller
334 if not self.creator:
335 raise XOSProgrammingError("VOLTTenant's self.creator was not set")
336
Scott Baker39b0d892015-04-15 20:59:15 -0700337 super(VOLTTenant, self).save(*args, **kwargs)
Scott Baker1027cd62015-07-08 19:01:56 -0700338 model_policy_volt(self.pk)
339 #self.manage_vcpe()
340 #self.manage_subscriber()
341 #self.cleanup_orphans()
Scott Baker39b0d892015-04-15 20:59:15 -0700342
343 def delete(self, *args, **kwargs):
344 self.cleanup_vcpe()
345 super(VOLTTenant, self).delete(*args, **kwargs)
346
Scott Baker1027cd62015-07-08 19:01:56 -0700347def model_policy_volt(pk):
348 # TODO: this should be made in to a real model_policy
349 with transaction.atomic():
350 volt = VOLTTenant.objects.select_for_update().filter(pk=pk)
351 if not volt:
352 return
353 volt = volt[0]
354 volt.manage_vcpe()
355 volt.manage_subscriber()
356 volt.cleanup_orphans()
357
Scott Baker39b0d892015-04-15 20:59:15 -0700358# -------------------------------------------
359# VCPE
360# -------------------------------------------
361
Scott Baker4698b922016-02-11 12:07:10 -0800362class VSGService(Service):
Scott Baker361c86c2015-07-06 14:38:02 -0700363 KIND = VCPE_KIND
Scott Baker39b0d892015-04-15 20:59:15 -0700364
Scott Bakera0886fb2015-07-15 18:08:06 -0700365 simple_attributes = ( ("bbs_api_hostname", None),
366 ("bbs_api_port", None),
Scott Baker3d2493b2015-07-15 17:42:43 -0700367 ("bbs_server", None),
Scott Baker4d3cc752016-02-18 06:43:02 -0800368 ("backend_network_label", "hpc_client"),
369 ("wan_container_gateway_ip", ""),
370 ("wan_container_gateway_mac", ""),
Scott Bakerda3122a2016-02-23 14:58:49 -0800371 ("wan_container_netbits", "24"),
372 ("dns_servers", "8.8.8.8") )
Scott Baker7ba272e2015-07-13 14:30:25 -0700373
374 def __init__(self, *args, **kwargs):
Scott Baker4698b922016-02-11 12:07:10 -0800375 super(VSGService, self).__init__(*args, **kwargs)
Scott Baker7ba272e2015-07-13 14:30:25 -0700376
Scott Baker39b0d892015-04-15 20:59:15 -0700377 class Meta:
378 app_label = "cord"
Scott Baker060f7352016-02-11 16:56:34 -0800379 verbose_name = "vSG Service"
Scott Baker39b0d892015-04-15 20:59:15 -0700380 proxy = True
Scott Baker1b241612015-04-14 17:19:16 -0700381
Scott Baker5c8abf82015-05-20 20:45:11 -0700382 def allocate_bbs_account(self):
Scott Baker664bbe52016-02-11 11:08:42 -0800383 vcpes = VSGTenant.get_tenant_objects().all()
Scott Baker5c8abf82015-05-20 20:45:11 -0700384 bbs_accounts = [vcpe.bbs_account for vcpe in vcpes]
385
386 # There's a bit of a race here; some other user could be trying to
387 # allocate a bbs_account at the same time we are.
388
Scott Bakerf4f61ca2015-06-09 12:03:56 -0700389 for i in range(2,21):
Scott Baker5c8abf82015-05-20 20:45:11 -0700390 account_name = "bbs%02d@onlab.us" % i
391 if (account_name not in bbs_accounts):
392 return account_name
393
394 raise XOSConfigurationError("We've run out of available broadbandshield accounts. Delete some vcpe and try again.")
395
Scott Baker3d2493b2015-07-15 17:42:43 -0700396 @property
397 def bbs_slice(self):
398 bbs_slice_id=self.get_attribute("bbs_slice_id")
399 if not bbs_slice_id:
400 return None
401 bbs_slices=Slice.objects.filter(id=bbs_slice_id)
402 if not bbs_slices:
403 return None
404 return bbs_slices[0]
405
406 @bbs_slice.setter
407 def bbs_slice(self, value):
408 if value:
409 value = value.id
410 self.set_attribute("bbs_slice_id", value)
411
Scott Baker4698b922016-02-11 12:07:10 -0800412VSGService.setup_simple_attributes()
Scott Baker7ba272e2015-07-13 14:30:25 -0700413
Scott Baker87891952016-02-02 16:56:18 -0800414#class STagBlock(PlCoreBase):
415# instance = models.ForeignKey(Instance, related_name="s_tags")
416# s_tag = models.CharField(null=false, blank=false, unique=true, max_length=10)
417# #c_tags = models.TextField(null=true, blank=true)
418#
419# def __unicode__(self): return u'%s' % (self.s_tag)
Scott Baker7ba272e2015-07-13 14:30:25 -0700420
Scott Baker664bbe52016-02-11 11:08:42 -0800421class VSGTenant(TenantWithContainer):
Scott Baker1b241612015-04-14 17:19:16 -0700422 class Meta:
423 proxy = True
424
Scott Baker361c86c2015-07-06 14:38:02 -0700425 KIND = VCPE_KIND
Scott Baker39b0d892015-04-15 20:59:15 -0700426
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700427 sync_attributes = ("nat_ip", "nat_mac",
428 "lan_ip", "lan_mac",
Scott Baker27fab492016-02-13 14:38:43 -0800429 "wan_ip", "wan_mac",
430 "wan_container_ip", "wan_container_mac",
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700431 "private_ip", "private_mac",
432 "hpc_client_ip", "hpc_client_mac")
Scott Baker9c8a2c72015-05-05 17:49:46 -0700433
Tony Mack3de59e32015-08-19 11:58:18 -0400434 default_attributes = {"instance_id": None,
Scott Bakerf7f7e572015-11-02 20:55:07 -0800435 "container_id": None,
Scott Baker5c8abf82015-05-20 20:45:11 -0700436 "users": [],
Scott Bakerd40a42d2015-06-09 12:22:29 -0700437 "bbs_account": None,
Scott Baker27fab492016-02-13 14:38:43 -0800438 "last_ansible_hash": None,
439 "wan_container_ip": None}
Scott Baker1b241612015-04-14 17:19:16 -0700440
Scott Baker697fb0b2015-04-20 09:16:17 -0700441 def __init__(self, *args, **kwargs):
Scott Baker664bbe52016-02-11 11:08:42 -0800442 super(VSGTenant, self).__init__(*args, **kwargs)
Scott Baker697fb0b2015-04-20 09:16:17 -0700443 self.cached_vbng=None
Scott Baker9c8a2c72015-05-05 17:49:46 -0700444
445 @property
Scott Baker697fb0b2015-04-20 09:16:17 -0700446 def vbng(self):
Scott Baker361c86c2015-07-06 14:38:02 -0700447 vbng = self.get_newest_subscribed_tenant(VBNGTenant)
448 if not vbng:
449 return None
450
451 # always return the same object when possible
452 if (self.cached_vbng) and (self.cached_vbng.id == vbng.id):
Scott Baker697fb0b2015-04-20 09:16:17 -0700453 return self.cached_vbng
Scott Baker361c86c2015-07-06 14:38:02 -0700454
Scott Baker9c8a2c72015-05-05 17:49:46 -0700455 vbng.caller = self.creator
Scott Baker697fb0b2015-04-20 09:16:17 -0700456 self.cached_vbng = vbng
457 return vbng
458
459 @vbng.setter
460 def vbng(self, value):
Scott Baker361c86c2015-07-06 14:38:02 -0700461 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 -0700462
Scott Bakercd349182015-07-06 17:21:21 -0700463 @property
464 def volt(self):
465 if not self.subscriber_tenant:
466 return None
467 volts = VOLTTenant.objects.filter(id=self.subscriber_tenant.id)
468 if not volts:
469 return None
470 return volts[0]
471
Scott Bakerf08823d2015-07-06 16:50:30 -0700472 @property
473 def bbs_account(self):
474 return self.get_attribute("bbs_account", self.default_attributes["bbs_account"])
475
476 @bbs_account.setter
477 def bbs_account(self, value):
478 return self.set_attribute("bbs_account", value)
479
480 @property
481 def last_ansible_hash(self):
482 return self.get_attribute("last_ansible_hash", self.default_attributes["last_ansible_hash"])
483
484 @last_ansible_hash.setter
485 def last_ansible_hash(self, value):
486 return self.set_attribute("last_ansible_hash", value)
487
488 @property
489 def ssh_command(self):
Tony Mack3de59e32015-08-19 11:58:18 -0400490 if self.instance:
491 return self.instance.get_ssh_command()
Scott Bakerf08823d2015-07-06 16:50:30 -0700492 else:
Tony Mack3de59e32015-08-19 11:58:18 -0400493 return "no-instance"
Scott Bakerf08823d2015-07-06 16:50:30 -0700494
495 @ssh_command.setter
496 def ssh_command(self, value):
497 pass
498
Scott Baker706bf972015-05-20 08:19:25 -0700499 @property
500 def addresses(self):
Scott Bakerf7f7e572015-11-02 20:55:07 -0800501 if self.instance:
502 ports = self.instance.ports.all()
503 elif self.container:
504 ports = self.container.ports.all()
505 else:
Scott Baker706bf972015-05-20 08:19:25 -0700506 return {}
507
508 addresses = {}
Scott Bakerf7f7e572015-11-02 20:55:07 -0800509 for ns in ports:
Scott Baker706bf972015-05-20 08:19:25 -0700510 if "lan" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700511 addresses["lan"] = (ns.ip, ns.mac)
Scott Baker706bf972015-05-20 08:19:25 -0700512 elif "wan" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700513 addresses["wan"] = (ns.ip, ns.mac)
Scott Baker706bf972015-05-20 08:19:25 -0700514 elif "private" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700515 addresses["private"] = (ns.ip, ns.mac)
Scott Baker706bf972015-05-20 08:19:25 -0700516 elif "nat" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700517 addresses["nat"] = (ns.ip, ns.mac)
Scott Baker710ad052015-06-04 10:26:44 -0700518 elif "hpc_client" in ns.network.name.lower():
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700519 addresses["hpc_client"] = (ns.ip, ns.mac)
Scott Baker706bf972015-05-20 08:19:25 -0700520 return addresses
521
Scott Baker27fab492016-02-13 14:38:43 -0800522 # ------------------------------------------------------------------------
523 # The following IP addresses all come from the VM
524 # Note: They might not be useful for the VTN-vSG
525
Scott Bakera6a7e032015-05-20 08:25:29 -0700526 @property
527 def nat_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700528 return self.addresses.get("nat", (None,None) )[0]
529
530 @property
531 def nat_mac(self):
532 return self.addresses.get("nat", (None,None) )[1]
Scott Bakera6a7e032015-05-20 08:25:29 -0700533
534 @property
535 def lan_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700536 return self.addresses.get("lan", (None, None) )[0]
537
538 @property
539 def lan_mac(self):
540 return self.addresses.get("lan", (None, None) )[1]
Scott Bakera6a7e032015-05-20 08:25:29 -0700541
542 @property
543 def wan_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700544 return self.addresses.get("wan", (None, None) )[0]
Scott Bakera6a7e032015-05-20 08:25:29 -0700545
546 @property
Scott Baker3f4fe402015-06-05 12:08:34 -0700547 def wan_mac(self):
Andy Baviera75c1192015-09-09 18:06:30 -0400548 return self.addresses.get("wan", (None, None) )[1]
Scott Baker3f4fe402015-06-05 12:08:34 -0700549
Scott Baker27fab492016-02-13 14:38:43 -0800550 # end of VM IP address stubs
551 # ------------------------------------------------------------------------
552
553 @property
554 def wan_container_ip(self):
555 if CORD_USE_VTN:
556 # When using VTN, wan_container_ip is stored and maintained inside
557 # of the vSG object.
558 return self.get_attribute("wan_container_ip", self.default_attributes["wan_container_ip"])
559 else:
560 # When not using VTN, wan_container_ip is the same as wan_ip.
561 # XXX Is this broken for multiple-containers-per-VM?
562 return self.wan_ip
563
564 @wan_container_ip.setter
565 def wan_container_ip(self, value):
566 if CORD_USE_VTN:
567 self.set_attribute("wan_container_ip", value)
568 else:
569 raise Exception("wan_container_ip.setter called on non-VTN CORD")
570
Scott Baker7aa5e992016-02-18 15:18:47 -0800571 def ip_to_mac(self, ip):
572 (a, b, c, d) = ip.split('.')
573 return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
574
Andy Bavier0fa3dd62015-10-29 14:01:47 -0400575 # Generate the MAC for the container interface connected to WAN
576 @property
577 def wan_container_mac(self):
Scott Baker27fab492016-02-13 14:38:43 -0800578 if not self.wan_container_ip:
579 return None
Scott Baker7aa5e992016-02-18 15:18:47 -0800580 return self.ip_to_mac(self.wan_container_ip)
Andy Bavier0fa3dd62015-10-29 14:01:47 -0400581
Scott Baker3f4fe402015-06-05 12:08:34 -0700582 @property
Scott Bakera6a7e032015-05-20 08:25:29 -0700583 def private_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700584 return self.addresses.get("private", (None, None) )[0]
585
586 @property
587 def private_mac(self):
588 return self.addresses.get("private", (None, None) )[1]
Scott Bakera6a7e032015-05-20 08:25:29 -0700589
Scott Baker710ad052015-06-04 10:26:44 -0700590 @property
591 def hpc_client_ip(self):
Scott Bakerd35a4bc2015-09-09 10:57:16 -0700592 return self.addresses.get("hpc_client", (None, None) )[0]
593
594 @property
595 def hpc_client_mac(self):
596 return self.addresses.get("hpc_client", (None, None) )[1]
Scott Baker710ad052015-06-04 10:26:44 -0700597
Scott Baker7bbdd2f2015-06-09 12:30:30 -0700598 @property
599 def is_synced(self):
600 return (self.enacted is not None) and (self.enacted >= self.updated)
601
602 @is_synced.setter
603 def is_synced(self, value):
604 pass
605
Scott Baker697fb0b2015-04-20 09:16:17 -0700606 def manage_vbng(self):
607 # Each vCPE object owns exactly one vBNG object
608
609 if self.deleted:
610 return
611
612 if self.vbng is None:
613 vbngServices = VBNGService.get_service_objects().all()
614 if not vbngServices:
Scott Bakerd921e1c2015-04-20 14:24:29 -0700615 raise XOSConfigurationError("No VBNG Services available")
Scott Baker697fb0b2015-04-20 09:16:17 -0700616
617 vbng = VBNGTenant(provider_service = vbngServices[0],
618 subscriber_tenant = self)
Scott Baker9c8a2c72015-05-05 17:49:46 -0700619 vbng.caller = self.creator
Scott Baker697fb0b2015-04-20 09:16:17 -0700620 vbng.save()
621
Scott Baker697fb0b2015-04-20 09:16:17 -0700622 def cleanup_vbng(self):
623 if self.vbng:
Scott Baker361c86c2015-07-06 14:38:02 -0700624 # print "XXX cleanup vnbg", self.vbng
Scott Baker697fb0b2015-04-20 09:16:17 -0700625 self.vbng.delete()
Scott Baker697fb0b2015-04-20 09:16:17 -0700626
Scott Baker5e76f802015-06-24 12:54:24 -0700627 def cleanup_orphans(self):
Scott Baker361c86c2015-07-06 14:38:02 -0700628 # ensure vCPE only has one vBNG
629 cur_vbng = self.vbng
630 for vbng in list(self.get_subscribed_tenants(VBNGTenant)):
631 if (not cur_vbng) or (vbng.id != cur_vbng.id):
632 # print "XXX clean up orphaned vbng", vbng
633 vbng.delete()
Scott Baker5e76f802015-06-24 12:54:24 -0700634
Tony Mack3de59e32015-08-19 11:58:18 -0400635 if self.orig_instance_id and (self.orig_instance_id != self.get_attribute("instance_id")):
636 instances=Instance.objects.filter(id=self.orig_instance_id)
637 if instances:
638 # print "XXX clean up orphaned instance", instances[0]
639 instances[0].delete()
Scott Baker5e76f802015-06-24 12:54:24 -0700640
Scott Baker87891952016-02-02 16:56:18 -0800641 def get_slice(self):
642 if not self.provider_service.slices.count():
643 raise XOSConfigurationError("The service has no slices")
644 slice = self.provider_service.slices.all()[0]
645 return slice
646
647 def find_instance_for_s_tag(self, s_tag):
648 #s_tags = STagBlock.objects.find(s_s_tag)
649 #if s_tags:
650 # return s_tags[0].instance
651
652 tags = Tag.objects.filter(name="s_tag", value=s_tag)
653 if tags:
654 return tags[0].content_object
655
656 return None
657
658 def find_or_make_instance_for_s_tag(self, s_tag):
659 instance = self.find_instance_for_s_tag(self.volt.s_tag)
660 if instance:
661 return instance
662
663 flavors = Flavor.objects.filter(name="m1.small")
664 if not flavors:
665 raise XOSConfigurationError("No m1.small flavor")
666
667 slice = self.provider_service.slices.all()[0]
668
669 if slice.default_isolation == "container_vm":
670 (node, parent) = ContainerVmScheduler(slice).pick()
671 else:
672 (node, parent) = LeastLoadedNodeScheduler(slice).pick()
673
674 instance = Instance(slice = slice,
675 node = node,
676 image = self.image,
677 creator = self.creator,
678 deployment = node.site_deployment.deployment,
679 flavor = flavors[0],
680 isolation = slice.default_isolation,
681 parent = parent)
Scott Bakerda5e8872016-02-12 16:17:04 -0800682
Scott Baker87891952016-02-02 16:56:18 -0800683 self.save_instance(instance)
684
685 return instance
686
687 def manage_container(self):
688 from core.models import Instance, Flavor
689
690 if self.deleted:
691 return
692
693 # For container or container_vm isolation, use what TenantWithCotnainer
694 # provides us
695 slice = self.get_slice()
696 if slice.default_isolation in ["container_vm", "container"]:
Scott Baker664bbe52016-02-11 11:08:42 -0800697 super(VSGTenant,self).manage_container()
Scott Baker87891952016-02-02 16:56:18 -0800698 return
699
700 if not self.volt:
701 raise XOSConfigurationError("This vCPE container has no volt")
702
Scott Baker4d3cc752016-02-18 06:43:02 -0800703 if self.instance:
704 # We're good.
705 return
706
Scott Baker87891952016-02-02 16:56:18 -0800707 instance = self.find_or_make_instance_for_s_tag(self.volt.s_tag)
708 self.instance = instance
709 super(TenantWithContainer, self).save()
710
711 def cleanup_container(self):
712 if self.get_slice().default_isolation in ["container_vm", "container"]:
Scott Baker664bbe52016-02-11 11:08:42 -0800713 super(VSGTenant,self).cleanup_container()
Scott Baker87891952016-02-02 16:56:18 -0800714
715 # To-do: cleanup unused instances
716 pass
717
Scott Baker5c8abf82015-05-20 20:45:11 -0700718 def manage_bbs_account(self):
719 if self.deleted:
720 return
721
Scott Bakerdb66fd32015-07-07 17:59:44 -0700722 if self.volt and self.volt.subscriber and self.volt.subscriber.url_filter_enable:
Scott Bakere2570112015-05-20 20:57:28 -0700723 if not self.bbs_account:
Scott Baker4698b922016-02-11 12:07:10 -0800724 # make sure we use the proxied VSGService object, not the generic Service object
725 vcpe_service = VSGService.objects.get(id=self.provider_service.id)
Scott Baker7c7b6312015-07-07 12:15:03 -0700726 self.bbs_account = vcpe_service.allocate_bbs_account()
Scott Baker664bbe52016-02-11 11:08:42 -0800727 super(VSGTenant, self).save()
Scott Bakere2570112015-05-20 20:57:28 -0700728 else:
729 if self.bbs_account:
730 self.bbs_account = None
Scott Baker664bbe52016-02-11 11:08:42 -0800731 super(VSGTenant, self).save()
Scott Baker5c8abf82015-05-20 20:45:11 -0700732
Scott Baker7aa5e992016-02-18 15:18:47 -0800733 def get_wan_address_from_pool(self):
734 ap = AddressPool.objects.filter(name="public_addresses")
735 if not ap:
736 raise Exception("AddressPool 'public_addresses' does not exist. Please configure it.")
737 ap = ap[0]
738
739 addr = ap.get_address()
740 if not addr:
741 raise Exception("AddressPool 'public_addresses' has run out of addresses.")
742 return addr
743
744 def put_wan_address_to_pool(self, addr):
745 AddressPool.objects.filter(name="public_addresses")[0].put_address(addr)
746
Scott Baker27fab492016-02-13 14:38:43 -0800747 def manage_wan_container_ip(self):
748 if CORD_USE_VTN:
749 if not self.wan_container_ip:
Scott Baker7aa5e992016-02-18 15:18:47 -0800750 addr = self.get_wan_address_from_pool()
Scott Baker27fab492016-02-13 14:38:43 -0800751
752 self.wan_container_ip = addr
Scott Baker4d3cc752016-02-18 06:43:02 -0800753 super(TenantWithContainer, self).save()
Scott Baker27fab492016-02-13 14:38:43 -0800754
755 def cleanup_wan_container_ip(self):
756 if CORD_USE_VTN and self.wan_container_ip:
Scott Baker7aa5e992016-02-18 15:18:47 -0800757 self.put_wan_address_to_pool(self.wan_container_ip)
Scott Baker27fab492016-02-13 14:38:43 -0800758 self.wan_container_ip = None
759
Scott Baker0a17ccc2015-11-16 22:53:49 -0800760 def find_or_make_port(self, instance, network, **kwargs):
761 port = Port.objects.filter(instance=instance, network=network)
762 if port:
763 port = port[0]
764 else:
765 port = Port(instance=instance, network=network, **kwargs)
766 port.save()
767 return port
768
Scott Bakerda5e8872016-02-12 16:17:04 -0800769 def get_lan_network(self, instance):
Scott Bakerda5e8872016-02-12 16:17:04 -0800770 slice = self.provider_service.slices.all()[0]
Scott Baker27fab492016-02-13 14:38:43 -0800771 if CORD_USE_VTN:
772 # there should only be one network private network, and its template should not be the management template
773 lan_networks = [x for x in slice.networks.all() if x.template.visibility=="private" and (not "management" in x.template.name)]
774 if len(lan_networks)>1:
775 raise XOSProgrammingError("The vSG slice should only have one non-management private network")
776 else:
777 lan_networks = [x for x in slice.networks.all() if "lan" in x.name]
Scott Bakerda5e8872016-02-12 16:17:04 -0800778 if not lan_networks:
779 raise XOSProgrammingError("No lan_network")
780 return lan_networks[0]
781
Scott Baker0a17ccc2015-11-16 22:53:49 -0800782 def save_instance(self, instance):
783 with transaction.atomic():
Andy Bavierde1707d2015-12-16 14:10:01 -0500784 instance.volumes = "/etc/dnsmasq.d,/etc/ufw"
Scott Baker664bbe52016-02-11 11:08:42 -0800785 super(VSGTenant, self).save_instance(instance)
Scott Baker0a17ccc2015-11-16 22:53:49 -0800786
787 if instance.isolation in ["container", "container_vm"]:
Scott Bakerda5e8872016-02-12 16:17:04 -0800788 lan_network = self.get_lan_network(instance)
789 port = self.find_or_make_port(instance, lan_network, ip="192.168.0.1", port_id="unmanaged")
Scott Baker0a17ccc2015-11-16 22:53:49 -0800790 port.set_parameter("c_tag", self.volt.c_tag)
791 port.set_parameter("s_tag", self.volt.s_tag)
792 port.set_parameter("device", "eth1")
Scott Bakerc7711632015-11-19 16:51:27 -0800793 port.set_parameter("bridge", "br-lan")
Scott Baker0a17ccc2015-11-16 22:53:49 -0800794
795 wan_networks = [x for x in instance.slice.networks.all() if "wan" in x.name]
796 if not wan_networks:
797 raise XOSProgrammingError("No wan_network")
798 port = self.find_or_make_port(instance, wan_networks[0])
799 port.set_parameter("next_hop", value="10.0.1.253") # FIX ME
800 port.set_parameter("device", "eth0")
801
Scott Bakerda5e8872016-02-12 16:17:04 -0800802 if instance.isolation in ["vm"]:
803 lan_network = self.get_lan_network(instance)
804 port = self.find_or_make_port(instance, lan_network)
805 port.set_parameter("c_tag", self.volt.c_tag)
806 port.set_parameter("s_tag", self.volt.s_tag)
807 port.set_parameter("neutron_port_name", "stag-%s" % self.volt.s_tag)
808 port.save()
809
Scott Baker87891952016-02-02 16:56:18 -0800810 # tag the instance with the s-tag, so we can easily find the
811 # instance later
812 if self.volt and self.volt.s_tag:
813 tags = Tag.objects.filter(name="s_tag", value=self.volt.s_tag)
814 if not tags:
815 tag = Tag(service=self.provider_service, content_object=instance, name="s_tag", value=self.volt.s_tag)
816 tag.save()
817
Scott Baker7aa5e992016-02-18 15:18:47 -0800818 # VTN-CORD needs a WAN address for the VM, so that the VM can
819 # be configured.
820 if CORD_USE_VTN:
821 tags = Tag.select_by_content_object(instance).filter(name="vm_wan_addr")
822 if not tags:
823 address = self.get_wan_address_from_pool()
824 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)))
825 tag.save()
826
Scott Baker1b241612015-04-14 17:19:16 -0700827 def save(self, *args, **kwargs):
Scott Baker9c8a2c72015-05-05 17:49:46 -0700828 if not self.creator:
829 if not getattr(self, "caller", None):
830 # caller must be set when creating a vCPE since it creates a slice
Scott Baker664bbe52016-02-11 11:08:42 -0800831 raise XOSProgrammingError("VSGTenant's self.caller was not set")
Scott Baker9c8a2c72015-05-05 17:49:46 -0700832 self.creator = self.caller
833 if not self.creator:
Scott Baker664bbe52016-02-11 11:08:42 -0800834 raise XOSProgrammingError("VSGTenant's self.creator was not set")
Scott Baker9c8a2c72015-05-05 17:49:46 -0700835
Scott Baker664bbe52016-02-11 11:08:42 -0800836 super(VSGTenant, self).save(*args, **kwargs)
Scott Baker1027cd62015-07-08 19:01:56 -0700837 model_policy_vcpe(self.pk)
Scott Baker1b241612015-04-14 17:19:16 -0700838
839 def delete(self, *args, **kwargs):
Scott Baker697fb0b2015-04-20 09:16:17 -0700840 self.cleanup_vbng()
Scott Bakere458afd2015-09-09 16:36:06 -0700841 self.cleanup_container()
Scott Baker27fab492016-02-13 14:38:43 -0800842 self.cleanup_wan_container_ip()
Scott Baker664bbe52016-02-11 11:08:42 -0800843 super(VSGTenant, self).delete(*args, **kwargs)
Scott Baker1b241612015-04-14 17:19:16 -0700844
Scott Baker1027cd62015-07-08 19:01:56 -0700845def model_policy_vcpe(pk):
846 # TODO: this should be made in to a real model_policy
847 with transaction.atomic():
Scott Baker664bbe52016-02-11 11:08:42 -0800848 vcpe = VSGTenant.objects.select_for_update().filter(pk=pk)
Scott Baker1027cd62015-07-08 19:01:56 -0700849 if not vcpe:
850 return
851 vcpe = vcpe[0]
Scott Baker27fab492016-02-13 14:38:43 -0800852 vcpe.manage_wan_container_ip()
Scott Bakere458afd2015-09-09 16:36:06 -0700853 vcpe.manage_container()
Scott Baker1027cd62015-07-08 19:01:56 -0700854 vcpe.manage_vbng()
855 vcpe.manage_bbs_account()
856 vcpe.cleanup_orphans()
857
Scott Baker697fb0b2015-04-20 09:16:17 -0700858#----------------------------------------------------------------------------
859# vBNG
860#----------------------------------------------------------------------------
861
862class VBNGService(Service):
Scott Baker361c86c2015-07-06 14:38:02 -0700863 KIND = VBNG_KIND
Scott Baker697fb0b2015-04-20 09:16:17 -0700864
Scott Bakere337e512015-10-20 22:12:51 -0700865 simple_attributes = ( ("vbng_url", ""), ) # "http://10.0.3.136:8181/onos/virtualbng/"
Scott Baker3d7ec1a2015-07-21 18:24:21 -0700866
Scott Baker697fb0b2015-04-20 09:16:17 -0700867 class Meta:
868 app_label = "cord"
869 verbose_name = "vBNG Service"
870 proxy = True
871
Scott Baker3d7ec1a2015-07-21 18:24:21 -0700872VBNGService.setup_simple_attributes()
873
Scott Baker697fb0b2015-04-20 09:16:17 -0700874class VBNGTenant(Tenant):
875 class Meta:
876 proxy = True
877
Scott Baker361c86c2015-07-06 14:38:02 -0700878 KIND = VBNG_KIND
Scott Baker697fb0b2015-04-20 09:16:17 -0700879
Scott Baker99ff5512015-06-02 14:34:04 -0700880 default_attributes = {"routeable_subnet": "",
Scott Baker790613c2015-06-08 19:09:53 -0700881 "mapped_ip": "",
882 "mapped_mac": "",
883 "mapped_hostname": ""}
Scott Baker697fb0b2015-04-20 09:16:17 -0700884
885 @property
886 def routeable_subnet(self):
887 return self.get_attribute("routeable_subnet", self.default_attributes["routeable_subnet"])
888
889 @routeable_subnet.setter
890 def routeable_subnet(self, value):
891 self.set_attribute("routeable_subnet", value)
Scott Baker99ff5512015-06-02 14:34:04 -0700892
893 @property
894 def mapped_ip(self):
895 return self.get_attribute("mapped_ip", self.default_attributes["mapped_ip"])
896
897 @mapped_ip.setter
898 def mapped_ip(self, value):
899 self.set_attribute("mapped_ip", value)
Scott Baker790613c2015-06-08 19:09:53 -0700900
901 @property
902 def mapped_mac(self):
903 return self.get_attribute("mapped_mac", self.default_attributes["mapped_mac"])
904
905 @mapped_mac.setter
906 def mapped_mac(self, value):
907 self.set_attribute("mapped_mac", value)
908
909 @property
910 def mapped_hostname(self):
911 return self.get_attribute("mapped_hostname", self.default_attributes["mapped_hostname"])
912
913 @mapped_hostname.setter
914 def mapped_hostname(self, value):
915 self.set_attribute("mapped_hostname", value)