186 lines
7.1 KiB
Python
186 lines
7.1 KiB
Python
# -*- encoding: utf-8 -*-
|
|
#
|
|
# Copyright © 2014-2015 eNovance
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from oslo_config import cfg
|
|
|
|
from gnocchi import storage
|
|
from gnocchi.storage import _carbonara
|
|
from gnocchi.storage.common import swift
|
|
|
|
swclient = swift.swclient
|
|
swift_utils = swift.swift_utils
|
|
|
|
OPTS = [
|
|
cfg.StrOpt('swift_auth_version',
|
|
default='1',
|
|
help='Swift authentication version to user.'),
|
|
cfg.StrOpt('swift_preauthurl',
|
|
help='Swift pre-auth URL.'),
|
|
cfg.StrOpt('swift_authurl',
|
|
default="http://localhost:8080/auth/v1.0",
|
|
help='Swift auth URL.'),
|
|
cfg.StrOpt('swift_preauthtoken',
|
|
secret=True,
|
|
help='Swift token to user to authenticate.'),
|
|
cfg.StrOpt('swift_user',
|
|
default="admin:admin",
|
|
help='Swift user.'),
|
|
cfg.StrOpt('swift_user_domain_name',
|
|
default='Default',
|
|
help='Swift user domain name.'),
|
|
cfg.StrOpt('swift_key',
|
|
secret=True,
|
|
default="admin",
|
|
help='Swift key/password.'),
|
|
cfg.StrOpt('swift_project_name',
|
|
help='Swift tenant name, only used in v2/v3 auth.',
|
|
deprecated_name="swift_tenant_name"),
|
|
cfg.StrOpt('swift_project_domain_name',
|
|
default='Default',
|
|
help='Swift project domain name.'),
|
|
cfg.StrOpt('swift_container_prefix',
|
|
default='gnocchi',
|
|
help='Prefix to namespace metric containers.'),
|
|
cfg.StrOpt('swift_endpoint_type',
|
|
default='publicURL',
|
|
help='Endpoint type to connect to Swift',),
|
|
cfg.IntOpt('swift_timeout',
|
|
min=0,
|
|
default=300,
|
|
help='Connection timeout in seconds.'),
|
|
]
|
|
|
|
|
|
class SwiftStorage(_carbonara.CarbonaraBasedStorage):
|
|
|
|
WRITE_FULL = True
|
|
|
|
def __init__(self, conf, incoming):
|
|
super(SwiftStorage, self).__init__(conf, incoming)
|
|
self.swift = swift.get_connection(conf)
|
|
self._container_prefix = conf.swift_container_prefix
|
|
|
|
def _container_name(self, metric):
|
|
return '%s.%s' % (self._container_prefix, str(metric.id))
|
|
|
|
@staticmethod
|
|
def _object_name(split_key, aggregation, granularity, version=3):
|
|
name = '%s_%s_%s' % (split_key, aggregation, granularity)
|
|
return name + '_v%s' % version if version else name
|
|
|
|
def _create_metric(self, metric):
|
|
# TODO(jd) A container per user in their account?
|
|
resp = {}
|
|
self.swift.put_container(self._container_name(metric),
|
|
response_dict=resp)
|
|
# put_container() should return 201 Created; if it returns 204, that
|
|
# means the metric was already created!
|
|
if resp['status'] == 204:
|
|
raise storage.MetricAlreadyExists(metric)
|
|
|
|
def _store_metric_measures(self, metric, timestamp_key, aggregation,
|
|
granularity, data, offset=None, version=3):
|
|
self.swift.put_object(
|
|
self._container_name(metric),
|
|
self._object_name(timestamp_key, aggregation, granularity,
|
|
version),
|
|
data)
|
|
|
|
def _delete_metric_measures(self, metric, timestamp_key, aggregation,
|
|
granularity, version=3):
|
|
self.swift.delete_object(
|
|
self._container_name(metric),
|
|
self._object_name(timestamp_key, aggregation, granularity,
|
|
version))
|
|
|
|
def _delete_metric(self, metric):
|
|
container = self._container_name(metric)
|
|
try:
|
|
headers, files = self.swift.get_container(
|
|
container, full_listing=True)
|
|
except swclient.ClientException as e:
|
|
if e.http_status != 404:
|
|
# Maybe it never has been created (no measure)
|
|
raise
|
|
else:
|
|
swift.bulk_delete(self.swift, container, files)
|
|
try:
|
|
self.swift.delete_container(container)
|
|
except swclient.ClientException as e:
|
|
if e.http_status != 404:
|
|
# Deleted in the meantime? Whatever.
|
|
raise
|
|
|
|
def _get_measures(self, metric, timestamp_key, aggregation, granularity,
|
|
version=3):
|
|
try:
|
|
headers, contents = self.swift.get_object(
|
|
self._container_name(metric), self._object_name(
|
|
timestamp_key, aggregation, granularity, version))
|
|
except swclient.ClientException as e:
|
|
if e.http_status == 404:
|
|
try:
|
|
self.swift.head_container(self._container_name(metric))
|
|
except swclient.ClientException as e:
|
|
if e.http_status == 404:
|
|
raise storage.MetricDoesNotExist(metric)
|
|
raise
|
|
raise storage.AggregationDoesNotExist(metric, aggregation)
|
|
raise
|
|
return contents
|
|
|
|
def _list_split_keys_for_metric(self, metric, aggregation, granularity,
|
|
version=3):
|
|
container = self._container_name(metric)
|
|
try:
|
|
headers, files = self.swift.get_container(
|
|
container, full_listing=True)
|
|
except swclient.ClientException as e:
|
|
if e.http_status == 404:
|
|
raise storage.MetricDoesNotExist(metric)
|
|
raise
|
|
keys = set()
|
|
for f in files:
|
|
try:
|
|
meta = f['name'].split('_')
|
|
if (aggregation == meta[1] and granularity == float(meta[2])
|
|
and self._version_check(f['name'], version)):
|
|
keys.add(meta[0])
|
|
except (ValueError, IndexError):
|
|
# Might be "none", or any other file. Be resilient.
|
|
continue
|
|
return keys
|
|
|
|
@staticmethod
|
|
def _build_unaggregated_timeserie_path(version):
|
|
return 'none' + ("_v%s" % version if version else "")
|
|
|
|
def _get_unaggregated_timeserie(self, metric, version=3):
|
|
try:
|
|
headers, contents = self.swift.get_object(
|
|
self._container_name(metric),
|
|
self._build_unaggregated_timeserie_path(version))
|
|
except swclient.ClientException as e:
|
|
if e.http_status == 404:
|
|
raise storage.MetricDoesNotExist(metric)
|
|
raise
|
|
return contents
|
|
|
|
def _store_unaggregated_timeserie(self, metric, data, version=3):
|
|
self.swift.put_object(self._container_name(metric),
|
|
self._build_unaggregated_timeserie_path(version),
|
|
data)
|