blob: 9ffb0777112408d9d91e0571cc07b0fae67c9e04 [file] [log] [blame]
Matteo Scandolo6288d5a2017-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
Sapan Bhatia51a804c2017-05-03 17:24:37 +020017sync_attributes = ("wan_container_ip", "wan_container_mac", "wan_container_netbits",
18 "wan_container_gateway_ip", "wan_container_gateway_mac",
19 "wan_vm_ip", "wan_vm_mac")
20
21
22def __init__(self, *args, **kwargs):
23 super(VEGTenant, self).__init__(*args, **kwargs)
24 self.cached_vrouter=None
25
26@property
27def vrouter(self):
28 vrouter = self.get_newest_subscribed_tenant(VRouterTenant)
29 if not vrouter:
30 return None
31
32 # always return the same object when possible
33 if (self.cached_vrouter) and (self.cached_vrouter.id == vrouter.id):
34 return self.cached_vrouter
35
36 vrouter.caller = self.creator
37 self.cached_vrouter = vrouter
38 return vrouter
39
40@vrouter.setter
41def vrouter(self, value):
42 raise XOSConfigurationError("vEGTenant.vrouter setter is not implemented")
43
44@property
45def volt(self):
46 from services.volt.models import VOLTTenant
47 if not self.subscriber_tenant:
48 return None
49 volts = VOLTTenant.objects.filter(id=self.subscriber_tenant.id)
50 if not volts:
51 return None
52 return volts[0]
53
54@volt.setter
55def volt(self, value):
56 raise XOSConfigurationError("VEGTenant.volt setter is not implemented")
57
58@property
59def ssh_command(self):
60 if self.instance:
61 return self.instance.get_ssh_command()
62 else:
63 return "no-instance"
64
65def get_vrouter_field(self, name, default=None):
66 if self.vrouter:
67 return getattr(self.vrouter, name, default)
68 else:
69 return default
70
71@property
72def wan_container_ip(self):
73 return self.get_vrouter_field("public_ip", None)
74
75@property
76def wan_container_mac(self):
77 return self.get_vrouter_field("public_mac", None)
78
79@property
80def wan_container_netbits(self):
81 return self.get_vrouter_field("netbits", None)
82
83@property
84def wan_container_gateway_ip(self):
85 return self.get_vrouter_field("gateway_ip", None)
86
87@property
88def wan_container_gateway_mac(self):
89 return self.get_vrouter_field("gateway_mac", None)
90
91@property
92def wan_vm_ip(self):
93 tags = Tag.select_by_content_object(self.instance).filter(name="vm_vrouter_tenant")
94 if tags:
95 tenant = VRouterTenant.objects.get(id=tags[0].value)
96 return tenant.public_ip
97 else:
98 raise Exception("no vm_vrouter_tenant tag for instance %s" % o.instance)
99
100@property
101def wan_vm_mac(self):
102 tags = Tag.select_by_content_object(self.instance).filter(name="vm_vrouter_tenant")
103 if tags:
104 tenant = VRouterTenant.objects.get(id=tags[0].value)
105 return tenant.public_mac
106 else:
107 raise Exception("no vm_vrouter_tenant tag for instance %s" % o.instance)
108
109@property
110def is_synced(self):
111 return (self.enacted is not None) and (self.enacted >= self.updated)
112
113@is_synced.setter
114def is_synced(self, value):
115 pass
116
117def get_vrouter_service(self):
118 vrouterServices = VRouterService.get_service_objects().all()
119 if not vrouterServices:
120 raise XOSConfigurationError("No VROUTER Services available")
121 return vrouterServices[0]
122
123def manage_vrouter(self):
124 # Each vEG object owns exactly one vRouterTenant object
125
126 if self.deleted:
127 return
128
129 if self.vrouter is None:
130 vrouter = self.get_vrouter_service().get_tenant(address_pool_name="addresses_veg", subscriber_tenant = self)
131 vrouter.caller = self.creator
132 vrouter.save()
133
134def cleanup_vrouter(self):
135 if self.vrouter:
136 # print "XXX cleanup vrouter", self.vrouter
137 self.vrouter.delete()
138
139def cleanup_orphans(self):
140 # ensure vEG only has one vRouter
141 cur_vrouter = self.vrouter
142 for vrouter in list(self.get_subscribed_tenants(VRouterTenant)):
143 if (not cur_vrouter) or (vrouter.id != cur_vrouter.id):
144 # print "XXX clean up orphaned vrouter", vrouter
145 vrouter.delete()
146
147 if self.orig_instance_id and (self.orig_instance_id != self.get_attribute("instance_id")):
148 instances=Instance.objects.filter(id=self.orig_instance_id)
149 if instances:
150 # print "XXX clean up orphaned instance", instances[0]
151 instances[0].delete()
152
153def get_slice(self):
154 if not self.provider_service.slices.count():
155 print self, "dio porco"
156 raise XOSConfigurationError("The service has no slices")
157 slice = self.provider_service.slices.all()[0]
158 return slice
159
160def get_veg_service(self):
161 return VEGService.get_service_objects().get(id=self.provider_service.id)
162
163def find_instance_for_s_tag(self, s_tag):
164 #s_tags = STagBlock.objects.find(s_s_tag)
165 #if s_tags:
166 # return s_tags[0].instance
167
168 tags = Tag.objects.filter(name="s_tag", value=s_tag)
169 if tags:
170 return tags[0].content_object
171
172 return None
173
174def find_or_make_instance_for_s_tag(self, s_tag):
175 instance = self.find_instance_for_s_tag(self.volt.s_tag)
176 if instance:
177 return instance
178
179 flavors = Flavor.objects.filter(name="m1.small")
180 if not flavors:
181 raise XOSConfigurationError("No m1.small flavor")
182
183 slice = self.provider_service.slices.all()[0]
184
185 if slice.default_isolation == "container_vm":
186 (node, parent) = ContainerVmScheduler(slice).pick()
187 else:
188 (node, parent) = LeastLoadedNodeScheduler(slice, label=self.get_veg_service().node_label).pick()
189
190 instance = Instance(slice = slice,
191 node = node,
192 image = self.image,
193 creator = self.creator,
194 deployment = node.site_deployment.deployment,
195 flavor = flavors[0],
196 isolation = slice.default_isolation,
197 parent = parent)
198
199 self.save_instance(instance)
200
201 return instance
202
203def manage_container(self):
204 from core.models import Instance, Flavor
205
206 if self.deleted:
207 return
208
209 # For container or container_vm isolation, use what TenantWithCotnainer
210 # provides us
211 slice = self.get_slice()
212 if slice.default_isolation in ["container_vm", "container"]:
213 super(VEGTenant,self).manage_container()
214 return
215
216 if not self.volt:
217 raise XOSConfigurationError("This vEG container has no volt")
218
219 if self.instance:
220 # We're good.
221 return
222
223 instance = self.find_or_make_instance_for_s_tag(self.volt.s_tag)
224 self.instance = instance
225 super(TenantWithContainer, self).save()
226
227def cleanup_container(self):
228 if self.get_slice().default_isolation in ["container_vm", "container"]:
229 super(VEGTenant,self).cleanup_container()
230
231 # To-do: cleanup unused instances
232 pass
233
234def find_or_make_port(self, instance, network, **kwargs):
235 port = Port.objects.filter(instance=instance, network=network)
236 if port:
237 port = port[0]
238 else:
239 port = Port(instance=instance, network=network, **kwargs)
240 port.save()
241 return port
242
243def get_lan_network(self, instance):
244 slice = self.provider_service.slices.all()[0]
245 # there should only be one network private network, and its template should not be the management template
246 lan_networks = [x for x in slice.networks.all() if x.template.visibility == "private" and (not "management" in x.template.name)]
247 if len(lan_networks) > 1:
248 raise XOSProgrammingError("The vEG slice should only have one non-management private network")
249 if not lan_networks:
250 raise XOSProgrammingError("No lan_network")
251 return lan_networks[0]
252
253def save_instance(self, instance):
254 with transaction.atomic():
255 instance.volumes = "/etc/dnsmasq.d,/etc/ufw"
256 super(VEGTenant, self).save_instance(instance)
257
258 if instance.isolation in ["container", "container_vm"]:
259 lan_network = self.get_lan_network(instance)
260 port = self.find_or_make_port(instance, lan_network, ip="192.168.0.1", port_id="unmanaged")
261 port.set_parameter("c_tag", self.volt.c_tag)
262 port.set_parameter("s_tag", self.volt.s_tag)
263 port.set_parameter("device", "eth1")
264 port.set_parameter("bridge", "br-lan")
265
266 wan_networks = [x for x in instance.slice.networks.all() if "wan" in x.name]
267 if not wan_networks:
268 raise XOSProgrammingError("No wan_network")
269 port = self.find_or_make_port(instance, wan_networks[0])
270 port.set_parameter("next_hop", value="10.0.1.253") # FIX ME
271 port.set_parameter("device", "eth0")
272
273 if instance.isolation in ["vm"]:
274 lan_network = self.get_lan_network(instance)
275 port = self.find_or_make_port(instance, lan_network)
276 port.set_parameter("c_tag", self.volt.c_tag)
277 port.set_parameter("s_tag", self.volt.s_tag)
278 port.set_parameter("neutron_port_name", "stag-%s" % self.volt.s_tag)
279 port.save()
280
281 # tag the instance with the s-tag, so we can easily find the
282 # instance later
283 if self.volt and self.volt.s_tag:
284 tags = Tag.objects.filter(name="s_tag", value=self.volt.s_tag)
285 if not tags:
286 tag = Tag(service=self.provider_service, content_object=instance, name="s_tag", value=self.volt.s_tag)
287 tag.save()
288
289 # VTN-CORD needs a WAN address for the VM, so that the VM can
290 # be configured.
291 tags = Tag.select_by_content_object(instance).filter(name="vm_vrouter_tenant")
292
293 if not tags:
294 vrouter = self.get_vrouter_service().get_tenant(address_pool_name="addresses_veg", subscriber_service=self.provider_service)
295 vrouter.set_attribute("tenant_for_instance_id", instance.id)
296 vrouter.save()
297 tag = Tag(service=self.provider_service, content_object=instance, name="vm_vrouter_tenant",value="%d" % vrouter.id)
298 tag.save()
299
300def save(self, *args, **kwargs):
301 if not self.creator:
302 if not getattr(self, "caller", None):
303 # caller must be set when creating a vEG since it creates a slice
304 raise XOSProgrammingError("VEGTenant's self.caller was not set")
305 self.creator = self.caller
306 if not self.creator:
307 raise XOSProgrammingError("VEGTenant's self.creator was not set")
308
309 super(VEGTenant, self).save(*args, **kwargs)
310 model_policy_veg(self.pk)
311
312def delete(self, *args, **kwargs):
313 self.cleanup_vrouter()
314 self.cleanup_container()
315 super(VEGTenant, self).delete(*args, **kwargs)
316