def _update_settings(self):
        try:
            admin = ConfigAdmin(logger=self._logger, context=self.context)
            settings = admin.update_config(
                default_timezone=self.default_timezone,
                scheduled_services=self.scheduled_services,
                schedule_clusters=self.schedule_clusters,
                create_rds_snapshot=self.create_rds_snapshot,
                tagname=self.tagname,
                regions=self.regions,
                cross_account_roles=self.cross_account_roles,
                schedule_lambda_account=self.schedule_lambda_account.lower() ==
                "true",
                use_metrics=self.use_metrics.lower() == "true",
                trace=self.trace.lower() == "true",
                started_tags=self.started_tags,
                stopped_tags=self.stopped_tags)

            self._logger.info(INF_CONFIG_SET, str(settings))

        except Exception as ex:
            self._logger.info(ERR_SETTING_CONFIG, ex)
            return False

        return True
    def __init__(self, event, context):
        """
        Initializes instance
        :param event: CFN event
        :param context: Lambda context
        """
        CustomResource.__init__(self, event, context)
        self.number_of_periods = 0

        classname = self.__class__.__name__
        dt = datetime.utcnow()
        logstream = LOG_STREAM.format(classname, dt.year, dt.month, dt.day)
        self._logger = Logger(logstream=logstream, buffersize=20, context=context)

        self._admin = ConfigAdmin(logger=self._logger, context=context)
Exemple #3
0
    def handle_request(self):
        """
        Handles the event
        :return: result of handling the event, result send back to REST admin api
        """

        # Setup logging
        classname = self.__class__.__name__
        dt = datetime.utcnow()
        logstream = LOG_STREAM.format(classname, dt.year, dt.month, dt.day)
        self._logger = Logger(logstream=logstream, buffersize=20, context=self._context)

        with Logger(logstream=logstream, buffersize=20, context=self._context) as logger:

            logger.info("Handler {} : Received request {}", self.__class__.__name__, json.dumps(self._event))

            # get access to admin api
            admin = ConfigAdmin(logger=logger, context=self._context)

            # get api action and map it to a function in the admin API
            fn = getattr(admin, self.action)
            if fn is None:
                raise ValueError("Action {} does not exist".format(self.action))

            # build parameters for admin API call
            temp = self._event.get("params", {})
            params = {p: temp[p] for p in temp}
            if "name" in self._event:
                params["name"] = self._event["name"]
            logger.info("Calling \"{}\" with parameters {}", fn.__name__, params)
            # call the admin API
            result = fn(**params)
            logger.info("Call result is {}", result)
            return result
