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)
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))
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()