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