blob: 254b5a00d1031457eaa4f7d29a35149bbd30d3a5 [file] [log] [blame]
Matteo Scandoloeb3487c2017-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 Lind28ab982016-08-29 18:42:52 +000017from django.db import models
Sapan Bhatia851e30c2017-05-19 23:10:52 +020018from core.models import Service, XOSBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, AddressPool, Port
19from core.models.xosbase import StrippedCharField
Pingping Lind28ab982016-08-29 18:42:52 +000020import os
21from django.db import models, transaction
22from django.forms.models import model_to_dict
23from django.db.models import Q
24from operator import itemgetter, attrgetter, methodcaller
25from core.models import Tag
26from core.models.service import LeastLoadedNodeScheduler
27import traceback
28from xos.exceptions import *
29from core.models import SlicePrivilege, SitePrivilege
30from sets import Set
31from xos.config import Config
32
33MCORD_KIND = "RAN" # This should be changed later I did it fo demo
34MCORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
35VBBU_KIND = "RAN"
36VSGW_KIND = "vSGW"
37VPGWC_KIND = "RAN"
38vbbu_net_types = ("s1u", "s1mme", "rru")
39vpgwc_net_types = ("s5s8")
40# The class to represent the service. Most of the service logic is given for us
41# in the Service class but, we have some configuration that is specific for
42# this example.
43class MCORDService(Service):
44 KIND = MCORD_KIND
45
46 class Meta:
47 # When the proxy field is set to True the model is represented as
48 # it's superclass in the database, but we can still change the python
49 # behavior. In this case HelloWorldServiceComplete is a Service in the
50 # database.
51 proxy = True
52 # The name used to find this service, all directories are named this
Pingping Lincff3a022016-09-19 21:29:54 +000053 app_label = "vbbu"
Pingping Lind28ab982016-08-29 18:42:52 +000054 verbose_name = "MCORD Service"
55
56# This is the class to represent the tenant. Most of the logic is given to use
57# in TenantWithContainer, however there is some configuration and logic that
58# we need to define for this example.
59class VBBUComponent(TenantWithContainer):
60
61 class Meta:
62 # Same as a above, HelloWorldTenantComplete is represented as a
63 # TenantWithContainer, but we change the python behavior.
64 proxy = True
65 verbose_name = "VBBU MCORD Service Component"
66
67 # The kind of the service is used on forms to differentiate this service
68 # from the other services.
69 KIND = VBBU_KIND
70
71 # Ansible requires that the sync_attributes field contain nat_ip and nat_mac
72 # these will be used to determine where to SSH to for ansible.
73 # Getters must be defined for every attribute specified here.
74 sync_attributes = ("s1u_ip", "s1u_mac",
75 "s1mme_ip", "s1mme_mac",
76 "rru_ip", "rru_mac")
77 # default_attributes is used cleanly indicate what the default values for
78 # the fields are.
79 default_attributes = {"display_message": "New vBBU Component", "s1u_tag": "901", "s1mme_tag": "900", "rru_tag": "999"}
80 def __init__(self, *args, **kwargs):
81 mcord_services = MCORDService.get_service_objects().all()
82 # When the tenant is created the default service in the form is set
83 # to be the first created HelloWorldServiceComplete
84 if mcord_services:
85 self._meta.get_field(
86 "provider_service").default = mcord_services[0].id
87 super(VBBUComponent, self).__init__(*args, **kwargs)
88
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):
94 if not self.creator:
95 if not getattr(self, "caller", None):
96 # caller must be set when creating a monitoring channel since it creates a slice
97 raise XOSProgrammingError("ServiceComponents's self.caller was not set")
98 self.creator = self.caller
99 if not self.creator:
100 raise XOSProgrammingError("ServiceComponents's self.creator was not set")
101
102 super(VBBUComponent, self).save(*args, **kwargs)
103 # This call needs to happen so that an instance is created for this
104 # tenant is created in the slice. One instance is created per tenant.
105 model_policy_mcord_servicecomponent(self.pk)
106
107 def save_instance(self, instance):
108 with transaction.atomic():
109 super(VBBUComponent, self).save_instance(instance)
110 if instance.isolation in ["vm"]:
111 for ntype in vbbu_net_types:
112 lan_network = self.get_lan_network(instance, ntype)
113 port = self.find_or_make_port(instance,lan_network)
114 if (ntype == "s1u"):
115 port.set_parameter("s_tag", self.s1u_tag)
116 port.set_parameter("neutron_port_name", "stag-%s" % self.s1u_tag)
117 port.save()
118 elif (ntype == "s1mme"):
119 port.set_parameter("s_tag", self.s1mme_tag)
120 port.set_parameter("neutron_port_name", "stag-%s" % self.s1mme_tag)
121 port.save()
122 elif (ntype == "rru"):
123 port.set_parameter("s_tag", self.rru_tag)
124 port.set_parameter("neutron_port_name", "stag-%s" % self.rru_tag)
125 port.save()
126
127 def delete(self, *args, **kwargs):
128 # Delete the instance that was created for this tenant
129 self.cleanup_container()
130 super(VBBUComponent, self).delete(*args, **kwargs)
131
132 def find_or_make_port(self, instance, network, **kwargs):
133 port = Port.objects.filter(instance=instance, network=network)
134 if port:
135 port = port[0]
136 print "port already exist", port[0]
137 else:
138 port = Port(instance=instance, network=network, **kwargs)
139 print "NETWORK", network, "MAKE_PORT", port
140 port.save()
141 return port
142
143 def get_lan_network(self, instance, ntype):
144 slice = self.provider_service.slices.all()[0]
145 lan_networks = [x for x in slice.networks.all() if ntype in x.name]
146 if not lan_networks:
147 raise XOSProgrammingError("No lan_network")
148 return lan_networks[0]
149
150 def manage_container(self):
151 from core.models import Instance, Flavor
152
153 if self.deleted:
154 return
155
156 # For container or container_vm isolation, use what TenantWithCotnainer
157 # provides us
158 slice = self.get_slice()
159 if slice.default_isolation in ["container_vm", "container"]:
160 super(VBBUComponent,self).manage_container()
161 return
162
163 if not self.s1u_tag:
164 raise XOSConfigurationError("S1U_TAG is missed")
165
166 if not self.s1mme_tag:
167 raise XOSConfigurationError("S1U_TAG is missed")
168
169 if not self.rru_tag:
170 raise XOSConfigurationError("S1U_TAG is missed")
171
172 if self.instance:
173 # We're good.
174 return
175
176 instance = self.make_instance()
177 self.instance = instance
178 super(TenantWithContainer, self).save()
179
180 def get_slice(self):
181 if not self.provider_service.slices.count():
182 raise XOSConfigurationError("The service has no slices")
183 slice = self.provider_service.slices.all()[0]
184 return slice
185
186 def make_instance(self):
187 slice = self.provider_service.slices.all()[0]
188 flavors = Flavor.objects.filter(name=slice.default_flavor)
189# flavors = Flavor.objects.filter(name="m1.xlarge")
190 if not flavors:
191 raise XOSConfigurationError("No default flavor")
192 default_flavor = slice.default_flavor
193 slice = self.provider_service.slices.all()[0]
194 if slice.default_isolation == "container_vm":
195 (node, parent) = ContainerVmScheduler(slice).pick()
196 else:
197 (node, parent) = LeastLoadedNodeScheduler(slice).pick()
198 instance = Instance(slice = slice,
199 node = node,
200 image = self.image,
201 creator = self.creator,
202 deployment = node.site_deployment.deployment,
203 flavor = flavors[0],
204 isolation = slice.default_isolation,
205 parent = parent)
206 self.save_instance(instance)
207 return instance
208
209 def ip_to_mac(self, ip):
210 (a, b, c, d) = ip.split('.')
211 return "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
212
213 # Getter for the message that will appear on the webpage
214 # By default it is "Hello World!"
215 @property
216 def display_message(self):
217 return self.get_attribute(
218 "display_message",
219 self.default_attributes['display_message'])
220
221 @display_message.setter
222 def display_message(self, value):
223 self.set_attribute("display_message", value)
224
225 @property
226 def s1u_tag(self):
227 return self.get_attribute(
228 "s1u_tag",
229 self.default_attributes['s1u_tag'])
230
231 @s1u_tag.setter
232 def s1u_tag(self, value):
233 self.set_attribute("s1u_tag", value)
234
235 @property
236 def s1mme_tag(self):
237 return self.get_attribute(
238 "s1mme_tag",
239 self.default_attributes['s1mme_tag'])
240
241 @s1mme_tag.setter
242 def s1mme_tag(self, value):
243 self.set_attribute("s1mme_tag", value)
244
245 @property
246 def rru_tag(self):
247 return self.get_attribute(
248 "rru_tag",
249 self.default_attributes['rru_tag'])
250
251 @rru_tag.setter
252 def rru_tag(self, value):
253 self.set_attribute("rru_tag", value)
254
255
256 @property
257 def addresses(self):
258 if (not self.id) or (not self.instance):
259 return {}
260
261 addresses = {}
262 for ns in self.instance.ports.all():
263 if "s1u" in ns.network.name.lower():
264 addresses["s1u"] = (ns.ip, ns.mac)
265 elif "s1mme" in ns.network.name.lower():
266 addresses["s1mme"] = (ns.ip, ns.mac)
267 elif "rru" in ns.network.name.lower():
268 addresses["rru"] = (ns.ip, ns.mac)
269 return addresses
270
271
272 @property
273 def s1u_ip(self):
274 return self.addresses.get("s1u", (None, None))[0]
275 @property
276 def s1u_mac(self):
277 return self.addresses.get("s1u", (None, None))[1]
278 @property
279 def s1mme_ip(self):
280 return self.addresses.get("s1mme", (None, None))[0]
281 @property
282 def s1mme_mac(self):
283 return self.addresses.get("s1mme", (None, None))[1]
284 @property
285 def rru_ip(self):
286 return self.addresses.get("rru", (None, None))[0]
287 @property
288 def rru_mac(self):
289 return self.addresses.get("rru", (None, None))[1]
290
291
Pingping Lind28ab982016-08-29 18:42:52 +0000292def model_policy_mcord_servicecomponent(pk):
293 # This section of code is atomic to prevent race conditions
294 with transaction.atomic():
295 # We find all of the tenants that are waiting to update
296 component = VBBUComponent.objects.select_for_update().filter(pk=pk)
297 if not component:
298 return
299 # Since this code is atomic it is safe to always use the first tenant
300 component = component[0]
301 component.manage_container()