9bc5c23885
Six is in use to help us to keep support for python 2.7. Since the ussuri cycle we decide to remove the python 2.7 support so we can go ahead and also remove six usage from the python code. Review process and help ----------------------- Removing six introduce a lot of changes and an huge amount of modified files To simplify reviews we decided to split changes into several patches to avoid painful reviews and avoid mistakes. To review this patch you can use the six documentation [1] to obtain help and understand choices. Additional informations ----------------------- Changes related to 'six.b(data)' [2] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ six.b [2] encode the given datas in latin-1 in python3 so I did the same things in this patch. Latin-1 is equal to iso-8859-1 [3]. This encoding is the default encoding [4] of certain descriptive HTTP headers. I suggest to keep latin-1 for the moment and to move to another encoding in a follow-up patch if needed to move to most powerful encoding (utf8). HTML4 support utf8 charset and utf8 is the default charset for HTML5 [5]. Note that this commit message is autogenerated and not necesserly contains changes related to 'six.b' [1] https://six.readthedocs.io/ [2] https://six.readthedocs.io/#six.b [3] https://docs.python.org/3/library/codecs.html#standard-encodings [4] https://www.w3schools.com/charsets/ref_html_8859.asp [5] https://www.w3schools.com/html/html_charset.asp Patch 14 of a serie of 28 patches Change-Id: Iccb743056c631f2e0728013942d764cd8b1ecfe0
107 lines
4.3 KiB
Python
107 lines
4.3 KiB
Python
#
|
|
# 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 datetime
|
|
|
|
from heat.common import exception
|
|
from heat.common.i18n import _
|
|
from heat.engine import resource
|
|
from oslo_log import log as logging
|
|
from oslo_utils import timeutils
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class CooldownMixin(object):
|
|
"""Utility class to encapsulate Cooldown related logic.
|
|
|
|
This logic includes both cooldown timestamp comparing and scaling in
|
|
progress checking.
|
|
"""
|
|
def _sanitize_cooldown(self, cooldown):
|
|
if cooldown is None:
|
|
return 0
|
|
return max(0, cooldown)
|
|
|
|
def _check_scaling_allowed(self, cooldown):
|
|
metadata = self.metadata_get()
|
|
if metadata.get('scaling_in_progress'):
|
|
LOG.info("Can not perform scaling action: resource %s "
|
|
"is already in scaling.", self.name)
|
|
reason = _('due to scaling activity')
|
|
raise resource.NoActionRequired(res_name=self.name,
|
|
reason=reason)
|
|
cooldown = self._sanitize_cooldown(cooldown)
|
|
# if both cooldown and cooldown_end not in metadata
|
|
if all(k not in metadata for k in ('cooldown', 'cooldown_end')):
|
|
# Note: this is for supporting old version cooldown checking
|
|
metadata.pop('scaling_in_progress', None)
|
|
if metadata and cooldown != 0:
|
|
last_adjust = next(iter(metadata.keys()))
|
|
if not timeutils.is_older_than(last_adjust, cooldown):
|
|
self._log_and_raise_no_action(cooldown)
|
|
|
|
elif 'cooldown_end' in metadata:
|
|
cooldown_end = next(iter(metadata['cooldown_end'].keys()))
|
|
now = timeutils.utcnow().isoformat()
|
|
if now < cooldown_end:
|
|
self._log_and_raise_no_action(cooldown)
|
|
|
|
elif cooldown != 0:
|
|
# Note: this is also for supporting old version cooldown checking
|
|
last_adjust = next(iter(metadata['cooldown'].keys()))
|
|
if not timeutils.is_older_than(last_adjust, cooldown):
|
|
self._log_and_raise_no_action(cooldown)
|
|
|
|
# Assumes _finished_scaling is called
|
|
# after the scaling operation completes
|
|
metadata['scaling_in_progress'] = True
|
|
self.metadata_set(metadata)
|
|
|
|
def _log_and_raise_no_action(self, cooldown):
|
|
LOG.info("Can not perform scaling action: "
|
|
"resource %(name)s is in cooldown (%(cooldown)s).",
|
|
{'name': self.name,
|
|
'cooldown': cooldown})
|
|
reason = _('due to cooldown, '
|
|
'cooldown %s') % cooldown
|
|
raise resource.NoActionRequired(
|
|
res_name=self.name, reason=reason)
|
|
|
|
def _finished_scaling(self, cooldown,
|
|
cooldown_reason, size_changed=True):
|
|
# If we wanted to implement the AutoScaling API like AWS does,
|
|
# we could maintain event history here, but since we only need
|
|
# the latest event for cooldown, just store that for now
|
|
metadata = self.metadata_get()
|
|
if size_changed:
|
|
cooldown = self._sanitize_cooldown(cooldown)
|
|
cooldown_end = (timeutils.utcnow() + datetime.timedelta(
|
|
seconds=cooldown)).isoformat()
|
|
if 'cooldown_end' in metadata:
|
|
cooldown_end = max(
|
|
next(iter(metadata['cooldown_end'].keys())),
|
|
cooldown_end)
|
|
metadata['cooldown_end'] = {cooldown_end: cooldown_reason}
|
|
metadata['scaling_in_progress'] = False
|
|
try:
|
|
self.metadata_set(metadata)
|
|
except exception.NotFound:
|
|
pass
|
|
|
|
def handle_metadata_reset(self):
|
|
metadata = self.metadata_get()
|
|
if 'scaling_in_progress' in metadata:
|
|
metadata['scaling_in_progress'] = False
|
|
self.metadata_set(metadata)
|