/usr/lib/python3/dist-packages/cloudkitty/utils.py is in python3-cloudkitty 7.0.0-4.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | # -*- coding: utf-8 -*-
# Copyright 2014 Objectif Libre
#
# 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.
#
# @author: Stéphane Albert
#
"""
Time calculations functions
We're mostly using oslo_utils for time calculations but we're encapsulating it
to ease maintenance in case of library modifications.
"""
import calendar
import contextlib
import datetime
import decimal
import fractions
import shutil
import six
import sys
import tempfile
import yaml
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import timeutils
from six import moves
from stevedore import extension
_ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f'
_ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
LOG = logging.getLogger(__name__)
collect_opts = [
cfg.StrOpt('collector',
default='gnocchi',
deprecated_for_removal=True,
help='Data collector.'),
cfg.IntOpt('window',
default=1800,
deprecated_for_removal=True,
help='Number of samples to collect per call.'),
cfg.IntOpt('period',
default=3600,
deprecated_for_removal=True,
help='Rating period in seconds.'),
cfg.IntOpt('wait_periods',
default=2,
deprecated_for_removal=True,
help='Wait for N periods before collecting new data.'),
cfg.ListOpt('services',
default=[
'compute',
'volume',
'network.bw.in',
'network.bw.out',
'network.floating',
'image',
],
deprecated_for_removal=True,
help='Services to monitor.'),
cfg.StrOpt('metrics_conf',
default='/etc/cloudkitty/metrics.yml',
help='Metrology configuration file.'),
]
CONF = cfg.CONF
CONF.register_opts(collect_opts, 'collect')
def isotime(at=None, subsecond=False):
"""Stringify time in ISO 8601 format."""
# Python provides a similar instance method for datetime.datetime objects
# called isoformat(). The format of the strings generated by isoformat()
# have a couple of problems:
# 1) The strings generated by isotime are used in tokens and other public
# APIs that we can't change without a deprecation period. The strings
# generated by isoformat are not the same format, so we can't just
# change to it.
# 2) The strings generated by isoformat do not include the microseconds if
# the value happens to be 0. This will likely show up as random failures
# as parsers may be written to always expect microseconds, and it will
# parse correctly most of the time.
if not at:
at = timeutils.utcnow()
st = at.strftime(_ISO8601_TIME_FORMAT
if not subsecond
else _ISO8601_TIME_FORMAT_SUBSECOND)
tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC'
st += ('Z' if tz == 'UTC' else tz)
return st
def iso8601_from_timestamp(timestamp, microsecond=False):
"""Returns an iso8601 formatted date from timestamp"""
# Python provides a similar instance method for datetime.datetime
# objects called isoformat() and utcfromtimestamp(). The format
# of the strings generated by isoformat() and utcfromtimestamp()
# have a couple of problems:
# 1) The method iso8601_from_timestamp in oslo_utils is realized
# by isotime, the strings generated by isotime are used in
# tokens and other public APIs that we can't change without a
# deprecation period. The strings generated by isoformat are
# not the same format, so we can't just change to it.
# 2) The strings generated by isoformat() and utcfromtimestamp()
# do not include the microseconds if the value happens to be 0.
# This will likely show up as random failures as parsers may be
# written to always expect microseconds, and it will parse
# correctly most of the time.
return isotime(datetime.datetime.utcfromtimestamp(timestamp), microsecond)
def dt2ts(orig_dt):
"""Translate a datetime into a timestamp."""
return calendar.timegm(orig_dt.timetuple())
def iso2dt(iso_date):
"""iso8601 format to datetime."""
iso_dt = timeutils.parse_isotime(iso_date)
trans_dt = timeutils.normalize_time(iso_dt)
return trans_dt
def ts2dt(timestamp):
"""timestamp to datetime format."""
if not isinstance(timestamp, float):
timestamp = float(timestamp)
return datetime.datetime.utcfromtimestamp(timestamp)
def ts2iso(timestamp):
"""timestamp to is8601 format."""
if not isinstance(timestamp, float):
timestamp = float(timestamp)
return iso8601_from_timestamp(timestamp)
def dt2iso(orig_dt):
"""datetime to is8601 format."""
return isotime(orig_dt)
def utcnow():
"""Returns a datetime for the current utc time."""
return timeutils.utcnow()
def utcnow_ts():
"""Returns a timestamp for the current utc time."""
return timeutils.utcnow_ts()
def get_month_days(dt):
return calendar.monthrange(dt.year, dt.month)[1]
def add_days(base_dt, days, stay_on_month=True):
if stay_on_month:
max_days = get_month_days(base_dt)
if days > max_days:
return get_month_end(base_dt)
return base_dt + datetime.timedelta(days=days)
def add_month(dt, stay_on_month=True):
next_month = get_next_month(dt)
return add_days(next_month, dt.day, stay_on_month)
def sub_month(dt, stay_on_month=True):
prev_month = get_last_month(dt)
return add_days(prev_month, dt.day, stay_on_month)
def get_month_start(dt=None):
if not dt:
dt = utcnow()
month_start = datetime.datetime(dt.year, dt.month, 1)
return month_start
def get_month_start_timestamp(dt=None):
return dt2ts(get_month_start(dt))
def get_month_end(dt=None):
month_start = get_month_start(dt)
days_of_month = get_month_days(month_start)
month_end = month_start.replace(day=days_of_month)
return month_end
def get_last_month(dt=None):
if not dt:
dt = utcnow()
month_end = get_month_start(dt) - datetime.timedelta(days=1)
return get_month_start(month_end)
def get_next_month(dt=None):
month_end = get_month_end(dt)
next_month = month_end + datetime.timedelta(days=1)
return next_month
def get_next_month_timestamp(dt=None):
return dt2ts(get_next_month(dt))
def refresh_stevedore(namespace=None):
"""Trigger reload of entry points.
Useful to have dynamic loading/unloading of stevedore modules.
"""
# NOTE(sheeprine): pkg_resources doesn't support reload on python3 due to
# defining basestring which is still there on reload hence executing
# python2 related code.
try:
del sys.modules['pkg_resources'].basestring
except AttributeError:
# python2, do nothing
pass
# Force working_set reload
moves.reload_module(sys.modules['pkg_resources'])
# Clear stevedore cache
cache = extension.ExtensionManager.ENTRY_POINT_CACHE
if namespace:
if namespace in cache:
del cache[namespace]
else:
cache.clear()
def check_time_state(timestamp=None, period=0, wait_time=0):
if not timestamp:
return get_month_start_timestamp()
now = utcnow_ts()
next_timestamp = timestamp + period
if next_timestamp + wait_time < now:
return next_timestamp
return 0
def get_metrics_conf(conf_path):
"""Return loaded yaml metrology configuration.
In case of empty /etc/cloudkitty folder,
a fallback is done on the former deprecated oslo config method.
"""
res = None
try:
with open(conf_path) as conf:
res = yaml.safe_load(conf)
res = res[0]
except Exception as exc:
LOG.warning('Error when trying to retrieve yaml metrology conf file.')
LOG.warning(exc)
LOG.warning('Fallback on the deprecated oslo config method.')
try:
res = {key: val for key, val in CONF.collect.items()}
except Exception as exc:
err_msg = 'Error when trying to retrieve ' \
'deprecated oslo config method.'
LOG.error(err_msg)
LOG.error(exc)
return res
@contextlib.contextmanager
def tempdir(**kwargs):
tmpdir = tempfile.mkdtemp(**kwargs)
try:
yield tmpdir
finally:
try:
shutil.rmtree(tmpdir)
except OSError as e:
LOG.debug('Could not remove tmpdir: %s',
six.text_type(e))
def num2decimal(num):
"""Converts a number into a decimal.Decimal.
The number may be an str in float, int or fraction format;
a fraction.Fraction, a decimal.Decimal, an int or a float.
"""
if isinstance(num, decimal.Decimal):
return num
if isinstance(num, str):
if '/' in num:
num = float(fractions.Fraction(num))
if isinstance(num, fractions.Fraction):
num = float(num)
return decimal.Decimal(num)
def convert_unit(value, factor=1, offset=0):
"""Return converted value depending on the provided factor and offset."""
return num2decimal(value) * num2decimal(factor) + num2decimal(offset)
def flat_dict(item, parent=None):
"""Returns a flat version of the nested dict item"""
if not parent:
parent = dict()
for k, val in item.items():
if isinstance(val, dict):
parent = flat_dict(val, parent)
else:
parent[k] = val
return parent
|