blob: c2f9b2e38ba77521cceef8253786eca13d6f9f53 [file] [log] [blame]
Matteo Scandolo86d63692017-08-08 13:05:26 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
Pingping Linf3e24a92016-09-19 21:35:16 +000017from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, AddressPool, Port
18from core.models.plcorebase import StrippedCharField
19import os
20from django.db import models, transaction
21from django.forms.models import model_to_dict
22from django.db.models import Q
23from operator import itemgetter, attrgetter, methodcaller
24from core.models import Tag
25from core.models.service import LeastLoadedNodeScheduler
26import traceback
27from xos.exceptions import *
28from core.models import SlicePrivilege, SitePrivilege
29from sets import Set
30from xos.config import Config
31
Pingping Linc537fd92017-01-17 20:52:09 -080032MCORD_KIND = "EPC"
33SERVICE_NAME_VERBOSE = 'VPGWC Service'
34SERVICE_NAME_VERBOSE_PLURAL = 'VPGWC Services'
35TENANT_NAME_VERBOSE = 'VPGWC Service Tenant'
36TENANT_NAME_VERBOSE_PLURAL = 'VPGWC Service Tenants'
37
Pingping Linf3e24a92016-09-19 21:35:16 +000038MCORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
Pingping Linf3e24a92016-09-19 21:35:16 +000039vbbu_net_types = ("s1u", "s1mme", "rru")
40vpgwc_net_types = ("s5s8")
41# The class to represent the service. Most of the service logic is given for us
42# in the Service class but, we have some configuration that is specific for
43# this example.
44class VPGWCService(Service):
45 KIND = MCORD_KIND
46
47 class Meta:
48 # When the proxy field is set to True the model is represented as
49 # it's superclass in the database, but we can still change the python
50 # behavior. In this case HelloWorldServiceComplete is a Service in the
51 # database.
52 proxy = True
53 # The name used to find this service, all directories are named this
54 app_label = "vpgwc"
55 verbose_name = "vPGWC Service"
56
57# This is the class to represent the tenant. Most of the logic is given to use
58# in TenantWithContainer, however there is some configuration and logic that
59# we need to define for this example.
Pingping Linc537fd92017-01-17 20:52:09 -080060class VPGWCTenant(TenantWithContainer):
Pingping Linf3e24a92016-09-19 21:35:16 +000061
Pingping Linc537fd92017-01-17 20:52:09 -080062
63 # The kind of the service is used on forms to differentiate this service
64 # from the other services.
65 KIND = MCORD_KIND
Pingping Linf3e24a92016-09-19 21:35:16 +000066 class Meta:
67 # Same as a above, HelloWorldTenantComplete is represented as a
68 # TenantWithContainer, but we change the python behavior.
69 proxy = True
70 verbose_name = "VPGWC Service Component"
71
Pingping Linf3e24a92016-09-19 21:35:16 +000072 # Ansible requires that the sync_attributes field contain nat_ip and nat_mac
73 # these will be used to determine where to SSH to for ansible.
74 # Getters must be defined for every attribute specified here.
75 sync_attributes = ("s5s8_pgw_ip", "s5s8_pgw_mac")
76
77 # default_attributes is used cleanly indicate what the default values for
78 # the fields are.
Saleil Bhat4f45a4f2017-02-01 17:04:22 -080079 default_attributes = {"display_message": "New vPGWC Component", "s5s8_pgw_tag": "300", "image_name": "default"}
Pingping Linf3e24a92016-09-19 21:35:16 +000080 def __init__(self, *args, **kwargs):
Pingping Linc537fd92017-01-17 20:52:09 -080081 pgwc_services = VPGWCService.get_service_objects().all()
Pingping Linf3e24a92016-09-19 21:35:16 +000082 # When the tenant is created the default service in the form is set
83 # to be the first created HelloWorldServiceComplete
Pingping Linc537fd92017-01-17 20:52:09 -080084 if pgwc_services:
Pingping Linf3e24a92016-09-19 21:35:16 +000085 self._meta.get_field(
Pingping Linc537fd92017-01-17 20:52:09 -080086 "provider_service").default = pgwc_services[0].id
87 super(VPGWCTenant, self).__init__(*args, **kwargs)
Pingping Linf3e24a92016-09-19 21:35:16 +000088
89 def can_update(self, user):
90 #Allow creation of this model instances for non-admin users also
91 return True
92
93 def save(self, *args, **kwargs):
Pingping Linc537fd92017-01-17 20:52:09 -080094 '''
Pingping Linf3e24a92016-09-19 21:35:16 +000095 if not self.creator:
96 if not getattr(self, "caller", None):
97 # caller must be set when creating a monitoring channel since it creates a slice
98 raise XOSProgrammingError("ServiceComponents's self.caller was not set")
99 self.creator = self.caller
100 if not self.creator:
101 raise XOSProgrammingError("ServiceComponents's self.creator was not set")
Pingping Linc537fd92017-01-17 20:52:09 -0800102 '''
103 super(VPGWCTenant, self).save(*args, **kwargs)
Pingping Linf3e24a92016-09-19 21:35:16 +0000104 # This call needs to happen so that an instance is created for this
105 # tenant is created in the slice. One instance is created per tenant.
106 model_policy_mcord_servicecomponent(self.pk)
107
JianHao052a7052017-02-08 05:59:11 +0000108# def save_instance(self, instance):
109# with transaction.atomic():
110# super(VPGWCTenant, self).save_instance(instance)
111# if instance.isolation in ["vm"]:
112# for ntype in vpgwc_net_types:
113# lan_network = self.get_lan_network(instance, ntype)
114# port = self.find_or_make_port(instance,lan_network)
115# if (ntype == "s5s8"):
116# port.set_parameter("s_tag", self.s5s8_pgw_tag)
117# port.set_parameter("neutron_port_name", "stag-%s" % self.s5s8_pgw_tag)
118# port.save()
119# else:
120# return True
121
Pingping Linf3e24a92016-09-19 21:35:16 +0000122 def save_instance(self, instance):
123 with transaction.atomic():
Pingping Linc537fd92017-01-17 20:52:09 -0800124 super(VPGWCTenant, self).save_instance(instance)
JianHao39d5a8b2017-02-19 18:23:47 +0800125 #if instance.isolation in ["vm"]:
126 # if self.image_name == 'pgwu':
127 # lan_network = self.get_lan_network(instance, "wan_network")
128 # port = self.find_or_make_port(instance,lan_network)
129 # port.set_parameter("neutron_port_ip", "102.0.0.8")
130 # port.save()
Pingping Linf3e24a92016-09-19 21:35:16 +0000131
132 def delete(self, *args, **kwargs):
133 # Delete the instance that was created for this tenant
134 self.cleanup_container()
Pingping Linc537fd92017-01-17 20:52:09 -0800135 super(VPGWCTenant, self).delete(*args, **kwargs)
Pingping Linf3e24a92016-09-19 21:35:16 +0000136
137 def find_or_make_port(self, instance, network, **kwargs):
138 port = Port.objects.filter(instance=instance, network=network)
139 if port:
140 port = port[0]
141 print "port already exist", port[0]
142 else:
143 port = Port(instance=instance, network=network, **kwargs)
JianHao39d5a8b2017-02-19 18:23:47 +0800144 print "NETWORK", network, "MAKE_PORT", port
Pingping Linf3e24a92016-09-19 21:35:16 +0000145 port.save()
146 return port
147
148 def get_lan_network(self, instance, ntype):
149 slice = self.provider_service.slices.all()[0]
150 lan_networks = [x for x in slice.networks.all() if ntype in x.name]
151 if not lan_networks:
Pingping Linb8b186e2017-01-19 11:33:45 -0800152 raise XOSProgrammingError("No lan_network")
Pingping Linf3e24a92016-09-19 21:35:16 +0000153 return lan_networks[0]
154
155 def manage_container(self):
156 from core.models import Instance, Flavor
157
158 if self.deleted:
159 return
160
161 # For container or container_vm isolation, use what TenantWithCotnainer
162 # provides us
163 slice = self.get_slice()
164 if slice.default_isolation in ["container_vm", "container"]:
Pingping Linc537fd92017-01-17 20:52:09 -0800165 super(VPGWCTenant,self).manage_container()
Pingping Linf3e24a92016-09-19 21:35:16 +0000166 return
167
168 if not self.s5s8_pgw_tag:
Pingping Linb8b186e2017-01-19 11:33:45 -0800169 raise XOSConfigurationError("S5S8_PGW_TAG is missed")
Pingping Linf3e24a92016-09-19 21:35:16 +0000170
171 if self.instance:
172 # We're good.
173 return
174
175 instance = self.make_instance()
176 self.instance = instance
177 super(TenantWithContainer, self).save()
178
179 def get_slice(self):
180 if not self.provider_service.slices.count():
181 raise XOSConfigurationError("The service has no slices")
182 slice = self.provider_service.slices.all()[0]
183 return slice
184
185 def make_instance(self):
JianHao39d5a8b2017-02-19 18:23:47 +0800186 slice = self.provider_service.slices.all()[0]
Pingping Linf3e24a92016-09-19 21:35:16 +0000187 flavors = Flavor.objects.filter(name=slice.default_flavor)
188# flavors = Flavor.objects.filter(name="m1.xlarge")
189 if not flavors:
190 raise XOSConfigurationError("No default flavor")
191 default_flavor = slice.default_flavor
192 slice = self.provider_service.slices.all()[0]
193 if slice.default_isolation == "container_vm":
194 (node, parent) = ContainerVmScheduler(slice).pick()
195 else:
196 (node, parent) = LeastLoadedNodeScheduler(slice).pick()
197 instance = Instance(slice = slice,
198 node = node,
199 image = self.image,
200 creator = self.creator,
201 deployment = node.site_deployment.deployment,
202 flavor = flavors[0],
203 isolation = slice.default_isolation,
204 parent = parent)
205 self.save_instance(instance)
206 return instance
207
208 def ip_to_mac(self, ip):
209 (a, b, c, d) = ip.split('.')
210 return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
211
JianHao39d5a8b2017-02-19 18:23:47 +0800212
Saleil Bhat4f45a4f2017-02-01 17:04:22 -0800213 @property
214 def image(self):
215 img = self.image_name.strip()
216 if img.lower() != "default":
217 return Image.objects.get(name=img)
JianHao39d5a8b2017-02-19 18:23:47 +0800218 else:
Saleil Bhat4dc77d62017-02-03 13:27:38 -0800219 return super(VPGWCTenant, self).image
Saleil Bhat4f45a4f2017-02-01 17:04:22 -0800220
Pingping Linf3e24a92016-09-19 21:35:16 +0000221 # Getter for the message that will appear on the webpage
222 # By default it is "Hello World!"
223 @property
224 def display_message(self):
225 return self.get_attribute(
226 "display_message",
227 self.default_attributes['display_message'])
228
229 @display_message.setter
230 def display_message(self, value):
231 self.set_attribute("display_message", value)
232
233 @property
Saleil Bhat4f45a4f2017-02-01 17:04:22 -0800234 def image_name(self):
235 return self.get_attribute(
236 "image_name",
237 self.default_attributes['image_name'])
238
239 @image_name.setter
240 def image_name(self, value):
241 self.set_attribute("image_name", value)
242
243 @property
Pingping Linf3e24a92016-09-19 21:35:16 +0000244 def s5s8_pgw_tag(self):
245 return self.get_attribute(
246 "s5s8_pgw_tag",
247 self.default_attributes['s5s8_pgw_tag'])
248
249 @s5s8_pgw_tag.setter
250 def s5s8_pgw_tag(self, value):
251 self.set_attribute("s5s8_pgw_tag", value)
252
253
254 @property
255 def addresses(self):
256 if (not self.id) or (not self.instance):
257 return {}
258
259 addresses = {}
260 for ns in self.instance.ports.all():
261 if "s5s8_pgw" in ns.network.name.lower():
262 addresses["s5s8_pgw"] = (ns.ip, ns.mac)
263 return addresses
264
265
266 @property
267 def s5s8_pgw_ip(self):
268 return self.addresses.get("s5s8_pgw", (None, None))[0]
269 @property
270 def s5s8_pgw_mac(self):
271 return self.addresses.get("s5s8_pgw", (None, None))[1]
272
273
274
275def model_policy_mcord_servicecomponent(pk):
276 # This section of code is atomic to prevent race conditions
277 with transaction.atomic():
278 # We find all of the tenants that are waiting to update
Pingping Linc537fd92017-01-17 20:52:09 -0800279 tenant = VPGWCTenant.objects.select_for_update().filter(pk=pk)
280 if not tenant:
Pingping Linf3e24a92016-09-19 21:35:16 +0000281 return
282 # Since this code is atomic it is safe to always use the first tenant
Pingping Linc537fd92017-01-17 20:52:09 -0800283 tenant = tenant[0]
284 tenant.manage_container()