17986301ae
Add check_time_state in utils since both Orchestrator and Worker has used. Get wait_time and period from cfg and put them in first place. Change-Id: Id04ab21dc68a2b4d0ced28354e7ddfb32160b850
194 lines
5.5 KiB
Python
194 lines
5.5 KiB
Python
# -*- 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 datetime
|
|
import sys
|
|
|
|
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'
|
|
|
|
|
|
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 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 timeutils.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:
|
|
month_start = get_month_start()
|
|
return dt2ts(month_start)
|
|
|
|
now = utcnow_ts()
|
|
next_timestamp = timestamp + period
|
|
if next_timestamp + wait_time < now:
|
|
return next_timestamp
|
|
return 0
|