class ScheduleResourceHandler(CustomResource):
    """
    Implements custom resource handler for CFN support for schedules/periods
    """

    def __init__(self, event, context):
        """
        Initializes instance
        :param event: CFN event
        :param context: Lambda context
        """
        CustomResource.__init__(self, event, context)
        self.number_of_periods = 0

        classname = self.__class__.__name__
        dt = datetime.utcnow()
        logstream = LOG_STREAM.format(classname, dt.year, dt.month, dt.day)
        self._logger = Logger(logstream=logstream, buffersize=20, context=context)

        self._admin = ConfigAdmin(logger=self._logger, context=context)

    @staticmethod
    def is_handling_request(event):
        """
        Tests if this handler handles the event
        :param event: Tested event
        :return: True if this is custom resource event for configuring schedule/periods
        """
        return event.get("StackId") is not None and event.get("ResourceType") == "Custom::ServiceInstanceSchedule"

    @classmethod
    def _set_if_specified(cls, source, source_name, dest, dest_name=None, default=None):
        val = source.get(source_name, default)
        if val is not None:
            dest[dest_name if dest_name is not None else source_name] = val

    @property
    def _schedule_resource_name(self):
        name = self.resource_properties.get(PROP_NAME,None)
        if name is None:
            name = self.logical_resource_id
        if str(self.resource_properties.get(PROP_NO_STACK_PREFIX, "False")).lower() == "true":
            return name
        return "{}-{}".format(self.stack_name, name)

    def _create_period(self, period):

        self.number_of_periods += 1

        period_name = PERIOD_NAME.format(self._schedule_resource_name, self.number_of_periods)
        self._logger.info(INF_PERIOD_NAME, period_name)

        for p in period:
            if p not in VALID_PERIOD_PROPERTIES:
                raise ValueError(ERR_INVALID_PERIOD_PROPERTY.format(p, ", ".join(VALID_PERIOD_PROPERTIES)))

        create_period_args = {

            configuration.NAME: period_name
        }

        self._set_if_specified(period, PROP_BEGIN_TIME, create_period_args, configuration.BEGINTIME)
        self._set_if_specified(period, PROP_END_TIME, create_period_args, configuration.ENDTIME)
        self._set_if_specified(period, PROP_MONTH_DAYS, create_period_args, configuration.MONTHDAYS)
        self._set_if_specified(period, PROP_MONTHS, create_period_args, configuration.MONTHS)
        self._set_if_specified(period, PROP_WEEKDAYS, create_period_args, configuration.WEEKDAYS)

        create_period_args[configuration.DESCRIPTION] = PERIOD_DESCRIPTION.format(self._schedule_resource_name,
                                                                                  self.number_of_periods)
        description_config = period.get(PROP_DESCRIPTION, None)
        if description_config is not None:
            create_period_args[configuration.DESCRIPTION] = "{}, {}".format(description_config,
                                                                            create_period_args[configuration.DESCRIPTION])

        period = self._admin.create_period(**create_period_args)

        self._logger.info(INF_PERIOD_CREATED, safe_json(period, 3))

        return period_name, period.get(PROP_INSTANCE_TYPE, None)

    def _delete_periods(self):
        i = 0
        while True:
            i += 1
            name = PERIOD_NAME.format(self._schedule_resource_name, i)
            period = self._admin.delete_period(name, exception_if_not_exists=False)
            if period is None:
                break
            else:
                self._logger.info(INF_DELETED_PERIOD, name)

    def _create_schedule(self):

        self._logger.info(INF_SCHEDULE_NAME, self._schedule_resource_name)

        create_schedule_args = {
            configuration.NAME: self._schedule_resource_name
        }

        ps = self.resource_properties

        for pr in ps:

            # fix for typo in older release, fix parameter if old version with typo is used for compatibility
            if pr == "UseMaintenaceWindow":
                pr = PROP_USE_MAINTENANCE_WINDOW

            if pr not in VALID_SCHEDULE_PROPERTIES:
                raise ValueError(ERR_INVALID_SCHEDULE_PROPERTY.format(pr, ", ".join(VALID_SCHEDULE_PROPERTIES)))

        self._set_if_specified(ps, PROP_METRICS, create_schedule_args, dest_name=configuration.METRICS)
        self._set_if_specified(ps, PROP_OVERWRITE, create_schedule_args, dest_name=configuration.OVERWRITE)
        self._set_if_specified(ps, PROP_OVERRIDE_STATUS, create_schedule_args, dest_name=configuration.OVERRIDE_STATUS)
        self._set_if_specified(ps, PROP_USE_MAINTENANCE_WINDOW, create_schedule_args,
                               dest_name=configuration.USE_MAINTENANCE_WINDOW)
        self._set_if_specified(ps, PROP_ENFORCED, create_schedule_args, dest_name=configuration.ENFORCED, default=False)
        self._set_if_specified(ps, PROP_HIBERNATE, create_schedule_args, dest_name=configuration.HIBERNATE, default=False)
        self._set_if_specified(ps, PROP_RETAIN_RUNNING, create_schedule_args, dest_name=configuration.RETAINED_RUNNING,
                               default=False)
        self._set_if_specified(ps, PROP_STOP_NEW, create_schedule_args, dest_name=configuration.STOP_NEW_INSTANCES, default=True)
        self._set_if_specified(ps, PROP_TIMEZONE, create_schedule_args, dest_name=configuration.TIMEZONE, default="UTC")
        self._set_if_specified(ps, PROP_DESCRIPTION, create_schedule_args, dest_name=configuration.DESCRIPTION)
        self._set_if_specified(ps, PROP_SSM_MAINTENANCE_WINDOW, create_schedule_args, dest_name=configuration.SSM_MAINTENANCE_WINDOW)

        create_schedule_args[configuration.SCHEDULE_CONFIG_STACK] = self.stack_id

        periods = []
        try:
            self.number_of_periods = 0
            for period in ps.get(PROP_PERIODS, []):
                period_name, instance_type = self._create_period(period)
                if instance_type is not None:
                    period_name = "{}{}{}".format(period_name, configuration.INSTANCE_TYPE_SEP, instance_type)
                periods.append(period_name)

            create_schedule_args[configuration.PERIODS] = periods
            schedule = self._admin.create_schedule(**create_schedule_args)
            self.physical_resource_id = self._schedule_resource_name

            self._logger.info(INF_SCHEDULE_CREATED, safe_json(schedule, 3))
        except Exception as ex:
            self._delete_periods()
            raise ex

    def _delete_schedule(self):
        schedule = self._admin.delete_schedule(name=self._schedule_resource_name, exception_if_not_exists=False)
        if schedule is not None:
            self._delete_periods()
            self._logger.info(INF_DELETE_SCHEDULE, self._schedule_resource_name)

    def _update_schedule(self):
        self._delete_schedule()
        self._create_schedule()

    def _create_request(self):
        try:
            self._create_schedule()
            return True
        except Exception as ex:
            self._logger.error(ex)
            return False
        finally:
            self._logger.flush()

    def _update_request(self):
        try:
            self._update_schedule()
            return True
        except Exception as ex:
            self._logger.error(ex)
            return False
        finally:
            self._logger.flush()

            # handles Delete request from CloudFormation

    def _delete_request(self):
        try:
            self._delete_schedule()
            return True
        except Exception as ex:
            self._logger.error(ex)
            return False
        finally:
            self._logger.flush()
    def _create_sample_schemas(self):

        try:
            admin = ConfigAdmin(logger=self._logger, context=self.context)

            admin.create_period(**demo_data.PERIOD_WORKING_DAYS)
            admin.create_period(**demo_data.PERIOD_WEEKENDS)
            admin.create_period(**demo_data.PERIOD_OFFICE_HOURS)
            admin.create_period(**demo_data.PERIOD_FIRST_MONDAY_IN_QUARTER)

            admin.create_schedule(**demo_data.SCHEDULE_SEATTLE_OFFICE_HOURS)
            admin.create_schedule(**demo_data.SCHEDULE_UK_OFFICE_HOURS)
            admin.create_schedule(**demo_data.SCHEDULE_STOPPED)
            admin.create_schedule(**demo_data.SCHEDULE_RUNNING)
            admin.create_schedule(**demo_data.SCHEDULE_SCALING)

        except Exception as ex:
            self._logger.error(
                "Error creating sample schedules and periods {}".format(ex))
