blob: 1aaa7b33af7ac1be8b71265f0b9866858ea38354 [file] [log] [blame]
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +00001import requests
2from six.moves import urllib
3import urllib2
4import pytz
5import datetime
6import time
7from rest_framework.decorators import api_view
8from rest_framework.response import Response
9from rest_framework.reverse import reverse
10from rest_framework import serializers
11from rest_framework import generics
12from rest_framework.views import APIView
13from core.models import *
14from services.monitoring.models import MonitoringChannel, CeilometerService
15from django.forms import widgets
16from django.utils import datastructures
17from django.utils.translation import ugettext_lazy as _
18from django.utils import timezone
19from django.core.exceptions import PermissionDenied
20from xos.logger import observer_logger as logger
21
22# This REST API endpoint provides information that the ceilometer view needs to display
23
24def getTenantCeilometerProxyURL(user):
25 monitoring_channel = None
26 for obj in MonitoringChannel.get_tenant_objects().all():
27 if (obj.creator.username == user.username):
28 monitoring_channel = obj
29 break
30 if not monitoring_channel:
31 raise XOSMissingField("Monitoring channel is missing for this tenant...Create one and invoke this REST API")
32 #TODO: Wait until URL is completely UP
33 MAX_ATTEMPTS = 5
34 attempts = 0
35 while True:
36 try:
Srikanth Vavilapalli71aa28d2017-01-31 00:43:13 +000037 response = urllib2.urlopen(monitoring_channel.ceilometer_ssh_proxy_url)
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +000038 break
39 except urllib2.HTTPError, e:
40 logger.info('HTTP error %(reason)s' % {'reason':e.reason})
41 break
42 except urllib2.URLError, e:
43 attempts += 1
44 if attempts >= MAX_ATTEMPTS:
45 raise XOSServiceUnavailable("Ceilometer channel is not ready yet...Try again later")
46 logger.info('URL error %(reason)s' % {'reason':e.reason})
47 time.sleep(1)
48 pass
Srikanth Vavilapalli71aa28d2017-01-31 00:43:13 +000049 logger.info("Ceilometer proxy URL for user %(user)s is %(url)s" % {'user':user.username,'url':monitoring_channel.ceilometer_ssh_proxy_url})
50 return monitoring_channel.ceilometer_ssh_proxy_url
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +000051
52def getTenantControllerTenantMap(user, slice=None):
53 tenantmap={}
54 if not slice:
55 slices = Slice.objects.filter(creator=user)
56 else:
57 slices = [slice]
58 for s in slices:
59 for cs in s.controllerslices.all():
60 if cs.tenant_id:
61 tenantmap[cs.tenant_id] = {"slice": cs.slice.name}
62 if cs.slice.service:
63 tenantmap[cs.tenant_id]["service"] = cs.slice.service.name
64 else:
65 logger.warn("SRIKANTH: Slice %(slice)s is not associated with any service" % {'slice':cs.slice.name})
66 tenantmap[cs.tenant_id]["service"] = "Other"
67 if not slice:
68 #TEMPORARY WORK AROUND: There are some resource in network like whitebox switches does not belong to a specific tenant.
69 #They are all associated with "default_admin_tenant" tenant
Srikanth Vavilapallif6eda8f2016-09-09 13:43:51 -040070 tenantmap["default_admin_tenant"] = {"slice": "default_admin_tenant", "service": "NetworkInfra"}
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +000071 return tenantmap
72
73def build_url(path, q, params=None):
74 """Convert list of dicts and a list of params to query url format.
75
76 This will convert the following:
77 "[{field=this,op=le,value=34},
78 {field=that,op=eq,value=foo,type=string}],
79 ['foo=bar','sna=fu']"
80 to:
81 "?q.field=this&q.field=that&
82 q.op=le&q.op=eq&
83 q.type=&q.type=string&
84 q.value=34&q.value=foo&
85 foo=bar&sna=fu"
86 """
87 if q:
88 query_params = {'q.field': [],
89 'q.value': [],
90 'q.op': [],
91 'q.type': []}
92
93 for query in q:
94 for name in ['field', 'op', 'value', 'type']:
95 query_params['q.%s' % name].append(query.get(name, ''))
96
97 # Transform the dict to a sequence of two-element tuples in fixed
98 # order, then the encoded string will be consistent in Python 2&3.
99 new_qparams = sorted(query_params.items(), key=lambda x: x[0])
100 path += "?" + urllib.parse.urlencode(new_qparams, doseq=True)
101
102 if params:
103 for p in params:
104 path += '&%s' % p
105 elif params:
106 path += '?%s' % params[0]
107 for p in params[1:]:
108 path += '&%s' % p
109 return path
110
111def concat_url(endpoint, url):
112 """Concatenate endpoint and final URL.
113
114 E.g., "http://keystone/v2.0/" and "/tokens" are concatenated to
115 "http://keystone/v2.0/tokens".
116
117 :param endpoint: the base URL
118 :param url: the final URL
119 """
120 return "%s/%s" % (endpoint.rstrip("/"), url.strip("/"))
121
122def resource_list(request, query=None, ceilometer_url=None, ceilometer_usage_object=None):
123 """List the resources."""
124 url = concat_url(ceilometer_url, build_url('/v2/resources', query))
125 try:
126 response = requests.get(url)
127 except requests.exceptions.RequestException as e:
128 raise e
129 return response.json()
130
131def sample_list(request, meter_name, ceilometer_url=None, query=None, limit=None):
132 """List the samples for this meters."""
133 params = ['limit=%s' % limit] if limit else []
134 url = concat_url(ceilometer_url, build_url('/v2/samples', query, params))
135 try:
136 response = requests.get(url)
137 except requests.exceptions.RequestException as e:
138 raise e
139 return response.json()
140
141def meter_list(request, ceilometer_url=None, query=None):
142 """List the user's meters."""
143 url = concat_url(ceilometer_url, build_url('/v2/meters', query))
144 try:
145 response = requests.get(url)
146 except requests.exceptions.RequestException as e:
147 raise e
148 return response.json()
149
150
151def statistic_list(request, meter_name, ceilometer_url=None, query=None, period=None):
152 """List of statistics."""
153 p = ['period=%s' % period] if period else []
154 url = concat_url(ceilometer_url, build_url('/v2/meters/' + meter_name + '/statistics', query, p))
155 try:
156 response = requests.get(url)
157 except requests.exceptions.RequestException as e:
158 raise e
159 return response.json()
160
161def diff_lists(a, b):
162 if not a:
163 return []
164 elif not b:
165 return a
166 else:
167 return list(set(a) - set(b))
168
169def get_resource_map(request, ceilometer_url, query=None):
170 resource_map = {}
171 try:
172 resources = resource_list(request, ceilometer_url=ceilometer_url, query=query)
173 for r in resources:
174 if 'display_name' in r['metadata']:
175 name = r['metadata']['display_name']
176 elif 'name' in r['metadata']:
177 name = r['metadata']['name']
Srikanth Vavilapallidf8fc372016-09-01 18:32:34 +0000178 #Output of 'resources' REST query has chnaged from kilo to mitaka,below if conditions to handle mitaka output
179 elif 'resource_metadata.display_name' in r['metadata']:
180 name = r['metadata']['resource_metadata.display_name']
181 elif 'resource_metadata.name' in r['metadata']:
182 name = r['metadata']['resource_metadata.name']
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +0000183 else:
184 name = r['resource_id']
185 resource_map[r['resource_id']] = name
186 except requests.exceptions.RequestException as e:
187 raise e
188
189 return resource_map
190
191class Meters(object):
192 """Class for listing of available meters.
193
194 It is listing meters defined in this class that are available
195 in Ceilometer meter_list.
196
197 It is storing information that is not available in Ceilometer, i.e.
198 label, description.
199
200 """
201
202 def __init__(self, request=None, ceilometer_meter_list=None, ceilometer_url=None, query=None, tenant_map=None, resource_map=None):
203 # Storing the request.
204 self._request = request
205 self.ceilometer_url = ceilometer_url
206 self.tenant_map = tenant_map
207 self.resource_map = resource_map
208
209 # Storing the Ceilometer meter list
210 if ceilometer_meter_list:
211 self._ceilometer_meter_list = ceilometer_meter_list
212 else:
213 try:
214 meter_query=[]
215 if query:
216 meter_query = query
217 self._ceilometer_meter_list = meter_list(request, self.ceilometer_url, meter_query)
218 except requests.exceptions.RequestException as e:
219 self._ceilometer_meter_list = []
220 raise e
221
222 # Storing the meters info categorized by their services.
223 self._nova_meters_info = self._get_nova_meters_info()
224 self._neutron_meters_info = self._get_neutron_meters_info()
225 self._glance_meters_info = self._get_glance_meters_info()
226 self._cinder_meters_info = self._get_cinder_meters_info()
227 self._swift_meters_info = self._get_swift_meters_info()
228 self._kwapi_meters_info = self._get_kwapi_meters_info()
229 self._ipmi_meters_info = self._get_ipmi_meters_info()
230 self._vcpe_meters_info = self._get_vcpe_meters_info()
231 self._volt_meters_info = self._get_volt_meters_info()
232 self._sdn_meters_info = self._get_sdn_meters_info()
Srikanth Vavilapalli01bf1f42016-09-08 20:38:40 +0000233 self._broadview_meters_info = self._get_broadview_meters_info()
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +0000234
235 # Storing the meters info of all services together.
236 all_services_meters = (self._nova_meters_info,
237 self._neutron_meters_info,
238 self._glance_meters_info,
239 self._cinder_meters_info,
240 self._swift_meters_info,
241 self._kwapi_meters_info,
242 self._ipmi_meters_info,
243 self._vcpe_meters_info,
244 self._volt_meters_info,
Srikanth Vavilapalli01bf1f42016-09-08 20:38:40 +0000245 self._sdn_meters_info,
246 self._broadview_meters_info)
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +0000247 self._all_meters_info = {}
248 for service_meters in all_services_meters:
249 self._all_meters_info.update(dict([(meter_name, meter_info)
250 for meter_name, meter_info
251 in service_meters.items()]))
252
253 # Here will be the cached Meter objects, that will be reused for
254 # repeated listing.
255 self._cached_meters = {}
Srikanth Vavilapallib66304b2017-01-19 20:25:19 +0000256 self._cached_all_meters = []
257
258 def get_all(self):
259 if not self._cached_all_meters:
260 for meter in self._ceilometer_meter_list:
261 meter_info = self._all_meters_info.get(meter['name'], None)
262 if meter_info:
263 label = meter_info["label"]
264 description = meter_info["description"]
265 meter_category = meter_info["type"]
266 else:
267 label = ""
268 description = ""
269 meter_category = "Other"
270 meter["label"] = label
271 meter["description"] = description
272 meter["category"] = meter_category
273 if meter["project_id"] in self.tenant_map.keys():
274 meter["slice"] = self.tenant_map[meter["project_id"]]["slice"]
275 meter["service"] = self.tenant_map[meter["project_id"]]["service"]
276 else:
277 meter["slice"] = meter["project_id"]
278 meter["service"] = "Other"
279 if meter["resource_id"] in self.resource_map.keys():
280 meter["resource_name"] = self.resource_map[meter["resource_id"]]
281
282 self._cached_all_meters.append(meter)
283
284 return self._cached_all_meters
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +0000285
286 def list_all(self, only_meters=None, except_meters=None):
287 """Returns a list of meters based on the meters names.
288
289 :Parameters:
290 - `only_meters`: The list of meter names we want to show.
291 - `except_meters`: The list of meter names we don't want to show.
292 """
293
294 return self._list(only_meters=only_meters,
295 except_meters=except_meters)
296
297 def list_nova(self, except_meters=None):
298 """Returns a list of meters tied to nova.
299
300 :Parameters:
301 - `except_meters`: The list of meter names we don't want to show.
302 """
303
304 return self._list(only_meters=self._nova_meters_info.keys(),
305 except_meters=except_meters)
306
307 def list_neutron(self, except_meters=None):
308 """Returns a list of meters tied to neutron.
309
310 :Parameters:
311 - `except_meters`: The list of meter names we don't want to show.
312 """
313
314 return self._list(only_meters=self._neutron_meters_info.keys(),
315 except_meters=except_meters)
316
317 def list_glance(self, except_meters=None):
318 """Returns a list of meters tied to glance.
319
320 :Parameters:
321 - `except_meters`: The list of meter names we don't want to show.
322 """
323
324 return self._list(only_meters=self._glance_meters_info.keys(),
325 except_meters=except_meters)
326
327 def list_cinder(self, except_meters=None):
328 """Returns a list of meters tied to cinder.
329
330 :Parameters:
331 - `except_meters`: The list of meter names we don't want to show.
332 """
333
334 return self._list(only_meters=self._cinder_meters_info.keys(),
335 except_meters=except_meters)
336
337 def list_swift(self, except_meters=None):
338 """Returns a list of meters tied to swift.
339
340 :Parameters:
341 - `except_meters`: The list of meter names we don't want to show.
342 """
343
344 return self._list(only_meters=self._swift_meters_info.keys(),
345 except_meters=except_meters)
346
347 def list_kwapi(self, except_meters=None):
348 """Returns a list of meters tied to kwapi.
349
350 :Parameters:
351 - `except_meters`: The list of meter names we don't want to show.
352 """
353
354 return self._list(only_meters=self._kwapi_meters_info.keys(),
355 except_meters=except_meters)
356
357 def list_ipmi(self, except_meters=None):
358 """Returns a list of meters tied to ipmi
359
360 :Parameters:
361 - `except_meters`: The list of meter names we don't want to show
362 """
363
364 return self._list(only_meters=self._ipmi_meters_info.keys(),
365 except_meters=except_meters)
366
367 def list_vcpe(self, except_meters=None):
368 """Returns a list of meters tied to vcpe service
369
370 :Parameters:
371 - `except_meters`: The list of meter names we don't want to show
372 """
373
374 return self._list(only_meters=self._vcpe_meters_info.keys(),
375 except_meters=except_meters)
376
377 def list_volt(self, except_meters=None):
378 """Returns a list of meters tied to volt service
379
380 :Parameters:
381 - `except_meters`: The list of meter names we don't want to show
382 """
383
384 return self._list(only_meters=self._volt_meters_info.keys(),
385 except_meters=except_meters)
386
387 def list_sdn(self, except_meters=None):
388 """Returns a list of meters tied to sdn service
389
390 :Parameters:
391 - `except_meters`: The list of meter names we don't want to show
392 """
393
394 return self._list(only_meters=self._sdn_meters_info.keys(),
395 except_meters=except_meters)
396
Srikanth Vavilapalli01bf1f42016-09-08 20:38:40 +0000397 def list_broadview(self, except_meters=None):
398 """Returns a list of meters tied to broadview service
399
400 :Parameters:
401 - `except_meters`: The list of meter names we don't want to show
402 """
403
404 return self._list(only_meters=self._broadview_meters_info.keys(),
405 except_meters=except_meters)
406
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +0000407 def list_other_services(self, except_meters=None):
408 """Returns a list of meters tied to ipmi
409
410 :Parameters:
411 - `except_meters`: The list of meter names we don't want to show
412 """
413 other_service_meters = [m for m in self._ceilometer_meter_list
414 if m.name not in self._all_meters_info.keys()]
415 other_service_meters = diff_lists(other_service_meters, except_meters)
416
417 meters = []
418 for meter in other_service_meters:
419 self._cached_meters[meter.name] = meter
420 meters.append(meter)
421 return meters
422
423 def _list(self, only_meters=None, except_meters=None):
424 """Returns a list of meters based on the meters names.
425
426 :Parameters:
427 - `only_meters`: The list of meter names we want to show.
428 - `except_meters`: The list of meter names we don't want to show.
429 """
430
431 # Get all wanted meter names.
432 if only_meters:
433 meter_names = only_meters
434 else:
435 meter_names = [meter_name for meter_name
436 in self._all_meters_info.keys()]
437
438 meter_names = diff_lists(meter_names, except_meters)
439 # Collect meters for wanted meter names.
440 return self._get_meters(meter_names)
441
442 def _get_meters(self, meter_names):
443 """Obtain meters based on meter_names.
444
445 The meters that do not exist in Ceilometer meter list are left out.
446
447 :Parameters:
448 - `meter_names`: A list of meter names we want to fetch.
449 """
450
451 meters = []
452 for meter_name in meter_names:
453 meter_candidates = self._get_meter(meter_name)
454 if meter_candidates:
455 meters.extend(meter_candidates)
456 return meters
457
458 def _get_meter(self, meter_name):
459 """Obtains a meter.
460
461 Obtains meter either from cache or from Ceilometer meter list
462 joined with statically defined meter info like label and description.
463
464 :Parameters:
465 - `meter_name`: A meter name we want to fetch.
466 """
467 meter_candidates = self._cached_meters.get(meter_name, None)
468 if not meter_candidates:
469 meter_candidates = [m for m in self._ceilometer_meter_list
470 if m["name"] == meter_name]
471
472 if meter_candidates:
473 meter_info = self._all_meters_info.get(meter_name, None)
474 if meter_info:
475 label = meter_info["label"]
476 description = meter_info["description"]
477 meter_category = meter_info["type"]
478 else:
479 label = ""
480 description = ""
481 meter_category = "Other"
482 for meter in meter_candidates:
483 meter["label"] = label
484 meter["description"] = description
485 meter["category"] = meter_category
486 if meter["project_id"] in self.tenant_map.keys():
487 meter["slice"] = self.tenant_map[meter["project_id"]]["slice"]
488 meter["service"] = self.tenant_map[meter["project_id"]]["service"]
489 else:
490 meter["slice"] = meter["project_id"]
491 meter["service"] = "Other"
492 if meter["resource_id"] in self.resource_map.keys():
493 meter["resource_name"] = self.resource_map[meter["resource_id"]]
494
495 self._cached_meters[meter_name] = meter_candidates
496
497 return meter_candidates
498
499 def _get_nova_meters_info(self):
500 """Returns additional info for each meter.
501
502 That will be used for augmenting the Ceilometer meter.
503 """
504
505 # TODO(lsmola) Unless the Ceilometer will provide the information
506 # below, I need to define it as a static here. I will be joining this
507 # to info that I am able to obtain from Ceilometer meters, hopefully
508 # some day it will be supported all.
509 meters_info = datastructures.SortedDict([
510 ("instance", {
511 'type': _("Nova"),
512 'label': '',
513 'description': _("Existence of instance"),
514 }),
515 ("instance:<type>", {
516 'type': _("Nova"),
517 'label': '',
518 'description': _("Existence of instance <type> "
519 "(openstack types)"),
520 }),
521 ("memory", {
522 'type': _("Nova"),
523 'label': '',
524 'description': _("Volume of RAM"),
525 }),
526 ("memory.usage", {
527 'type': _("Nova"),
528 'label': '',
529 'description': _("Volume of RAM used"),
530 }),
531 ("cpu", {
532 'type': _("Nova"),
533 'label': '',
534 'description': _("CPU time used"),
535 }),
536 ("cpu_util", {
537 'type': _("Nova"),
538 'label': '',
539 'description': _("Average CPU utilization"),
540 }),
541 ("vcpus", {
542 'type': _("Nova"),
543 'label': '',
544 'description': _("Number of VCPUs"),
545 }),
546 ("disk.read.requests", {
547 'type': _("Nova"),
548 'label': '',
549 'description': _("Number of read requests"),
550 }),
551 ("disk.write.requests", {
552 'type': _("Nova"),
553 'label': '',
554 'description': _("Number of write requests"),
555 }),
556 ("disk.read.bytes", {
557 'type': _("Nova"),
558 'label': '',
559 'description': _("Volume of reads"),
560 }),
561 ("disk.write.bytes", {
562 'type': _("Nova"),
563 'label': '',
564 'description': _("Volume of writes"),
565 }),
566 ("disk.read.requests.rate", {
567 'type': _("Nova"),
568 'label': '',
569 'description': _("Average rate of read requests"),
570 }),
571 ("disk.write.requests.rate", {
572 'type': _("Nova"),
573 'label': '',
574 'description': _("Average rate of write requests"),
575 }),
576 ("disk.read.bytes.rate", {
577 'type': _("Nova"),
578 'label': '',
579 'description': _("Average rate of reads"),
580 }),
581 ("disk.write.bytes.rate", {
582 'type': _("Nova"),
583 'label': '',
584 'description': _("Average volume of writes"),
585 }),
586 ("disk.root.size", {
587 'type': _("Nova"),
588 'label': '',
589 'description': _("Size of root disk"),
590 }),
591 ("disk.ephemeral.size", {
592 'type': _("Nova"),
593 'label': '',
594 'description': _("Size of ephemeral disk"),
595 }),
596 ("network.incoming.bytes", {
597 'type': _("Nova"),
598 'label': '',
599 'description': _("Number of incoming bytes "
600 "on the network for a VM interface"),
601 }),
602 ("network.outgoing.bytes", {
603 'type': _("Nova"),
604 'label': '',
605 'description': _("Number of outgoing bytes "
606 "on the network for a VM interface"),
607 }),
608 ("network.incoming.packets", {
609 'type': _("Nova"),
610 'label': '',
611 'description': _("Number of incoming "
612 "packets for a VM interface"),
613 }),
614 ("network.outgoing.packets", {
615 'type': _("Nova"),
616 'label': '',
617 'description': _("Number of outgoing "
618 "packets for a VM interface"),
619 }),
620 ("network.incoming.bytes.rate", {
621 'type': _("Nova"),
622 'label': '',
623 'description': _("Average rate per sec of incoming "
624 "bytes on a VM network interface"),
625 }),
626 ("network.outgoing.bytes.rate", {
627 'type': _("Nova"),
628 'label': '',
629 'description': _("Average rate per sec of outgoing "
630 "bytes on a VM network interface"),
631 }),
632 ("network.incoming.packets.rate", {
633 'type': _("Nova"),
634 'label': '',
635 'description': _("Average rate per sec of incoming "
636 "packets on a VM network interface"),
637 }),
638 ("network.outgoing.packets.rate", {
639 'type': _("Nova"),
640 'label': '',
641 'description': _("Average rate per sec of outgoing "
642 "packets on a VM network interface"),
643 }),
644 ])
645 # Adding flavor based meters into meters_info dict
646 # TODO(lsmola) this kind of meter will be probably deprecated
647 # https://bugs.launchpad.net/ceilometer/+bug/1208365 . Delete it then.
648 #for flavor in get_flavor_names(self._request):
649 # name = 'instance:%s' % flavor
650 # meters_info[name] = dict(meters_info["instance:<type>"])
651
652 # meters_info[name]['description'] = (
653 # _('Duration of instance type %s (openstack flavor)') %
654 # flavor)
655
656 # TODO(lsmola) allow to set specific in local_settings. For all meters
657 # because users can have their own agents and meters.
658 return meters_info
659
660 def _get_neutron_meters_info(self):
661 """Returns additional info for each meter.
662
663 That will be used for augmenting the Ceilometer meter.
664 """
665
666 # TODO(lsmola) Unless the Ceilometer will provide the information
667 # below, I need to define it as a static here. I will be joining this
668 # to info that I am able to obtain from Ceilometer meters, hopefully
669 # some day it will be supported all.
670 return datastructures.SortedDict([
671 ('network', {
672 'type': _("Neutron"),
673 'label': '',
674 'description': _("Existence of network"),
675 }),
676 ('network.create', {
677 'type': _("Neutron"),
678 'label': '',
679 'description': _("Creation requests for this network"),
680 }),
681 ('network.update', {
682 'type': _("Neutron"),
683 'label': '',
684 'description': _("Update requests for this network"),
685 }),
686 ('subnet', {
687 'type': _("Neutron"),
688 'label': '',
689 'description': _("Existence of subnet"),
690 }),
691 ('subnet.create', {
692 'type': _("Neutron"),
693 'label': '',
694 'description': _("Creation requests for this subnet"),
695 }),
696 ('subnet.update', {
697 'type': _("Neutron"),
698 'label': '',
699 'description': _("Update requests for this subnet"),
700 }),
701 ('port', {
702 'type': _("Neutron"),
703 'label': '',
704 'description': _("Existence of port"),
705 }),
706 ('port.create', {
707 'type': _("Neutron"),
708 'label': '',
709 'description': _("Creation requests for this port"),
710 }),
711 ('port.update', {
712 'type': _("Neutron"),
713 'label': '',
714 'description': _("Update requests for this port"),
715 }),
716 ('router', {
717 'type': _("Neutron"),
718 'label': '',
719 'description': _("Existence of router"),
720 }),
721 ('router.create', {
722 'type': _("Neutron"),
723 'label': '',
724 'description': _("Creation requests for this router"),
725 }),
726 ('router.update', {
727 'type': _("Neutron"),
728 'label': '',
729 'description': _("Update requests for this router"),
730 }),
731 ('ip.floating', {
732 'type': _("Neutron"),
733 'label': '',
734 'description': _("Existence of floating ip"),
735 }),
736 ('ip.floating.create', {
737 'type': _("Neutron"),
738 'label': '',
739 'description': _("Creation requests for this floating ip"),
740 }),
741 ('ip.floating.update', {
742 'type': _("Neutron"),
743 'label': '',
744 'description': _("Update requests for this floating ip"),
745 }),
746 ])
747
748 def _get_glance_meters_info(self):
749 """Returns additional info for each meter.
750
751 That will be used for augmenting the Ceilometer meter.
752 """
753
754 # TODO(lsmola) Unless the Ceilometer will provide the information
755 # below, I need to define it as a static here. I will be joining this
756 # to info that I am able to obtain from Ceilometer meters, hopefully
757 # some day it will be supported all.
758 return datastructures.SortedDict([
759 ('image', {
760 'type': _("Glance"),
761 'label': '',
762 'description': _("Image existence check"),
763 }),
764 ('image.size', {
765 'type': _("Glance"),
766 'label': '',
767 'description': _("Uploaded image size"),
768 }),
769 ('image.update', {
770 'type': _("Glance"),
771 'label': '',
772 'description': _("Number of image updates"),
773 }),
774 ('image.upload', {
775 'type': _("Glance"),
776 'label': '',
777 'description': _("Number of image uploads"),
778 }),
779 ('image.delete', {
780 'type': _("Glance"),
781 'label': '',
782 'description': _("Number of image deletions"),
783 }),
784 ('image.download', {
785 'type': _("Glance"),
786 'label': '',
787 'description': _("Image is downloaded"),
788 }),
789 ('image.serve', {
790 'type': _("Glance"),
791 'label': '',
792 'description': _("Image is served out"),
793 }),
794 ])
795
796 def _get_cinder_meters_info(self):
797 """Returns additional info for each meter.
798
799 That will be used for augmenting the Ceilometer meter.
800 """
801
802 # TODO(lsmola) Unless the Ceilometer will provide the information
803 # below, I need to define it as a static here. I will be joining this
804 # to info that I am able to obtain from Ceilometer meters, hopefully
805 # some day it will be supported all.
806 return datastructures.SortedDict([
807 ('volume', {
808 'type': _("Cinder"),
809 'label': '',
810 'description': _("Existence of volume"),
811 }),
812 ('volume.size', {
813 'type': _("Cinder"),
814 'label': '',
815 'description': _("Size of volume"),
816 }),
817 ])
818
819 def _get_swift_meters_info(self):
820 """Returns additional info for each meter.
821
822 That will be used for augmenting the Ceilometer meter.
823 """
824
825 # TODO(lsmola) Unless the Ceilometer will provide the information
826 # below, I need to define it as a static here. I will be joining this
827 # to info that I am able to obtain from Ceilometer meters, hopefully
828 # some day it will be supported all.
829 return datastructures.SortedDict([
830 ('storage.objects', {
831 'type': _("Swift"),
832 'label': '',
833 'description': _("Number of objects"),
834 }),
835 ('storage.objects.size', {
836 'type': _("Swift"),
837 'label': '',
838 'description': _("Total size of stored objects"),
839 }),
840 ('storage.objects.containers', {
841 'type': _("Swift"),
842 'label': '',
843 'description': _("Number of containers"),
844 }),
845 ('storage.objects.incoming.bytes', {
846 'type': _("Swift"),
847 'label': '',
848 'description': _("Number of incoming bytes"),
849 }),
850 ('storage.objects.outgoing.bytes', {
851 'type': _("Swift"),
852 'label': '',
853 'description': _("Number of outgoing bytes"),
854 }),
855 ('storage.api.request', {
856 'type': _("Swift"),
857 'label': '',
858 'description': _("Number of API requests against swift"),
859 }),
860 ])
861
862 def _get_kwapi_meters_info(self):
863 """Returns additional info for each meter.
864
865 That will be used for augmenting the Ceilometer meter.
866 """
867
868 # TODO(lsmola) Unless the Ceilometer will provide the information
869 # below, I need to define it as a static here. I will be joining this
870 # to info that I am able to obtain from Ceilometer meters, hopefully
871 # some day it will be supported all.
872 return datastructures.SortedDict([
873 ('energy', {
874 'type': _("Kwapi"),
875 'label': '',
876 'description': _("Amount of energy"),
877 }),
878 ('power', {
879 'type': _("Kwapi"),
880 'label': '',
881 'description': _("Power consumption"),
882 }),
883 ])
884
885 def _get_ipmi_meters_info(self):
886 """Returns additional info for each meter
887
888 That will be used for augmenting the Ceilometer meter
889 """
890
891 # TODO(lsmola) Unless the Ceilometer will provide the information
892 # below, I need to define it as a static here. I will be joining this
893 # to info that I am able to obtain from Ceilometer meters, hopefully
894 # some day it will be supported all.
895 return datastructures.SortedDict([
896 ('hardware.ipmi.node.power', {
897 'type': _("IPMI"),
898 'label': '',
899 'description': _("System Current Power"),
900 }),
901 ('hardware.ipmi.fan', {
902 'type': _("IPMI"),
903 'label': '',
904 'description': _("Fan RPM"),
905 }),
906 ('hardware.ipmi.temperature', {
907 'type': _("IPMI"),
908 'label': '',
909 'description': _("Sensor Temperature Reading"),
910 }),
911 ('hardware.ipmi.current', {
912 'type': _("IPMI"),
913 'label': '',
914 'description': _("Sensor Current Reading"),
915 }),
916 ('hardware.ipmi.voltage', {
917 'type': _("IPMI"),
918 'label': '',
919 'description': _("Sensor Voltage Reading"),
920 }),
921 ('hardware.ipmi.node.inlet_temperature', {
922 'type': _("IPMI"),
923 'label': '',
924 'description': _("System Inlet Temperature Reading"),
925 }),
926 ('hardware.ipmi.node.outlet_temperature', {
927 'type': _("IPMI"),
928 'label': '',
929 'description': _("System Outlet Temperature Reading"),
930 }),
931 ('hardware.ipmi.node.airflow', {
932 'type': _("IPMI"),
933 'label': '',
934 'description': _("System Airflow Reading"),
935 }),
936 ('hardware.ipmi.node.cups', {
937 'type': _("IPMI"),
938 'label': '',
939 'description': _("System CUPS Reading"),
940 }),
941 ('hardware.ipmi.node.cpu_util', {
942 'type': _("IPMI"),
943 'label': '',
944 'description': _("System CPU Utility Reading"),
945 }),
946 ('hardware.ipmi.node.mem_util', {
947 'type': _("IPMI"),
948 'label': '',
949 'description': _("System Memory Utility Reading"),
950 }),
951 ('hardware.ipmi.node.io_util', {
952 'type': _("IPMI"),
953 'label': '',
954 'description': _("System IO Utility Reading"),
955 }),
956 ])
957
958 def _get_vcpe_meters_info(self):
959 """Returns additional info for each meter
960
961 That will be used for augmenting the Ceilometer meter
962 """
963
964 # TODO(lsmola) Unless the Ceilometer will provide the information
965 # below, I need to define it as a static here. I will be joining this
966 # to info that I am able to obtain from Ceilometer meters, hopefully
967 # some day it will be supported all.
968 return datastructures.SortedDict([
969 ('vsg', {
970 'type': _("VSG"),
971 'label': '',
972 'description': _("Existence of vsg instance"),
973 }),
974 ('vsg.dns.cache.size', {
975 'type': _("VSG"),
976 'label': '',
977 'description': _("Number of entries in DNS cache"),
978 }),
979 ('vsg.dns.total_instered_entries', {
980 'type': _("VSG"),
981 'label': '',
982 'description': _("Total number of inserted entries into the cache"),
983 }),
984 ('vsg.dns.replaced_unexpired_entries', {
985 'type': _("VSG"),
986 'label': '',
987 'description': _("Unexpired entries that were thrown out of cache"),
988 }),
989 ('vsg.dns.queries_answered_locally', {
990 'type': _("VSG"),
991 'label': '',
992 'description': _("Number of cache hits"),
993 }),
994 ('vsg.dns.queries_forwarded', {
995 'type': _("VSG"),
996 'label': '',
997 'description': _("Number of cache misses"),
998 }),
999 ('vsg.dns.server.queries_sent', {
1000 'type': _("VSG"),
1001 'label': '',
1002 'description': _("For each upstream server, the number of queries sent"),
1003 }),
1004 ('vsg.dns.server.queries_failed', {
1005 'type': _("VSG"),
1006 'label': '',
1007 'description': _("For each upstream server, the number of queries failed"),
1008 }),
1009 ])
1010
1011 def _get_volt_meters_info(self):
1012 """Returns additional info for each meter
1013
1014 That will be used for augmenting the Ceilometer meter
1015 """
1016
1017 # TODO(lsmola) Unless the Ceilometer will provide the information
1018 # below, I need to define it as a static here. I will be joining this
1019 # to info that I am able to obtain from Ceilometer meters, hopefully
1020 # some day it will be supported all.
1021 return datastructures.SortedDict([
1022 ('volt.device', {
1023 'type': _("VOLT"),
1024 'label': '',
1025 'description': _("Existence of olt device"),
1026 }),
1027 ('volt.device.disconnect', {
1028 'type': _("VOLT"),
1029 'label': '',
1030 'description': _("Olt device disconnected"),
1031 }),
1032 ('volt.device.subscriber', {
1033 'type': _("VOLT"),
1034 'label': '',
1035 'description': _("Existence of olt subscriber"),
1036 }),
1037 ('volt.device.subscriber.unregister', {
1038 'type': _("VOLT"),
1039 'label': '',
1040 'description': _("Olt subscriber unregistered"),
1041 }),
1042 ])
1043
1044 def _get_sdn_meters_info(self):
1045 """Returns additional info for each meter
1046
1047 That will be used for augmenting the Ceilometer meter
1048 """
1049
1050 # TODO(lsmola) Unless the Ceilometer will provide the information
1051 # below, I need to define it as a static here. I will be joining this
1052 # to info that I am able to obtain from Ceilometer meters, hopefully
1053 # some day it will be supported all.
1054 return datastructures.SortedDict([
1055 ('switch', {
1056 'type': _("SDN"),
1057 'label': '',
1058 'description': _("Existence of switch"),
1059 }),
1060 ('switch.port', {
1061 'type': _("SDN"),
1062 'label': '',
1063 'description': _("Existence of port"),
1064 }),
1065 ('switch.port.receive.packets', {
1066 'type': _("SDN"),
1067 'label': '',
1068 'description': _("Packets received on port"),
1069 }),
1070 ('switch.port.transmit.packets', {
1071 'type': _("SDN"),
1072 'label': '',
1073 'description': _("Packets transmitted on port"),
1074 }),
1075 ('switch.port.receive.drops', {
1076 'type': _("SDN"),
1077 'label': '',
1078 'description': _("Drops received on port"),
1079 }),
1080 ('switch.port.transmit.drops', {
1081 'type': _("SDN"),
1082 'label': '',
1083 'description': _("Drops transmitted on port"),
1084 }),
1085 ('switch.port.receive.errors', {
1086 'type': _("SDN"),
1087 'label': '',
1088 'description': _("Errors received on port"),
1089 }),
1090 ('switch.port.transmit.errors', {
1091 'type': _("SDN"),
1092 'label': '',
1093 'description': _("Errors transmitted on port"),
1094 }),
1095 ('switch.flow', {
1096 'type': _("SDN"),
1097 'label': '',
1098 'description': _("Duration of flow"),
1099 }),
1100 ('switch.flow.packets', {
1101 'type': _("SDN"),
1102 'label': '',
1103 'description': _("Packets received"),
1104 }),
1105 ('switch.table', {
1106 'type': _("SDN"),
1107 'label': '',
1108 'description': _("Existence of table"),
1109 }),
1110 ('switch.table.active.entries', {
1111 'type': _("SDN"),
1112 'label': '',
1113 'description': _("Active entries in table"),
1114 }),
1115 ])
1116
Srikanth Vavilapalli01bf1f42016-09-08 20:38:40 +00001117 def _get_broadview_meters_info(self):
1118 """Returns additional info for each meter
1119
1120 That will be used for augmenting the Ceilometer meter
1121 """
1122
1123 # TODO(lsmola) Unless the Ceilometer will provide the information
1124 # below, I need to define it as a static here. I will be joining this
1125 # to info that I am able to obtain from Ceilometer meters, hopefully
1126 # some day it will be supported all.
1127 return datastructures.SortedDict([
1128 ('broadview.bst.device', {
1129 'type': _("BROADVIEW"),
1130 'label': '',
1131 'description': _("Existence of BST device"),
1132 }),
1133 ('broadview.bst.ingress-port-priority-group', {
1134 'type': _("BROADVIEW"),
1135 'label': '',
1136 'description': _("Existence of BST device"),
1137 }),
1138 ('broadview.bst.ingress-port-service-pool', {
1139 'type': _("BROADVIEW"),
1140 'label': '',
1141 'description': _("Existence of BST device"),
1142 }),
1143 ('broadview.bst.egress-cpu-queue', {
1144 'type': _("BROADVIEW"),
1145 'label': '',
1146 'description': _("Existence of BST device"),
1147 }),
1148 ('broadview.bst.egress-mc-queue', {
1149 'type': _("BROADVIEW"),
1150 'label': '',
1151 'description': _("Existence of BST device"),
1152 }),
1153 ('broadview.bst.egress-port-service-pool', {
1154 'type': _("BROADVIEW"),
1155 'label': '',
1156 'description': _("Existence of BST device"),
1157 }),
1158 ('broadview.bst.egress-rqe-queue', {
1159 'type': _("BROADVIEW"),
1160 'label': '',
1161 'description': _("Existence of BST device"),
1162 }),
1163 ('broadview.bst.egress-service-pool', {
1164 'type': _("BROADVIEW"),
1165 'label': '',
1166 'description': _("Existence of BST device"),
1167 }),
1168 ('broadview.bst.egress-uc-queue', {
1169 'type': _("BROADVIEW"),
1170 'label': '',
1171 'description': _("Existence of BST device"),
1172 }),
Srikanth Vavilapallib8614202016-09-19 19:07:38 -04001173 ('broadview.pt.packet-trace-lag-resolution', {
1174 'type': _("BROADVIEW"),
1175 'label': '',
1176 'description': _("Existence of BST device"),
1177 }),
1178 ('broadview.pt.packet-trace-ecmp-resolution', {
1179 'type': _("BROADVIEW"),
1180 'label': '',
1181 'description': _("Existence of BST device"),
1182 }),
1183 ('broadview.pt.packet-trace-profile', {
1184 'type': _("BROADVIEW"),
1185 'label': '',
1186 'description': _("Existence of BST device"),
1187 }),
Srikanth Vavilapalli01bf1f42016-09-08 20:38:40 +00001188 ])
1189
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +00001190def make_query(user_id=None, tenant_id=None, resource_id=None,
1191 user_ids=None, tenant_ids=None, resource_ids=None):
1192 """Returns query built from given parameters.
1193
1194 This query can be then used for querying resources, meters and
1195 statistics.
1196
1197 :Parameters:
1198 - `user_id`: user_id, has a priority over list of ids
1199 - `tenant_id`: tenant_id, has a priority over list of ids
1200 - `resource_id`: resource_id, has a priority over list of ids
1201 - `user_ids`: list of user_ids
1202 - `tenant_ids`: list of tenant_ids
1203 - `resource_ids`: list of resource_ids
1204 """
1205 user_ids = user_ids or []
1206 tenant_ids = tenant_ids or []
1207 resource_ids = resource_ids or []
1208
1209 query = []
1210 if user_id:
1211 user_ids = [user_id]
1212 for u_id in user_ids:
1213 query.append({"field": "user_id", "op": "eq", "value": u_id})
1214
1215 if tenant_id:
1216 tenant_ids = [tenant_id]
1217 for t_id in tenant_ids:
1218 query.append({"field": "project_id", "op": "eq", "value": t_id})
1219
1220 if resource_id:
1221 resource_ids = [resource_id]
1222 for r_id in resource_ids:
1223 query.append({"field": "resource_id", "op": "eq", "value": r_id})
1224
1225 return query
1226
1227def calc_date_args(date_from, date_to, date_options):
1228 # TODO(lsmola) all timestamps should probably work with
1229 # current timezone. And also show the current timezone in chart.
1230 if date_options == "other":
1231 try:
1232 if date_from:
1233 date_from = pytz.utc.localize(
1234 datetime.datetime.strptime(str(date_from), "%Y-%m-%d"))
1235 else:
1236 # TODO(lsmola) there should be probably the date
1237 # of the first sample as default, so it correctly
1238 # counts the time window. Though I need ordering
1239 # and limit of samples to obtain that.
1240 pass
1241 if date_to:
1242 date_to = pytz.utc.localize(
1243 datetime.datetime.strptime(str(date_to), "%Y-%m-%d"))
1244 # It returns the beginning of the day, I want the end of
1245 # the day, so I add one day without a second.
1246 date_to = (date_to + datetime.timedelta(days=1) -
1247 datetime.timedelta(seconds=1))
1248 else:
1249 date_to = timezone.now()
1250 except Exception:
1251 raise ValueError(_("The dates haven't been recognized"))
1252 else:
1253 try:
1254 date_to = timezone.now()
1255 date_from = date_to - datetime.timedelta(days=float(date_options))
1256 except Exception as e:
1257 raise e
1258 #raise ValueError(_("The time delta must be a number representing "
1259 # "the time span in days"))
1260 return date_from, date_to
1261
1262class MetersList(APIView):
1263 method_kind = "list"
1264 method_name = "meters"
1265
1266 def get(self, request, format=None):
1267 if (not request.user.is_authenticated()):
1268 raise PermissionDenied("You must be authenticated in order to use this API")
1269 tenant_ceilometer_url = getTenantCeilometerProxyURL(request.user)
1270 if (not tenant_ceilometer_url):
1271 raise XOSMissingField("Tenant ceilometer URL is missing")
1272
1273 tenant_id = request.query_params.get('tenant', None)
1274 resource_id = request.query_params.get('resource', None)
1275
1276 query = []
1277 if tenant_id:
1278 query.extend(make_query(tenant_id=tenant_id))
1279 if resource_id:
1280 query.extend(make_query(resource_id=resource_id))
1281
1282 tenant_map = getTenantControllerTenantMap(request.user)
1283 resource_map = get_resource_map(request, ceilometer_url=tenant_ceilometer_url, query=query)
1284 meters = Meters(request, ceilometer_url=tenant_ceilometer_url, query=query, tenant_map=tenant_map, resource_map=resource_map)
Srikanth Vavilapallib66304b2017-01-19 20:25:19 +00001285 return Response(meters.get_all())
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +00001286
1287class MeterStatisticsList(APIView):
1288 method_kind = "list"
1289 method_name = "meterstatistics"
1290
1291 def get(self, request, format=None):
1292 if (not request.user.is_authenticated()):
1293 raise PermissionDenied("You must be authenticated in order to use this API")
1294 tenant_ceilometer_url = getTenantCeilometerProxyURL(request.user)
1295 if (not tenant_ceilometer_url):
1296 raise XOSMissingField("Tenant ceilometer URL is missing")
1297 tenant_map = getTenantControllerTenantMap(request.user)
1298
1299 date_options = request.query_params.get('period', 1)
1300 date_from = request.query_params.get('date_from', '')
1301 date_to = request.query_params.get('date_to', '')
1302
1303 try:
1304 date_from, date_to = calc_date_args(date_from,
1305 date_to,
1306 date_options)
1307 except Exception as e:
1308 raise e
1309
1310 additional_query = []
1311 if date_from:
1312 additional_query.append({'field': 'timestamp',
1313 'op': 'ge',
1314 'value': date_from})
1315 if date_to:
1316 additional_query.append({'field': 'timestamp',
1317 'op': 'le',
1318 'value': date_to})
1319
1320 meter_name = request.query_params.get('meter', None)
1321 tenant_id = request.query_params.get('tenant', None)
1322 resource_id = request.query_params.get('resource', None)
1323
1324 query = []
1325 if tenant_id:
1326 query.extend(make_query(tenant_id=tenant_id))
1327 if resource_id:
1328 query.extend(make_query(resource_id=resource_id))
1329
1330 if meter_name:
1331 #Statistics query for one meter
1332 if additional_query:
1333 query = query + additional_query
1334 statistics = statistic_list(request, meter_name,
1335 ceilometer_url=tenant_ceilometer_url, query=query, period=3600*24)
1336 statistic = statistics[-1]
1337 row = {"name": 'none',
1338 "meter": meter_name,
1339 "time": statistic["period_end"],
1340 "value": statistic["avg"]}
1341 return Response(row)
1342
1343 #Statistics query for all meter
1344 resource_map = get_resource_map(request, ceilometer_url=tenant_ceilometer_url, query=query)
1345 meters = Meters(request, ceilometer_url=tenant_ceilometer_url, query=query, tenant_map=tenant_map, resource_map=resource_map)
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +00001346 report_rows = []
Srikanth Vavilapallib66304b2017-01-19 20:25:19 +00001347 for meter in meters.get_all():
1348 query = make_query(tenant_id=meter["project_id"],resource_id=meter["resource_id"])
1349 if additional_query:
1350 query = query + additional_query
1351 try:
1352 statistics = statistic_list(request, meter["name"],
1353 ceilometer_url=tenant_ceilometer_url, query=query, period=3600*24)
1354 except Exception as e:
1355 logger.error('Exception during statistics query for meter %(meter)s and reason:%(reason)s' % {'meter':meter["name"], 'reason':str(e)})
1356 statistics = None
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +00001357
Srikanth Vavilapallib66304b2017-01-19 20:25:19 +00001358 if not statistics:
1359 continue
1360 statistic = statistics[-1]
1361 row = {"name": 'none',
1362 "slice": meter["slice"],
1363 "project_id": meter["project_id"],
1364 "service": meter["service"],
1365 "resource_id": meter["resource_id"],
1366 "resource_name": meter["resource_name"],
1367 "meter": meter["name"],
1368 "description": meter["description"],
1369 "category": meter["category"],
1370 "time": statistic["period_end"],
1371 "value": statistic["avg"],
1372 "unit": meter["unit"]}
1373 report_rows.append(row)
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +00001374
1375 return Response(report_rows)
1376
1377
1378class MeterSamplesList(APIView):
1379 method_kind = "list"
1380 method_name = "metersamples"
1381
1382 def get(self, request, format=None):
1383 if (not request.user.is_authenticated()):
1384 raise PermissionDenied("You must be authenticated in order to use this API")
1385 tenant_ceilometer_url = getTenantCeilometerProxyURL(request.user)
1386 if (not tenant_ceilometer_url):
1387 raise XOSMissingField("Tenant ceilometer URL is missing")
1388 meter_name = request.query_params.get('meter', None)
1389 if not meter_name:
1390 raise XOSMissingField("Meter name in query params is missing")
1391 limit = request.query_params.get('limit', 10)
1392 tenant_id = request.query_params.get('tenant', None)
1393 resource_id = request.query_params.get('resource', None)
1394 query = []
1395 if tenant_id:
1396 query.extend(make_query(tenant_id=tenant_id))
1397 if resource_id:
1398 query.extend(make_query(resource_id=resource_id))
1399 query.append({"field": "meter", "op": "eq", "value": meter_name})
1400 samples = sample_list(request, meter_name,
1401 ceilometer_url=tenant_ceilometer_url, query=query, limit=limit)
1402 if samples:
1403 tenant_map = getTenantControllerTenantMap(request.user)
1404 resource_map = get_resource_map(request, ceilometer_url=tenant_ceilometer_url)
1405 for sample in samples:
1406 if sample["project_id"] in tenant_map.keys():
1407 sample["slice"] = tenant_map[sample["project_id"]]["slice"]
1408 else:
1409 sample["slice"] = sample["project_id"]
1410 if sample["resource_id"] in resource_map.keys():
1411 sample["resource_name"] = resource_map[sample["resource_id"]]
1412 else:
1413 sample["resource_name"] = sample["resource_id"]
1414 return Response(samples)
1415
1416class XOSSliceServiceList(APIView):
1417 method_kind = "list"
1418 method_name = "xos-slice-service-mapping"
1419
1420 def get(self, request, format=None):
1421 if (not request.user.is_authenticated()):
1422 raise PermissionDenied("You must be authenticated in order to use this API")
1423 tenant_map = getTenantControllerTenantMap(request.user)
1424 service_map={}
1425 for k,v in tenant_map.iteritems():
1426 if not (v['service'] in service_map.keys()):
1427 service_map[v['service']] = {}
1428 service_map[v['service']]['service'] = v['service']
1429 service_map[v['service']]['slices'] = []
1430 slice_details = {'slice':v['slice'], 'project_id':k}
1431 service_map[v['service']]['slices'].append(slice_details)
1432 return Response(service_map.values())
1433
1434class XOSInstanceStatisticsList(APIView):
1435 method_kind = "list"
1436 method_name = "xos-instance-statistics"
1437
1438 def get(self, request, format=None):
1439 if (not request.user.is_authenticated()):
1440 raise PermissionDenied("You must be authenticated in order to use this API")
1441 tenant_ceilometer_url = getTenantCeilometerProxyURL(request.user)
1442 if (not tenant_ceilometer_url):
1443 raise XOSMissingField("Tenant ceilometer URL is missing")
1444 instance_uuid = request.query_params.get('instance-uuid', None)
1445 if not instance_uuid:
1446 raise XOSMissingField("Instance UUID in query params is missing")
1447 if not Instance.objects.filter(instance_uuid=instance_uuid):
1448 raise XOSMissingField("XOS Instance object is missing for this uuid")
1449 xos_instance = Instance.objects.filter(instance_uuid=instance_uuid)[0]
1450 tenant_map = getTenantControllerTenantMap(request.user, xos_instance.slice)
1451 tenant_id = tenant_map.keys()[0]
1452 resource_ids = []
1453 resource_ids.append(instance_uuid)
1454 for p in xos_instance.ports.all():
1455 #neutron port resource id is represented in ceilometer as "nova instance-name"+"-"+"nova instance-id"+"-"+"tap"+first 11 characters of port-id
1456 resource_ids.append(xos_instance.instance_id+"-"+instance_uuid+"-tap"+p.port_id[:11])
1457
1458 date_options = request.query_params.get('period', 1)
1459 date_from = request.query_params.get('date_from', '')
1460 date_to = request.query_params.get('date_to', '')
1461
1462 try:
1463 date_from, date_to = calc_date_args(date_from,
1464 date_to,
1465 date_options)
1466 except Exception as e:
1467 raise e
1468
1469 additional_query = []
1470 if date_from:
1471 additional_query.append({'field': 'timestamp',
1472 'op': 'ge',
1473 'value': date_from})
1474 if date_to:
1475 additional_query.append({'field': 'timestamp',
1476 'op': 'le',
1477 'value': date_to})
1478
1479 report_rows = []
1480 for resource_id in resource_ids:
1481 query = []
1482 if tenant_id:
1483 query.extend(make_query(tenant_id=tenant_id))
1484 if resource_id:
1485 query.extend(make_query(resource_id=resource_id))
1486
1487 #Statistics query for all meter
1488 resource_map = get_resource_map(request, ceilometer_url=tenant_ceilometer_url, query=query)
1489 meters = Meters(request, ceilometer_url=tenant_ceilometer_url, query=query, tenant_map=tenant_map, resource_map=resource_map)
1490 exclude_nova_meters_info = [ "instance", "instance:<type>", "disk.read.requests", "disk.write.requests",
1491 "disk.read.bytes", "disk.write.bytes", "disk.read.requests.rate", "disk.write.requests.rate", "disk.read.bytes.rate",
1492 "disk.write.bytes.rate", "disk.root.size", "disk.ephemeral.size"]
1493 exclude_neutron_meters_info = [ 'network.create', 'network.update', 'subnet.create',
1494 'subnet.update', 'port.create', 'port.update', 'router.create', 'router.update',
1495 'ip.floating.create', 'ip.floating.update']
1496 services = {
1497 _('Nova'): meters.list_nova(except_meters=exclude_nova_meters_info),
1498 _('Neutron'): meters.list_neutron(except_meters=exclude_neutron_meters_info),
1499 _('VSG'): meters.list_vcpe(),
1500 _('VOLT'): meters.list_volt(),
1501 _('SDN'): meters.list_sdn(),
Srikanth Vavilapalli01bf1f42016-09-08 20:38:40 +00001502 _('BROADVIEW'): meters.list_broadview(),
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +00001503 }
1504 for service,meters in services.items():
1505 for meter in meters:
1506 query = make_query(tenant_id=meter["project_id"],resource_id=meter["resource_id"])
1507 if additional_query:
1508 query = query + additional_query
1509 try:
1510 statistics = statistic_list(request, meter["name"],
1511 ceilometer_url=tenant_ceilometer_url, query=query, period=3600*24)
1512 except Exception as e:
1513 logger.error('Exception during statistics query for meter %(meter)s and reason:%(reason)s' % {'meter':meter["name"], 'reason':str(e)})
1514 statistics = None
1515
1516 if not statistics:
1517 continue
1518 statistic = statistics[-1]
1519 row = {"name": 'none',
1520 "slice": meter["slice"],
1521 "project_id": meter["project_id"],
1522 "service": meter["service"],
1523 "resource_id": meter["resource_id"],
1524 "resource_name": meter["resource_name"],
1525 "meter": meter["name"],
1526 "description": meter["description"],
1527 "category": service,
1528 "time": statistic["period_end"],
1529 "value": statistic["avg"],
1530 "unit": meter["unit"]}
1531 report_rows.append(row)
1532
1533 return Response(report_rows)
1534
1535class ServiceAdjustScale(APIView):
1536 method_kind = "list"
1537 method_name = "serviceadjustscale"
1538
1539 def get(self, request, format=None):
1540 if (not request.user.is_authenticated()) or (not request.user.is_admin):
1541 raise PermissionDenied("You must be authenticated admin user in order to use this API")
1542 service = request.query_params.get('service', None)
1543 slice_hint = request.query_params.get('slice_hint', None)
1544 scale = request.query_params.get('scale', None)
1545 if not service or not slice_hint or not scale:
1546 raise XOSMissingField("Mandatory fields missing")
1547 services = Service.select_by_user(request.user)
1548 logger.info('SRIKANTH: Services for this user %(services)s' % {'services':services})
1549 if not services or (not services.get(name=service)):
1550 raise XOSMissingField("Service not found")
1551 service = services.get(name=service)
1552 service.adjust_scale(slice_hint, int(scale))
1553 return Response("Success")