blob: bfc43a0968d0e19e51762e0aa0d072478caa35c8 [file] [log] [blame]
Pingping Linf3e24a92016-09-19 21:35:16 +00001from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, AddressPool, Port
2from core.models.plcorebase import StrippedCharField
3import os
4from django.db import models, transaction
5from django.forms.models import model_to_dict
6from django.db.models import Q
7from operator import itemgetter, attrgetter, methodcaller
8from core.models import Tag
9from core.models.service import LeastLoadedNodeScheduler
10import traceback
11from xos.exceptions import *
12from core.models import SlicePrivilege, SitePrivilege
13from sets import Set
14from xos.config import Config
15
Pingping Linc537fd92017-01-17 20:52:09 -080016MCORD_KIND = "EPC"
17SERVICE_NAME_VERBOSE = 'VPGWC Service'
18SERVICE_NAME_VERBOSE_PLURAL = 'VPGWC Services'
19TENANT_NAME_VERBOSE = 'VPGWC Service Tenant'
20TENANT_NAME_VERBOSE_PLURAL = 'VPGWC Service Tenants'
21
Pingping Linf3e24a92016-09-19 21:35:16 +000022MCORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
Pingping Linf3e24a92016-09-19 21:35:16 +000023vbbu_net_types = ("s1u", "s1mme", "rru")
24vpgwc_net_types = ("s5s8")
25# The class to represent the service. Most of the service logic is given for us
26# in the Service class but, we have some configuration that is specific for
27# this example.
28class VPGWCService(Service):
29 KIND = MCORD_KIND
30
31 class Meta:
32 # When the proxy field is set to True the model is represented as
33 # it's superclass in the database, but we can still change the python
34 # behavior. In this case HelloWorldServiceComplete is a Service in the
35 # database.
36 proxy = True
37 # The name used to find this service, all directories are named this
38 app_label = "vpgwc"
39 verbose_name = "vPGWC Service"
40
41# This is the class to represent the tenant. Most of the logic is given to use
42# in TenantWithContainer, however there is some configuration and logic that
43# we need to define for this example.
Pingping Linc537fd92017-01-17 20:52:09 -080044class VPGWCTenant(TenantWithContainer):
Pingping Linf3e24a92016-09-19 21:35:16 +000045
Pingping Linc537fd92017-01-17 20:52:09 -080046
47 # The kind of the service is used on forms to differentiate this service
48 # from the other services.
49 KIND = MCORD_KIND
Pingping Linf3e24a92016-09-19 21:35:16 +000050 class Meta:
51 # Same as a above, HelloWorldTenantComplete is represented as a
52 # TenantWithContainer, but we change the python behavior.
53 proxy = True
54 verbose_name = "VPGWC Service Component"
55
Pingping Linf3e24a92016-09-19 21:35:16 +000056 # Ansible requires that the sync_attributes field contain nat_ip and nat_mac
57 # these will be used to determine where to SSH to for ansible.
58 # Getters must be defined for every attribute specified here.
59 sync_attributes = ("s5s8_pgw_ip", "s5s8_pgw_mac")
60
61 # default_attributes is used cleanly indicate what the default values for
62 # the fields are.
Saleil Bhat4f45a4f2017-02-01 17:04:22 -080063 default_attributes = {"display_message": "New vPGWC Component", "s5s8_pgw_tag": "300", "image_name": "default"}
Pingping Linf3e24a92016-09-19 21:35:16 +000064 def __init__(self, *args, **kwargs):
Pingping Linc537fd92017-01-17 20:52:09 -080065 pgwc_services = VPGWCService.get_service_objects().all()
Pingping Linf3e24a92016-09-19 21:35:16 +000066 # When the tenant is created the default service in the form is set
67 # to be the first created HelloWorldServiceComplete
Pingping Linc537fd92017-01-17 20:52:09 -080068 if pgwc_services:
Pingping Linf3e24a92016-09-19 21:35:16 +000069 self._meta.get_field(
Pingping Linc537fd92017-01-17 20:52:09 -080070 "provider_service").default = pgwc_services[0].id
71 super(VPGWCTenant, self).__init__(*args, **kwargs)
Pingping Linf3e24a92016-09-19 21:35:16 +000072
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):
Pingping Linc537fd92017-01-17 20:52:09 -080078 '''
Pingping Linf3e24a92016-09-19 21:35:16 +000079 if not self.creator:
80 if not getattr(self, "caller", None):
81 # caller must be set when creating a monitoring channel since it creates a slice
82 raise XOSProgrammingError("ServiceComponents's self.caller was not set")
83 self.creator = self.caller
84 if not self.creator:
85 raise XOSProgrammingError("ServiceComponents's self.creator was not set")
Pingping Linc537fd92017-01-17 20:52:09 -080086 '''
87 super(VPGWCTenant, self).save(*args, **kwargs)
Pingping Linf3e24a92016-09-19 21:35:16 +000088 # This call needs to happen so that an instance is created for this
89 # tenant is created in the slice. One instance is created per tenant.
90 model_policy_mcord_servicecomponent(self.pk)
91
92 def save_instance(self, instance):
93 with transaction.atomic():
Pingping Linc537fd92017-01-17 20:52:09 -080094 super(VPGWCTenant, self).save_instance(instance)
Pingping Linf3e24a92016-09-19 21:35:16 +000095 if instance.isolation in ["vm"]:
96 for ntype in vpgwc_net_types:
97 lan_network = self.get_lan_network(instance, ntype)
98 port = self.find_or_make_port(instance,lan_network)
99 if (ntype == "s5s8"):
100 port.set_parameter("s_tag", self.s5s8_pgw_tag)
101 port.set_parameter("neutron_port_name", "stag-%s" % self.s5s8_pgw_tag)
102 port.save()
103 else:
104 return True
105
106 def delete(self, *args, **kwargs):
107 # Delete the instance that was created for this tenant
108 self.cleanup_container()
Pingping Linc537fd92017-01-17 20:52:09 -0800109 super(VPGWCTenant, self).delete(*args, **kwargs)
Pingping Linf3e24a92016-09-19 21:35:16 +0000110
111 def find_or_make_port(self, instance, network, **kwargs):
112 port = Port.objects.filter(instance=instance, network=network)
113 if port:
114 port = port[0]
115 print "port already exist", port[0]
116 else:
117 port = Port(instance=instance, network=network, **kwargs)
118 print "NETWORK", network, "MAKE_PORT", port
119 port.save()
120 return port
121
122 def get_lan_network(self, instance, ntype):
123 slice = self.provider_service.slices.all()[0]
124 lan_networks = [x for x in slice.networks.all() if ntype in x.name]
125 if not lan_networks:
Pingping Linb8b186e2017-01-19 11:33:45 -0800126 raise XOSProgrammingError("No lan_network")
Pingping Linf3e24a92016-09-19 21:35:16 +0000127 return lan_networks[0]
128
129 def manage_container(self):
130 from core.models import Instance, Flavor
131
132 if self.deleted:
133 return
134
135 # For container or container_vm isolation, use what TenantWithCotnainer
136 # provides us
137 slice = self.get_slice()
138 if slice.default_isolation in ["container_vm", "container"]:
Pingping Linc537fd92017-01-17 20:52:09 -0800139 super(VPGWCTenant,self).manage_container()
Pingping Linf3e24a92016-09-19 21:35:16 +0000140 return
141
142 if not self.s5s8_pgw_tag:
Pingping Linb8b186e2017-01-19 11:33:45 -0800143 raise XOSConfigurationError("S5S8_PGW_TAG is missed")
Pingping Linf3e24a92016-09-19 21:35:16 +0000144
145 if self.instance:
146 # We're good.
147 return
148
149 instance = self.make_instance()
150 self.instance = instance
151 super(TenantWithContainer, self).save()
152
153 def get_slice(self):
154 if not self.provider_service.slices.count():
155 raise XOSConfigurationError("The service has no slices")
156 slice = self.provider_service.slices.all()[0]
157 return slice
158
159 def make_instance(self):
160 slice = self.provider_service.slices.all()[0]
161 flavors = Flavor.objects.filter(name=slice.default_flavor)
162# flavors = Flavor.objects.filter(name="m1.xlarge")
163 if not flavors:
164 raise XOSConfigurationError("No default flavor")
165 default_flavor = slice.default_flavor
166 slice = self.provider_service.slices.all()[0]
167 if slice.default_isolation == "container_vm":
168 (node, parent) = ContainerVmScheduler(slice).pick()
169 else:
170 (node, parent) = LeastLoadedNodeScheduler(slice).pick()
171 instance = Instance(slice = slice,
172 node = node,
173 image = self.image,
174 creator = self.creator,
175 deployment = node.site_deployment.deployment,
176 flavor = flavors[0],
177 isolation = slice.default_isolation,
178 parent = parent)
179 self.save_instance(instance)
180 return instance
181
182 def ip_to_mac(self, ip):
183 (a, b, c, d) = ip.split('.')
184 return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
185
Saleil Bhat4f45a4f2017-02-01 17:04:22 -0800186
187 @property
188 def image(self):
189 img = self.image_name.strip()
190 if img.lower() != "default":
191 return Image.objects.get(name=img)
192 else:
Saleil Bhat4dc77d62017-02-03 13:27:38 -0800193 return super(VPGWCTenant, self).image
Saleil Bhat4f45a4f2017-02-01 17:04:22 -0800194
Pingping Linf3e24a92016-09-19 21:35:16 +0000195 # Getter for the message that will appear on the webpage
196 # By default it is "Hello World!"
197 @property
198 def display_message(self):
199 return self.get_attribute(
200 "display_message",
201 self.default_attributes['display_message'])
202
203 @display_message.setter
204 def display_message(self, value):
205 self.set_attribute("display_message", value)
206
207 @property
Saleil Bhat4f45a4f2017-02-01 17:04:22 -0800208 def image_name(self):
209 return self.get_attribute(
210 "image_name",
211 self.default_attributes['image_name'])
212
213 @image_name.setter
214 def image_name(self, value):
215 self.set_attribute("image_name", value)
216
217 @property
Pingping Linf3e24a92016-09-19 21:35:16 +0000218 def s5s8_pgw_tag(self):
219 return self.get_attribute(
220 "s5s8_pgw_tag",
221 self.default_attributes['s5s8_pgw_tag'])
222
223 @s5s8_pgw_tag.setter
224 def s5s8_pgw_tag(self, value):
225 self.set_attribute("s5s8_pgw_tag", value)
226
227
228 @property
229 def addresses(self):
230 if (not self.id) or (not self.instance):
231 return {}
232
233 addresses = {}
234 for ns in self.instance.ports.all():
235 if "s5s8_pgw" in ns.network.name.lower():
236 addresses["s5s8_pgw"] = (ns.ip, ns.mac)
237 return addresses
238
239
240 @property
241 def s5s8_pgw_ip(self):
242 return self.addresses.get("s5s8_pgw", (None, None))[0]
243 @property
244 def s5s8_pgw_mac(self):
245 return self.addresses.get("s5s8_pgw", (None, None))[1]
246
247
248
249def model_policy_mcord_servicecomponent(pk):
250 # This section of code is atomic to prevent race conditions
251 with transaction.atomic():
252 # We find all of the tenants that are waiting to update
Pingping Linc537fd92017-01-17 20:52:09 -0800253 tenant = VPGWCTenant.objects.select_for_update().filter(pk=pk)
254 if not tenant:
Pingping Linf3e24a92016-09-19 21:35:16 +0000255 return
256 # Since this code is atomic it is safe to always use the first tenant
Pingping Linc537fd92017-01-17 20:52:09 -0800257 tenant = tenant[0]
258 tenant.manage_container()