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