blob: 2a3b1ba2c6787be5a732866dc7188e8550da2812 [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:
37 response = urllib2.urlopen(monitoring_channel.ceilometer_url)
38 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
49 logger.info("Ceilometer proxy URL for user %(user)s is %(url)s" % {'user':user.username,'url':monitoring_channel.ceilometer_url})
50 return monitoring_channel.ceilometer_url
51
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
70 tenantmap["default_admin_tenant"] = {"slice": "default_admin_tenant", "service": "Other"}
71 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 = {}
256
257 def list_all(self, only_meters=None, except_meters=None):
258 """Returns a list of meters based on the meters names.
259
260 :Parameters:
261 - `only_meters`: The list of meter names we want to show.
262 - `except_meters`: The list of meter names we don't want to show.
263 """
264
265 return self._list(only_meters=only_meters,
266 except_meters=except_meters)
267
268 def list_nova(self, except_meters=None):
269 """Returns a list of meters tied to nova.
270
271 :Parameters:
272 - `except_meters`: The list of meter names we don't want to show.
273 """
274
275 return self._list(only_meters=self._nova_meters_info.keys(),
276 except_meters=except_meters)
277
278 def list_neutron(self, except_meters=None):
279 """Returns a list of meters tied to neutron.
280
281 :Parameters:
282 - `except_meters`: The list of meter names we don't want to show.
283 """
284
285 return self._list(only_meters=self._neutron_meters_info.keys(),
286 except_meters=except_meters)
287
288 def list_glance(self, except_meters=None):
289 """Returns a list of meters tied to glance.
290
291 :Parameters:
292 - `except_meters`: The list of meter names we don't want to show.
293 """
294
295 return self._list(only_meters=self._glance_meters_info.keys(),
296 except_meters=except_meters)
297
298 def list_cinder(self, except_meters=None):
299 """Returns a list of meters tied to cinder.
300
301 :Parameters:
302 - `except_meters`: The list of meter names we don't want to show.
303 """
304
305 return self._list(only_meters=self._cinder_meters_info.keys(),
306 except_meters=except_meters)
307
308 def list_swift(self, except_meters=None):
309 """Returns a list of meters tied to swift.
310
311 :Parameters:
312 - `except_meters`: The list of meter names we don't want to show.
313 """
314
315 return self._list(only_meters=self._swift_meters_info.keys(),
316 except_meters=except_meters)
317
318 def list_kwapi(self, except_meters=None):
319 """Returns a list of meters tied to kwapi.
320
321 :Parameters:
322 - `except_meters`: The list of meter names we don't want to show.
323 """
324
325 return self._list(only_meters=self._kwapi_meters_info.keys(),
326 except_meters=except_meters)
327
328 def list_ipmi(self, except_meters=None):
329 """Returns a list of meters tied to ipmi
330
331 :Parameters:
332 - `except_meters`: The list of meter names we don't want to show
333 """
334
335 return self._list(only_meters=self._ipmi_meters_info.keys(),
336 except_meters=except_meters)
337
338 def list_vcpe(self, except_meters=None):
339 """Returns a list of meters tied to vcpe service
340
341 :Parameters:
342 - `except_meters`: The list of meter names we don't want to show
343 """
344
345 return self._list(only_meters=self._vcpe_meters_info.keys(),
346 except_meters=except_meters)
347
348 def list_volt(self, except_meters=None):
349 """Returns a list of meters tied to volt service
350
351 :Parameters:
352 - `except_meters`: The list of meter names we don't want to show
353 """
354
355 return self._list(only_meters=self._volt_meters_info.keys(),
356 except_meters=except_meters)
357
358 def list_sdn(self, except_meters=None):
359 """Returns a list of meters tied to sdn service
360
361 :Parameters:
362 - `except_meters`: The list of meter names we don't want to show
363 """
364
365 return self._list(only_meters=self._sdn_meters_info.keys(),
366 except_meters=except_meters)
367
Srikanth Vavilapalli01bf1f42016-09-08 20:38:40 +0000368 def list_broadview(self, except_meters=None):
369 """Returns a list of meters tied to broadview service
370
371 :Parameters:
372 - `except_meters`: The list of meter names we don't want to show
373 """
374
375 return self._list(only_meters=self._broadview_meters_info.keys(),
376 except_meters=except_meters)
377
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +0000378 def list_other_services(self, except_meters=None):
379 """Returns a list of meters tied to ipmi
380
381 :Parameters:
382 - `except_meters`: The list of meter names we don't want to show
383 """
384 other_service_meters = [m for m in self._ceilometer_meter_list
385 if m.name not in self._all_meters_info.keys()]
386 other_service_meters = diff_lists(other_service_meters, except_meters)
387
388 meters = []
389 for meter in other_service_meters:
390 self._cached_meters[meter.name] = meter
391 meters.append(meter)
392 return meters
393
394 def _list(self, only_meters=None, except_meters=None):
395 """Returns a list of meters based on the meters names.
396
397 :Parameters:
398 - `only_meters`: The list of meter names we want to show.
399 - `except_meters`: The list of meter names we don't want to show.
400 """
401
402 # Get all wanted meter names.
403 if only_meters:
404 meter_names = only_meters
405 else:
406 meter_names = [meter_name for meter_name
407 in self._all_meters_info.keys()]
408
409 meter_names = diff_lists(meter_names, except_meters)
410 # Collect meters for wanted meter names.
411 return self._get_meters(meter_names)
412
413 def _get_meters(self, meter_names):
414 """Obtain meters based on meter_names.
415
416 The meters that do not exist in Ceilometer meter list are left out.
417
418 :Parameters:
419 - `meter_names`: A list of meter names we want to fetch.
420 """
421
422 meters = []
423 for meter_name in meter_names:
424 meter_candidates = self._get_meter(meter_name)
425 if meter_candidates:
426 meters.extend(meter_candidates)
427 return meters
428
429 def _get_meter(self, meter_name):
430 """Obtains a meter.
431
432 Obtains meter either from cache or from Ceilometer meter list
433 joined with statically defined meter info like label and description.
434
435 :Parameters:
436 - `meter_name`: A meter name we want to fetch.
437 """
438 meter_candidates = self._cached_meters.get(meter_name, None)
439 if not meter_candidates:
440 meter_candidates = [m for m in self._ceilometer_meter_list
441 if m["name"] == meter_name]
442
443 if meter_candidates:
444 meter_info = self._all_meters_info.get(meter_name, None)
445 if meter_info:
446 label = meter_info["label"]
447 description = meter_info["description"]
448 meter_category = meter_info["type"]
449 else:
450 label = ""
451 description = ""
452 meter_category = "Other"
453 for meter in meter_candidates:
454 meter["label"] = label
455 meter["description"] = description
456 meter["category"] = meter_category
457 if meter["project_id"] in self.tenant_map.keys():
458 meter["slice"] = self.tenant_map[meter["project_id"]]["slice"]
459 meter["service"] = self.tenant_map[meter["project_id"]]["service"]
460 else:
461 meter["slice"] = meter["project_id"]
462 meter["service"] = "Other"
463 if meter["resource_id"] in self.resource_map.keys():
464 meter["resource_name"] = self.resource_map[meter["resource_id"]]
465
466 self._cached_meters[meter_name] = meter_candidates
467
468 return meter_candidates
469
470 def _get_nova_meters_info(self):
471 """Returns additional info for each meter.
472
473 That will be used for augmenting the Ceilometer meter.
474 """
475
476 # TODO(lsmola) Unless the Ceilometer will provide the information
477 # below, I need to define it as a static here. I will be joining this
478 # to info that I am able to obtain from Ceilometer meters, hopefully
479 # some day it will be supported all.
480 meters_info = datastructures.SortedDict([
481 ("instance", {
482 'type': _("Nova"),
483 'label': '',
484 'description': _("Existence of instance"),
485 }),
486 ("instance:<type>", {
487 'type': _("Nova"),
488 'label': '',
489 'description': _("Existence of instance <type> "
490 "(openstack types)"),
491 }),
492 ("memory", {
493 'type': _("Nova"),
494 'label': '',
495 'description': _("Volume of RAM"),
496 }),
497 ("memory.usage", {
498 'type': _("Nova"),
499 'label': '',
500 'description': _("Volume of RAM used"),
501 }),
502 ("cpu", {
503 'type': _("Nova"),
504 'label': '',
505 'description': _("CPU time used"),
506 }),
507 ("cpu_util", {
508 'type': _("Nova"),
509 'label': '',
510 'description': _("Average CPU utilization"),
511 }),
512 ("vcpus", {
513 'type': _("Nova"),
514 'label': '',
515 'description': _("Number of VCPUs"),
516 }),
517 ("disk.read.requests", {
518 'type': _("Nova"),
519 'label': '',
520 'description': _("Number of read requests"),
521 }),
522 ("disk.write.requests", {
523 'type': _("Nova"),
524 'label': '',
525 'description': _("Number of write requests"),
526 }),
527 ("disk.read.bytes", {
528 'type': _("Nova"),
529 'label': '',
530 'description': _("Volume of reads"),
531 }),
532 ("disk.write.bytes", {
533 'type': _("Nova"),
534 'label': '',
535 'description': _("Volume of writes"),
536 }),
537 ("disk.read.requests.rate", {
538 'type': _("Nova"),
539 'label': '',
540 'description': _("Average rate of read requests"),
541 }),
542 ("disk.write.requests.rate", {
543 'type': _("Nova"),
544 'label': '',
545 'description': _("Average rate of write requests"),
546 }),
547 ("disk.read.bytes.rate", {
548 'type': _("Nova"),
549 'label': '',
550 'description': _("Average rate of reads"),
551 }),
552 ("disk.write.bytes.rate", {
553 'type': _("Nova"),
554 'label': '',
555 'description': _("Average volume of writes"),
556 }),
557 ("disk.root.size", {
558 'type': _("Nova"),
559 'label': '',
560 'description': _("Size of root disk"),
561 }),
562 ("disk.ephemeral.size", {
563 'type': _("Nova"),
564 'label': '',
565 'description': _("Size of ephemeral disk"),
566 }),
567 ("network.incoming.bytes", {
568 'type': _("Nova"),
569 'label': '',
570 'description': _("Number of incoming bytes "
571 "on the network for a VM interface"),
572 }),
573 ("network.outgoing.bytes", {
574 'type': _("Nova"),
575 'label': '',
576 'description': _("Number of outgoing bytes "
577 "on the network for a VM interface"),
578 }),
579 ("network.incoming.packets", {
580 'type': _("Nova"),
581 'label': '',
582 'description': _("Number of incoming "
583 "packets for a VM interface"),
584 }),
585 ("network.outgoing.packets", {
586 'type': _("Nova"),
587 'label': '',
588 'description': _("Number of outgoing "
589 "packets for a VM interface"),
590 }),
591 ("network.incoming.bytes.rate", {
592 'type': _("Nova"),
593 'label': '',
594 'description': _("Average rate per sec of incoming "
595 "bytes on a VM network interface"),
596 }),
597 ("network.outgoing.bytes.rate", {
598 'type': _("Nova"),
599 'label': '',
600 'description': _("Average rate per sec of outgoing "
601 "bytes on a VM network interface"),
602 }),
603 ("network.incoming.packets.rate", {
604 'type': _("Nova"),
605 'label': '',
606 'description': _("Average rate per sec of incoming "
607 "packets on a VM network interface"),
608 }),
609 ("network.outgoing.packets.rate", {
610 'type': _("Nova"),
611 'label': '',
612 'description': _("Average rate per sec of outgoing "
613 "packets on a VM network interface"),
614 }),
615 ])
616 # Adding flavor based meters into meters_info dict
617 # TODO(lsmola) this kind of meter will be probably deprecated
618 # https://bugs.launchpad.net/ceilometer/+bug/1208365 . Delete it then.
619 #for flavor in get_flavor_names(self._request):
620 # name = 'instance:%s' % flavor
621 # meters_info[name] = dict(meters_info["instance:<type>"])
622
623 # meters_info[name]['description'] = (
624 # _('Duration of instance type %s (openstack flavor)') %
625 # flavor)
626
627 # TODO(lsmola) allow to set specific in local_settings. For all meters
628 # because users can have their own agents and meters.
629 return meters_info
630
631 def _get_neutron_meters_info(self):
632 """Returns additional info for each meter.
633
634 That will be used for augmenting the Ceilometer meter.
635 """
636
637 # TODO(lsmola) Unless the Ceilometer will provide the information
638 # below, I need to define it as a static here. I will be joining this
639 # to info that I am able to obtain from Ceilometer meters, hopefully
640 # some day it will be supported all.
641 return datastructures.SortedDict([
642 ('network', {
643 'type': _("Neutron"),
644 'label': '',
645 'description': _("Existence of network"),
646 }),
647 ('network.create', {
648 'type': _("Neutron"),
649 'label': '',
650 'description': _("Creation requests for this network"),
651 }),
652 ('network.update', {
653 'type': _("Neutron"),
654 'label': '',
655 'description': _("Update requests for this network"),
656 }),
657 ('subnet', {
658 'type': _("Neutron"),
659 'label': '',
660 'description': _("Existence of subnet"),
661 }),
662 ('subnet.create', {
663 'type': _("Neutron"),
664 'label': '',
665 'description': _("Creation requests for this subnet"),
666 }),
667 ('subnet.update', {
668 'type': _("Neutron"),
669 'label': '',
670 'description': _("Update requests for this subnet"),
671 }),
672 ('port', {
673 'type': _("Neutron"),
674 'label': '',
675 'description': _("Existence of port"),
676 }),
677 ('port.create', {
678 'type': _("Neutron"),
679 'label': '',
680 'description': _("Creation requests for this port"),
681 }),
682 ('port.update', {
683 'type': _("Neutron"),
684 'label': '',
685 'description': _("Update requests for this port"),
686 }),
687 ('router', {
688 'type': _("Neutron"),
689 'label': '',
690 'description': _("Existence of router"),
691 }),
692 ('router.create', {
693 'type': _("Neutron"),
694 'label': '',
695 'description': _("Creation requests for this router"),
696 }),
697 ('router.update', {
698 'type': _("Neutron"),
699 'label': '',
700 'description': _("Update requests for this router"),
701 }),
702 ('ip.floating', {
703 'type': _("Neutron"),
704 'label': '',
705 'description': _("Existence of floating ip"),
706 }),
707 ('ip.floating.create', {
708 'type': _("Neutron"),
709 'label': '',
710 'description': _("Creation requests for this floating ip"),
711 }),
712 ('ip.floating.update', {
713 'type': _("Neutron"),
714 'label': '',
715 'description': _("Update requests for this floating ip"),
716 }),
717 ])
718
719 def _get_glance_meters_info(self):
720 """Returns additional info for each meter.
721
722 That will be used for augmenting the Ceilometer meter.
723 """
724
725 # TODO(lsmola) Unless the Ceilometer will provide the information
726 # below, I need to define it as a static here. I will be joining this
727 # to info that I am able to obtain from Ceilometer meters, hopefully
728 # some day it will be supported all.
729 return datastructures.SortedDict([
730 ('image', {
731 'type': _("Glance"),
732 'label': '',
733 'description': _("Image existence check"),
734 }),
735 ('image.size', {
736 'type': _("Glance"),
737 'label': '',
738 'description': _("Uploaded image size"),
739 }),
740 ('image.update', {
741 'type': _("Glance"),
742 'label': '',
743 'description': _("Number of image updates"),
744 }),
745 ('image.upload', {
746 'type': _("Glance"),
747 'label': '',
748 'description': _("Number of image uploads"),
749 }),
750 ('image.delete', {
751 'type': _("Glance"),
752 'label': '',
753 'description': _("Number of image deletions"),
754 }),
755 ('image.download', {
756 'type': _("Glance"),
757 'label': '',
758 'description': _("Image is downloaded"),
759 }),
760 ('image.serve', {
761 'type': _("Glance"),
762 'label': '',
763 'description': _("Image is served out"),
764 }),
765 ])
766
767 def _get_cinder_meters_info(self):
768 """Returns additional info for each meter.
769
770 That will be used for augmenting the Ceilometer meter.
771 """
772
773 # TODO(lsmola) Unless the Ceilometer will provide the information
774 # below, I need to define it as a static here. I will be joining this
775 # to info that I am able to obtain from Ceilometer meters, hopefully
776 # some day it will be supported all.
777 return datastructures.SortedDict([
778 ('volume', {
779 'type': _("Cinder"),
780 'label': '',
781 'description': _("Existence of volume"),
782 }),
783 ('volume.size', {
784 'type': _("Cinder"),
785 'label': '',
786 'description': _("Size of volume"),
787 }),
788 ])
789
790 def _get_swift_meters_info(self):
791 """Returns additional info for each meter.
792
793 That will be used for augmenting the Ceilometer meter.
794 """
795
796 # TODO(lsmola) Unless the Ceilometer will provide the information
797 # below, I need to define it as a static here. I will be joining this
798 # to info that I am able to obtain from Ceilometer meters, hopefully
799 # some day it will be supported all.
800 return datastructures.SortedDict([
801 ('storage.objects', {
802 'type': _("Swift"),
803 'label': '',
804 'description': _("Number of objects"),
805 }),
806 ('storage.objects.size', {
807 'type': _("Swift"),
808 'label': '',
809 'description': _("Total size of stored objects"),
810 }),
811 ('storage.objects.containers', {
812 'type': _("Swift"),
813 'label': '',
814 'description': _("Number of containers"),
815 }),
816 ('storage.objects.incoming.bytes', {
817 'type': _("Swift"),
818 'label': '',
819 'description': _("Number of incoming bytes"),
820 }),
821 ('storage.objects.outgoing.bytes', {
822 'type': _("Swift"),
823 'label': '',
824 'description': _("Number of outgoing bytes"),
825 }),
826 ('storage.api.request', {
827 'type': _("Swift"),
828 'label': '',
829 'description': _("Number of API requests against swift"),
830 }),
831 ])
832
833 def _get_kwapi_meters_info(self):
834 """Returns additional info for each meter.
835
836 That will be used for augmenting the Ceilometer meter.
837 """
838
839 # TODO(lsmola) Unless the Ceilometer will provide the information
840 # below, I need to define it as a static here. I will be joining this
841 # to info that I am able to obtain from Ceilometer meters, hopefully
842 # some day it will be supported all.
843 return datastructures.SortedDict([
844 ('energy', {
845 'type': _("Kwapi"),
846 'label': '',
847 'description': _("Amount of energy"),
848 }),
849 ('power', {
850 'type': _("Kwapi"),
851 'label': '',
852 'description': _("Power consumption"),
853 }),
854 ])
855
856 def _get_ipmi_meters_info(self):
857 """Returns additional info for each meter
858
859 That will be used for augmenting the Ceilometer meter
860 """
861
862 # TODO(lsmola) Unless the Ceilometer will provide the information
863 # below, I need to define it as a static here. I will be joining this
864 # to info that I am able to obtain from Ceilometer meters, hopefully
865 # some day it will be supported all.
866 return datastructures.SortedDict([
867 ('hardware.ipmi.node.power', {
868 'type': _("IPMI"),
869 'label': '',
870 'description': _("System Current Power"),
871 }),
872 ('hardware.ipmi.fan', {
873 'type': _("IPMI"),
874 'label': '',
875 'description': _("Fan RPM"),
876 }),
877 ('hardware.ipmi.temperature', {
878 'type': _("IPMI"),
879 'label': '',
880 'description': _("Sensor Temperature Reading"),
881 }),
882 ('hardware.ipmi.current', {
883 'type': _("IPMI"),
884 'label': '',
885 'description': _("Sensor Current Reading"),
886 }),
887 ('hardware.ipmi.voltage', {
888 'type': _("IPMI"),
889 'label': '',
890 'description': _("Sensor Voltage Reading"),
891 }),
892 ('hardware.ipmi.node.inlet_temperature', {
893 'type': _("IPMI"),
894 'label': '',
895 'description': _("System Inlet Temperature Reading"),
896 }),
897 ('hardware.ipmi.node.outlet_temperature', {
898 'type': _("IPMI"),
899 'label': '',
900 'description': _("System Outlet Temperature Reading"),
901 }),
902 ('hardware.ipmi.node.airflow', {
903 'type': _("IPMI"),
904 'label': '',
905 'description': _("System Airflow Reading"),
906 }),
907 ('hardware.ipmi.node.cups', {
908 'type': _("IPMI"),
909 'label': '',
910 'description': _("System CUPS Reading"),
911 }),
912 ('hardware.ipmi.node.cpu_util', {
913 'type': _("IPMI"),
914 'label': '',
915 'description': _("System CPU Utility Reading"),
916 }),
917 ('hardware.ipmi.node.mem_util', {
918 'type': _("IPMI"),
919 'label': '',
920 'description': _("System Memory Utility Reading"),
921 }),
922 ('hardware.ipmi.node.io_util', {
923 'type': _("IPMI"),
924 'label': '',
925 'description': _("System IO Utility Reading"),
926 }),
927 ])
928
929 def _get_vcpe_meters_info(self):
930 """Returns additional info for each meter
931
932 That will be used for augmenting the Ceilometer meter
933 """
934
935 # TODO(lsmola) Unless the Ceilometer will provide the information
936 # below, I need to define it as a static here. I will be joining this
937 # to info that I am able to obtain from Ceilometer meters, hopefully
938 # some day it will be supported all.
939 return datastructures.SortedDict([
940 ('vsg', {
941 'type': _("VSG"),
942 'label': '',
943 'description': _("Existence of vsg instance"),
944 }),
945 ('vsg.dns.cache.size', {
946 'type': _("VSG"),
947 'label': '',
948 'description': _("Number of entries in DNS cache"),
949 }),
950 ('vsg.dns.total_instered_entries', {
951 'type': _("VSG"),
952 'label': '',
953 'description': _("Total number of inserted entries into the cache"),
954 }),
955 ('vsg.dns.replaced_unexpired_entries', {
956 'type': _("VSG"),
957 'label': '',
958 'description': _("Unexpired entries that were thrown out of cache"),
959 }),
960 ('vsg.dns.queries_answered_locally', {
961 'type': _("VSG"),
962 'label': '',
963 'description': _("Number of cache hits"),
964 }),
965 ('vsg.dns.queries_forwarded', {
966 'type': _("VSG"),
967 'label': '',
968 'description': _("Number of cache misses"),
969 }),
970 ('vsg.dns.server.queries_sent', {
971 'type': _("VSG"),
972 'label': '',
973 'description': _("For each upstream server, the number of queries sent"),
974 }),
975 ('vsg.dns.server.queries_failed', {
976 'type': _("VSG"),
977 'label': '',
978 'description': _("For each upstream server, the number of queries failed"),
979 }),
980 ])
981
982 def _get_volt_meters_info(self):
983 """Returns additional info for each meter
984
985 That will be used for augmenting the Ceilometer meter
986 """
987
988 # TODO(lsmola) Unless the Ceilometer will provide the information
989 # below, I need to define it as a static here. I will be joining this
990 # to info that I am able to obtain from Ceilometer meters, hopefully
991 # some day it will be supported all.
992 return datastructures.SortedDict([
993 ('volt.device', {
994 'type': _("VOLT"),
995 'label': '',
996 'description': _("Existence of olt device"),
997 }),
998 ('volt.device.disconnect', {
999 'type': _("VOLT"),
1000 'label': '',
1001 'description': _("Olt device disconnected"),
1002 }),
1003 ('volt.device.subscriber', {
1004 'type': _("VOLT"),
1005 'label': '',
1006 'description': _("Existence of olt subscriber"),
1007 }),
1008 ('volt.device.subscriber.unregister', {
1009 'type': _("VOLT"),
1010 'label': '',
1011 'description': _("Olt subscriber unregistered"),
1012 }),
1013 ])
1014
1015 def _get_sdn_meters_info(self):
1016 """Returns additional info for each meter
1017
1018 That will be used for augmenting the Ceilometer meter
1019 """
1020
1021 # TODO(lsmola) Unless the Ceilometer will provide the information
1022 # below, I need to define it as a static here. I will be joining this
1023 # to info that I am able to obtain from Ceilometer meters, hopefully
1024 # some day it will be supported all.
1025 return datastructures.SortedDict([
1026 ('switch', {
1027 'type': _("SDN"),
1028 'label': '',
1029 'description': _("Existence of switch"),
1030 }),
1031 ('switch.port', {
1032 'type': _("SDN"),
1033 'label': '',
1034 'description': _("Existence of port"),
1035 }),
1036 ('switch.port.receive.packets', {
1037 'type': _("SDN"),
1038 'label': '',
1039 'description': _("Packets received on port"),
1040 }),
1041 ('switch.port.transmit.packets', {
1042 'type': _("SDN"),
1043 'label': '',
1044 'description': _("Packets transmitted on port"),
1045 }),
1046 ('switch.port.receive.drops', {
1047 'type': _("SDN"),
1048 'label': '',
1049 'description': _("Drops received on port"),
1050 }),
1051 ('switch.port.transmit.drops', {
1052 'type': _("SDN"),
1053 'label': '',
1054 'description': _("Drops transmitted on port"),
1055 }),
1056 ('switch.port.receive.errors', {
1057 'type': _("SDN"),
1058 'label': '',
1059 'description': _("Errors received on port"),
1060 }),
1061 ('switch.port.transmit.errors', {
1062 'type': _("SDN"),
1063 'label': '',
1064 'description': _("Errors transmitted on port"),
1065 }),
1066 ('switch.flow', {
1067 'type': _("SDN"),
1068 'label': '',
1069 'description': _("Duration of flow"),
1070 }),
1071 ('switch.flow.packets', {
1072 'type': _("SDN"),
1073 'label': '',
1074 'description': _("Packets received"),
1075 }),
1076 ('switch.table', {
1077 'type': _("SDN"),
1078 'label': '',
1079 'description': _("Existence of table"),
1080 }),
1081 ('switch.table.active.entries', {
1082 'type': _("SDN"),
1083 'label': '',
1084 'description': _("Active entries in table"),
1085 }),
1086 ])
1087
Srikanth Vavilapalli01bf1f42016-09-08 20:38:40 +00001088 def _get_broadview_meters_info(self):
1089 """Returns additional info for each meter
1090
1091 That will be used for augmenting the Ceilometer meter
1092 """
1093
1094 # TODO(lsmola) Unless the Ceilometer will provide the information
1095 # below, I need to define it as a static here. I will be joining this
1096 # to info that I am able to obtain from Ceilometer meters, hopefully
1097 # some day it will be supported all.
1098 return datastructures.SortedDict([
1099 ('broadview.bst.device', {
1100 'type': _("BROADVIEW"),
1101 'label': '',
1102 'description': _("Existence of BST device"),
1103 }),
1104 ('broadview.bst.ingress-port-priority-group', {
1105 'type': _("BROADVIEW"),
1106 'label': '',
1107 'description': _("Existence of BST device"),
1108 }),
1109 ('broadview.bst.ingress-port-service-pool', {
1110 'type': _("BROADVIEW"),
1111 'label': '',
1112 'description': _("Existence of BST device"),
1113 }),
1114 ('broadview.bst.egress-cpu-queue', {
1115 'type': _("BROADVIEW"),
1116 'label': '',
1117 'description': _("Existence of BST device"),
1118 }),
1119 ('broadview.bst.egress-mc-queue', {
1120 'type': _("BROADVIEW"),
1121 'label': '',
1122 'description': _("Existence of BST device"),
1123 }),
1124 ('broadview.bst.egress-port-service-pool', {
1125 'type': _("BROADVIEW"),
1126 'label': '',
1127 'description': _("Existence of BST device"),
1128 }),
1129 ('broadview.bst.egress-rqe-queue', {
1130 'type': _("BROADVIEW"),
1131 'label': '',
1132 'description': _("Existence of BST device"),
1133 }),
1134 ('broadview.bst.egress-service-pool', {
1135 'type': _("BROADVIEW"),
1136 'label': '',
1137 'description': _("Existence of BST device"),
1138 }),
1139 ('broadview.bst.egress-uc-queue', {
1140 'type': _("BROADVIEW"),
1141 'label': '',
1142 'description': _("Existence of BST device"),
1143 }),
1144 ])
1145
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +00001146def make_query(user_id=None, tenant_id=None, resource_id=None,
1147 user_ids=None, tenant_ids=None, resource_ids=None):
1148 """Returns query built from given parameters.
1149
1150 This query can be then used for querying resources, meters and
1151 statistics.
1152
1153 :Parameters:
1154 - `user_id`: user_id, has a priority over list of ids
1155 - `tenant_id`: tenant_id, has a priority over list of ids
1156 - `resource_id`: resource_id, has a priority over list of ids
1157 - `user_ids`: list of user_ids
1158 - `tenant_ids`: list of tenant_ids
1159 - `resource_ids`: list of resource_ids
1160 """
1161 user_ids = user_ids or []
1162 tenant_ids = tenant_ids or []
1163 resource_ids = resource_ids or []
1164
1165 query = []
1166 if user_id:
1167 user_ids = [user_id]
1168 for u_id in user_ids:
1169 query.append({"field": "user_id", "op": "eq", "value": u_id})
1170
1171 if tenant_id:
1172 tenant_ids = [tenant_id]
1173 for t_id in tenant_ids:
1174 query.append({"field": "project_id", "op": "eq", "value": t_id})
1175
1176 if resource_id:
1177 resource_ids = [resource_id]
1178 for r_id in resource_ids:
1179 query.append({"field": "resource_id", "op": "eq", "value": r_id})
1180
1181 return query
1182
1183def calc_date_args(date_from, date_to, date_options):
1184 # TODO(lsmola) all timestamps should probably work with
1185 # current timezone. And also show the current timezone in chart.
1186 if date_options == "other":
1187 try:
1188 if date_from:
1189 date_from = pytz.utc.localize(
1190 datetime.datetime.strptime(str(date_from), "%Y-%m-%d"))
1191 else:
1192 # TODO(lsmola) there should be probably the date
1193 # of the first sample as default, so it correctly
1194 # counts the time window. Though I need ordering
1195 # and limit of samples to obtain that.
1196 pass
1197 if date_to:
1198 date_to = pytz.utc.localize(
1199 datetime.datetime.strptime(str(date_to), "%Y-%m-%d"))
1200 # It returns the beginning of the day, I want the end of
1201 # the day, so I add one day without a second.
1202 date_to = (date_to + datetime.timedelta(days=1) -
1203 datetime.timedelta(seconds=1))
1204 else:
1205 date_to = timezone.now()
1206 except Exception:
1207 raise ValueError(_("The dates haven't been recognized"))
1208 else:
1209 try:
1210 date_to = timezone.now()
1211 date_from = date_to - datetime.timedelta(days=float(date_options))
1212 except Exception as e:
1213 raise e
1214 #raise ValueError(_("The time delta must be a number representing "
1215 # "the time span in days"))
1216 return date_from, date_to
1217
1218class MetersList(APIView):
1219 method_kind = "list"
1220 method_name = "meters"
1221
1222 def get(self, request, format=None):
1223 if (not request.user.is_authenticated()):
1224 raise PermissionDenied("You must be authenticated in order to use this API")
1225 tenant_ceilometer_url = getTenantCeilometerProxyURL(request.user)
1226 if (not tenant_ceilometer_url):
1227 raise XOSMissingField("Tenant ceilometer URL is missing")
1228
1229 tenant_id = request.query_params.get('tenant', None)
1230 resource_id = request.query_params.get('resource', None)
1231
1232 query = []
1233 if tenant_id:
1234 query.extend(make_query(tenant_id=tenant_id))
1235 if resource_id:
1236 query.extend(make_query(resource_id=resource_id))
1237
1238 tenant_map = getTenantControllerTenantMap(request.user)
1239 resource_map = get_resource_map(request, ceilometer_url=tenant_ceilometer_url, query=query)
1240 meters = Meters(request, ceilometer_url=tenant_ceilometer_url, query=query, tenant_map=tenant_map, resource_map=resource_map)
1241 services = {
1242 _('Nova'): meters.list_nova(),
1243 _('Neutron'): meters.list_neutron(),
1244 _('VSG'): meters.list_vcpe(),
1245 _('VOLT'): meters.list_volt(),
1246 _('SDN'): meters.list_sdn(),
Srikanth Vavilapalli01bf1f42016-09-08 20:38:40 +00001247 _('BROADVIEW'): meters.list_broadview(),
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +00001248 }
1249 meters = []
1250 for service,smeters in services.iteritems():
1251 meters.extend(smeters)
1252 return Response(meters)
1253
1254class MeterStatisticsList(APIView):
1255 method_kind = "list"
1256 method_name = "meterstatistics"
1257
1258 def get(self, request, format=None):
1259 if (not request.user.is_authenticated()):
1260 raise PermissionDenied("You must be authenticated in order to use this API")
1261 tenant_ceilometer_url = getTenantCeilometerProxyURL(request.user)
1262 if (not tenant_ceilometer_url):
1263 raise XOSMissingField("Tenant ceilometer URL is missing")
1264 tenant_map = getTenantControllerTenantMap(request.user)
1265
1266 date_options = request.query_params.get('period', 1)
1267 date_from = request.query_params.get('date_from', '')
1268 date_to = request.query_params.get('date_to', '')
1269
1270 try:
1271 date_from, date_to = calc_date_args(date_from,
1272 date_to,
1273 date_options)
1274 except Exception as e:
1275 raise e
1276
1277 additional_query = []
1278 if date_from:
1279 additional_query.append({'field': 'timestamp',
1280 'op': 'ge',
1281 'value': date_from})
1282 if date_to:
1283 additional_query.append({'field': 'timestamp',
1284 'op': 'le',
1285 'value': date_to})
1286
1287 meter_name = request.query_params.get('meter', None)
1288 tenant_id = request.query_params.get('tenant', None)
1289 resource_id = request.query_params.get('resource', None)
1290
1291 query = []
1292 if tenant_id:
1293 query.extend(make_query(tenant_id=tenant_id))
1294 if resource_id:
1295 query.extend(make_query(resource_id=resource_id))
1296
1297 if meter_name:
1298 #Statistics query for one meter
1299 if additional_query:
1300 query = query + additional_query
1301 statistics = statistic_list(request, meter_name,
1302 ceilometer_url=tenant_ceilometer_url, query=query, period=3600*24)
1303 statistic = statistics[-1]
1304 row = {"name": 'none',
1305 "meter": meter_name,
1306 "time": statistic["period_end"],
1307 "value": statistic["avg"]}
1308 return Response(row)
1309
1310 #Statistics query for all meter
1311 resource_map = get_resource_map(request, ceilometer_url=tenant_ceilometer_url, query=query)
1312 meters = Meters(request, ceilometer_url=tenant_ceilometer_url, query=query, tenant_map=tenant_map, resource_map=resource_map)
1313 services = {
1314 _('Nova'): meters.list_nova(),
1315 _('Neutron'): meters.list_neutron(),
1316 _('VSG'): meters.list_vcpe(),
1317 _('VOLT'): meters.list_volt(),
1318 _('SDN'): meters.list_sdn(),
Srikanth Vavilapalli01bf1f42016-09-08 20:38:40 +00001319 _('BROADVIEW'): meters.list_broadview(),
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +00001320 }
1321 report_rows = []
1322 for service,meters in services.items():
1323 for meter in meters:
1324 query = make_query(tenant_id=meter["project_id"],resource_id=meter["resource_id"])
1325 if additional_query:
1326 query = query + additional_query
1327 try:
1328 statistics = statistic_list(request, meter["name"],
1329 ceilometer_url=tenant_ceilometer_url, query=query, period=3600*24)
1330 except Exception as e:
1331 logger.error('Exception during statistics query for meter %(meter)s and reason:%(reason)s' % {'meter':meter["name"], 'reason':str(e)})
1332 statistics = None
1333
1334 if not statistics:
1335 continue
1336 statistic = statistics[-1]
1337 row = {"name": 'none',
1338 "slice": meter["slice"],
1339 "project_id": meter["project_id"],
1340 "service": meter["service"],
1341 "resource_id": meter["resource_id"],
1342 "resource_name": meter["resource_name"],
1343 "meter": meter["name"],
1344 "description": meter["description"],
1345 "category": service,
1346 "time": statistic["period_end"],
1347 "value": statistic["avg"],
1348 "unit": meter["unit"]}
1349 report_rows.append(row)
1350
1351 return Response(report_rows)
1352
1353
1354class MeterSamplesList(APIView):
1355 method_kind = "list"
1356 method_name = "metersamples"
1357
1358 def get(self, request, format=None):
1359 if (not request.user.is_authenticated()):
1360 raise PermissionDenied("You must be authenticated in order to use this API")
1361 tenant_ceilometer_url = getTenantCeilometerProxyURL(request.user)
1362 if (not tenant_ceilometer_url):
1363 raise XOSMissingField("Tenant ceilometer URL is missing")
1364 meter_name = request.query_params.get('meter', None)
1365 if not meter_name:
1366 raise XOSMissingField("Meter name in query params is missing")
1367 limit = request.query_params.get('limit', 10)
1368 tenant_id = request.query_params.get('tenant', None)
1369 resource_id = request.query_params.get('resource', None)
1370 query = []
1371 if tenant_id:
1372 query.extend(make_query(tenant_id=tenant_id))
1373 if resource_id:
1374 query.extend(make_query(resource_id=resource_id))
1375 query.append({"field": "meter", "op": "eq", "value": meter_name})
1376 samples = sample_list(request, meter_name,
1377 ceilometer_url=tenant_ceilometer_url, query=query, limit=limit)
1378 if samples:
1379 tenant_map = getTenantControllerTenantMap(request.user)
1380 resource_map = get_resource_map(request, ceilometer_url=tenant_ceilometer_url)
1381 for sample in samples:
1382 if sample["project_id"] in tenant_map.keys():
1383 sample["slice"] = tenant_map[sample["project_id"]]["slice"]
1384 else:
1385 sample["slice"] = sample["project_id"]
1386 if sample["resource_id"] in resource_map.keys():
1387 sample["resource_name"] = resource_map[sample["resource_id"]]
1388 else:
1389 sample["resource_name"] = sample["resource_id"]
1390 return Response(samples)
1391
1392class XOSSliceServiceList(APIView):
1393 method_kind = "list"
1394 method_name = "xos-slice-service-mapping"
1395
1396 def get(self, request, format=None):
1397 if (not request.user.is_authenticated()):
1398 raise PermissionDenied("You must be authenticated in order to use this API")
1399 tenant_map = getTenantControllerTenantMap(request.user)
1400 service_map={}
1401 for k,v in tenant_map.iteritems():
1402 if not (v['service'] in service_map.keys()):
1403 service_map[v['service']] = {}
1404 service_map[v['service']]['service'] = v['service']
1405 service_map[v['service']]['slices'] = []
1406 slice_details = {'slice':v['slice'], 'project_id':k}
1407 service_map[v['service']]['slices'].append(slice_details)
1408 return Response(service_map.values())
1409
1410class XOSInstanceStatisticsList(APIView):
1411 method_kind = "list"
1412 method_name = "xos-instance-statistics"
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 instance_uuid = request.query_params.get('instance-uuid', None)
1421 if not instance_uuid:
1422 raise XOSMissingField("Instance UUID in query params is missing")
1423 if not Instance.objects.filter(instance_uuid=instance_uuid):
1424 raise XOSMissingField("XOS Instance object is missing for this uuid")
1425 xos_instance = Instance.objects.filter(instance_uuid=instance_uuid)[0]
1426 tenant_map = getTenantControllerTenantMap(request.user, xos_instance.slice)
1427 tenant_id = tenant_map.keys()[0]
1428 resource_ids = []
1429 resource_ids.append(instance_uuid)
1430 for p in xos_instance.ports.all():
1431 #neutron port resource id is represented in ceilometer as "nova instance-name"+"-"+"nova instance-id"+"-"+"tap"+first 11 characters of port-id
1432 resource_ids.append(xos_instance.instance_id+"-"+instance_uuid+"-tap"+p.port_id[:11])
1433
1434 date_options = request.query_params.get('period', 1)
1435 date_from = request.query_params.get('date_from', '')
1436 date_to = request.query_params.get('date_to', '')
1437
1438 try:
1439 date_from, date_to = calc_date_args(date_from,
1440 date_to,
1441 date_options)
1442 except Exception as e:
1443 raise e
1444
1445 additional_query = []
1446 if date_from:
1447 additional_query.append({'field': 'timestamp',
1448 'op': 'ge',
1449 'value': date_from})
1450 if date_to:
1451 additional_query.append({'field': 'timestamp',
1452 'op': 'le',
1453 'value': date_to})
1454
1455 report_rows = []
1456 for resource_id in resource_ids:
1457 query = []
1458 if tenant_id:
1459 query.extend(make_query(tenant_id=tenant_id))
1460 if resource_id:
1461 query.extend(make_query(resource_id=resource_id))
1462
1463 #Statistics query for all meter
1464 resource_map = get_resource_map(request, ceilometer_url=tenant_ceilometer_url, query=query)
1465 meters = Meters(request, ceilometer_url=tenant_ceilometer_url, query=query, tenant_map=tenant_map, resource_map=resource_map)
1466 exclude_nova_meters_info = [ "instance", "instance:<type>", "disk.read.requests", "disk.write.requests",
1467 "disk.read.bytes", "disk.write.bytes", "disk.read.requests.rate", "disk.write.requests.rate", "disk.read.bytes.rate",
1468 "disk.write.bytes.rate", "disk.root.size", "disk.ephemeral.size"]
1469 exclude_neutron_meters_info = [ 'network.create', 'network.update', 'subnet.create',
1470 'subnet.update', 'port.create', 'port.update', 'router.create', 'router.update',
1471 'ip.floating.create', 'ip.floating.update']
1472 services = {
1473 _('Nova'): meters.list_nova(except_meters=exclude_nova_meters_info),
1474 _('Neutron'): meters.list_neutron(except_meters=exclude_neutron_meters_info),
1475 _('VSG'): meters.list_vcpe(),
1476 _('VOLT'): meters.list_volt(),
1477 _('SDN'): meters.list_sdn(),
Srikanth Vavilapalli01bf1f42016-09-08 20:38:40 +00001478 _('BROADVIEW'): meters.list_broadview(),
Srikanth Vavilapallia9bbe6f2016-08-09 00:17:22 +00001479 }
1480 for service,meters in services.items():
1481 for meter in meters:
1482 query = make_query(tenant_id=meter["project_id"],resource_id=meter["resource_id"])
1483 if additional_query:
1484 query = query + additional_query
1485 try:
1486 statistics = statistic_list(request, meter["name"],
1487 ceilometer_url=tenant_ceilometer_url, query=query, period=3600*24)
1488 except Exception as e:
1489 logger.error('Exception during statistics query for meter %(meter)s and reason:%(reason)s' % {'meter':meter["name"], 'reason':str(e)})
1490 statistics = None
1491
1492 if not statistics:
1493 continue
1494 statistic = statistics[-1]
1495 row = {"name": 'none',
1496 "slice": meter["slice"],
1497 "project_id": meter["project_id"],
1498 "service": meter["service"],
1499 "resource_id": meter["resource_id"],
1500 "resource_name": meter["resource_name"],
1501 "meter": meter["name"],
1502 "description": meter["description"],
1503 "category": service,
1504 "time": statistic["period_end"],
1505 "value": statistic["avg"],
1506 "unit": meter["unit"]}
1507 report_rows.append(row)
1508
1509 return Response(report_rows)
1510
1511class ServiceAdjustScale(APIView):
1512 method_kind = "list"
1513 method_name = "serviceadjustscale"
1514
1515 def get(self, request, format=None):
1516 if (not request.user.is_authenticated()) or (not request.user.is_admin):
1517 raise PermissionDenied("You must be authenticated admin user in order to use this API")
1518 service = request.query_params.get('service', None)
1519 slice_hint = request.query_params.get('slice_hint', None)
1520 scale = request.query_params.get('scale', None)
1521 if not service or not slice_hint or not scale:
1522 raise XOSMissingField("Mandatory fields missing")
1523 services = Service.select_by_user(request.user)
1524 logger.info('SRIKANTH: Services for this user %(services)s' % {'services':services})
1525 if not services or (not services.get(name=service)):
1526 raise XOSMissingField("Service not found")
1527 service = services.get(name=service)
1528 service.adjust_scale(slice_hint, int(scale))
1529 return Response("Success")