Exemple #6
0
    def handle_request(self):
        """
        Handles the event
        :return: result of handling the event, result send back to REST admin api
        """
        def snake_to_pascal_case(s):
            converted = ""
            s = s.strip("_").capitalize()
            i = 0

            while i < len(s):
                if s[i] == "_":
                    i += 1
                    converted += s[i].upper()
                else:
                    converted += s[i]
                i += 1

            return converted

        # noinspection PyShadowingNames
        def dict_to_pascal_case(d):

            d_result = {}

            if isinstance(d, dict):
                for i in d:
                    key = snake_to_pascal_case(i)
                    d_result[key] = dict_to_pascal_case(d[i])
                return d_result

            elif isinstance(d, list):
                return [dict_to_pascal_case(l) for l in d]

            return d

        try:
            self._logger.info("Handler {} : Received CLI request {}",
                              self.__class__.__name__, json.dumps(self._event))

            # get access to admin api
            admin = ConfigAdmin(logger=self._logger, context=self._context)

            # get api action and map it to a function in the admin API
            fn_name = self.commands.get(self.action, None)
            if fn_name is None:
                raise ValueError("Command {} does not exist".format(
                    self.action))
            fn = getattr(admin, fn_name)

            # calling the mapped admin api method
            self._logger.info("Calling \"{}\" with parameters {}", fn.__name__,
                              self.parameters)
            api_result = fn(**self.parameters)

            # convert to awscli PascalCase output format
            result = dict_to_pascal_case(api_result)

            # perform output transformation
            if fn_name in self.transformations:
                result = jmespath.search(self.transformations[fn_name], result)

            # log formatted result
            json_result = safe_json(result, 3)
            self._logger.info("Call result is {}", json_result)

            return result

        except Exception as ex:
            self._logger.info("Call failed, error is {}", str(ex))
            return {"Error": str(ex)}
        finally:
            self._logger.flush()