blob: 74b10b9e4626623acf24d514365430038fb2010d [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
JianHao052a7052017-02-08 05:59:11 +000092# def save_instance(self, instance):
93# with transaction.atomic():
94# super(VPGWCTenant, self).save_instance(instance)
95# 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
Pingping Linf3e24a92016-09-19 21:35:16 +0000106 def save_instance(self, instance):
107 with transaction.atomic():
Pingping Linc537fd92017-01-17 20:52:09 -0800108 super(VPGWCTenant, self).save_instance(instance)
JianHao39d5a8b2017-02-19 18:23:47 +0800109 #if instance.isolation in ["vm"]:
110 # if self.image_name == 'pgwu':
111 # lan_network = self.get_lan_network(instance, "wan_network")
112 # port = self.find_or_make_port(instance,lan_network)
113 # port.set_parameter("neutron_port_ip", "102.0.0.8")
114 # port.save()
Pingping Linf3e24a92016-09-19 21:35:16 +0000115
116 def delete(self, *args, **kwargs):
117 # Delete the instance that was created for this tenant
118 self.cleanup_container()
Pingping Linc537fd92017-01-17 20:52:09 -0800119 super(VPGWCTenant, self).delete(*args, **kwargs)
Pingping Linf3e24a92016-09-19 21:35:16 +0000120
121 def find_or_make_port(self, instance, network, **kwargs):
122 port = Port.objects.filter(instance=instance, network=network)
123 if port:
124 port = port[0]
125 print "port already exist", port[0]
126 else:
127 port = Port(instance=instance, network=network, **kwargs)
JianHao39d5a8b2017-02-19 18:23:47 +0800128 print "NETWORK", network, "MAKE_PORT", port
Pingping Linf3e24a92016-09-19 21:35:16 +0000129 port.save()
130 return port
131
132 def get_lan_network(self, instance, ntype):
133 slice = self.provider_service.slices.all()[0]
134 lan_networks = [x for x in slice.networks.all() if ntype in x.name]
135 if not lan_networks:
Pingping Linb8b186e2017-01-19 11:33:45 -0800136 raise XOSProgrammingError("No lan_network")
Pingping Linf3e24a92016-09-19 21:35:16 +0000137 return lan_networks[0]
138
139 def manage_container(self):
140 from core.models import Instance, Flavor
141
142 if self.deleted:
143 return
144
145 # For container or container_vm isolation, use what TenantWithCotnainer
146 # provides us
147 slice = self.get_slice()
148 if slice.default_isolation in ["container_vm", "container"]:
Pingping Linc537fd92017-01-17 20:52:09 -0800149 super(VPGWCTenant,self).manage_container()
Pingping Linf3e24a92016-09-19 21:35:16 +0000150 return
151
152 if not self.s5s8_pgw_tag:
Pingping Linb8b186e2017-01-19 11:33:45 -0800153 raise XOSConfigurationError("S5S8_PGW_TAG is missed")
Pingping Linf3e24a92016-09-19 21:35:16 +0000154
155 if self.instance:
156 # We're good.
157 return
158
159 instance = self.make_instance()
160 self.instance = instance
161 super(TenantWithContainer, self).save()
162
163 def get_slice(self):
164 if not self.provider_service.slices.count():
165 raise XOSConfigurationError("The service has no slices")
166 slice = self.provider_service.slices.all()[0]
167 return slice
168
169 def make_instance(self):
JianHao39d5a8b2017-02-19 18:23:47 +0800170 slice = self.provider_service.slices.all()[0]
Pingping Linf3e24a92016-09-19 21:35:16 +0000171 flavors = Flavor.objects.filter(name=slice.default_flavor)
172# flavors = Flavor.objects.filter(name="m1.xlarge")
173 if not flavors:
174 raise XOSConfigurationError("No default flavor")
175 default_flavor = slice.default_flavor
176 slice = self.provider_service.slices.all()[0]
177 if slice.default_isolation == "container_vm":
178 (node, parent) = ContainerVmScheduler(slice).pick()
179 else:
180 (node, parent) = LeastLoadedNodeScheduler(slice).pick()
181 instance = Instance(slice = slice,
182 node = node,
183 image = self.image,
184 creator = self.creator,
185 deployment = node.site_deployment.deployment,
186 flavor = flavors[0],
187 isolation = slice.default_isolation,
188 parent = parent)
189 self.save_instance(instance)
190 return instance
191
192 def ip_to_mac(self, ip):
193 (a, b, c, d) = ip.split('.')
194 return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
195
JianHao39d5a8b2017-02-19 18:23:47 +0800196
Saleil Bhat4f45a4f2017-02-01 17:04:22 -0800197 @property
198 def image(self):
199 img = self.image_name.strip()
200 if img.lower() != "default":
201 return Image.objects.get(name=img)
JianHao39d5a8b2017-02-19 18:23:47 +0800202 else:
Saleil Bhat4dc77d62017-02-03 13:27:38 -0800203 return super(VPGWCTenant, self).image
Saleil Bhat4f45a4f2017-02-01 17:04:22 -0800204
Pingping Linf3e24a92016-09-19 21:35:16 +0000205 # Getter for the message that will appear on the webpage
206 # By default it is "Hello World!"
207 @property
208 def display_message(self):
209 return self.get_attribute(
210 "display_message",
211 self.default_attributes['display_message'])
212
213 @display_message.setter
214 def display_message(self, value):
215 self.set_attribute("display_message", value)
216
217 @property
Saleil Bhat4f45a4f2017-02-01 17:04:22 -0800218 def image_name(self):
219 return self.get_attribute(
220 "image_name",
221 self.default_attributes['image_name'])
222
223 @image_name.setter
224 def image_name(self, value):
225 self.set_attribute("image_name", value)
226
227 @property
Pingping Linf3e24a92016-09-19 21:35:16 +0000228 def s5s8_pgw_tag(self):
229 return self.get_attribute(
230 "s5s8_pgw_tag",
231 self.default_attributes['s5s8_pgw_tag'])
232
233 @s5s8_pgw_tag.setter
234 def s5s8_pgw_tag(self, value):
235 self.set_attribute("s5s8_pgw_tag", value)
236
237
238 @property
239 def addresses(self):
240 if (not self.id) or (not self.instance):
241 return {}
242
243 addresses = {}
244 for ns in self.instance.ports.all():
245 if "s5s8_pgw" in ns.network.name.lower():
246 addresses["s5s8_pgw"] = (ns.ip, ns.mac)
247 return addresses
248
249
250 @property
251 def s5s8_pgw_ip(self):
252 return self.addresses.get("s5s8_pgw", (None, None))[0]
253 @property
254 def s5s8_pgw_mac(self):
255 return self.addresses.get("s5s8_pgw", (None, None))[1]
256
257
258
259def model_policy_mcord_servicecomponent(pk):
260 # This section of code is atomic to prevent race conditions
261 with transaction.atomic():
262 # We find all of the tenants that are waiting to update
Pingping Linc537fd92017-01-17 20:52:09 -0800263 tenant = VPGWCTenant.objects.select_for_update().filter(pk=pk)
264 if not tenant:
Pingping Linf3e24a92016-09-19 21:35:16 +0000265 return
266 # Since this code is atomic it is safe to always use the first tenant
Pingping Linc537fd92017-01-17 20:52:09 -0800267 tenant = tenant[0]
268 tenant.manage_container()