Skip to content

Commit 1e49dd0

Browse files
authored
Merge branch 'stackhpc/yoga' into upstream/yoga-2024-05-27
2 parents 913f061 + f45727b commit 1e49dd0

14 files changed

Lines changed: 131 additions & 26 deletions

File tree

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @stackhpc/openstack
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
name: Tag & Release
3+
'on':
4+
push:
5+
branches:
6+
- stackhpc/yoga
7+
permissions:
8+
actions: read
9+
contents: write
10+
jobs:
11+
tag-and-release:
12+
uses: stackhpc/.github/.github/workflows/tag-and-release.yml@main

.github/workflows/tox.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
name: Tox Continuous Integration
3+
'on':
4+
pull_request:
5+
jobs:
6+
tox:
7+
uses: stackhpc/.github/.github/workflows/tox.yml@main

cloudkitty/collector/__init__.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ def MetricDict(value):
9393
# (NONE, NUMBOOL, NOTNUMBOOL, FLOOR, CEIL).
9494
# Defaults to NONE
9595
Required('mutate', default='NONE'):
96-
In(['NONE', 'NUMBOOL', 'NOTNUMBOOL', 'FLOOR', 'CEIL']),
96+
In(['NONE', 'NUMBOOL', 'NOTNUMBOOL', 'FLOOR', 'CEIL', 'MAP']),
97+
# Map dict used if mutate == 'MAP'
98+
Optional('mutate_map'): dict,
9799
# Collector-specific args. Should be overriden by schema provided for
98100
# the given collector
99101
Optional('extra_args'): dict,
@@ -270,6 +272,22 @@ def check_duplicates(metric_name, metric):
270272
return metric
271273

272274

275+
def validate_map_mutator(metric_name, metric):
276+
"""Validates MAP mutator"""
277+
mutate = metric.get('mutate')
278+
mutate_map = metric.get('mutate_map')
279+
280+
if mutate == 'MAP' and mutate_map is None:
281+
raise InvalidConfiguration(
282+
'Metric {} uses MAP mutator but mutate_map is missing: {}'.format(
283+
metric_name, metric))
284+
285+
if mutate != 'MAP' and mutate_map is not None:
286+
raise InvalidConfiguration(
287+
'Metric {} not using MAP mutator but mutate_map is present: '
288+
'{}'.format(metric_name, metric))
289+
290+
273291
def validate_conf(conf):
274292
"""Validates the provided configuration."""
275293
collector = get_collector_without_invoke()
@@ -278,4 +296,5 @@ def validate_conf(conf):
278296
if 'alt_name' not in metric.keys():
279297
metric['alt_name'] = metric_name
280298
check_duplicates(metric_name, metric)
299+
validate_map_mutator(metric_name, metric)
281300
return output

cloudkitty/collector/gnocchi.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,9 @@ def _format_data(self, metconf, data, resources_info=None):
434434
qty = data['measures']['measures']['aggregated'][0][2]
435435
converted_qty = ck_utils.convert_unit(
436436
qty, metconf['factor'], metconf['offset'])
437-
mutated_qty = ck_utils.mutate(converted_qty, metconf['mutate'])
437+
mutate_map = metconf.get('mutate_map')
438+
mutated_qty = ck_utils.mutate(converted_qty, metconf['mutate'],
439+
mutate_map=mutate_map)
438440
return metadata, groupby, mutated_qty
439441

