"Fossies" - the Fresh Open Source Software Archive 
Member "aodh-15.0.0/aodh/api/controllers/v2/alarm_rules/gnocchi.py" (5 Oct 2022, 9125 Bytes) of package /linux/misc/openstack/aodh-15.0.0.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
1 #
2 # Copyright 2015 eNovance
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # 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, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
14 # under the License.
15 import json
16 import threading
17
18 import cachetools
19 from gnocchiclient import client
20 from gnocchiclient import exceptions
21 from keystoneauth1 import exceptions as ka_exceptions
22 from oslo_config import cfg
23 from oslo_log import log
24 import pecan
25 import wsme
26 from wsme import types as wtypes
27
28 from aodh.api.controllers.v2 import base
29 from aodh.api.controllers.v2 import utils as v2_utils
30 from aodh import keystone_client
31
32 LOG = log.getLogger(__name__)
33
34 GNOCCHI_OPTS = [
35 cfg.StrOpt('gnocchi_external_project_owner',
36 default="service",
37 help='Project name of resources creator in Gnocchi. '
38 '(For example the Ceilometer project name'),
39 cfg.StrOpt('gnocchi_external_domain_name',
40 default="Default",
41 help='Domain name of resources creator in Gnocchi. '
42 '(For example, default or service_domain'),
43 ]
44
45
46 class GnocchiUnavailable(Exception):
47 code = 503
48
49
50 class AlarmGnocchiThresholdRule(base.AlarmRule):
51 comparison_operator = base.AdvEnum('comparison_operator', str,
52 'lt', 'le', 'eq', 'ne', 'ge', 'gt',
53 default='eq')
54 "The comparison against the alarm threshold"
55
56 threshold = wsme.wsattr(float, mandatory=True)
57 "The threshold of the alarm"
58
59 aggregation_method = wsme.wsattr(wtypes.text, mandatory=True)
60 "The aggregation_method to compare to the threshold"
61
62 evaluation_periods = wsme.wsattr(wtypes.IntegerType(minimum=1), default=1)
63 "The number of historical periods to evaluate the threshold"
64
65 granularity = wsme.wsattr(wtypes.IntegerType(minimum=1), default=60)
66 "The time range in seconds over which query"
67
68 cache = cachetools.TTLCache(maxsize=1, ttl=3600)
69 lock = threading.RLock()
70
71 @classmethod
72 def validate_alarm(cls, alarm):
73 alarm_rule = getattr(alarm, "%s_rule" % alarm.type)
74 aggregation_method = alarm_rule.aggregation_method
75 if aggregation_method not in cls._get_aggregation_methods():
76 raise base.ClientSideError(
77 'aggregation_method should be in %s not %s' % (
78 cls._get_aggregation_methods(), aggregation_method))
79
80 @staticmethod
81 @cachetools.cached(cache, lock=lock)
82 def _get_aggregation_methods():
83 conf = pecan.request.cfg
84 gnocchi_client = client.Client(
85 '1', keystone_client.get_session(conf),
86 adapter_options={
87 'interface': conf.service_credentials.interface,
88 'region_name': conf.service_credentials.region_name})
89 try:
90 return gnocchi_client.capabilities.list().get(
91 'aggregation_methods', [])
92 except exceptions.ClientException as e:
93 raise base.ClientSideError(e.message, status_code=e.code)
94 except Exception as e:
95 raise GnocchiUnavailable(e)
96
97
98 class MetricOfResourceRule(AlarmGnocchiThresholdRule):
99 metric = wsme.wsattr(wtypes.text, mandatory=True)
100 "The name of the metric"
101
102 resource_id = wsme.wsattr(wtypes.text, mandatory=True)
103 "The id of a resource"
104
105 resource_type = wsme.wsattr(wtypes.text, mandatory=True)
106 "The resource type"
107
108 def as_dict(self):
109 rule = self.as_dict_from_keys(['granularity', 'comparison_operator',
110 'threshold', 'aggregation_method',
111 'evaluation_periods',
112 'metric',
113 'resource_id',
114 'resource_type'])
115 return rule
116
117
118 class AggregationMetricByResourcesLookupRule(AlarmGnocchiThresholdRule):
119 metric = wsme.wsattr(wtypes.text, mandatory=True)
120 "The name of the metric"
121
122 query = wsme.wsattr(wtypes.text, mandatory=True)
123 ('The query to filter the metric, Don\'t forget to filter out '
124 'deleted resources (example: {"and": [{"=": {"ended_at": null}}, ...]}), '
125 'Otherwise Gnocchi will try to create the aggregate against obsolete '
126 'resources')
127
128 resource_type = wsme.wsattr(wtypes.text, mandatory=True)
129 "The resource type"
130
131 def as_dict(self):
132 rule = self.as_dict_from_keys(['granularity', 'comparison_operator',
133 'threshold', 'aggregation_method',
134 'evaluation_periods',
135 'metric',
136 'query',
137 'resource_type'])
138 return rule
139
140 cache = cachetools.TTLCache(maxsize=1, ttl=3600)
141 lock = threading.RLock()
142
143 @staticmethod
144 @cachetools.cached(cache, lock=lock)
145 def get_external_project_owner():
146 kc = keystone_client.get_client(pecan.request.cfg)
147 project_name = pecan.request.cfg.api.gnocchi_external_project_owner
148 domain_name = pecan.request.cfg.api.gnocchi_external_domain_name
149 try:
150 domains = kc.domains.list(name=domain_name)
151 project = kc.projects.find(
152 name=project_name,
153 domain_id=domains[0].id)
154 return project.id
155 except ka_exceptions.NotFound:
156 LOG.warning("Unable to get domain or project information. "
157 "domain_name : %(domain_name)s, "
158 "project_name : %(project_name)s",
159 {'domain_name': domain_name,
160 'project_name': project_name})
161 return None
162
163 @classmethod
164 def validate_alarm(cls, alarm):
165 super(AggregationMetricByResourcesLookupRule,
166 cls).validate_alarm(alarm)
167
168 rule = alarm.gnocchi_aggregation_by_resources_threshold_rule
169
170 # check the query string is a valid json
171 try:
172 query = json.loads(rule.query)
173 except ValueError:
174 raise wsme.exc.InvalidInput('rule/query', rule.query)
175
176 conf = pecan.request.cfg
177
178 # Scope the alarm to the project id if needed
179 auth_project = v2_utils.get_auth_project(alarm.project_id)
180 if auth_project:
181
182 perms_filter = {"=": {"created_by_project_id": auth_project}}
183
184 external_project_owner = cls.get_external_project_owner()
185 if external_project_owner:
186 perms_filter = {"or": [
187 perms_filter,
188 {"and": [
189 {"=": {"created_by_project_id":
190 external_project_owner}},
191 {"=": {"project_id": auth_project}}]}
192 ]}
193
194 query = {"and": [perms_filter, query]}
195 rule.query = json.dumps(query)
196
197 gnocchi_client = client.Client(
198 '1', keystone_client.get_session(conf),
199 adapter_options={
200 'interface': conf.service_credentials.interface,
201 'region_name': conf.service_credentials.region_name})
202 try:
203 gnocchi_client.aggregates.fetch(
204 operations=[
205 'aggregate', rule.aggregation_method,
206 [
207 'metric', rule.metric,
208 rule.aggregation_method.lstrip('rate:')
209 ]
210 ],
211 search=query,
212 needed_overlap=0,
213 start="-1 day",
214 stop="now",
215 resource_type=rule.resource_type)
216 except exceptions.ClientException as e:
217 if e.code == 404:
218 # NOTE(sileht): We are fine here, we just want to ensure the
219 # 'query' payload is valid for Gnocchi If the metric
220 # doesn't exists yet, it doesn't matter.
221 return
222 if e.code == 400 and 'Metrics not found' in e.message["cause"]:
223 # NOTE(tkajinam): Gnocchi<4.5 returns 400 instead of 404
224 return
225 raise base.ClientSideError(e.message, status_code=e.code)
226 except Exception as e:
227 raise GnocchiUnavailable(e)
228
229
230 class AggregationMetricsByIdLookupRule(AlarmGnocchiThresholdRule):
231 metrics = wsme.wsattr([wtypes.text], mandatory=True)
232 "A list of metric Ids"
233
234 def as_dict(self):
235 rule = self.as_dict_from_keys(['granularity', 'comparison_operator',
236 'threshold', 'aggregation_method',
237 'evaluation_periods',
238 'metrics'])
239 return rule