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