blob: 6d070d85599eaa413e3f0b6a02eca19d0ded2305 [file] [log] [blame]
Pingping Lind28ab982016-08-29 18:42:52 +00001from django.db import models
2from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, AddressPool, Port
3from core.models.plcorebase import StrippedCharField
4import os
5from django.db import models, transaction
6from django.forms.models import model_to_dict
7from django.db.models import Q
8from operator import itemgetter, attrgetter, methodcaller
9from core.models import Tag
10from core.models.service import LeastLoadedNodeScheduler
11import traceback
12from xos.exceptions import *
13from core.models import SlicePrivilege, SitePrivilege
14from sets import Set
15from xos.config import Config
16
17MCORD_KIND = "RAN" # This should be changed later I did it fo demo
18MCORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
19VBBU_KIND = "RAN"
20VSGW_KIND = "vSGW"
21VPGWC_KIND = "RAN"
22vbbu_net_types = ("s1u", "s1mme", "rru")
23vpgwc_net_types = ("s5s8")
24# The class to represent the service. Most of the service logic is given for us
25# in the Service class but, we have some configuration that is specific for
26# this example.
27class MCORDService(Service):
28 KIND = MCORD_KIND
29
30 class Meta:
31 # When the proxy field is set to True the model is represented as
32 # it's superclass in the database, but we can still change the python
33 # behavior. In this case HelloWorldServiceComplete is a Service in the
34 # database.
35 proxy = True
36 # The name used to find this service, all directories are named this
37 app_label = "mcord"
38 verbose_name = "MCORD Service"
39
40# This is the class to represent the tenant. Most of the logic is given to use
41# in TenantWithContainer, however there is some configuration and logic that
42# we need to define for this example.
43class VBBUComponent(TenantWithContainer):
44
45 class Meta:
46 # Same as a above, HelloWorldTenantComplete is represented as a
47 # TenantWithContainer, but we change the python behavior.
48 proxy = True
49 verbose_name = "VBBU MCORD Service Component"
50
51 # The kind of the service is used on forms to differentiate this service
52 # from the other services.
53 KIND = VBBU_KIND
54
55 # Ansible requires that the sync_attributes field contain nat_ip and nat_mac
56 # these will be used to determine where to SSH to for ansible.
57 # Getters must be defined for every attribute specified here.
58 sync_attributes = ("s1u_ip", "s1u_mac",
59 "s1mme_ip", "s1mme_mac",
60 "rru_ip", "rru_mac")
61 # default_attributes is used cleanly indicate what the default values for
62 # the fields are.
63 default_attributes = {"display_message": "New vBBU Component", "s1u_tag": "901", "s1mme_tag": "900", "rru_tag": "999"}
64 def __init__(self, *args, **kwargs):
65 mcord_services = MCORDService.get_service_objects().all()
66 # When the tenant is created the default service in the form is set
67 # to be the first created HelloWorldServiceComplete
68 if mcord_services:
69 self._meta.get_field(
70 "provider_service").default = mcord_services[0].id
71 super(VBBUComponent, self).__init__(*args, **kwargs)
72
73 def can_update(self, user):
74 #Allow creation of this model instances for non-admin users also
75 return True
76
77 def save(self, *args, **kwargs):
78 if not self.creator:
79 if not getattr(self, "caller", None):
80 # caller must be set when creating a monitoring channel since it creates a slice
81 raise XOSProgrammingError("ServiceComponents's self.caller was not set")
82 self.creator = self.caller
83 if not self.creator:
84 raise XOSProgrammingError("ServiceComponents's self.creator was not set")
85
86 super(VBBUComponent, self).save(*args, **kwargs)
87 # This call needs to happen so that an instance is created for this
88 # tenant is created in the slice. One instance is created per tenant.
89 model_policy_mcord_servicecomponent(self.pk)
90
91 def save_instance(self, instance):
92 with transaction.atomic():
93 super(VBBUComponent, self).save_instance(instance)
94 if instance.isolation in ["vm"]:
95 for ntype in vbbu_net_types:
96 lan_network = self.get_lan_network(instance, ntype)
97 port = self.find_or_make_port(instance,lan_network)
98 if (ntype == "s1u"):
99 port.set_parameter("s_tag", self.s1u_tag)
100 port.set_parameter("neutron_port_name", "stag-%s" % self.s1u_tag)
101 port.save()
102 elif (ntype == "s1mme"):
103 port.set_parameter("s_tag", self.s1mme_tag)
104 port.set_parameter("neutron_port_name", "stag-%s" % self.s1mme_tag)
105 port.save()
106 elif (ntype == "rru"):
107 port.set_parameter("s_tag", self.rru_tag)
108 port.set_parameter("neutron_port_name", "stag-%s" % self.rru_tag)
109 port.save()
110
111 def delete(self, *args, **kwargs):
112 # Delete the instance that was created for this tenant
113 self.cleanup_container()
114 super(VBBUComponent, self).delete(*args, **kwargs)
115
116 def find_or_make_port(self, instance, network, **kwargs):
117 port = Port.objects.filter(instance=instance, network=network)
118 if port:
119 port = port[0]
120 print "port already exist", port[0]
121 else:
122 port = Port(instance=instance, network=network, **kwargs)
123 print "NETWORK", network, "MAKE_PORT", port
124 port.save()
125 return port
126
127 def get_lan_network(self, instance, ntype):
128 slice = self.provider_service.slices.all()[0]
129 lan_networks = [x for x in slice.networks.all() if ntype in x.name]
130 if not lan_networks:
131 raise XOSProgrammingError("No lan_network")
132 return lan_networks[0]
133
134 def manage_container(self):
135 from core.models import Instance, Flavor
136
137 if self.deleted:
138 return
139
140 # For container or container_vm isolation, use what TenantWithCotnainer
141 # provides us
142 slice = self.get_slice()
143 if slice.default_isolation in ["container_vm", "container"]:
144 super(VBBUComponent,self).manage_container()
145 return
146
147 if not self.s1u_tag:
148 raise XOSConfigurationError("S1U_TAG is missed")
149
150 if not self.s1mme_tag:
151 raise XOSConfigurationError("S1U_TAG is missed")
152
153 if not self.rru_tag:
154 raise XOSConfigurationError("S1U_TAG is missed")
155
156 if self.instance:
157 # We're good.
158 return
159
160 instance = self.make_instance()
161 self.instance = instance
162 super(TenantWithContainer, self).save()
163
164 def get_slice(self):
165 if not self.provider_service.slices.count():
166 raise XOSConfigurationError("The service has no slices")
167 slice = self.provider_service.slices.all()[0]
168 return slice
169
170 def make_instance(self):
171 slice = self.provider_service.slices.all()[0]
172 flavors = Flavor.objects.filter(name=slice.default_flavor)
173# flavors = Flavor.objects.filter(name="m1.xlarge")
174 if not flavors:
175 raise XOSConfigurationError("No default flavor")
176 default_flavor = slice.default_flavor
177 slice = self.provider_service.slices.all()[0]
178 if slice.default_isolation == "container_vm":
179 (node, parent) = ContainerVmScheduler(slice).pick()
180 else:
181 (node, parent) = LeastLoadedNodeScheduler(slice).pick()
182 instance = Instance(slice = slice,
183 node = node,
184 image = self.image,
185 creator = self.creator,
186 deployment = node.site_deployment.deployment,
187 flavor = flavors[0],
188 isolation = slice.default_isolation,
189 parent = parent)
190 self.save_instance(instance)
191 return instance
192
193 def ip_to_mac(self, ip):
194 (a, b, c, d) = ip.split('.')
195 return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
196
197 # Getter for the message that will appear on the webpage
198 # By default it is "Hello World!"
199 @property
200 def display_message(self):
201 return self.get_attribute(
202 "display_message",
203 self.default_attributes['display_message'])
204
205 @display_message.setter
206 def display_message(self, value):
207 self.set_attribute("display_message", value)
208
209 @property
210 def s1u_tag(self):
211 return self.get_attribute(
212 "s1u_tag",
213 self.default_attributes['s1u_tag'])
214
215 @s1u_tag.setter
216 def s1u_tag(self, value):
217 self.set_attribute("s1u_tag", value)
218
219 @property
220 def s1mme_tag(self):
221 return self.get_attribute(
222 "s1mme_tag",
223 self.default_attributes['s1mme_tag'])
224
225 @s1mme_tag.setter
226 def s1mme_tag(self, value):
227 self.set_attribute("s1mme_tag", value)
228
229 @property
230 def rru_tag(self):
231 return self.get_attribute(
232 "rru_tag",
233 self.default_attributes['rru_tag'])
234
235 @rru_tag.setter
236 def rru_tag(self, value):
237 self.set_attribute("rru_tag", value)
238
239
240 @property
241 def addresses(self):
242 if (not self.id) or (not self.instance):
243 return {}
244
245 addresses = {}
246 for ns in self.instance.ports.all():
247 if "s1u" in ns.network.name.lower():
248 addresses["s1u"] = (ns.ip, ns.mac)
249 elif "s1mme" in ns.network.name.lower():
250 addresses["s1mme"] = (ns.ip, ns.mac)
251 elif "rru" in ns.network.name.lower():
252 addresses["rru"] = (ns.ip, ns.mac)
253 return addresses
254
255
256 @property
257 def s1u_ip(self):
258 return self.addresses.get("s1u", (None, None))[0]
259 @property
260 def s1u_mac(self):
261 return self.addresses.get("s1u", (None, None))[1]
262 @property
263 def s1mme_ip(self):
264 return self.addresses.get("s1mme", (None, None))[0]
265 @property
266 def s1mme_mac(self):
267 return self.addresses.get("s1mme", (None, None))[1]
268 @property
269 def rru_ip(self):
270 return self.addresses.get("rru", (None, None))[0]
271 @property
272 def rru_mac(self):
273 return self.addresses.get("rru", (None, None))[1]
274
275
276# This is the class to represent the tenant. Most of the logic is given to use
277# in TenantWithContainer, however there is some configuration and logic that
278# we need to define for this example.
279class VPGWCComponent(TenantWithContainer):
280
281 class Meta:
282 # Same as a above, HelloWorldTenantComplete is represented as a
283 # TenantWithContainer, but we change the python behavior.
284 proxy = True
285 verbose_name = "VPGWC MCORD Service Component"
286
287 # The kind of the service is used on forms to differentiate this service
288 # from the other services.
289 KIND = VPGWC_KIND
290
291 # Ansible requires that the sync_attributes field contain nat_ip and nat_mac
292 # these will be used to determine where to SSH to for ansible.
293 # Getters must be defined for every attribute specified here.
294 sync_attributes = ("s5s8_pgw_ip", "s5s8_pgw_mac")
295
296 # default_attributes is used cleanly indicate what the default values for
297 # the fields are.
298 default_attributes = {"display_message": "New vPGWC Component", "s5s8_pgw_tag": "300"}
299 def __init__(self, *args, **kwargs):
300 mcord_services = MCORDService.get_service_objects().all()
301 # When the tenant is created the default service in the form is set
302 # to be the first created HelloWorldServiceComplete
303 if mcord_services:
304 self._meta.get_field(
305 "provider_service").default = mcord_services[0].id
306 super(VPGWCComponent, self).__init__(*args, **kwargs)
307
308 def can_update(self, user):
309 #Allow creation of this model instances for non-admin users also
310 return True
311
312 def save(self, *args, **kwargs):
313 if not self.creator:
314 if not getattr(self, "caller", None):
315 # caller must be set when creating a monitoring channel since it creates a slice
316 raise XOSProgrammingError("ServiceComponents's self.caller was not set")
317 self.creator = self.caller
318 if not self.creator:
319 raise XOSProgrammingError("ServiceComponents's self.creator was not set")
320
321 super(VPGWCComponent, self).save(*args, **kwargs)
322 # This call needs to happen so that an instance is created for this
323 # tenant is created in the slice. One instance is created per tenant.
324 model_policy_mcord_servicecomponent(self.pk)
325
326 def save_instance(self, instance):
327 with transaction.atomic():
328 super(VPGWCComponent, self).save_instance(instance)
329 if instance.isolation in ["vm"]:
330 for ntype in vpgwc_net_types:
331 lan_network = self.get_lan_network(instance, ntype)
332 port = self.find_or_make_port(instance,lan_network)
333 if (ntype == "s5s8"):
334 port.set_parameter("s_tag", self.s5s8_pgw_tag)
335 port.set_parameter("neutron_port_name", "stag-%s" % self.s5s8_pgw_tag)
336 port.save()
337 else:
338 return True
339
340 def delete(self, *args, **kwargs):
341 # Delete the instance that was created for this tenant
342 self.cleanup_container()
343 super(VPGWCComponent, self).delete(*args, **kwargs)
344
345 def find_or_make_port(self, instance, network, **kwargs):
346 port = Port.objects.filter(instance=instance, network=network)
347 if port:
348 port = port[0]
349 print "port already exist", port[0]
350 else:
351 port = Port(instance=instance, network=network, **kwargs)
352 print "NETWORK", network, "MAKE_PORT", port
353 port.save()
354 return port
355
356 def get_lan_network(self, instance, ntype):
357 slice = self.provider_service.slices.all()[0]
358 lan_networks = [x for x in slice.networks.all() if ntype in x.name]
359 if not lan_networks:
360 raise XOSProgrammingError("No lan_network")
361 return lan_networks[0]
362
363 def manage_container(self):
364 from core.models import Instance, Flavor
365
366 if self.deleted:
367 return
368
369 # For container or container_vm isolation, use what TenantWithCotnainer
370 # provides us
371 slice = self.get_slice()
372 if slice.default_isolation in ["container_vm", "container"]:
373 super(VPGWCComponent,self).manage_container()
374 return
375
376 if not self.s5s8_pgw_tag:
377 raise XOSConfigurationError("S5S8_PGW_TAG is missed")
378
379 if self.instance:
380 # We're good.
381 return
382
383 instance = self.make_instance()
384 self.instance = instance
385 super(TenantWithContainer, self).save()
386
387 def get_slice(self):
388 if not self.provider_service.slices.count():
389 raise XOSConfigurationError("The service has no slices")
390 slice = self.provider_service.slices.all()[0]
391 return slice
392
393 def make_instance(self):
394 slice = self.provider_service.slices.all()[0]
395 flavors = Flavor.objects.filter(name=slice.default_flavor)
396# flavors = Flavor.objects.filter(name="m1.xlarge")
397 if not flavors:
398 raise XOSConfigurationError("No default flavor")
399 default_flavor = slice.default_flavor
400 slice = self.provider_service.slices.all()[0]
401 if slice.default_isolation == "container_vm":
402 (node, parent) = ContainerVmScheduler(slice).pick()
403 else:
404 (node, parent) = LeastLoadedNodeScheduler(slice).pick()
405 instance = Instance(slice = slice,
406 node = node,
407 image = self.image,
408 creator = self.creator,
409 deployment = node.site_deployment.deployment,
410 flavor = flavors[0],
411 isolation = slice.default_isolation,
412 parent = parent)
413 self.save_instance(instance)
414 return instance
415
416 def ip_to_mac(self, ip):
417 (a, b, c, d) = ip.split('.')
418 return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
419
420 # Getter for the message that will appear on the webpage
421 # By default it is "Hello World!"
422 @property
423 def display_message(self):
424 return self.get_attribute(
425 "display_message",
426 self.default_attributes['display_message'])
427
428 @display_message.setter
429 def display_message(self, value):
430 self.set_attribute("display_message", value)
431
432 @property
433 def s5s8_pgw_tag(self):
434 return self.get_attribute(
435 "s5s8_pgw_tag",
436 self.default_attributes['s5s8_pgw_tag'])
437
438 @s5s8_pgw_tag.setter
439 def s5s8_pgw_tag(self, value):
440 self.set_attribute("s5s8_pgw_tag", value)
441
442
443 @property
444 def addresses(self):
445 if (not self.id) or (not self.instance):
446 return {}
447
448 addresses = {}
449 for ns in self.instance.ports.all():
450 if "s5s8_pgw" in ns.network.name.lower():
451 addresses["s5s8_pgw"] = (ns.ip, ns.mac)
452 return addresses
453
454
455 @property
456 def s5s8_pgw_ip(self):
457 return self.addresses.get("s5s8_pgw", (None, None))[0]
458 @property
459 def s5s8_pgw_mac(self):
460 return self.addresses.get("s5s8_pgw", (None, None))[1]
461
462def model_policy_mcord_servicecomponent(pk):
463 # This section of code is atomic to prevent race conditions
464 with transaction.atomic():
465 # We find all of the tenants that are waiting to update
466 component = VBBUComponent.objects.select_for_update().filter(pk=pk)
467 if not component:
468 return
469 # Since this code is atomic it is safe to always use the first tenant
470 component = component[0]
471 component.manage_container()
472
473
474def model_policy_mcord_servicecomponent(pk):
475 # This section of code is atomic to prevent race conditions
476 with transaction.atomic():
477 # We find all of the tenants that are waiting to update
478 component = VPGWCComponent.objects.select_for_update().filter(pk=pk)
479 if not component:
480 return
481 # Since this code is atomic it is safe to always use the first tenant
482 component = component[0]
483 component.manage_container()