Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/c7n_gcp/filters/labels.py: 23%
52 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
1# Copyright The Cloud Custodian Authors.
2# SPDX-License-Identifier: Apache-2.0
4from datetime import datetime, timedelta
6from c7n.utils import type_schema
7from c7n.filters import Filter, FilterValidationError
8from c7n.filters.offhours import Time
10DEFAULT_TAG = "custodian_status"
13class LabelActionFilter(Filter):
14 """Filter resources for label specified future action
16 Filters resources by a 'custodian_status' label which specifies a future
17 date for an action.
19 The filter parses the label values looking for an 'op@date'
20 string. The date is parsed and compared to do today's date, the
21 filter succeeds if today's date is gte to the target date.
23 The optional 'skew' parameter provides for incrementing today's
24 date a number of days into the future. An example use case might
25 be sending a final notice email a few days before terminating an
26 instance, or snapshotting a volume prior to deletion.
28 The optional 'skew_hours' parameter provides for incrementing the current
29 time a number of hours into the future.
31 Optionally, the 'tz' parameter can get used to specify the timezone
32 in which to interpret the clock (default value is 'utc')
34 :example:
36 .. code-block :: yaml
38 policies:
39 - name: vm-stop-marked
40 resource: gcp.instance
41 filters:
42 - type: marked-for-op
43 # The default label used is custodian_status
44 # but that is configurable
45 label: custodian_status
46 op: stop
47 # Another optional label is skew
48 tz: utc
51 """
52 schema = type_schema(
53 'marked-for-op',
54 label={'type': 'string'},
55 tz={'type': 'string'},
56 skew={'type': 'number', 'minimum': 0},
57 skew_hours={'type': 'number', 'minimum': 0},
58 op={'type': 'string'})
60 def validate(self):
61 op = self.data.get('op')
62 if self.manager and op not in self.manager.action_registry.keys():
63 raise FilterValidationError(
64 "Invalid marked-for-op op:%s in %s" % (op, self.manager.data))
66 tz = Time.get_tz(self.data.get('tz', 'utc'))
67 if not tz:
68 raise FilterValidationError(
69 "Invalid timezone specified '%s' in %s" % (
70 self.data.get('tz'), self.manager.data))
71 self.valid_actions = sorted(
72 self.manager.action_registry.keys(), key=lambda k: len(k), reverse=True)
73 return self
75 def process(self, resources, event=None):
76 self.label = self.data.get('label', DEFAULT_TAG)
77 self.op = self.data.get('op', 'stop')
78 self.skew = self.data.get('skew', 0)
79 self.skew_hours = self.data.get('skew_hours', 0)
80 self.tz = Time.get_tz(self.data.get('tz', 'utc'))
81 return super(LabelActionFilter, self).process(resources, event)
83 def parse(self, v: str):
84 remainder, action_date = v.rsplit('-', 1)
85 found = False
86 msg = ""
87 for a in self.valid_actions:
88 if remainder.endswith(a):
89 found = a
90 msg = remainder[:-len(a) - 1]
91 break
92 return msg, found, action_date
94 def __call__(self, i):
95 v = i.get('labels', {}).get(self.label, None)
97 if v is None:
98 return False
99 if '-' not in v or '_' not in v:
100 return False
102 msg, action, action_date_str = self.parse(v)
104 if action != self.op or not action:
105 return False
107 try:
108 action_date = datetime.strptime(action_date_str, '%Y_%m_%d__%H_%M')
109 except Exception:
110 self.log.error("could not parse label:%s value:%s on %s" % (
111 self.label, v, i['name']))
112 return False
114 # current_date must match timezones with the parsed date string
115 if action_date.tzinfo:
116 action_date = action_date.astimezone(self.tz)
117 current_date = datetime.now(tz=self.tz)
118 else:
119 current_date = datetime.now()
121 return current_date >= (action_date - timedelta(days=self.skew, hours=self.skew_hours))