Skip to content

Commit 6e84dd0

Browse files
leo-naekaAdrian Turjak
authored and
Adrian Turjak
committed
Add incoming/indexer/storage information to V1 API's status endpoint
1 parent 5eeb3f0 commit 6e84dd0

23 files changed

+241
-35
lines changed

gnocchi/common/ceph.py

+21
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
# License for the specific language governing permissions and limitations
1313
# under the License.
1414

15+
from collections import OrderedDict
1516
import errno
17+
import six
1618

1719
import daiquiri
1820

@@ -97,3 +99,22 @@ def errno_to_exception(ret):
9799
raise rados.Error("Unhandled error '%s'" % ret)
98100
else:
99101
raise getattr(rados, name)
102+
103+
104+
def get_ceph_health_status(driver):
105+
"""Return ceph status.
106+
107+
Include ceph stats.
108+
"""
109+
response = OrderedDict([
110+
('name', driver.__class__.__name__)
111+
])
112+
try:
113+
stats = driver.rados.get_cluster_stats()
114+
except Exception as e:
115+
response['is_available'] = False
116+
response['error'] = six.text_type(e)
117+
else:
118+
response['is_available'] = True
119+
response['stats'] = stats
120+
return response

gnocchi/common/file.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
18+
19+
def get_file_health_status(driver):
20+
"""Return file status."""
21+
return OrderedDict([
22+
('name', driver.__class__.__name__),
23+
('is_available', True)
24+
])

gnocchi/common/redis.py

+20
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from __future__ import absolute_import
1818

19+
from collections import OrderedDict
1920
from oslo_config import cfg
2021
import six
2122
from six.moves.urllib import parse
@@ -167,3 +168,22 @@ def get_client(conf, scripts=None):
167168
}
168169

169170
return client, scripts
171+
172+
173+
def get_redis_health_status(driver):
174+
"""Return redis status.
175+
176+
Include redis info.
177+
"""
178+
response = OrderedDict([
179+
('name', driver.__class__.__name__)
180+
])
181+
try:
182+
info = driver._client.info()
183+
except Exception as e:
184+
response['is_available'] = False
185+
response['error'] = six.text_type(e)
186+
else:
187+
response['is_available'] = True
188+
response['info'] = info
189+
return response

gnocchi/common/s3.py

+19
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1414
# License for the specific language governing permissions and limitations
1515
# under the License.
16+
17+
from collections import OrderedDict
18+
import six
19+
1620
import daiquiri
1721

1822
import tenacity
@@ -82,3 +86,18 @@ def bulk_delete(conn, bucket, objects):
8286
deleted += len(response['Deleted'])
8387
LOG.debug('%s objects deleted, %s objects skipped',
8488
deleted, len(objects) - deleted)
89+
90+
91+
def get_s3_health_status(driver):
92+
"""Return s3 status."""
93+
response = OrderedDict([
94+
('name', driver.__class__.__name__)
95+
])
96+
try:
97+
driver.s3.head_bucket(Bucket=driver._bucket_name)
98+
except Exception as e:
99+
response['is_available'] = False
100+
response['error'] = six.text_type(e)
101+
else:
102+
response['is_available'] = True
103+
return response

gnocchi/common/sqlalchemy.py

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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_sqlalchemy_health_status(driver):
21+
"""Return sqlalchemy status."""
22+
response = OrderedDict([
23+
('name', driver.__class__.__name__)
24+
])
25+
try:
26+
with driver.facade.independent_reader() as session:
27+
session.execute('SELECT 1')
28+
except Exception as e:
29+
response['is_available'] = False
30+
response['error'] = six.text_type(e)
31+
else:
32+
response['is_available'] = True
33+
return response

gnocchi/common/swift.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@
1111
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1212
# License for the specific language governing permissions and limitations
1313
# under the License.
14-
import daiquiri
14+
15+
from collections import OrderedDict
16+
import six
1517
from six.moves.urllib.parse import quote
1618

