Add time format check in API

Change-Id: Idca9266d503caa8096ca8d4265e813b4244be653
Closes-Bug: #1727672
This commit is contained in:
jiaopengju 2017-10-29 17:34:42 +08:00
parent d853f172d6
commit c59f1aff8d
10 changed files with 81 additions and 50 deletions

View File

@ -13,6 +13,8 @@
"""The triggers api."""
from datetime import datetime
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import uuidutils
from webob import exc
@ -26,6 +28,7 @@ from karbor.policies import triggers as trigger_policy
from karbor.services.operationengine import api as operationengine_api
from karbor import utils
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
@ -94,9 +97,18 @@ class TriggersController(wsgi.Controller):
if not trigger_name or not trigger_type or not trigger_property:
msg = _("Trigger name or type or property is not provided.")
raise exc.HTTPBadRequest(explanation=msg)
self.validate_name_and_description(trigger_info)
trigger_format = trigger_property.get('format', None)
if trigger_format != CONF.time_format:
msg = _("Trigger format(%s) is invalid.") % trigger_format
raise exc.HTTPBadRequest(explanation=msg)
trigger_pattern = trigger_property.get('pattern', None)
if CONF.time_format == 'calendar':
utils.validate_calendar_time_format(trigger_pattern)
if CONF.time_format == 'crontab':
utils.validate_crontab_time_format(trigger_pattern)
trigger_property.setdefault(
'start_time', datetime.utcnow().replace(microsecond=0))
trigger_definition = {

View File

@ -73,6 +73,14 @@ global_opts = [
CONF.register_opts(global_opts)
global_trigger_opts = [
cfg.StrOpt('time_format',
default='calendar',
choices=['crontab', 'calendar'],
help='The type of time format which is used to compute time')
]
CONF.register_opts(global_trigger_opts)
service_client_opts = [
cfg.StrOpt('service_name',

View File

@ -65,6 +65,7 @@ _opts = [
karbor.common.config.core_opts,
karbor.common.config.debug_opts,
karbor.common.config.global_opts,
karbor.common.config.global_trigger_opts,
karbor.api.common.api_common_opts,
karbor.api.v1.protectables.query_instance_filters_opts,
karbor.api.v1.providers.query_provider_filters_opts,

View File

@ -39,10 +39,6 @@ time_trigger_opts = [
default=1800,
help='The maximum window time'),
cfg.StrOpt('time_format',
default='calendar',
choices=['crontab', 'calendar'],
help='The type of time format which is used to compute time'),
cfg.IntOpt('trigger_poll_interval',
default=15,
help='Interval, in seconds, in which Karbor will poll for '

View File

@ -14,13 +14,11 @@ import os
from datetime import timedelta
from dateutil import rrule
from icalendar import Calendar
from oslo_serialization import jsonutils
from oslo_utils import timeutils
from karbor import exception
from karbor.i18n import _
from karbor.services.operationengine.engine.triggers.timetrigger import \
timeformats
from karbor import utils
RATE = 2
@ -46,21 +44,12 @@ class ICal(timeformats.TimeFormat):
def __init__(self, start_time, pattern):
super(ICal, self).__init__(start_time, pattern)
cal = Calendar.from_ical(self._decode_calendar_pattern(pattern))
cal = Calendar.from_ical(utils.decode_calendar_pattern(pattern))
vevent = cal.walk('VEVENT')[0]
self.dtstart = start_time
self.min_freq = self._get_min_freq(vevent)
self.rrule_obj = self._get_rrule_obj(vevent, start_time)
@staticmethod
def _decode_calendar_pattern(pattern):
try:
pattern.index('\\')
pattern_dict = jsonutils.loads('{"pattern": "%s"}' % pattern)
return pattern_dict["pattern"]
except Exception:
return pattern
@staticmethod
def _get_rrule_obj(vevent, dtstart):
rrules = vevent.get('RRULE')
@ -85,25 +74,7 @@ class ICal(timeformats.TimeFormat):
:param pattern: The pattern of the icalendar time
"""
try:
cal_obj = Calendar.from_ical(cls._decode_calendar_pattern(pattern))
except Exception:
msg = (_("The trigger pattern(%s) is invalid") % pattern)
raise exception.InvalidInput(msg)
try:
vevent = cal_obj.walk('VEVENT')[0]
except Exception:
msg = (_("The trigger pattern(%s) must include less than one "
"VEVENT component") % pattern)
raise exception.InvalidInput(msg)
try:
vevent.decoded('RRULE')
except Exception:
msg = (_("The first VEVENT component of trigger pattern(%s) must "
"include less than one RRULE property") % pattern)
raise exception.InvalidInput(msg)
utils.validate_calendar_time_format(pattern)
def compute_next_time(self, current_time):
"""Compute next time

View File

@ -14,10 +14,9 @@ from croniter import croniter
from datetime import datetime
from oslo_utils import timeutils
from karbor import exception
from karbor.i18n import _
from karbor.services.operationengine.engine.triggers.timetrigger import \
timeformats
from karbor import utils
class Crontab(timeformats.TimeFormat):
@ -29,15 +28,7 @@ class Crontab(timeformats.TimeFormat):
@classmethod
def check_time_format(cls, pattern):
if not pattern:
msg = (_("The trigger pattern is None"))
raise exception.InvalidInput(msg)
try:
croniter(pattern)
except Exception:
msg = (_("The trigger pattern(%s) is invalid") % pattern)
raise exception.InvalidInput(msg)
utils.validate_crontab_time_format(pattern)
def compute_next_time(self, current_time):
time = current_time if current_time >= self._start_time else (

View File

@ -24,7 +24,10 @@ from karbor.tests.fullstack import karbor_objects as objects
from karbor.tests.fullstack import utils
pattern = "BEGIN:VEVENT\nRRULE:FREQ=WEEKLY;INTERVAL=1;\nEND:VEVENT"
DEFAULT_PROPERTY = {'pattern': pattern}
DEFAULT_PROPERTY = {
'pattern': pattern,
'format': 'calendar'
}
class ScheduledOperationsTest(karbor_base.KarborBaseTest):

View File

@ -11,6 +11,7 @@
# under the License.
import mock
from oslo_config import cfg
from oslo_utils import uuidutils
from webob import exc
@ -51,6 +52,7 @@ class ScheduledOperationApiTest(base.TestCase):
self.ctxt = context.RequestContext('demo', 'fakeproject', True)
self.req = fakes.HTTPRequest.blank('/v1/scheduled_operations')
cfg.CONF.set_default('time_format', 'crontab')
trigger = self._create_trigger()
self._plan = self._create_plan(uuidutils.generate_uuid())
self.default_create_operation_param = {

View File

@ -9,7 +9,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from webob import exc
from karbor.api.v1 import triggers as trigger_api
@ -45,6 +45,7 @@ class TriggerApiTest(base.TestCase):
self.controller.operationengine_api = FakeRemoteOperationApi()
self.ctxt = context.RequestContext('demo', 'fakeproject',
True)
cfg.CONF.set_default('time_format', 'crontab')
self.req = fakes.HTTPRequest.blank('/v1/triggers')
self.default_create_trigger_param = {
"name": "123",

View File

@ -19,9 +19,12 @@ import six
import tempfile
import webob.exc
from croniter import croniter
from icalendar import Calendar
from keystoneclient import discover as ks_discover
from oslo_config import cfg
from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import importutils
from oslo_utils import strutils
from oslo_utils import timeutils
@ -34,6 +37,49 @@ CONF = cfg.CONF
LOG = logging.getLogger(__name__)
def decode_calendar_pattern(pattern):
try:
pattern.index('\\')
pattern_dict = jsonutils.loads('{"pattern": "%s"}' % pattern)
return pattern_dict["pattern"]
except Exception:
return pattern
def validate_calendar_time_format(pattern):
try:
cal_obj = Calendar.from_ical(decode_calendar_pattern(pattern))
except Exception:
msg = (_("The trigger pattern(%s) is invalid") % pattern)
raise exception.InvalidInput(msg)
try:
vevent = cal_obj.walk('VEVENT')[0]
except Exception:
msg = (_("The trigger pattern(%s) must include less than one "
"VEVENT component") % pattern)
raise exception.InvalidInput(msg)
try:
vevent.decoded('RRULE')
except Exception:
msg = (_("The first VEVENT component of trigger pattern(%s) must "
"include less than one RRULE property") % pattern)
raise exception.InvalidInput(msg)
def validate_crontab_time_format(pattern):
if not pattern:
msg = (_("The trigger pattern is None"))
raise exception.InvalidInput(msg)
try:
croniter(pattern)
except Exception:
msg = (_("The trigger pattern(%s) is invalid") % pattern)
raise exception.InvalidInput(msg)
def find_config(config_path):
"""Find a configuration file using the given hint.