vitrage/vitrage/common/utils.py

158 lines
4.7 KiB
Python

# -*- encoding: utf-8 -*-
# Copyright 2015 - Alcatel-Lucent
# Copyright © 2014-2015 eNovance
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2011 Justin Santa Barbara
#
# 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.
import base64
from collections import defaultdict
import copy
import hashlib
import itertools
import random
import six
from six.moves import cPickle
import threading
import time
import zlib
from oslo_config import cfg
from oslo_log import log
import cProfile
LOG = log.getLogger(__name__)
def recursive_keypairs(d, separator='.'):
# taken from ceilometer and gnocchi
for name, value in sorted(d.items()):
if isinstance(value, dict):
for subname, subvalue in recursive_keypairs(value, separator):
yield ('%s%s%s' % (name, separator, subname), subvalue)
else:
yield name, value
def opt_exists(conf_parent, opt):
try:
return conf_parent[opt]
except cfg.NoSuchOptError:
return False
def do_cprofile(func):
def profiled_func(*args, **kwargs):
profile = cProfile.Profile()
try:
profile.enable()
result = func(*args, **kwargs)
profile.disable()
return result
finally:
profile.print_stats('cumulative')
return profiled_func
def get_portion(lst, num_of_portions, portion_index):
"""Split a list into n slices and return the i'th slice
:rtype: list
"""
# First shuffle the items to create an even distribution
# Use the same random seed to always get the same shuffle
if num_of_portions < 1 or portion_index < 0 or \
portion_index >= num_of_portions:
raise Exception('Cannot get_portion %s %s',
str(num_of_portions),
str(portion_index))
list_copy = copy.copy(lst)
random.Random(0.5).shuffle(list_copy)
portions = defaultdict(list)
portion_indexes = range(num_of_portions)
g = itertools.cycle(portion_indexes)
for curr_item in list_copy:
curr_portion = next(g)
portions[curr_portion].append(curr_item)
return portions[portion_index]
def spawn(target, *args, **kwargs):
t = threading.Thread(target=target, args=args, kwargs=kwargs)
t.daemon = True
t.start()
return t
def md5(obj):
if isinstance(obj, tuple):
obj = str([str(o) for o in obj])
if isinstance(obj, six.string_types):
if six.PY2:
return hashlib.md5(obj).hexdigest()
else:
return hashlib.md5(obj.encode('utf-8')).hexdigest()
raise Exception('Unknown object for md5 %s', str(obj))
def fmt(docstr):
"""Format a docstring for use as documentation in sample config."""
# Replace newlines with spaces, as docstrings contain literal newlines that
# should not be rendered into the sample configuration file (instead, line
# wrappings should be applied automatically).
docstr = docstr.replace('\n', ' ')
# Because it's common for docstrings to begin and end with a newline, there
# is now whitespace at the beginning and end of the documentation as a side
# effect of replacing newlines with spaces.
docstr = docstr.strip()
return docstr
def timed_method(log_results=False, warn_above_sec=-1):
def _decorator(function):
def wrapper(*args, **kwargs):
t1 = time.time()
result = function(*args, **kwargs)
t2 = time.time()
if 0 < warn_above_sec < t2 - t1:
LOG.warning(
'Function %s runtime crossed limit %s seconds.',
function.__name__, t2 - t1)
elif log_results:
LOG.info('Function %s timed %s', function.__name__, t2 - t1)
return result
return wrapper
return _decorator
def compress_obj(obj, level=9):
str_data = cPickle.dumps(obj)
data = base64.b64encode(zlib.compress(str_data, level))
return data
def decompress_obj(blob):
decoded_blob = base64.standard_b64decode(blob)
str_data = zlib.decompress(decoded_blob)
obj = cPickle.loads(str_data)
del decoded_blob
del str_data
return obj