19+
import daiquiri
20+
1721
try:
1822
from swiftclient import client as swclient
1923
from swiftclient import utils as swift_utils
@@ -71,3 +75,22 @@ def bulk_delete(conn, container, objects):
7175
resp = swift_utils.parse_api_response(headers, body)
7276
LOG.debug('# of objects deleted: %s, # of objects skipped: %s',
7377
resp['Number Deleted'], resp['Number Not Found'])
78+
79+
80+
def get_swift_health_status(driver):
81+
"""Return swift status.
82+
83+
Include swift account info.
84+
"""
85+
response = OrderedDict([
86+
('name', driver.__class__.__name__)
87+
])
88+
try:
89+
info = driver.swift.head_account()
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+
response['info'] = info
96+
return response

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

+2
Original file line numberDiff line numberDiff line change
@@ -232,3 +232,5 @@ def process_measures_for_sack(self, sack):
232232
self.ioctx.remove_omap_keys(op, tuple(processed_keys))
233233
self.ioctx.operate_write_op(op, str(sack),
234234
flags=self.OMAP_WRITE_FLAGS)
235+
236+
get_health_status = ceph.get_ceph_health_status

gnocchi/incoming/file.py

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import numpy
2525
import six
2626

27+
from gnocchi.common.status import get_file_health_status
2728
from gnocchi import incoming
2829
from gnocchi import utils
2930

@@ -206,3 +207,5 @@ 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+
get_health_status = get_file_health_status

gnocchi/incoming/redis.py

+2
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,5 @@ def finish_sack_processing(self, sack):
193193
# Delete the sack key which handles no data but is used to get a SET
194194
# notification in iter_on_sacks_to_process
195195
self._client.delete(str(sack))
196+
197+
get_health_status = redis.get_redis_health_status

gnocchi/incoming/s3.py

+15-13
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,21 @@ def __init__(self, conf, greedy=True):
4242
s3.get_connection(conf)
4343
)
4444

