| #!/usr/bin/env python |
| # |
| # Copyright 2007 Google Inc. |
| # |
| # 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. |
| # |
| |
| |
| |
| |
| """CronInfo tools. |
| |
| A library for working with CronInfo records, describing cron entries for an |
| application. Supports loading the records from yaml. |
| """ |
| |
| |
| |
| |
| |
| |
| |
| import logging |
| import sys |
| import traceback |
| |
| try: |
| import pytz |
| except ImportError: |
| pytz = None |
| |
| from google.appengine.cron import groc |
| from google.appengine.cron import groctimespecification |
| from google.appengine.api import appinfo |
| from google.appengine.api import validation |
| from google.appengine.api import yaml_builder |
| from google.appengine.api import yaml_listener |
| from google.appengine.api import yaml_object |
| |
| _URL_REGEX = r'^/.*$' |
| _TIMEZONE_REGEX = r'^.{0,100}$' |
| _DESCRIPTION_REGEX = ur'^.{0,499}$' |
| |
| |
| SERVER_ID_RE_STRING = r'(?!-)[a-z\d\-]{1,63}' |
| |
| |
| SERVER_VERSION_RE_STRING = r'(?!-)[a-z\d\-]{1,100}' |
| _VERSION_REGEX = r'^(?:(?:(%s):)?)(%s)$' % (SERVER_ID_RE_STRING, |
| SERVER_VERSION_RE_STRING) |
| |
| |
| |
| |
| class GrocValidator(validation.Validator): |
| """Checks that a schedule is in valid groc format.""" |
| |
| def Validate(self, value, key=None): |
| """Validates a schedule.""" |
| if value is None: |
| raise validation.MissingAttribute('schedule must be specified') |
| if not isinstance(value, basestring): |
| raise TypeError('schedule must be a string, not \'%r\''%type(value)) |
| try: |
| groctimespecification.GrocTimeSpecification(value) |
| except groc.GrocException, e: |
| raise validation.ValidationError('schedule \'%s\' failed to parse: %s'%( |
| value, e.args[0])) |
| return value |
| |
| |
| class TimezoneValidator(validation.Validator): |
| """Checks that a timezone can be correctly parsed and is known.""" |
| |
| def Validate(self, value, key=None): |
| """Validates a timezone.""" |
| if value is None: |
| |
| return |
| if not isinstance(value, basestring): |
| raise TypeError('timezone must be a string, not \'%r\'' % type(value)) |
| if pytz is None: |
| |
| return value |
| try: |
| pytz.timezone(value) |
| except pytz.UnknownTimeZoneError: |
| raise validation.ValidationError('timezone \'%s\' is unknown' % value) |
| except IOError: |
| |
| |
| return value |
| except: |
| |
| |
| unused_e, v, t = sys.exc_info() |
| logging.warning('pytz raised an unexpected error: %s.\n' % (v) + |
| 'Traceback:\n' + '\n'.join(traceback.format_tb(t))) |
| raise |
| return value |
| |
| |
| CRON = 'cron' |
| |
| URL = 'url' |
| SCHEDULE = 'schedule' |
| TIMEZONE = 'timezone' |
| DESCRIPTION = 'description' |
| TARGET = 'target' |
| |
| |
| class MalformedCronfigurationFile(Exception): |
| """Configuration file for Cron is malformed.""" |
| pass |
| |
| |
| class CronEntry(validation.Validated): |
| """A cron entry describes a single cron job.""" |
| ATTRIBUTES = { |
| URL: _URL_REGEX, |
| SCHEDULE: GrocValidator(), |
| TIMEZONE: TimezoneValidator(), |
| DESCRIPTION: validation.Optional(_DESCRIPTION_REGEX), |
| TARGET: validation.Optional(_VERSION_REGEX), |
| } |
| |
| |
| class CronInfoExternal(validation.Validated): |
| """CronInfoExternal describes all cron entries for an application.""" |
| ATTRIBUTES = { |
| appinfo.APPLICATION: validation.Optional(appinfo.APPLICATION_RE_STRING), |
| CRON: validation.Optional(validation.Repeated(CronEntry)) |
| } |
| |
| |
| def LoadSingleCron(cron_info, open_fn=None): |
| """Load a cron.yaml file or string and return a CronInfoExternal object.""" |
| builder = yaml_object.ObjectBuilder(CronInfoExternal) |
| handler = yaml_builder.BuilderHandler(builder) |
| listener = yaml_listener.EventListener(handler) |
| listener.Parse(cron_info) |
| |
| cron_info = handler.GetResults() |
| if len(cron_info) < 1: |
| raise MalformedCronfigurationFile('Empty cron configuration.') |
| if len(cron_info) > 1: |
| raise MalformedCronfigurationFile('Multiple cron sections ' |
| 'in configuration.') |
| return cron_info[0] |