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

1# Copyright The Cloud Custodian Authors. 

2# SPDX-License-Identifier: Apache-2.0 

3 

4from .core import BaseAction 

5from c7n.utils import local_session, type_schema 

6 

7# parameters to save to/restore from a tag 

8tag_params = ['Min', 'Max', 'Desired'] 

9 

10 

11class AutoscalingBase(BaseAction): 

12 """Action to resize the min/max/desired count in an application autoscaling target 

13 

14 There are several ways to use this action: 

15 

16 1. apply a fixed resize of min, max or desired, optionally saving the 

17 previous values to a named tag (for restoring later): 

18 

19 .. code-block:: yaml 

20 

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 

34 

35 2. restore previous values for min/max/desired from a tag: 

36 

37 .. code-block:: yaml 

38 

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 

50 

51 """ 

52 

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 ) 

74 

75 def get_permissions(self): 

76 return self.autoscaling_permissions + self.permissions 

77 

78 @property 

79 def scalable_dimension(self): 

80 """ the scalable dimension for the Application Autoscaling target """ 

81 

82 @property 

83 def service_namespace(self): 

84 """ the service namespace for interacting with Application Autoscaling """ 

85 

86 def get_resource_id(self, resource): 

87 """ return the id for the provided resource """ 

88 raise NotImplementedError 

89 

90 def get_resource_tag(self, resource, key): 

91 """ return the tag for the provided resource """ 

92 raise NotImplementedError 

93 

94 def get_resource_desired(self, resource): 

95 """ return the current desired value for the provided resource """ 

96 raise NotImplementedError 

97 

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

103 

104 def set_resource_desired(self, resource, desired): 

105 """ set the desired for the provided resource """ 

106 raise NotImplementedError 

107 

108 def process_suspend_scaling(self, target): 

109 self.update_scaling_suspended_state(target, True) 

110 

111 def process_restore_scaling(self, target): 

112 self.update_scaling_suspended_state(target, False) 

113 

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 

120 

121 if update_suspended_state: 

122 self.log.debug('Target %s updating suspended_state=%s' % 

123 (resource_id, update_suspended_state)) 

124 

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 ) 

133 

134 def update_scaling_options(self, resource, target, new_min, new_max, new_desired): 

135 updated = False 

136 

137 cur_min = target['MinCapacity'] 

138 cur_max = target['MaxCapacity'] 

139 cur_desired = self.get_resource_desired(resource) 

140 

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 

146 

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) 

152 

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 

158 

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 

172 

173 return updated 

174 

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']) 

183 

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) 

194 

195 return self.update_scaling_options(resource, target, new_min, new_max, new_desired) 

196 

197 return False 

198 

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) 

204 

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

217 

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 ) 

227 

228 for response in response_iterator: 

229 for target in response['ScalableTargets']: 

230 resource_id = target['ResourceId'] 

231 

232 if resource_id not in resource_ids: 

233 continue 

234 

235 if target['ScalableDimension'] != self.scalable_dimension: 

236 continue 

237 

238 resource = resources_by_id[resource_id] 

239 

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) 

246 

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) 

253 

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)