blob: 05aeebc1cdce8dc4cef98866513a49bfce215c40 [file] [log] [blame]
Sapan Bhatia46482ee2017-04-21 17:59:48 +02001from header import *
Scott Baker761e1062016-06-20 17:18:17 -07002
Scott Baker761e1062016-06-20 17:18:17 -07003
Scott Baker761e1062016-06-20 17:18:17 -07004
Sapan Bhatia46482ee2017-04-21 17:59:48 +02005#from core.models.service import Service
6from core.models import Service
7
8
9
10#from core.models.tenantwithcontainer import TenantWithContainer
11from core.models import TenantWithContainer
12
13
14
15
Scott Baker761e1062016-06-20 17:18:17 -070016
17class VSGService(Service):
Scott Baker761e1062016-06-20 17:18:17 -070018
Sapan Bhatia46482ee2017-04-21 17:59:48 +020019 KIND = "vCPE"
Scott Baker761e1062016-06-20 17:18:17 -070020
Sapan Bhatia46482ee2017-04-21 17:59:48 +020021 class Meta:
22 app_label = "vsg"
23 name = "vsg"
24 verbose_name = "vSG Service"
Scott Baker761e1062016-06-20 17:18:17 -070025
Sapan Bhatia46482ee2017-04-21 17:59:48 +020026 # Primitive Fields (Not Relations)
27 url_filter_kind = StrippedCharField( blank = True, max_length = 30, null = True, db_index = False, choices = ((None, 'None'), ('safebrowsing', 'Safe Browsing'), ('answerx', 'AnswerX')) )
28 dns_servers = StrippedCharField( default = "8.8.8.8", max_length = 255, null = False, db_index = False, blank = False )
29 node_label = StrippedCharField( db_index = False, max_length = 30, null = True, blank = True )
30 docker_image_name = StrippedCharField( default = "docker.io/xosproject/vsg", max_length = 255, null = False, db_index = False, blank = False )
31 docker_insecure_registry = BooleanField( default = False, null = False, blank = True, db_index = False )
32
33
34 # Relations
35
36
37
38 pass
39
40
41
Scott Baker761e1062016-06-20 17:18:17 -070042
43class VSGTenant(TenantWithContainer):
Scott Baker761e1062016-06-20 17:18:17 -070044
Sapan Bhatia46482ee2017-04-21 17:59:48 +020045 KIND = "vCPE"
Scott Bakerecee9b12017-03-08 09:56:20 -080046
Sapan Bhatia46482ee2017-04-21 17:59:48 +020047 class Meta:
48 app_label = "vsg"
49 name = "vsg"
50 verbose_name = "vSG Service"
Scott Bakerecee9b12017-03-08 09:56:20 -080051
Sapan Bhatia46482ee2017-04-21 17:59:48 +020052 # Primitive Fields (Not Relations)
53 last_ansible_hash = StrippedCharField( db_index = False, max_length = 128, null = True, blank = True )
54
Scott Baker761e1062016-06-20 17:18:17 -070055
Sapan Bhatia46482ee2017-04-21 17:59:48 +020056 # Relations
57
Scott Baker761e1062016-06-20 17:18:17 -070058
Sapan Bhatia46482ee2017-04-21 17:59:48 +020059 sync_attributes = ("wan_container_ip", "wan_container_mac", "wan_container_netbits",
60 "wan_container_gateway_ip", "wan_container_gateway_mac",
61 "wan_vm_ip", "wan_vm_mac")
62
63 def __init__(self, *args, **kwargs):
64 super(VSGTenant, self).__init__(*args, **kwargs)
65 self.cached_vrouter=None
66
67 @property
68 def vrouter(self):
69 vrouter = self.get_newest_subscribed_tenant(VRouterTenant)
70 if not vrouter:
71 return None
72
73 # always return the same object when possible
74 if (self.cached_vrouter) and (self.cached_vrouter.id == vrouter.id):
75 return self.cached_vrouter
76
77 vrouter.caller = self.creator
78 self.cached_vrouter = vrouter
79 return vrouter
80
81 @vrouter.setter
82 def vrouter(self, value):
83 raise XOSConfigurationError("VSGTenant.vrouter setter is not implemented")
84
85 @property
86 def volt(self):
87 from services.volt.models import VOLTTenant
88 if not self.subscriber_tenant:
89 return None
90 volts = VOLTTenant.objects.filter(id=self.subscriber_tenant.id)
91 if not volts:
92 return None
93 return volts[0]
94
95 @volt.setter
96 def volt(self, value):
97 raise XOSConfigurationError("VSGTenant.volt setter is not implemented")
98
99 @property
100 def ssh_command(self):
101 if self.instance:
102 return self.instance.get_ssh_command()
103 else:
104 return "no-instance"
105
106 def get_vrouter_field(self, name, default=None):
107 if self.vrouter:
108 return getattr(self.vrouter, name, default)
109 else:
110 return default
111
112 @property
113 def wan_container_ip(self):
114 return self.get_vrouter_field("public_ip", None)
115
116 @property
117 def wan_container_mac(self):
118 return self.get_vrouter_field("public_mac", None)
119
120 @property
121 def wan_container_netbits(self):
122 return self.get_vrouter_field("netbits", None)
123
124 @property
125 def wan_container_gateway_ip(self):
126 return self.get_vrouter_field("gateway_ip", None)
127
128 @property
129 def wan_container_gateway_mac(self):
130 return self.get_vrouter_field("gateway_mac", None)
131
132 @property
133 def wan_vm_ip(self):
134 tags = Tag.select_by_content_object(self.instance).filter(name="vm_vrouter_tenant")
135 if tags:
136 tenant = VRouterTenant.objects.get(id=tags[0].value)
137 return tenant.public_ip
138 else:
139 raise Exception("no vm_vrouter_tenant tag for instance %s" % o.instance)
140
141 @property
142 def wan_vm_mac(self):
143 tags = Tag.select_by_content_object(self.instance).filter(name="vm_vrouter_tenant")
144 if tags:
145 tenant = VRouterTenant.objects.get(id=tags[0].value)
146 return tenant.public_mac
147 else:
148 raise Exception("no vm_vrouter_tenant tag for instance %s" % o.instance)
149
150 @property
151 def is_synced(self):
152 return (self.enacted is not None) and (self.enacted >= self.updated)
153
154 @is_synced.setter
155 def is_synced(self, value):
156 pass
157
158 def get_vrouter_service(self):
159 vrouterServices = VRouterService.get_service_objects().all()
160 if not vrouterServices:
161 raise XOSConfigurationError("No VROUTER Services available")
162 return vrouterServices[0]
163
164 def manage_vrouter(self):
165 # Each vCPE object owns exactly one vRouterTenant object
166
167 if self.deleted:
168 return
169
170 if self.vrouter is None:
171 vrouter = self.get_vrouter_service().get_tenant(address_pool_name="addresses_vsg", subscriber_tenant = self)
172 vrouter.caller = self.creator
173 vrouter.save()
174
175 def cleanup_vrouter(self):
176 if self.vrouter:
177 # print "XXX cleanup vrouter", self.vrouter
178 self.vrouter.delete()
179
180 def cleanup_orphans(self):
181 # ensure vCPE only has one vRouter
182 cur_vrouter = self.vrouter
183 for vrouter in list(self.get_subscribed_tenants(VRouterTenant)):
184 if (not cur_vrouter) or (vrouter.id != cur_vrouter.id):
185 # print "XXX clean up orphaned vrouter", vrouter
186 vrouter.delete()
187
188 if self.orig_instance_id and (self.orig_instance_id != self.get_attribute("instance_id")):
189 instances=Instance.objects.filter(id=self.orig_instance_id)
190 if instances:
191 # print "XXX clean up orphaned instance", instances[0]
192 instances[0].delete()
193
194 def get_slice(self):
195 if not self.provider_service.slices.count():
196 print self, "dio porco"
197 raise XOSConfigurationError("The service has no slices")
198 slice = self.provider_service.slices.all()[0]
199 return slice
200
201 def get_vsg_service(self):
202 return VSGService.get_service_objects().get(id=self.provider_service.id)
203
204 def find_instance_for_s_tag(self, s_tag):
205 #s_tags = STagBlock.objects.find(s_s_tag)
206 #if s_tags:
207 # return s_tags[0].instance
208
209 tags = Tag.objects.filter(name="s_tag", value=s_tag)
210 if tags:
211 return tags[0].content_object
212
213 return None
214
215 def find_or_make_instance_for_s_tag(self, s_tag):
216 instance = self.find_instance_for_s_tag(self.volt.s_tag)
217 if instance:
218 return instance
219
220 flavors = Flavor.objects.filter(name="m1.small")
221 if not flavors:
222 raise XOSConfigurationError("No m1.small flavor")
223
224 slice = self.provider_service.slices.all()[0]
225
226 if slice.default_isolation == "container_vm":
227 (node, parent) = ContainerVmScheduler(slice).pick()
228 else:
229 (node, parent) = LeastLoadedNodeScheduler(slice, label=self.get_vsg_service().node_label).pick()
230
231 instance = Instance(slice = slice,
232 node = node,
233 image = self.image,
234 creator = self.creator,
235 deployment = node.site_deployment.deployment,
236 flavor = flavors[0],
237 isolation = slice.default_isolation,
238 parent = parent)
239
240 self.save_instance(instance)
241
242 return instance
243
244 def manage_container(self):
245 from core.models import Instance, Flavor
246
247 if self.deleted:
248 return
249
250 # For container or container_vm isolation, use what TenantWithCotnainer
251 # provides us
252 slice = self.get_slice()
253 if slice.default_isolation in ["container_vm", "container"]:
254 super(VSGTenant,self).manage_container()
255 return
256
257 if not self.volt:
258 raise XOSConfigurationError("This vCPE container has no volt")
259
260 if self.instance:
261 # We're good.
262 return
263
264 instance = self.find_or_make_instance_for_s_tag(self.volt.s_tag)
265 self.instance = instance
266 super(TenantWithContainer, self).save()
267
268 def cleanup_container(self):
269 if self.get_slice().default_isolation in ["container_vm", "container"]:
270 super(VSGTenant,self).cleanup_container()
271
272 # To-do: cleanup unused instances
273 pass
274
275 def find_or_make_port(self, instance, network, **kwargs):
276 port = Port.objects.filter(instance=instance, network=network)
277 if port:
278 port = port[0]
279 else:
280 port = Port(instance=instance, network=network, **kwargs)
281 port.save()
282 return port
283
284 def get_lan_network(self, instance):
285 slice = self.provider_service.slices.all()[0]
286 # there should only be one network private network, and its template should not be the management template
287 lan_networks = [x for x in slice.networks.all() if x.template.visibility=="private" and (not "management" in x.template.name)]
288 if len(lan_networks)>1:
289 raise XOSProgrammingError("The vSG slice should only have one non-management private network")
290 if not lan_networks:
291 raise XOSProgrammingError("No lan_network")
292 return lan_networks[0]
293
294 def save_instance(self, instance):
295 with transaction.atomic():
296 instance.volumes = "/etc/dnsmasq.d,/etc/ufw"
297 super(VSGTenant, self).save_instance(instance)
298
299 if instance.isolation in ["container", "container_vm"]:
300 lan_network = self.get_lan_network(instance)
301 port = self.find_or_make_port(instance, lan_network, ip="192.168.0.1", port_id="unmanaged")
302 port.set_parameter("c_tag", self.volt.c_tag)
303 port.set_parameter("s_tag", self.volt.s_tag)
304 port.set_parameter("device", "eth1")
305 port.set_parameter("bridge", "br-lan")
306
307 wan_networks = [x for x in instance.slice.networks.all() if "wan" in x.name]
308 if not wan_networks:
309 raise XOSProgrammingError("No wan_network")
310 port = self.find_or_make_port(instance, wan_networks[0])
311 port.set_parameter("next_hop", value="10.0.1.253") # FIX ME
312 port.set_parameter("device", "eth0")
313
314 if instance.isolation in ["vm"]:
315 lan_network = self.get_lan_network(instance)
316 port = self.find_or_make_port(instance, lan_network)
317 port.set_parameter("c_tag", self.volt.c_tag)
318 port.set_parameter("s_tag", self.volt.s_tag)
319 port.set_parameter("neutron_port_name", "stag-%s" % self.volt.s_tag)
320 port.save()
321
322 # tag the instance with the s-tag, so we can easily find the
323 # instance later
324 if self.volt and self.volt.s_tag:
325 tags = Tag.objects.filter(name="s_tag", value=self.volt.s_tag)
326 if not tags:
327 tag = Tag(service=self.provider_service, content_object=instance, name="s_tag", value=self.volt.s_tag)
328 tag.save()
329
330 # VTN-CORD needs a WAN address for the VM, so that the VM can
331 # be configured.
332 tags = Tag.select_by_content_object(instance).filter(name="vm_vrouter_tenant")
333 if not tags:
334 vrouter = self.get_vrouter_service().get_tenant(address_pool_name="addresses_vsg", subscriber_service = self.provider_service)
335 vrouter.set_attribute("tenant_for_instance_id", instance.id)
336 vrouter.save()
337 tag = Tag(service=self.provider_service, content_object=instance, name="vm_vrouter_tenant", value="%d" % vrouter.id)
338 tag.save()
339
340 def save(self, *args, **kwargs):
341 if not self.creator:
342 if not getattr(self, "caller", None):
343 # caller must be set when creating a vCPE since it creates a slice
344 raise XOSProgrammingError("VSGTenant's self.caller was not set")
345 self.creator = self.caller
346 if not self.creator:
347 raise XOSProgrammingError("VSGTenant's self.creator was not set")
348
349 super(VSGTenant, self).save(*args, **kwargs)
350 model_policy_vcpe(self.pk)
351
352 def delete(self, *args, **kwargs):
353 self.cleanup_vrouter()
354 self.cleanup_container()
355 super(VSGTenant, self).delete(*args, **kwargs)
356
357 pass
Scott Baker761e1062016-06-20 17:18:17 -0700358
359def model_policy_vcpe(pk):
360 # TODO: this should be made in to a real model_policy
361 with transaction.atomic():
362 vcpe = VSGTenant.objects.select_for_update().filter(pk=pk)
363 if not vcpe:
364 return
365 vcpe = vcpe[0]
366 vcpe.manage_container()
367 vcpe.manage_vrouter()
Scott Baker761e1062016-06-20 17:18:17 -0700368 vcpe.cleanup_orphans()
369
370