Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/c7n/actions/autoscaling.py: 22%
108 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 .core import BaseAction
5from c7n.utils import local_session, type_schema
7# parameters to save to/restore from a tag
8tag_params = ['Min', 'Max', 'Desired']
11class AutoscalingBase(BaseAction):
12 """Action to resize the min/max/desired count in an application autoscaling target
14 There are several ways to use this action:
16 1. apply a fixed resize of min, max or desired, optionally saving the
17 previous values to a named tag (for restoring later):
19 .. code-block:: yaml
21 policies:
22 - name: offhours-ecs-off
23 resource: ecs-service
24 filters:
25 - type: offhour
26 offhour: 19
27 default_tz: bst
28 actions:
29 - type: resize
30 min-capacity: 0
31 desired: 0
32 save-options-tag: OffHoursPrevious
33 suspend-scaling: true
35 2. restore previous values for min/max/desired from a tag:
37 .. code-block:: yaml
39 policies:
40 - name: offhours-ecs-on
41 resource: ecs-service
42 filters:
43 - type: onhour
44 onhour: 8
45 default_tz: bst
46 actions:
47 - type: resize
48 restore-options-tag: OffHoursPrevious
49 restore-scaling: true
51 """
53 schema = type_schema(
54 'resize',
55 **{
56 'min-capacity': {'type': 'integer', 'minimum': 0},
57 'max-capacity': {'type': 'integer', 'minimum': 0},
58 'desired': {
59 "anyOf": [
60 {'enum': ["current"]},
61 {'type': 'integer', 'minimum': 0}
62 ]
63 },
64 'save-options-tag': {'type': 'string'},
65 'restore-options-tag': {'type': 'string'},
66 'suspend-scaling': {'type': 'boolean'},
67 'restore-scaling': {'type': 'boolean'},
68 }
69 )
70 autoscaling_permissions = (
71 'application-autoscaling:DescribeScalableTargets',
72 'application-autoscaling:RegisterScalableTarget',
73 )
75 def get_permissions(self):
76 return self.autoscaling_permissions + self.permissions
78 @property
79 def scalable_dimension(self):
80 """ the scalable dimension for the Application Autoscaling target """
82 @property
83 def service_namespace(self):
84 """ the service namespace for interacting with Application Autoscaling """
86 def get_resource_id(self, resource):
87 """ return the id for the provided resource """
88 raise NotImplementedError
90 def get_resource_tag(self, resource, key):
91 """ return the tag for the provided resource """
92 raise NotImplementedError
94 def get_resource_desired(self, resource):
95 """ return the current desired value for the provided resource """
96 raise NotImplementedError
98 def set_resource_tag(self, resource, key, value):
99 """ set the tag for the provided resource """
100 """ default implementation is to use `UniversalTag` """
101 tag_action = self.manager.action_registry.get('tag')
102 tag_action({'key': key, 'value': value}, self.manager).process([resource])
104 def set_resource_desired(self, resource, desired):
105 """ set the desired for the provided resource """
106 raise NotImplementedError
108 def process_suspend_scaling(self, target):
109 self.update_scaling_suspended_state(target, True)
111 def process_restore_scaling(self, target):
112 self.update_scaling_suspended_state(target, False)
114 def update_scaling_suspended_state(self, target, suspended_value):
115 resource_id = target['ResourceId']
116 update_suspended_state = {}
117 for state, suspended in target['SuspendedState'].items():
118 if suspended != suspended_value:
119 update_suspended_state[state] = suspended_value
121 if update_suspended_state:
122 self.log.debug('Target %s updating suspended_state=%s' %
123 (resource_id, update_suspended_state))
125 client = local_session(self.manager.session_factory).client(
126 'application-autoscaling')
127 client.register_scalable_target(
128 ServiceNamespace=self.service_namespace,
129 ResourceId=resource_id,
130 ScalableDimension=self.scalable_dimension,
131 SuspendedState=update_suspended_state,
132 )
134 def update_scaling_options(self, resource, target, new_min, new_max, new_desired):
135 updated = False
137 cur_min = target['MinCapacity']
138 cur_max = target['MaxCapacity']
139 cur_desired = self.get_resource_desired(resource)
141 if new_desired is not None and new_desired != cur_desired:
142 self.log.debug('Target %s updating desired=%d' %
143 (target['ResourceId'], new_desired))
144 self.set_resource_desired(resource, new_desired)
145 updated = True
147 # Lower MinCapacity if desired is below
148 if new_min is not None:
149 new_min = min(new_desired, new_min)
150 else:
151 new_min = min(new_desired, cur_min)
153 capacity_changes = {}
154 if new_min is not None and new_min != cur_min:
155 capacity_changes['MinCapacity'] = new_min
156 if new_max is not None and new_max != cur_max:
157 capacity_changes['MaxCapacity'] = new_max
159 if capacity_changes:
160 resource_id = target['ResourceId']
161 self.log.debug('Target %s updating min=%s, max=%s'
162 % (resource_id, new_min, new_max))
163 client = local_session(self.manager.session_factory).client(
164 'application-autoscaling')
165 client.register_scalable_target(
166 ServiceNamespace=self.service_namespace,
167 ResourceId=resource_id,
168 ScalableDimension=self.scalable_dimension,
169 **capacity_changes,
170 )
171 updated = True
173 return updated
175 def process_restore_scaling_options_from_tag(self, resource, target):
176 # we want to restore all ASG size params from saved data
177 self.log.debug(
178 'Want to restore resource %s from tag %s' %
179 (target['ResourceId'], self.data['restore-options-tag']))
180 restore_options = self.get_resource_tag(
181 resource,
182 self.data['restore-options-tag'])
184 new_min, new_max, new_desired = None, None, None
185 if restore_options is not None:
186 for field in restore_options.split(':'):
187 (param, value) = field.split('=')
188 if param == 'Min':
189 new_min = int(value)
190 elif param == 'Max':
191 new_max = int(value)
192 elif param == 'Desired':
193 new_desired = int(value)
195 return self.update_scaling_options(resource, target, new_min, new_max, new_desired)
197 return False
199 def process_update_scaling_options(self, resource, target):
200 new_min = self.data.get('min-capacity', None)
201 new_max = self.data.get('max-capacity', None)
202 new_desired = self.data.get('desired', None)
203 return self.update_scaling_options(resource, target, new_min, new_max, new_desired)
205 def process_save_scaling_options_to_tag(self, resource, target):
206 current_desired = self.get_resource_desired(resource)
207 # save existing params to a tag before changing them
208 self.log.debug('Saving resource %s size to tag %s' %
209 (target['ResourceId'], self.data['save-options-tag']))
210 self.set_resource_tag(
211 resource,
212 self.data['save-options-tag'],
213 'Min=%d:Max=%d:Desired=%d' % (
214 target['MinCapacity'],
215 target['MaxCapacity'],
216 current_desired))
218 def process(self, resources):
219 resources_by_id = {self.get_resource_id(r): r for r in resources}
220 resource_ids = list(resources_by_id.keys())
221 client = local_session(self.manager.session_factory).client(
222 'application-autoscaling')
223 paginator = client.get_paginator('describe_scalable_targets')
224 response_iterator = paginator.paginate(
225 ServiceNamespace=self.service_namespace,
226 )
228 for response in response_iterator:
229 for target in response['ScalableTargets']:
230 resource_id = target['ResourceId']
232 if resource_id not in resource_ids:
233 continue
235 if target['ScalableDimension'] != self.scalable_dimension:
236 continue
238 resource = resources_by_id[resource_id]
240 if self.data.get('suspend-scaling'):
241 # suspend scaling activities
242 self.process_suspend_scaling(target)
243 if self.data.get('restore-scaling'):
244 # restore scaling activities
245 self.process_restore_scaling(target)
247 if 'restore-options-tag' in self.data:
248 # resize based on prior TAG values
249 updated = self.process_restore_scaling_options_from_tag(resource, target)
250 else:
251 # resize based on params in policy
252 updated = self.process_update_scaling_options(resource, target)
254 if 'save-options-tag' in self.data and updated:
255 # save prior values as tags
256 self.process_save_scaling_options_to_tag(resource, target)