def load_event_definitions(self): self.scheduler_table.reread_table() self.events = self.scheduler_table.get_dict() for e in self.events: if not isinstance(self.events[e], str): continue digest = misc.sha256("%s:%s" % (e, self.events[e])) event_name = "CS-Cron-%s%s-%s" % (self.context["GroupName"], self.context["VariantNumber"], digest[:10]) try: self.event_names.append({ "Name": event_name, "EventName": e, "Event": self.events[e], "Data": misc.parse_line_as_list_of_dict(self.events[e], leading_keyname="schedule") }) except Exception as ex: log.exception( "Failed to parse Scheduler event '%s' (%s) : %s" % (e, self.events[e], ex))
def is_maintenance_time(self, fleet=None, meta=None): if not self.is_feature_enabled("maintenance_window"): return False now = self.context["now"] sa = max(Cfg.get_duration_secs("ssm.feature.maintenance_window.start_ahead"), 30) # We compute a predictive jitter to avoid all subleets starting exactly at the same time group_name = self.context["GroupName"] jitter_salt = int(misc.sha256(f"{group_name}:{fleet}")[:3], 16) / (16 * 16 * 16) * sa jitter = Cfg.get_abs_or_percent("ssm.feature.maintenance_window.start_ahead.max_jitter", 0, jitter_salt) start_ahead = timedelta(seconds=(sa-jitter)) windows = copy.deepcopy(self._get_maintenance_windows_for_fleet(fleet=fleet)) for w in windows: window_id= w["WindowId"] if "NextExecutionTime" in w: end_time = w["NextExecutionTime"] + timedelta(hours=int(w["Duration"])) if now >= (w["NextExecutionTime"] - start_ahead) and now < end_time: # We are entering a new maintenance window period. Remember it... self.o_state.set_state(f"ssm.events.maintenance_window.last_next_execution_time.{window_id}", w["NextExecutionTime"], TTL=self.ttl) self.o_state.set_state(f"ssm.events.maintenance_window.last_next_execution_duration.{window_id}", w["Duration"], TTL=self.ttl) w["_FutureNextExecutionTime"] = w["NextExecutionTime"] # SSM maintenance windows do not always have a NextExecutionTime field -=OR=- it contains the future # NextExecutionTime of the next iteration. In both case, we westore it from a backuped one. next_execution_time = self.o_state.get_state_date(f"ssm.events.maintenance_window.last_next_execution_time.{window_id}", TTL=self.ttl) if next_execution_time is not None: w["NextExecutionTime"] = next_execution_time if "Duration" not in w: next_execution_duration = self.o_state.get_state(f"ssm.events.maintenance_window.last_next_execution_duration.{window_id}", TTL=self.ttl) if next_execution_duration is not None: w["Duration"] = next_execution_duration valid_windows = [w for w in windows if "NextExecutionTime" in w and "Duration" in w] fleetname = "Main" if fleet is None else fleet next_window = None for w in sorted(valid_windows, key=lambda w: w["NextExecutionTime"]): end_time = w["NextExecutionTime"] + timedelta(hours=int(w["Duration"])) start_time = w["NextExecutionTime"] - start_ahead if now >= start_time and now < end_time: if meta is not None: meta["MatchingWindow"] = w meta["MatchingWindowMessage"] = f"Found ACTIVE matching window for fleet {fleetname} : {w}" meta["StartTime"] = w["NextExecutionTime"] meta["EndTime"] = end_time return True if ("_FutureNextExecutionTime" in w and w["_FutureNextExecutionTime"] > now and (next_window is None or w["_FutureNextExecutionTime"] < next_window["_FutureNextExecutionTime"])): next_window = w if next_window is not None and meta is not None: meta["NextWindowMessage"] = (f"Next SSM Maintenance Window for {fleetname} fleet is '%s/%s in %s " f"(Fleet will start ahead at %s)." % (w["WindowId"], w["Name"], (w["_FutureNextExecutionTime"] - now), w["_FutureNextExecutionTime"] - start_ahead)) return False
def call_sqs(self, arn, region, account_id, service_path, content, e): misc.initialize_clients(["sqs"], self.context) client = self.context["sqs.client"] response = client.get_queue_url( QueueName=service_path, QueueOwnerAWSAccountId=account_id ) log.info("Notifying to SQS Queue '%s' for event '%s'..." % (arn, e)) args = { "QueueUrl": response["QueueUrl"], "MessageBody": content } if service_path.endswith(".fifo"): # create a message group id that is unique to this CS deployment to allow # SQS FIFO sharing between multiple deployment concurrently. args["MessageGroupId"] = f"CS-notif-channel-{account_id}-%s" % (self.context["GroupName"]) args["MessageDeduplicationId"] = misc.sha256(content) response = client.send_message(**args)
def _format_query(query, metric_id, metric): uniq_id = "id%s" % misc.sha256(metric_id) query["IdMapping"][uniq_id] = metric_id q = { "Id": uniq_id, "MetricStat": { "Metric": { "MetricName": metric["MetricName"], "Namespace": metric["Namespace"] }, "Period": metric["Period"], "Stat": metric["Statistic"] }, "ReturnData": True } if "Dimensions" in metric: q["MetricStat"]["Metric"]["Dimensions"] = metric["Dimensions"] if "Unit" in metric: q["MetricStat"]["Unit"] = metric["Unit"] query["Queries"].append(q)