blob: 987260ba4d572333c54e20a0b95a9bed027000de [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.
63 default_attributes = {"display_message": "New vPGWC Component", "s5s8_pgw_tag": "300"}
64 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
186 # Getter for the message that will appear on the webpage
187 # By default it is "Hello World!"
188 @property
189 def display_message(self):
190 return self.get_attribute(
191 "display_message",
192 self.default_attributes['display_message'])
193
194 @display_message.setter
195 def display_message(self, value):
196 self.set_attribute("display_message", value)
197
198 @property
199 def s5s8_pgw_tag(self):
200 return self.get_attribute(
201 "s5s8_pgw_tag",
202 self.default_attributes['s5s8_pgw_tag'])
203
204 @s5s8_pgw_tag.setter
205 def s5s8_pgw_tag(self, value):
206 self.set_attribute("s5s8_pgw_tag", value)
207
208
209 @property
210 def addresses(self):
211 if (not self.id) or (not self.instance):
212 return {}
213
214 addresses = {}
215 for ns in self.instance.ports.all():
216 if "s5s8_pgw" in ns.network.name.lower():
217 addresses["s5s8_pgw"] = (ns.ip, ns.mac)
218 return addresses
219
220
221 @property
222 def s5s8_pgw_ip(self):
223 return self.addresses.get("s5s8_pgw", (None, None))[0]
224 @property
225 def s5s8_pgw_mac(self):
226 return self.addresses.get("s5s8_pgw", (None, None))[1]
227
228
229
230def model_policy_mcord_servicecomponent(pk):
231 # This section of code is atomic to prevent race conditions
232 with transaction.atomic():
233 # We find all of the tenants that are waiting to update
Pingping Linc537fd92017-01-17 20:52:09 -0800234 tenant = VPGWCTenant.objects.select_for_update().filter(pk=pk)
235 if not tenant:
Pingping Linf3e24a92016-09-19 21:35:16 +0000236 return
237 # Since this code is atomic it is safe to always use the first tenant
Pingping Linc537fd92017-01-17 20:52:09 -0800238 tenant = tenant[0]
239 tenant.manage_container()