1570462507
explode metadata key/values to their own tables/rows (based on type). build a key string using dot notation similar to other nosql db and filter based on that. Blueprint: sqlalchemy-metadata-query Related-Bug: #1093625 Change-Id: I2076e67b79448f98124a57b62b5bfed7aa8ae2ad
106 lines
3.6 KiB
Python
106 lines
3.6 KiB
Python
# Copyright 2010 United States Government as represented by the
|
|
# Administrator of the National Aeronautics and Space Administration.
|
|
# Copyright 2011 Justin Santa Barbara
|
|
|
|
# All Rights Reserved.
|
|
#
|
|
# 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.
|
|
|
|
"""Utilities and helper functions."""
|
|
|
|
import calendar
|
|
import datetime
|
|
import decimal
|
|
|
|
from ceilometer.openstack.common import timeutils
|
|
|
|
|
|
def recursive_keypairs(d, separator=':'):
|
|
"""Generator that produces sequence of keypairs for nested dictionaries.
|
|
"""
|
|
for name, value in sorted(d.iteritems()):
|
|
if isinstance(value, dict):
|
|
for subname, subvalue in recursive_keypairs(value):
|
|
yield ('%s%s%s' % (name, separator, subname), subvalue)
|
|
elif isinstance(value, (tuple, list)):
|
|
# When doing a pair of JSON encode/decode operations to the tuple,
|
|
# the tuple would become list. So we have to generate the value as
|
|
# list here.
|
|
yield name, list(map(lambda x: unicode(x).encode('utf-8'),
|
|
value))
|
|
else:
|
|
yield name, value
|
|
|
|
|
|
def dt_to_decimal(utc):
|
|
"""Datetime to Decimal.
|
|
|
|
Some databases don't store microseconds in datetime
|
|
so we always store as Decimal unixtime.
|
|
"""
|
|
decimal.getcontext().prec = 30
|
|
return decimal.Decimal(str(calendar.timegm(utc.utctimetuple()))) + \
|
|
(decimal.Decimal(str(utc.microsecond)) /
|
|
decimal.Decimal("1000000.0"))
|
|
|
|
|
|
def decimal_to_dt(dec):
|
|
"""Return a datetime from Decimal unixtime format.
|
|
"""
|
|
if dec is None:
|
|
return None
|
|
integer = int(dec)
|
|
micro = (dec - decimal.Decimal(integer)) * decimal.Decimal(1000000)
|
|
daittyme = datetime.datetime.utcfromtimestamp(integer)
|
|
return daittyme.replace(microsecond=int(round(micro)))
|
|
|
|
|
|
def sanitize_timestamp(timestamp):
|
|
"""Return a naive utc datetime object."""
|
|
if not timestamp:
|
|
return timestamp
|
|
if not isinstance(timestamp, datetime.datetime):
|
|
timestamp = timeutils.parse_isotime(timestamp)
|
|
return timeutils.normalize_time(timestamp)
|
|
|
|
|
|
def stringify_timestamps(data):
|
|
"""Stringify any datetimes in given dict."""
|
|
isa_timestamp = lambda v: isinstance(v, datetime.datetime)
|
|
return dict((k, v.isoformat() if isa_timestamp(v) else v)
|
|
for (k, v) in data.iteritems())
|
|
|
|
|
|
def dict_to_keyval(value, key_base=None):
|
|
"""Expand a given dict to its corresponding key-value pairs.
|
|
|
|
Generated keys are fully qualified, delimited using dot notation.
|
|
ie. key = 'key.child_key.grandchild_key[0]'
|
|
"""
|
|
val_iter, key_func = None, None
|
|
if isinstance(value, dict):
|
|
val_iter = value.iteritems()
|
|
key_func = lambda k: key_base + '.' + k if key_base else k
|
|
elif isinstance(value, (tuple, list)):
|
|
val_iter = enumerate(value)
|
|
key_func = lambda k: key_base + '[%d]' % k
|
|
|
|
if val_iter:
|
|
for k, v in val_iter:
|
|
key_gen = key_func(k)
|
|
if isinstance(v, dict) or isinstance(v, (tuple, list)):
|
|
for key_gen, v in dict_to_keyval(v, key_gen):
|
|
yield key_gen, v
|
|
else:
|
|
yield key_gen, v
|