diff --git a/aodh/api/controllers/v2/alarms.py b/aodh/api/controllers/v2/alarms.py index dadbeed5f..658d9b434 100644 --- a/aodh/api/controllers/v2/alarms.py +++ b/aodh/api/controllers/v2/alarms.py @@ -30,14 +30,20 @@ from oslo_utils import timeutils from oslo_utils import uuidutils import pecan from pecan import rest -import pytz -import pytz.exceptions from stevedore import extension from urllib import parse as urlparse import wsme from wsme import types as wtypes import wsmeext.pecan as wsme_pecan +try: + import zoneinfo +except ImportError: + # zoneinfo is available in Python >= 3.9 + import pytz + import pytz.exceptions + zoneinfo = None + import aodh from aodh.api.controllers.v2 import base from aodh.api.controllers.v2 import utils as v2_utils @@ -176,9 +182,12 @@ class AlarmTimeConstraint(base.Base): @staticmethod def validate(tc): if tc.timezone: + checker = zoneinfo.ZoneInfo if zoneinfo else pytz.timezone + exc = (zoneinfo.ZoneInfoNotFoundError if zoneinfo else + pytz.exceptions.UnknownTimeZoneError) try: - pytz.timezone(tc.timezone) - except pytz.exceptions.UnknownTimeZoneError: + checker(tc.timezone) + except exc: raise base.ClientSideError(_("Timezone %s is not valid") % tc.timezone) return tc diff --git a/aodh/evaluator/__init__.py b/aodh/evaluator/__init__.py index a18719bec..c833450f7 100644 --- a/aodh/evaluator/__init__.py +++ b/aodh/evaluator/__init__.py @@ -27,9 +27,15 @@ from oslo_config import cfg from oslo_log import log from oslo_utils import timeutils from oslo_utils import uuidutils -import pytz from stevedore import extension +try: + import zoneinfo +except ImportError: + # zoneinfo is available in Python >= 3.9 + import pytz + zoneinfo = None + import aodh from aodh import coordination from aodh import keystone_client @@ -148,9 +154,13 @@ class Evaluator(object, metaclass=abc.ABCMeta): if not alarm.time_constraints: return True - now_utc = timeutils.utcnow().replace(tzinfo=pytz.utc) + now_utc = timeutils.utcnow().replace(tzinfo=datetime.timezone.utc) for tc in alarm.time_constraints: - tz = pytz.timezone(tc['timezone']) if tc['timezone'] else None + if zoneinfo: + tz = (zoneinfo.ZoneInfo(tc['timezone']) + if tc['timezone'] else None) + else: + tz = pytz.timezone(tc['timezone']) if tc['timezone'] else None now_tz = now_utc.astimezone(tz) if tz else now_utc start_cron = croniter.croniter(tc['start'], now_tz) if cls._is_exact_match(start_cron, now_tz): diff --git a/aodh/tests/unit/evaluator/test_gnocchi.py b/aodh/tests/unit/evaluator/test_gnocchi.py index f6aa127e6..838757f36 100644 --- a/aodh/tests/unit/evaluator/test_gnocchi.py +++ b/aodh/tests/unit/evaluator/test_gnocchi.py @@ -22,7 +22,13 @@ from unittest import mock from gnocchiclient import exceptions from oslo_utils import timeutils from oslo_utils import uuidutils -import pytz + +try: + import zoneinfo +except ImportError: + # zoneinfo is available in Python >= 3.9 + import pytz + zoneinfo = None from aodh.evaluator import gnocchi from aodh import messaging @@ -351,9 +357,12 @@ class TestGnocchiResourceThresholdEvaluate(TestGnocchiEvaluatorBase): 'duration': 10800, # 3 hours 'timezone': 'Europe/Ljubljana'} ] - dt = datetime.datetime(2014, 1, 1, 15, 0, 0, - tzinfo=pytz.timezone('Europe/Ljubljana')) - mock_utcnow.return_value = dt.astimezone(pytz.UTC) + if zoneinfo: + tzinfo = zoneinfo.ZoneInfo('Europe/Ljubljana') + else: + tzinfo = pytz.timezone('Europe/Ljubljana') + dt = datetime.datetime(2014, 1, 1, 15, 0, 0, tzinfo=tzinfo) + mock_utcnow.return_value = dt.astimezone(datetime.timezone.utc) self.client.metric.get_measures.return_value = [] self._evaluate_all_alarms() self._assert_all_alarms('ok') @@ -373,9 +382,12 @@ class TestGnocchiResourceThresholdEvaluate(TestGnocchiEvaluatorBase): 'duration': 10800, # 3 hours 'timezone': 'Europe/Ljubljana'} ] - dt = datetime.datetime(2014, 1, 1, 12, 0, 0, - tzinfo=pytz.timezone('Europe/Ljubljana')) - mock_utcnow.return_value = dt.astimezone(pytz.UTC) + if zoneinfo: + tzinfo = zoneinfo.ZoneInfo('Europe/Ljubljana') + else: + tzinfo = pytz.timezone('Europe/Ljubljana') + dt = datetime.datetime(2014, 1, 1, 12, 0, 0, tzinfo=tzinfo) + mock_utcnow.return_value = dt.astimezone(datetime.timezone.utc) self.client.metric.get_measures.return_value = [] self._evaluate_all_alarms() self._assert_all_alarms('insufficient data') diff --git a/requirements.txt b/requirements.txt index 551d6a0af..09f3df662 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,7 +19,7 @@ oslo.messaging>=5.2.0 # Apache-2.0 oslo.middleware>=3.22.0 # Apache-2.0 oslo.utils>=3.5.0 # Apache-2.0 python-keystoneclient>=1.6.0 -pytz>=2013.6 +pytz>=2013.6;python_version<"3.9" # MIT requests>=2.5.2 stevedore>=1.5.0 # Apache-2.0 SQLAlchemy>=1.4.1 @@ -34,3 +34,4 @@ python-observabilityclient>=0.0.4 python-octaviaclient>=1.8.0 python-dateutil>=2.8.2 # BSD python-heatclient>=1.17.0 +tzdata>=2022.4;python_version>="3.9" # MIT