blob: a839b4a5b7765a906f8d5d6f12f478032e3967bc [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
19
20 class Meta:
Srikanth Vavilapallid84b7b72016-06-28 00:19:07 +000021 app_label = "monitoring"
Scott Baker31acc652016-06-23 15:47:56 -070022 verbose_name = "Ceilometer Service"
23 proxy = True
24
25 @property
26 def ceilometer_pub_sub_url(self):
27 return self.get_attribute("ceilometer_pub_sub_url", None)
28
29 @ceilometer_pub_sub_url.setter
30 def ceilometer_pub_sub_url(self, value):
31 self.set_attribute("ceilometer_pub_sub_url", value)
32
33class MonitoringChannel(TenantWithContainer): # aka 'CeilometerTenant'
34 class Meta:
35 proxy = True
36
37 KIND = CEILOMETER_KIND
38 LOOK_FOR_IMAGES=[ #"trusty-server-multi-nic-docker", # CloudLab
39 "ceilometer-trusty-server-multi-nic",
40 #"trusty-server-multi-nic",
41 ]
42
43
44 sync_attributes = ("private_ip", "private_mac",
45 "ceilometer_ip", "ceilometer_mac",
46 "nat_ip", "nat_mac", "ceilometer_port",)
47
48 default_attributes = {}
49 def __init__(self, *args, **kwargs):
50 ceilometer_services = CeilometerService.get_service_objects().all()
51 if ceilometer_services:
52 self._meta.get_field("provider_service").default = ceilometer_services[0].id
53 super(MonitoringChannel, self).__init__(*args, **kwargs)
54 self.set_attribute("use_same_instance_for_multiple_tenants", True)
55
56 def can_update(self, user):
57 #Allow creation of this model instances for non-admin users also
58 return True
59
60 def save(self, *args, **kwargs):
61 if not self.creator:
62 if not getattr(self, "caller", None):
63 # caller must be set when creating a monitoring channel since it creates a slice
64 raise XOSProgrammingError("MonitoringChannel's self.caller was not set")
65 self.creator = self.caller
66 if not self.creator:
67 raise XOSProgrammingError("MonitoringChannel's self.creator was not set")
68
69 if self.pk is None:
70 #Allow only one monitoring channel per user
71 channel_count = sum ( [1 for channel in MonitoringChannel.objects.filter(kind=CEILOMETER_KIND) if (channel.creator == self.creator)] )
72 if channel_count > 0:
73 raise XOSValidationError("Already %s channels exist for user Can only create max 1 MonitoringChannel instance per user" % str(channel_count))
74
75 super(MonitoringChannel, self).save(*args, **kwargs)
76 model_policy_monitoring_channel(self.pk)
77
78 def delete(self, *args, **kwargs):
79 self.cleanup_container()
80 super(MonitoringChannel, self).delete(*args, **kwargs)
81
82 @property
83 def addresses(self):
84 if (not self.id) or (not self.instance):
85 return {}
86
87 addresses = {}
88 for ns in self.instance.ports.all():
89 if "private" in ns.network.name.lower():
90 addresses["private"] = (ns.ip, ns.mac)
91 elif ("nat" in ns.network.name.lower()) or ("management" in ns.network.name.lower()):
92 addresses["nat"] = (ns.ip, ns.mac)
93 #TODO: Do we need this client_access_network. Revisit in VTN context
94 #elif "ceilometer_client_access" in ns.network.labels.lower():
95 # addresses["ceilometer"] = (ns.ip, ns.mac)
96 return addresses
97
98 @property
99 def nat_ip(self):
100 return self.addresses.get("nat", (None, None))[0]
101
102 @property
103 def nat_mac(self):
104 return self.addresses.get("nat", (None, None))[1]
105
106 @property
107 def private_ip(self):
108 return self.addresses.get("nat", (None, None))[0]
109
110 @property
111 def private_mac(self):
112 return self.addresses.get("nat", (None, None))[1]
113
114 @property
115 def ceilometer_ip(self):
116 return self.addresses.get("ceilometer", (None, None))[0]
117
118 @property
119 def ceilometer_mac(self):
120 return self.addresses.get("ceilometer", (None, None))[1]
121
122 @property
123 def site_tenant_list(self):
124 tenant_ids = Set()
125 for sp in SitePrivilege.objects.filter(user=self.creator):
126 site = sp.site
127 for cs in site.controllersite.all():
128 if cs.tenant_id:
129 tenant_ids.add(cs.tenant_id)
130 return tenant_ids
131
132 @property
133 def slice_tenant_list(self):
134 tenant_ids = Set()
135 for sp in SlicePrivilege.objects.filter(user=self.creator):
136 slice = sp.slice
137 for cs in slice.controllerslices.all():
138 if cs.tenant_id:
139 tenant_ids.add(cs.tenant_id)
140 for slice in Slice.objects.filter(creator=self.creator):
141 for cs in slice.controllerslices.all():
142 if cs.tenant_id:
143 tenant_ids.add(cs.tenant_id)
144 if self.creator.is_admin:
145 #TODO: Ceilometer publishes the SDN meters without associating to any tenant IDs.
146 #For now, ceilometer code is changed to pusblish all such meters with tenant
147 #id as "default_admin_tenant". Here add that default tenant as authroized tenant_id
148 #for all admin users.
149 tenant_ids.add("default_admin_tenant")
150 return tenant_ids
151
152 @property
153 def tenant_list(self):
154 return self.slice_tenant_list | self.site_tenant_list
155
156 @property
157 def tenant_list_str(self):
158 return ", ".join(self.tenant_list)
159
160 @property
161 def ceilometer_port(self):
162 # TODO: Find a better logic to choose unique ceilometer port number for each instance
163 if not self.id:
164 return None
165 return 8888+self.id
166
167 @property
168 def ceilometer_url(self):
169 if not self.private_ip:
170 return None
171 return "http://" + self.private_ip + ":" + str(self.ceilometer_port) + "/"
172
173def model_policy_monitoring_channel(pk):
174 # TODO: this should be made in to a real model_policy
175 with transaction.atomic():
176 mc = MonitoringChannel.objects.select_for_update().filter(pk=pk)
177 if not mc:
178 return
179 mc = mc[0]
180 mc.manage_container()
181
182
183SFLOW_KIND = "sflow"
184SFLOW_PORT = 6343
185SFLOW_API_PORT = 33333
186
187class SFlowService(Service):
188 KIND = SFLOW_KIND
189
190 class Meta:
Srikanth Vavilapallid84b7b72016-06-28 00:19:07 +0000191 app_label = "monitoring"
Scott Baker31acc652016-06-23 15:47:56 -0700192 verbose_name = "sFlow Collection Service"
193 proxy = True
194
195 default_attributes = {"sflow_port": SFLOW_PORT, "sflow_api_port": SFLOW_API_PORT}
196
197 sync_attributes = ("sflow_port", "sflow_api_port",)
198
199 @property
200 def sflow_port(self):
201 return self.get_attribute("sflow_port", self.default_attributes["sflow_port"])
202
203 @sflow_port.setter
204 def sflow_port(self, value):
205 self.set_attribute("sflow_port", value)
206
207 @property
208 def sflow_api_port(self):
209 return self.get_attribute("sflow_api_port", self.default_attributes["sflow_api_port"])
210
211 @sflow_api_port.setter
212 def sflow_api_port(self, value):
213 self.set_attribute("sflow_api_port", value)
214
215 def get_instance(self):
216 if self.slices.exists():
217 slice = self.slices.all()[0]
218 if slice.instances.exists():
219 return slice.instances.all()[0]
220
221 return None
222
223 @property
224 def sflow_api_url(self):
225 if not self.get_instance():
226 return None
227 return "http://" + self.get_instance().get_ssh_ip() + ":" + str(self.sflow_api_port) + "/"
228
229class SFlowTenant(Tenant):
230 class Meta:
231 proxy = True
232
233 KIND = SFLOW_KIND
234
235 sync_attributes = ("listening_endpoint", )
236
237 default_attributes = {}
238 def __init__(self, *args, **kwargs):
239 sflow_services = SFlowService.get_service_objects().all()
240 if sflow_services:
241 self._meta.get_field("provider_service").default = sflow_services[0].id
242 super(SFlowTenant, self).__init__(*args, **kwargs)
243
244 @property
245 def creator(self):
246 from core.models import User
247 if getattr(self, "cached_creator", None):
248 return self.cached_creator
249 creator_id=self.get_attribute("creator_id")
250 if not creator_id:
251 return None
252 users=User.objects.filter(id=creator_id)
253 if not users:
254 return None
255 user=users[0]
256 self.cached_creator = users[0]
257 return user
258
259 @creator.setter
260 def creator(self, value):
261 if value:
262 value = value.id
263 if (value != self.get_attribute("creator_id", None)):
264 self.cached_creator=None
265 self.set_attribute("creator_id", value)
266
267 @property
268 def listening_endpoint(self):
269 return self.get_attribute("listening_endpoint", None)
270
271 @listening_endpoint.setter
272 def listening_endpoint(self, value):
273 if urlparse(value).scheme != 'udp':
274 raise XOSProgrammingError("SFlowTenant: Only UDP listening endpoint URLs are accepted...valid syntax is: udp://ip:port")
275 self.set_attribute("listening_endpoint", value)
276
277 def save(self, *args, **kwargs):
278 if not self.creator:
279 if not getattr(self, "caller", None):
280 # caller must be set when creating a SFlow tenant since it creates a slice
281 raise XOSProgrammingError("SFlowTenant's self.caller was not set")
282 self.creator = self.caller
283 if not self.creator:
284 raise XOSProgrammingError("SFlowTenant's self.creator was not set")
285
286 if not self.listening_endpoint:
287 raise XOSProgrammingError("SFlowTenant's self.listening_endpoint was not set")
288
289 if self.pk is None:
290 #Allow only one sflow channel per user and listening_endpoint
291 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))] )
292 if channel_count > 0:
293 raise XOSValidationError("Already %s sflow channels exist for user Can only create max 1 tenant per user and listening endpoint" % str(channel_count))
294
295 super(SFlowTenant, self).save(*args, **kwargs)
296
297 def delete(self, *args, **kwargs):
298 super(MonitoringChannel, self).delete(*args, **kwargs)
299
300 @property
301 def authorized_resource_list(self):
302 return ['all']
303
304 @property
305 def authorized_resource_list_str(self):
306 return ", ".join(self.authorized_resource_list)
307