440442
def fetch_all(self, metric_name, start, end,

cloudkitty/collector/monasca.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,9 @@ def _format_data(self, metconf, data, resources_info=None):
199199
qty = data['statistics'][0][1]
200200
converted_qty = ck_utils.convert_unit(
201201
qty, metconf['factor'], metconf['offset'])
202-
mutated_qty = ck_utils.mutate(converted_qty, metconf['mutate'])
202+
mutate_map = metconf.get('mutate_map')
203+
mutated_qty = ck_utils.mutate(converted_qty, metconf['mutate'],
204+
mutate_map=mutate_map)
203205
return metadata, groupby, mutated_qty
204206

205207
def fetch_all(self, metric_name, start, end,

cloudkitty/collector/prometheus.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ def _format_data(self, metric_name, scope_key, scope_id, start, end, data):
148148
self.conf[metric_name]['factor'],
149149
self.conf[metric_name]['offset'],
150150
)
151-
qty = ck_utils.mutate(qty, self.conf[metric_name]['mutate'])
151+
mutate_map = self.conf[metric_name].get('mutate_map')
152+
qty = ck_utils.mutate(qty, self.conf[metric_name]['mutate'],
153+
mutate_map=mutate_map)
152154

153155
return metadata, groupby, qty
154156

@@ -211,6 +213,8 @@ def fetch_all(self, metric_name, start, end, scope_id, q_filter=None):
211213
if query_suffix:
212214
query = "{0} {1}".format(query, query_suffix)
213215

216+
LOG.debug("Calling Prometheus with query: %s", query)
217+
214218
try:
215219
res = self._conn.get_instant(
216220
query,

cloudkitty/storage/v2/elasticsearch/client.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,15 @@ def _build_composite(self, groupby):
9898
sources = []
9999
for elem in groupby:
100100
if elem == 'type':
101-
sources.append({'type': {'terms': {'field': 'type'}}})
101+
sources.append({'type': {'terms': {'field': 'type.keyword'}}})
102102
elif elem == 'time':
103103
# Not doing a date_histogram aggregation because we don't know
104104
# the period
105105
sources.append({'begin': {'terms': {'field': 'start'}}})
106106
sources.append({'end': {'terms': {'field': 'end'}}})
107107
else:
108-
sources.append({elem: {'terms': {'field': 'groupby.' + elem}}})
108+
field = 'groupby.' + elem + '.keyword'
109+
sources.append({elem: {'terms': {'field': field}}})
109110

110111
return {"sources": sources}
111112

@@ -158,12 +159,9 @@ def put_mapping(self, mapping):
158159
:rtype: requests.models.Response
159160
"""
160161
url = '/'.join(
161-
(self._url, self._index_name, '_mapping', self._mapping_name))
162-
# NOTE(peschk_l): This is done for compatibility with
163-
# Elasticsearch 6 and 7.
164-
param = {"include_type_name": "true"}
162+
(self._url, self._index_name, self._mapping_name))
165163
return self._req(
166-
self._sess.put, url, json.dumps(mapping), param, deserialize=False)
164+
self._sess.post, url, json.dumps(mapping), {}, deserialize=False)
167165

168166
def get_index(self):
169167
"""Does a GET request against ES's index API.
@@ -228,7 +226,7 @@ def bulk_with_instruction(self, instruction, terms):
228226
"""Does a POST request against ES's bulk API
229227
230228
The POST request will be done against
231-
`/<index_name>/<mapping_name>/_bulk`
229+
`/<index_name>/_bulk`
232230
233231
The instruction will be appended before each term. For example,
234232
bulk_with_instruction('instr', ['one', 'two']) will produce::
@@ -249,7 +247,7 @@ def bulk_with_instruction(self, instruction, terms):
249247
*[(instruction, json.dumps(term)) for term in terms]
250248
)) + '\n'
251249
url = '/'.join(
252-
(self._url, self._index_name, self._mapping_name, '_bulk'))
250+
(self._url, self._index_name, '_bulk'))
253251
return self._req(self._sess.post, url, data, None, deserialize=False)
254252

255253
def bulk_index(self, terms):

cloudkitty/tests/collectors/test_validation.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,26 @@ def test_check_duplicates(self):
188188
self.assertRaises(
189189
collector.InvalidConfiguration,
190190
collector.check_duplicates, metric_name, metric)
191+
192+
def test_validate_map_mutator(self):
193+
data = copy.deepcopy(self.base_data)
194+
195+
# Check that validation succeeds when MAP mutator is not used
196+
for metric_name, metric in data['metrics'].items():
197+
collector.validate_map_mutator(metric_name, metric)
198+
199+
# Check that validation raises an exception when mutate_map is missing
200+
for metric_name, metric in data['metrics'].items():
201+
metric['mutate'] = 'MAP'
202+
self.assertRaises(
203+
collector.InvalidConfiguration,
204+
collector.validate_map_mutator, metric_name, metric)
205+
206+
data = copy.deepcopy(self.base_data)
207+
# Check that validation raises an exception when mutate_map is present
208+
# but MAP mutator is not used
209+
for metric_name, metric in data['metrics'].items():
210+
metric['mutate_map'] = {}
211+
self.assertRaises(
212+
collector.InvalidConfiguration,
213+
collector.validate_map_mutator, metric_name, metric)

cloudkitty/tests/storage/v2/elasticsearch/test_client.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ def test_build_composite(self):
8686
self.assertEqual(
8787
self.client._build_composite(['one', 'type', 'two']),
8888
{'sources': [
89-
{'one': {'terms': {'field': 'groupby.one'}}},
90-
{'type': {'terms': {'field': 'type'}}},
91-
{'two': {'terms': {'field': 'groupby.two'}}},
89+
{'one': {'terms': {'field': 'groupby.one.keyword'}}},
90+
{'type': {'terms': {'field': 'type.keyword'}}},
91+
{'two': {'terms': {'field': 'groupby.two.keyword'}}},
9292
]},
9393
)
9494

@@ -186,9 +186,9 @@ def test_put_mapping(self):
186186
with mock.patch.object(self.client, '_req') as rmock:
187187
self.client.put_mapping(mapping)
188188
rmock.assert_called_once_with(
189-
self.client._sess.put,
190-
'http://elasticsearch:9200/index_name/_mapping/test_mapping',
191-
'{"a": "b"}', {'include_type_name': 'true'}, deserialize=False)
189+
self.client._sess.post,
190+
'http://elasticsearch:9200/index_name/test_mapping',
191+
'{"a": "b"}', {}, deserialize=False)
192192

193193
def test_get_index(self):
194194
with mock.patch.object(self.client, '_req') as rmock:
@@ -259,7 +259,7 @@ def test_bulk_with_instruction(self):
259259
self.client.bulk_with_instruction(instruction, terms)
260260
rmock.assert_called_once_with(
261261
self.client._sess.post,
262-
'http://elasticsearch:9200/index_name/test_mapping/_bulk',
262+
'http://elasticsearch:9200/index_name/_bulk',
263263
expected_data, None, deserialize=False)
264264

265265
def test_bulk_index(self):

0 commit comments

Comments
 (0)