Skip to content

Commit 4e2f605

Browse files
leo-naekaAdrian Turjak
authored and
Adrian Turjak
committed
Add incoming/indexer/storage information to V1 API's status endpoint
1 parent 18c135a commit 4e2f605

File tree

18 files changed

+220
-21
lines changed

18 files changed

+220
-21
lines changed

gnocchi/incoming/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ def finish_sack_processing(sack):
257257
"""Mark sack processing has finished."""
258258
pass
259259

260+
@staticmethod
261+
def get_health_status():
262+
raise exceptions.NotImplementedError
263+
260264

261265
@utils.retry_on_exception_and_log("Unable to initialize incoming driver")
262266
def get_driver(conf):

gnocchi/incoming/ceph.py

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
from gnocchi.common import ceph
2525
from gnocchi import incoming
26+
from gnocchi.status import get_ceph_health_status
2627

2728
rados = ceph.rados
2829

@@ -232,3 +233,6 @@ def process_measures_for_sack(self, sack):
232233
self.ioctx.remove_omap_keys(op, tuple(processed_keys))
233234
self.ioctx.operate_write_op(op, str(sack),
234235
flags=self.OMAP_WRITE_FLAGS)
236+
237+
def get_health_status(self):
238+
return get_ceph_health_status(self)

gnocchi/incoming/file.py

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import six
2626

2727
from gnocchi import incoming
28+
from gnocchi.status import get_file_health_status
2829
from gnocchi import utils
2930

3031
LOG = daiquiri.getLogger(__name__)
@@ -206,3 +207,6 @@ def process_measures_for_sack(self, sack):
206207

207208
for metric_id, files in six.iteritems(processed_files):
208209
self._delete_measures_files_for_metric(metric_id, files)
210+
211+
def get_health_status(self):
212+
return get_file_health_status(self)

gnocchi/incoming/redis.py

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
from gnocchi.common import redis
2323
from gnocchi import incoming
24+
from gnocchi.status import get_redis_health_status
2425

2526

2627
LOG = daiquiri.getLogger(__name__)
@@ -193,3 +194,6 @@ def finish_sack_processing(self, sack):
193194
# Delete the sack key which handles no data but is used to get a SET
194195
# notification in iter_on_sacks_to_process
195196
self._client.delete(str(sack))
197+
198+
def get_health_status(self):
199+
return get_redis_health_status(self)

gnocchi/incoming/s3.py

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
from gnocchi.common import s3
2626
from gnocchi import incoming
27+
from gnocchi.status import get_s3_health_status
2728

2829
boto3 = s3.boto3
2930
botocore = s3.botocore
@@ -188,3 +189,6 @@ def process_measures_for_sack(self, sack):
188189

189190
# Now clean objects
190191
s3.bulk_delete(self.s3, self._bucket_name_measures, files)
192+
193+
def get_health_status(self):
194+
return get_s3_health_status(self)

gnocchi/incoming/swift.py

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
from gnocchi.common import swift
2424
from gnocchi import incoming
25+
from gnocchi.status import get_swift_health_status
2526
from gnocchi import utils
2627

2728
swclient = swift.swclient
@@ -144,3 +145,6 @@ def process_measures_for_sack(self, sack):
144145
yield measures
145146

146147
swift.bulk_delete(self.swift, sack_name, files)
148+
149+
def get_health_status(self):
150+
return get_swift_health_status(self)

gnocchi/indexer/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -477,3 +477,7 @@ def get_resource_attributes_schemas():
477477
@staticmethod
478478
def get_resource_type_schema():
479479
raise exceptions.NotImplementedError
480+
481+
@staticmethod
482+
def get_health_status():
483+
raise exceptions.NotImplementedError

gnocchi/indexer/sqlalchemy.py

+4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
from gnocchi.indexer import sqlalchemy_base as base
5151
from gnocchi.indexer import sqlalchemy_types as types
5252
from gnocchi import resource_type
53+
from gnocchi.status import get_sqlalchemy_health_status
5354
from gnocchi import utils
5455

5556
Base = base.Base
@@ -1176,6 +1177,9 @@ def _build_sort_keys(sorts, unique_keys):
11761177

11771178
return sort_keys, sort_dirs
11781179

1180+
def get_health_status(self):
1181+
return get_sqlalchemy_health_status(self)
1182+
11791183

11801184
def _operator_in(field_name, value):
11811185
# Do not generate empty IN comparison

gnocchi/rest/api.py

