blob: 4ab3c154fe0eefd3aff379345e4761cc12351622 [file] [log] [blame]
Scott Baker31acc652016-06-23 15:47:56 -07001from django.db import models
2from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber
3from core.models.plcorebase import StrippedCharField
4import os
5from django.db import models, transaction
6from django.forms.models import model_to_dict
7from django.db.models import Q
8from operator import itemgetter, attrgetter, methodcaller
9import traceback
10from xos.exceptions import *
11from core.models import SlicePrivilege, SitePrivilege
12from sets import Set
13from urlparse import urlparse
14
15CEILOMETER_KIND = "ceilometer"
16
17class CeilometerService(Service):
18 KIND = CEILOMETER_KIND
Srikanth Vavilapallifd8c9b32016-08-15 22:59:28 +000019 LOOK_FOR_IMAGES=[ "ceilometer-service-trusty-server-multi-nic",
20 ]
Scott Baker31acc652016-06-23 15:47:56 -070021
22 class Meta:
Srikanth Vavilapallid84b7b72016-06-28 00:19:07 +000023 app_label = "monitoring"
Scott Baker31acc652016-06-23 15:47:56 -070024 verbose_name = "Ceilometer Service"
25 proxy = True
26
Srikanth Vavilapallifd8c9b32016-08-15 22:59:28 +000027 def get_instance(self):
28 for slice in self.slices.all():
29 for instance in slice.instances.all():
30 if instance.image.name in self.LOOK_FOR_IMAGES:
31 return instance
32 return None
33
34 @property
35 def addresses(self):
36 if (not self.id) or (not self.get_instance()):
37 return {}
38
39 addresses = {}
40 for ns in self.get_instance().ports.all():
41 if "private" in ns.network.name.lower():
42 addresses["private"] = (ns.ip, ns.mac)
43 elif ("nat" in ns.network.name.lower()) or ("management" in ns.network.name.lower()):
44 addresses["nat"] = (ns.ip, ns.mac)
45 #TODO: Do we need this client_access_network. Revisit in VTN context
46 #elif "ceilometer_client_access" in ns.network.labels.lower():
47 # addresses["ceilometer"] = (ns.ip, ns.mac)
48 return addresses
49
50 @property
51 def nat_ip(self):
52 return self.addresses.get("nat", (None, None))[0]
53
54 @property
55 def nat_mac(self):
56 return self.addresses.get("nat", (None, None))[1]
57
58 @property
59 def private_ip(self):
60 return self.addresses.get("private", (None, None))[0]
61
62 @property
63 def private_mac(self):
64 return self.addresses.get("private", (None, None))[1]
65
66 def get_controller(self):
67 if not self.slices.count:
68 raise XOSConfigurationError("The service has no slices")
69 cslice = self.slices.all()[0].controllerslices.all()[0]
70 controller = cslice.controller
71 if not controller:
72 raise XOSConfigurationError("The service slice has no controller")
73 return controller
74
Scott Baker31acc652016-06-23 15:47:56 -070075 @property
76 def ceilometer_pub_sub_url(self):
Srikanth Vavilapallifd8c9b32016-08-15 22:59:28 +000077 if not self.get_instance():
78 return self.get_attribute("ceilometer_pub_sub_url", None)
79 if not self.private_ip:
80 return None
81 return "http://" + self.private_ip + ":4455/"
Scott Baker31acc652016-06-23 15:47:56 -070082
83 @ceilometer_pub_sub_url.setter
84 def ceilometer_pub_sub_url(self, value):
85 self.set_attribute("ceilometer_pub_sub_url", value)
86
Srikanth Vavilapallifd8c9b32016-08-15 22:59:28 +000087 @property
88 def ceilometer_auth_url(self):
89 #FIXME: Crude way to determine if monitoring service is getting deployed with its own ceilometer+keystone
90 if not self.get_instance():
91 return self.get_controller().auth_url
92 if not self.private_ip:
93 return None
94 return "http://" + self.private_ip + ":5000/v2.0"
95
96 @property
97 def ceilometer_admin_user(self):
98 if not self.get_instance():
99 return self.get_controller().admin_user
100 return 'admin'
101
102 @property
103 def ceilometer_admin_password(self):
104 if not self.get_instance():
105 return self.get_controller().admin_password
106 return 'password'
107
108 @property
109 def ceilometer_admin_tenant(self):
110 if not self.get_instance():
111 return self.get_controller().admin_tenant
112 return 'admin'
113
Scott Baker31acc652016-06-23 15:47:56 -0700114class MonitoringChannel(TenantWithContainer): # aka 'CeilometerTenant'
115 class Meta:
116 proxy = True
117
118 KIND = CEILOMETER_KIND
119 LOOK_FOR_IMAGES=[ #"trusty-server-multi-nic-docker", # CloudLab
120 "ceilometer-trusty-server-multi-nic",
121 #"trusty-server-multi-nic",
122 ]
123
124
125 sync_attributes = ("private_ip", "private_mac",
126 "ceilometer_ip", "ceilometer_mac",
127 "nat_ip", "nat_mac", "ceilometer_port",)
128
129 default_attributes = {}
130 def __init__(self, *args, **kwargs):
131 ceilometer_services = CeilometerService.get_service_objects().all()
132 if ceilometer_services:
133 self._meta.get_field("provider_service").default = ceilometer_services[0].id
134 super(MonitoringChannel, self).__init__(*args, **kwargs)
135 self.set_attribute("use_same_instance_for_multiple_tenants", True)
136
137 def can_update(self, user):
138 #Allow creation of this model instances for non-admin users also
139 return True
140
141 def save(self, *args, **kwargs):
142 if not self.creator:
143 if not getattr(self, "caller", None):
144 # caller must be set when creating a monitoring channel since it creates a slice
145 raise XOSProgrammingError("MonitoringChannel's self.caller was not set")
146 self.creator = self.caller
147 if not self.creator:
148 raise XOSProgrammingError("MonitoringChannel's self.creator was not set")
149
150 if self.pk is None:
151 #Allow only one monitoring channel per user
152 channel_count = sum ( [1 for channel in MonitoringChannel.objects.filter(kind=CEILOMETER_KIND) if (channel.creator == self.creator)] )
153 if channel_count > 0:
154 raise XOSValidationError("Already %s channels exist for user Can only create max 1 MonitoringChannel instance per user" % str(channel_count))
155
156 super(MonitoringChannel, self).save(*args, **kwargs)
157 model_policy_monitoring_channel(self.pk)
158
159 def delete(self, *args, **kwargs):
160 self.cleanup_container()
161 super(MonitoringChannel, self).delete(*args, **kwargs)
162
163 @property
164 def addresses(self):
165 if (not self.id) or (not self.instance):
166 return {}
167
168 addresses = {}
169 for ns in self.instance.ports.all():
170 if "private" in ns.network.name.lower():
171 addresses["private"] = (ns.ip, ns.mac)
172 elif ("nat" in ns.network.name.lower()) or ("management" in ns.network.name.lower()):
173 addresses["nat"] = (ns.ip, ns.mac)
174 #TODO: Do we need this client_access_network. Revisit in VTN context
175 #elif "ceilometer_client_access" in ns.network.labels.lower():
176 # addresses["ceilometer"] = (ns.ip, ns.mac)
177 return addresses
178
179 @property
180 def nat_ip(self):
181 return self.addresses.get("nat", (None, None))[0]
182
183 @property
184 def nat_mac(self):
185 return self.addresses.get("nat", (None, None))[1]
186
187 @property
188 def private_ip(self):
189 return self.addresses.get("nat", (None, None))[0]
190
191 @property
192 def private_mac(self):
193 return self.addresses.get("nat", (None, None))[1]
194
195 @property
196 def ceilometer_ip(self):
197 return self.addresses.get("ceilometer", (None, None))[0]
198
199 @property
200 def ceilometer_mac(self):
201 return self.addresses.get("ceilometer", (None, None))[1]
202
203 @property
204 def site_tenant_list(self):
205 tenant_ids = Set()
206 for sp in SitePrivilege.objects.filter(user=self.creator):
207 site = sp.site
208 for cs in site.controllersite.all():
209 if cs.tenant_id:
210 tenant_ids.add(cs.tenant_id)
211 return tenant_ids
212
213 @property
214 def slice_tenant_list(self):
215 tenant_ids = Set()
216 for sp in SlicePrivilege.objects.filter(user=self.creator):
217 slice = sp.slice
218 for cs in slice.controllerslices.all():
219 if cs.tenant_id:
220 tenant_ids.add(cs.tenant_id)
221 for slice in Slice.objects.filter(creator=self.creator):
222 for cs in slice.controllerslices.all():
223 if cs.tenant_id:
224 tenant_ids.add(cs.tenant_id)
225 if self.creator.is_admin:
226 #TODO: Ceilometer publishes the SDN meters without associating to any tenant IDs.
227 #For now, ceilometer code is changed to pusblish all such meters with tenant
228 #id as "default_admin_tenant". Here add that default tenant as authroized tenant_id
229 #for all admin users.
230 tenant_ids.add("default_admin_tenant")
231 return tenant_ids
232
233 @property
234 def tenant_list(self):
235 return self.slice_tenant_list | self.site_tenant_list
236
237 @property
238 def tenant_list_str(self):
239 return ", ".join(self.tenant_list)
240
241 @property
242 def ceilometer_port(self):
243 # TODO: Find a better logic to choose unique ceilometer port number for each instance
244 if not self.id:
245 return None
246 return 8888+self.id
247
248 @property
Srikanth Vavilapallifd8c9b32016-08-15 22:59:28 +0000249 def ssh_proxy_tunnel(self):
250 return self.get_attribute("ssh_proxy_tunnel", False)
251
252 @ssh_proxy_tunnel.setter
253 def ssh_proxy_tunnel(self, value):
254 self.set_attribute("ssh_proxy_tunnel", value)
255
256 @property
257 def ssh_tunnel_port(self):
258 return self.get_attribute("ssh_tunnel_port")
259
260 @ssh_tunnel_port.setter
261 def ssh_tunnel_port(self, value):
262 self.set_attribute("ssh_tunnel_port", value)
263
264 @property
265 def ssh_tunnel_ip(self):
266 return self.get_attribute("ssh_tunnel_ip")
267
268 @ssh_tunnel_ip.setter
269 def ssh_tunnel_ip(self, value):
270 self.set_attribute("ssh_tunnel_ip", value)
271
272 @property
Scott Baker31acc652016-06-23 15:47:56 -0700273 def ceilometer_url(self):
Srikanth Vavilapallifd8c9b32016-08-15 22:59:28 +0000274 if self.ssh_proxy_tunnel:
275 if self.ssh_tunnel_ip and self.ssh_tunnel_port:
276 return "http://" + self.ssh_tunnel_ip + ":" + str(self.ssh_tunnel_port) + "/"
277 else:
278 return None
279 else:
280 if self.private_ip and self.ceilometer_port:
281 return "http://" + self.private_ip + ":" + str(self.ceilometer_port) + "/"
282 else:
283 return None
Scott Baker31acc652016-06-23 15:47:56 -0700284
285def model_policy_monitoring_channel(pk):
286 # TODO: this should be made in to a real model_policy
287 with transaction.atomic():
288 mc = MonitoringChannel.objects.select_for_update().filter(pk=pk)
289 if not mc:
290 return
291 mc = mc[0]
292 mc.manage_container()
293
294
295SFLOW_KIND = "sflow"
296SFLOW_PORT = 6343
297SFLOW_API_PORT = 33333
298
299class SFlowService(Service):
300 KIND = SFLOW_KIND
301
302 class Meta:
Srikanth Vavilapallid84b7b72016-06-28 00:19:07 +0000303 app_label = "monitoring"
Scott Baker31acc652016-06-23 15:47:56 -0700304 verbose_name = "sFlow Collection Service"
305 proxy = True
306
307 default_attributes = {"sflow_port": SFLOW_PORT, "sflow_api_port": SFLOW_API_PORT}
308
309 sync_attributes = ("sflow_port", "sflow_api_port",)
310
311 @property
312 def sflow_port(self):
313 return self.get_attribute("sflow_port", self.default_attributes["sflow_port"])
314
315 @sflow_port.setter
316 def sflow_port(self, value):
317 self.set_attribute("sflow_port", value)
318
319 @property
320 def sflow_api_port(self):
321 return self.get_attribute("sflow_api_port", self.default_attributes["sflow_api_port"])
322
323 @sflow_api_port.setter
324 def sflow_api_port(self, value):
325 self.set_attribute("sflow_api_port", value)
326
327 def get_instance(self):
328 if self.slices.exists():
329 slice = self.slices.all()[0]
330 if slice.instances.exists():
331 return slice.instances.all()[0]
332
333 return None
334
335 @property
336 def sflow_api_url(self):
337 if not self.get_instance():
338 return None
339 return "http://" + self.get_instance().get_ssh_ip() + ":" + str(self.sflow_api_port) + "/"
340
341class SFlowTenant(Tenant):
342 class Meta:
343 proxy = True
344
345 KIND = SFLOW_KIND
346
347 sync_attributes = ("listening_endpoint", )
348
349 default_attributes = {}
350 def __init__(self, *args, **kwargs):
351 sflow_services = SFlowService.get_service_objects().all()
352 if sflow_services:
353 self._meta.get_field("provider_service").default = sflow_services[0].id
354 super(SFlowTenant, self).__init__(*args, **kwargs)
355
356 @property
357 def creator(self):
358 from core.models import User
359 if getattr(self, "cached_creator", None):
360 return self.cached_creator
361 creator_id=self.get_attribute("creator_id")
362 if not creator_id:
363 return None
364 users=User.objects.filter(id=creator_id)
365 if not users:
366 return None
367 user=users[0]
368 self.cached_creator = users[0]
369 return user
370
371 @creator.setter
372 def creator(self, value):
373 if value:
374 value = value.id
375 if (value != self.get_attribute("creator_id", None)):
376 self.cached_creator=None
377 self.set_attribute("creator_id", value)
378
379 @property
380 def listening_endpoint(self):
381 return self.get_attribute("listening_endpoint", None)
382
383 @listening_endpoint.setter
384 def listening_endpoint(self, value):
385 if urlparse(value).scheme != 'udp':
386 raise XOSProgrammingError("SFlowTenant: Only UDP listening endpoint URLs are accepted...valid syntax is: udp://ip:port")
387 self.set_attribute("listening_endpoint", value)
388
389 def save(self, *args, **kwargs):
390 if not self.creator:
391 if not getattr(self, "caller", None):
392 # caller must be set when creating a SFlow tenant since it creates a slice
393 raise XOSProgrammingError("SFlowTenant's self.caller was not set")
394 self.creator = self.caller
395 if not self.creator:
396 raise XOSProgrammingError("SFlowTenant's self.creator was not set")
397
398 if not self.listening_endpoint:
399 raise XOSProgrammingError("SFlowTenant's self.listening_endpoint was not set")
400
401 if self.pk is None:
402 #Allow only one sflow channel per user and listening_endpoint
403 channel_count = sum ( [1 for channel in SFlowTenant.objects.filter(kind=SFLOW_KIND) if ((channel.creator == self.creator) and (channel.listening_endpoint == self.listening_endpoint))] )
404 if channel_count > 0:
405 raise XOSValidationError("Already %s sflow channels exist for user Can only create max 1 tenant per user and listening endpoint" % str(channel_count))
406
407 super(SFlowTenant, self).save(*args, **kwargs)
408
409 def delete(self, *args, **kwargs):
410 super(MonitoringChannel, self).delete(*args, **kwargs)
411
412 @property
413 def authorized_resource_list(self):
414 return ['all']
415
416 @property
417 def authorized_resource_list_str(self):
418 return ", ".join(self.authorized_resource_list)
419