45-
self._bucket_name_measures = (
45+
self._bucket_name = (
4646
self._bucket_prefix + "-" + self.MEASURE_PREFIX
4747
)
4848

4949
def __str__(self):
50-
return "%s: %s" % (self.__class__.__name__, self._bucket_name_measures)
50+
return "%s: %s" % (self.__class__.__name__, self._bucket_name)
5151

5252
def _get_storage_sacks(self):
53-
response = self.s3.get_object(Bucket=self._bucket_name_measures,
53+
response = self.s3.get_object(Bucket=self._bucket_name,
5454
Key=self.CFG_PREFIX)
5555
return json.loads(response['Body'].read().decode())[self.CFG_SACKS]
5656

5757
def set_storage_settings(self, num_sacks):
5858
data = {self.CFG_SACKS: num_sacks}
59-
self.s3.put_object(Bucket=self._bucket_name_measures,
59+
self.s3.put_object(Bucket=self._bucket_name,
6060
Key=self.CFG_PREFIX,
6161
Body=json.dumps(data).encode())
6262

@@ -67,7 +67,7 @@ def remove_sacks(num_sacks):
6767

6868
def upgrade(self, num_sacks):
6969
try:
70-
s3.create_bucket(self.s3, self._bucket_name_measures,
70+
s3.create_bucket(self.s3, self._bucket_name,
7171
self._region_name)
7272
except botocore.exceptions.ClientError as e:
7373
if e.response['Error'].get('Code') not in (
@@ -80,7 +80,7 @@ def upgrade(self, num_sacks):
8080
def _store_new_measures(self, metric_id, data):
8181
now = datetime.datetime.utcnow().strftime("_%Y%m%d_%H:%M:%S")
8282
self.s3.put_object(
83-
Bucket=self._bucket_name_measures,
83+
Bucket=self._bucket_name,
8484
Key="/".join((str(self.sack_for_metric(metric_id)),
8585
str(metric_id),
8686
str(uuid.uuid4()) + now)),
@@ -97,7 +97,7 @@ def _build_report(self, details):
9797
else:
9898
kwargs = {}
9999
response = self.s3.list_objects_v2(
100-
Bucket=self._bucket_name_measures,
100+
Bucket=self._bucket_name,
101101
**kwargs)
102102
for c in response.get('Contents', ()):
103103
if c['Key'] != self.CFG_PREFIX:
@@ -118,7 +118,7 @@ def _list_files(self, path_items, **kwargs):
118118
except KeyError:
119119
pass
120120
response = self.s3.list_objects_v2(
121-
Bucket=self._bucket_name_measures,
121+
Bucket=self._bucket_name,
122122
Prefix="/".join(path_items) + "/",
123123
**kwargs)
124124
yield response
@@ -136,7 +136,7 @@ def _list_measure_files_for_metric(self, sack, metric_id):
136136
def delete_unprocessed_measures_for_metric(self, metric_id):
137137
sack = self.sack_for_metric(metric_id)
138138
files = self._list_measure_files_for_metric(sack, metric_id)
139-
s3.bulk_delete(self.s3, self._bucket_name_measures, files)
139+
s3.bulk_delete(self.s3, self._bucket_name, files)
140140

141141
def has_unprocessed(self, metric_id):
142142
sack = self.sack_for_metric(metric_id)
@@ -152,7 +152,7 @@ def process_measure_for_metrics(self, metric_ids):
152152
all_files.extend(files)
153153
for f in files:
154154
response = self.s3.get_object(
155-
Bucket=self._bucket_name_measures,
155+
Bucket=self._bucket_name,
156156
Key=f)
157157
measures[metric_id] = numpy.concatenate((
158158
measures[metric_id],
@@ -162,7 +162,7 @@ def process_measure_for_metrics(self, metric_ids):
162162
yield measures
163163

164164
# Now clean objects
165-
s3.bulk_delete(self.s3, self._bucket_name_measures, all_files)
165+
s3.bulk_delete(self.s3, self._bucket_name, all_files)
166166

167167
@contextlib.contextmanager
168168
def process_measures_for_sack(self, sack):
@@ -177,7 +177,7 @@ def process_measures_for_sack(self, sack):
177177
continue
178178

179179
response = self.s3.get_object(
180-
Bucket=self._bucket_name_measures,
180+
Bucket=self._bucket_name,
181181
Key=f)
182182
measures[metric_id] = numpy.concatenate((
183183
measures[metric_id],
@@ -187,4 +187,6 @@ def process_measures_for_sack(self, sack):
187187
yield measures
188188

189189
# Now clean objects
190-
s3.bulk_delete(self.s3, self._bucket_name_measures, files)
190+
s3.bulk_delete(self.s3, self._bucket_name, files)
191+
192+
get_health_status = s3.get_s3_health_status

gnocchi/incoming/swift.py

+2
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,5 @@ def process_measures_for_sack(self, sack):
144144
yield measures
145145

146146
swift.bulk_delete(self.swift, sack_name, files)
147+
148+
get_health_status = swift.get_swift_health_status

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

+3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
from sqlalchemy import types as sa_types
4646
import sqlalchemy_utils
4747

48+
from gnocchi.common.sqlalchemy import get_sqlalchemy_health_status
4849
from gnocchi import exceptions
4950
from gnocchi import indexer
5051
from gnocchi.indexer import sqlalchemy_base as base
@@ -1176,6 +1177,8 @@ def _build_sort_keys(sorts, unique_keys):
11761177

11771178
return sort_keys, sort_dirs
11781179

1180+
get_health_status = get_sqlalchemy_health_status
1181+
11791182

11801183
def _operator_in(field_name, value):
11811184
# Do not generate empty IN comparison

0 commit comments

Comments
 (0)