def _validate_schedule(self, **schedule):

        result = {}

        # allowed parameters
        valid_parameters = [
            configuration.TIMEZONE, configuration.PERIODS, configuration.NAME,
            configuration.DESCRIPTION, configuration.OVERWRITE,
            configuration.METRICS, configuration.STOP_NEW_INSTANCES,
            configuration.USE_MAINTENANCE_WINDOW,
            configuration.SSM_MAINTENANCE_WINDOW,
            configuration.RETAINED_RUNNING, configuration.ENFORCED,
            configuration.HIBERNATE, configuration.OVERRIDE_STATUS,
            configuration.SCHEDULE_CONFIG_STACK
        ]

        for attr in schedule:

            if attr == ConfigAdmin.TYPE_ATTR:
                continue

            if attr not in valid_parameters:
                raise ValueError(
                    ERR_SCHEDULE_UNKNOWN_PARAMETER.format(
                        attr, valid_parameters))

            # skip None values
            if schedule[attr] is None or len(str(schedule[attr])) == 0:
                continue

            # check periods set
            if attr == configuration.PERIODS:
                temp = self._ensure_set(schedule[attr])
                if len(temp) > 0:
                    result[attr] = temp
                continue

            if attr in [
                    configuration.NAME, configuration.SSM_MAINTENANCE_WINDOW
            ]:
                result[attr] = schedule[attr]
                continue

            # make sure these fields are valid booleans
            if attr in [
                    configuration.METRICS, configuration.STOP_NEW_INSTANCES,
                    configuration.USE_MAINTENANCE_WINDOW,
                    configuration.RETAINED_RUNNING, configuration.HIBERNATE,
                    configuration.ENFORCED
            ]:
                bool_value = self._ensure_bool(schedule[attr])
                if bool_value is None:
                    raise ValueError(
                        ERR_SCHEDULE_INVALID_BOOLEAN.format(
                            schedule[attr], attr))
                result[attr] = bool_value
                continue

            # overwrite status, now deprecated, use PROP_OVERRIDE_STATUS instead
            if attr == configuration.OVERWRITE:

                if configuration.OVERRIDE_STATUS in schedule:
                    raise ValueError(
                        ERR_SCHEDULE_OVERWRITE_OVERRIDE_EXCLUSIVE.format(
                            configuration.OVERWRITE,
                            configuration.OVERRIDE_STATUS))

                bool_value = self._ensure_bool(schedule[attr])
                if bool_value is None:
                    raise ValueError(
                        ERR_SCHEDULE_INVALID_BOOLEAN.format(
                            schedule[attr], attr))
                result[
                    configuration.OVERRIDE_STATUS] = configuration.OVERRIDE_STATUS_RUNNING if bool_value \
                    else configuration.OVERRIDE_STATUS_STOPPED
                continue

            if attr == configuration.OVERRIDE_STATUS:

                if configuration.OVERWRITE in schedule:
                    raise ValueError(
                        ERR_SCHEDULE_OVERWRITE_OVERRIDE_EXCLUSIVE.format(
                            configuration.OVERWRITE,
                            configuration.OVERRIDE_STATUS))
                if schedule[attr] not in configuration.OVERRIDE_STATUS_VALUES:
                    raise ValueError(
                        ERR_SCHEDULE_INVALID_OVERRIDE.format(
                            schedule[attr], attr,
                            ",".join(configuration.OVERRIDE_STATUS_VALUES)))
                result[attr] = schedule[attr]
                continue

            # description
            if attr in [
                    configuration.DESCRIPTION,
                    configuration.SCHEDULE_CONFIG_STACK
            ]:
                result[attr] = schedule[attr]
                continue

            # validate timezone
            if attr == configuration.TIMEZONE:
                timezone = schedule[configuration.TIMEZONE]
                if not SchedulerConfigBuilder.is_valid_timezone(timezone):
                    raise ValueError(
                        ERR_SCHEDULE_INVALID_TIMEZONE.format(
                            timezone, configuration.TIMEZONE))
                result[attr] = timezone

        # name is mandatory
        if configuration.NAME not in result:
            raise ValueError(ERR_SCHEDULE_NAME_MISSING)

        # if there is no overwrite there must be at least one period
        if configuration.OVERRIDE_STATUS not in schedule:
            if configuration.PERIODS not in schedule or len(
                    schedule[configuration.PERIODS]) == 0:
                raise ValueError(ERR_SCHEDULE_NO_PERIOD)

        # validate if periods are in configuration
        if configuration.PERIODS in result:
            # get list of all configured periods
            periods = [p[configuration.NAME] for p in self._list_periods()]
            for period in result[configuration.PERIODS]:
                if period.split(
                        configuration.INSTANCE_TYPE_SEP)[0] not in periods:
                    raise ValueError(
                        ERR_SCHEDULE_PERIOD_DOES_NOT_EXISTS.format(period))

        # indicates this s a schedule
        result[ConfigAdmin.TYPE_ATTR] = "schedule"

        return result
    def update_config(self, **settings):
        """
        Updates configuration, validates new values
        :param settings: settings values
        :return: updated values
        """
        valid_attributes = [
            configuration.METRICS, configuration.CROSS_ACCOUNT_ROLES,
            configuration.DEFAULT_TIMEZONE, configuration.REGIONS,
            configuration.SCHEDULE_LAMBDA_ACCOUNT, configuration.TAGNAME,
            configuration.TRACE, ConfigAdmin.TYPE_ATTR,
            configuration.SCHEDULED_SERVICES, configuration.SCHEDULE_CLUSTERS,
            configuration.CREATE_RDS_SNAPSHOT, configuration.STARTED_TAGS,
            configuration.STOPPED_TAGS
        ]

        checked_settings = {}

        for attr in settings:

            if attr in [ConfigAdmin.TYPE_ATTR, configuration.NAME]:
                continue

            # only valid fields
            if attr not in valid_attributes:
                raise ValueError(ERR_UPDATE_UNKNOWN_PARAMETER.format(attr))

            # remove None fields
            if settings[attr] is None:
                continue

            # remove empty strings
            if len(str(settings[attr])) == 0:
                continue

            # make sure these fields are set as sets
            if attr in [
                    configuration.REGIONS, configuration.CROSS_ACCOUNT_ROLES,
                    configuration.SCHEDULED_SERVICES
            ]:
                temp = self._ensure_set(settings[attr])
                if len(settings[attr]) > 0:
                    checked_settings[attr] = temp

                continue

            # make sure these fields are valid booleans
            if attr in [
                    configuration.METRICS, configuration.TRACE,
                    configuration.SCHEDULE_LAMBDA_ACCOUNT,
                    configuration.CREATE_RDS_SNAPSHOT,
                    configuration.SCHEDULE_CLUSTERS
            ]:
                bool_value = self._ensure_bool(settings[attr])
                if bool_value is None:
                    raise ValueError(
                        ERR_UPDATE_INVALID_BOOL_PARAM.format(
                            settings[attr], attr))
                checked_settings[attr] = bool_value
                continue

            # validate timezone
            if attr == configuration.DEFAULT_TIMEZONE:
                default_tz = settings[configuration.DEFAULT_TIMEZONE]
                if not SchedulerConfigBuilder.is_valid_timezone(default_tz):
                    raise ValueError(
                        ERR_UPDATE_INVALID_TZ_PARAMETER.format(
                            default_tz, configuration.DEFAULT_TIMEZONE))
                checked_settings[attr] = default_tz
                continue

            checked_settings[attr] = settings[attr]

            if configuration.TAGNAME not in settings:
                raise ValueError(ERR_UPDATE_TAGNAME_EMPTY)

            for service in settings.get(configuration.SCHEDULED_SERVICES, []):
                if service not in ConfigAdmin.SUPPORTED_SERVICES:
                    raise ValueError(
                        ERR_UPDATE_UNKNOWN_SERVICE.format(service))

        # keys for config item
        checked_settings[ConfigAdmin.TYPE_ATTR] = "config"
        checked_settings[configuration.NAME] = "scheduler"

        self._table.put_item_with_retries(Item=checked_settings)

        return ConfigAdmin._for_output(checked_settings)