+45-19
Original file line numberDiff line numberDiff line change
@@ -2092,42 +2092,68 @@ class StatusController(rest.RestController):
20922092
@pecan.expose('json')
20932093
def get(details=True):
20942094
enforce("get status", {})
2095-
try:
2096-
members_req = pecan.request.coordinator.get_members(
2097-
metricd.MetricProcessor.GROUP_ID)
2098-
except tooz.NotImplemented:
2099-
members_req = None
2095+
2096+
# Add status for incoming/indexer/storage
2097+
response_data = {
2098+
'incoming': {
2099+
'status': pecan.request.incoming.get_health_status(),
2100+
},
2101+
'indexer': {
2102+
'status': pecan.request.indexer.get_health_status(),
2103+
},
2104+
'storage': {
2105+
'status': pecan.request.storage.get_health_status(),
2106+
}
2107+
}
2108+
2109+
# Always return the detail, but set status code to 503
2110+
# if a component is not available
2111+
pecan.response.status = 200 if all([
2112+
component['status']['is_available']
2113+
for component in response_data.values()]) else 503
2114+
2115+
# Add storage measures to process
21002116
try:
21012117
report = pecan.request.incoming.measures_report(
2102-
strtobool("details", details))
2118+
strtobool('details', details))
21032119
except incoming.ReportGenerationError:
2104-
abort(503, 'Unable to generate status. Please retry.')
2105-
report_dict = {"storage": {"summary": report['summary']}}
2120+
abort(503, "Unable to generate status. Please retry.")
2121+
response_data['storage']['summary'] = report['summary']
21062122
if 'details' in report:
2107-
report_dict["storage"]["measures_to_process"] = report['details']
2108-
report_dict['metricd'] = {}
2109-
if members_req:
2123+
response_data['storage']['measures_to_process'] = report['details']
2124+
2125+
# Add metricd status
2126+
try:
2127+
members_req = pecan.request.coordinator.get_members(
2128+
metricd.MetricProcessor.GROUP_ID)
2129+
except tooz.NotImplemented:
2130+
response_data['metricd'] = {
2131+
'processors': None,
2132+
'statistics': {}
2133+
}
2134+
else:
21102135
members = members_req.get()
21112136
caps = [
21122137
pecan.request.coordinator.get_member_capabilities(
21132138
metricd.MetricProcessor.GROUP_ID, member)
21142139
for member in members
21152140
]
2116-
report_dict['metricd']['processors'] = [
2117-
member.decode() for member in members
2118-
]
21192141
members_data = {}
21202142
for member, cap in six.moves.zip(members, caps):
21212143
caps_data = {
21222144
six.ensure_str(k): v
21232145
for k, v in six.iteritems(cap.get())
21242146
}
21252147
members_data[member.decode()] = caps_data
2126-
report_dict['metricd']['statistics'] = members_data
2127-
else:
2128-
report_dict['metricd']['processors'] = None
2129-
report_dict['metricd']['statistics'] = {}
2130-
return report_dict
2148+
2149+
response_data['metricd'] = {
2150+
'processors': [
2151+
member.decode() for member in members
2152+
],
2153+
'statistics': members_data
2154+
}
2155+
2156+
return response_data
21312157

21322158

21332159
class MetricsBatchController(object):

