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