gnocchi/status.py

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# -*- coding:Utf-8 -*-
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
from __future__ import unicode_literals
15+
16+
from collections import OrderedDict
17+
import six
18+
19+
20+
def get_ceph_health_status(driver):
21+
"""Return ceph status.
22+
23+
Include ceph stats.
24+
"""
25+
response = OrderedDict([
26+
('name', driver.__class__.__name__)
27+
])
28+
try:
29+
stats = driver.rados.get_cluster_stats()
30+
except Exception as e:
31+
response['is_available'] = False
32+
response['error'] = six.text_type(e)
33+
else:
34+
response['is_available'] = True
35+
response['stats'] = stats
36+
return response
37+
38+
39+
def get_file_health_status(driver):
40+
"""Return file status."""
41+
return OrderedDict([
42+
('name', driver.__class__.__name__),
43+
('is_available', True)
44+
])
45+
46+
47+
def get_redis_health_status(driver):
48+
"""Return redis status.
49+
50+
Include redis info.
51+
"""
52+
response = OrderedDict([
53+
('name', driver.__class__.__name__)
54+
])
55+
try:
56+
info = driver._client.info()
57+
except Exception as e:
58+
response['is_available'] = False
59+
response['error'] = six.text_type(e)
60+
else:
61+
response['is_available'] = True
62+
response['info'] = info
63+
return response
64+
65+
66+
def get_s3_health_status(driver):
67+
"""Return s3 status."""
68+
response = OrderedDict([
69+
('name', driver.__class__.__name__)
70+
])
71+
try:
72+
driver.s3.list_objects_v2(
73+
Bucket=driver._bucket_name_measures, Prefix='/')
74+
except Exception as e:
75+
response['is_available'] = False
76+
response['error'] = six.text_type(e)
77+
else:
78+
response['is_available'] = True
79+
return response
80+
81+
82+
def get_sqlalchemy_health_status(driver):
83+
"""Return sqlalchemy status."""
84+
response = OrderedDict([
85+
('name', driver.__class__.__name__)
86+
])
87+
try:
88+
with driver.facade.independent_reader() as session:
89+
session.execute('SELECT 1')
90+
except Exception as e:
91+
response['is_available'] = False
92+
response['error'] = six.text_type(e)
93+
else:
94+
response['is_available'] = True
95+
return response
96+
97+
98+
def get_swift_health_status(driver):
99+
"""Return swift status.
100+
101+
Include swift account info.
102+
"""
103+
response = OrderedDict([
104+
('name', driver.__class__.__name__)
105+
])
106+
try:
107+
info = driver.swift.head_account()
108+
except Exception as e:
109+
response['is_available'] = False
110+
response['error'] = six.text_type(e)
111+
else:
112+
response['is_available'] = True
113+
response['info'] = info
114+
return response

gnocchi/storage/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -692,3 +692,7 @@ def _map_compute_splits_operations(bound_timeserie):
692692
with self.statistics.time("raw measures store"):
693693
self._store_unaggregated_timeseries(new_boundts)
694694
self.statistics["raw measures store"] += len(new_boundts)
695+
696+
@staticmethod
697+
def get_health_status():
698+
raise NotImplementedError

gnocchi/storage/ceph.py

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
from gnocchi import carbonara
2323
from gnocchi.common import ceph
24+
from gnocchi.status import get_ceph_health_status
2425
from gnocchi import storage
2526
from gnocchi import utils
2627

@@ -232,3 +233,6 @@ def _get_object_content(self, name):
232233
content += data
233234
offset += len(data)
234235
return content
236+
237+
def get_health_status(self):
238+
return get_ceph_health_status(self)

gnocchi/storage/file.py

+4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import six
3030

3131
from gnocchi import carbonara
32+
from gnocchi.status import get_file_health_status
3233
from gnocchi import storage
3334
from gnocchi import utils
3435

@@ -247,3 +248,6 @@ def _get_splits_unbatched(self, metric, key, aggregation, version=3):
247248
if e.errno == errno.ENOENT:
248249
return
249250
raise
251+
252+
def get_health_status(self):
253+
return get_file_health_status(self)

gnocchi/storage/redis.py

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
from gnocchi import carbonara
2121
from gnocchi.common import redis
22+
from gnocchi.status import get_redis_health_status
2223
from gnocchi import storage
2324
from gnocchi import utils
2425

@@ -193,3 +194,6 @@ def _get_splits(self, metrics_aggregations_keys, version=3):
193194
results[metric][aggregation] = result
194195

195196
return results
197+
198+
def get_health_status(self):
199+
return get_redis_health_status(self)

gnocchi/storage/s3.py

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from gnocchi import carbonara
2222
from gnocchi.common import s3
23+
from gnocchi.status import get_s3_health_status
2324
from gnocchi import storage
2425
from gnocchi import utils
2526

@@ -242,3 +243,6 @@ def _store_unaggregated_timeseries_unbatched(
242243
Bucket=self._bucket_name,
243244
Key=self._build_unaggregated_timeserie_path(metric, version),
244245
Body=data)
246+
247+
def get_health_status(self):
248+
return get_s3_health_status(self)

gnocchi/storage/swift.py

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
from gnocchi import carbonara
2323
from gnocchi.common import swift
24+
from gnocchi.status import get_swift_health_status
2425
from gnocchi import storage
2526
from gnocchi import utils
2627

@@ -221,3 +222,6 @@ def _store_unaggregated_timeseries_unbatched(
221222
self._container_name(metric),
222223
self._build_unaggregated_timeserie_path(version),
223224
data)
225+
226+
def get_health_status(self):
227+
return get_swift_health_status(self)

0 commit comments

Comments
 (0)