Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/c7n/resources/rdsparamgroup.py: 60%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

163 statements  

1# Copyright The Cloud Custodian Authors. 

2# SPDX-License-Identifier: Apache-2.0 

3import logging 

4import itertools 

5 

6from botocore.exceptions import ClientError 

7 

8from c7n.actions import ActionRegistry, BaseAction 

9from c7n.filters import FilterRegistry, ValueFilter 

10from c7n.manager import resources 

11from c7n.query import QueryResourceManager, TypeInfo 

12from c7n.utils import (type_schema, local_session, chunks) 

13from c7n.tags import universal_augment 

14from c7n.resources.rds import ParameterFilter 

15 

16log = logging.getLogger('custodian.rds-param-group') 

17 

18pg_filters = FilterRegistry('rds-param-group.filters') 

19pg_actions = ActionRegistry('rds-param-group.actions') 

20 

21 

22@resources.register('rds-param-group') 

23class RDSParamGroup(QueryResourceManager): 

24 """Resource manager for RDS parameter groups. 

25 """ 

26 

27 class resource_type(TypeInfo): 

28 

29 service = 'rds' 

30 arn_type = 'pg' 

31 enum_spec = ('describe_db_parameter_groups', 'DBParameterGroups', None) 

32 name = id = 'DBParameterGroupName' 

33 arn = 'DBParameterGroupArn' 

34 dimension = 'DBParameterGroupName' 

35 permissions_enum = ('rds:DescribeDBParameterGroups',) 

36 cfn_type = 'AWS::RDS::DBParameterGroup' 

37 universal_taggable = object() 

38 permissions_augment = ("rds:ListTagsForResource",) 

39 

40 augment = universal_augment 

41 

42 filter_registry = pg_filters 

43 action_registry = pg_actions 

44 

45 

46pg_cluster_filters = FilterRegistry('rds-cluster-param-group.filters') 

47pg_cluster_actions = ActionRegistry('rds-cluster-param-group.actions') 

48 

49 

50@resources.register('rds-cluster-param-group') 

51class RDSClusterParamGroup(QueryResourceManager): 

52 """ Resource manager for RDS cluster parameter groups. 

53 """ 

54 

55 class resource_type(TypeInfo): 

56 

57 service = 'rds' 

58 arn_type = 'cluster-pg' 

59 arn = 'DBClusterParameterGroupArn' 

60 enum_spec = ('describe_db_cluster_parameter_groups', 'DBClusterParameterGroups', None) 

61 name = id = 'DBClusterParameterGroupName' 

62 dimension = 'DBClusterParameterGroupName' 

63 permissions_enum = ('rds:DescribeDBClusterParameterGroups',) 

64 cfn_type = 'AWS::RDS::DBClusterParameterGroup' 

65 universal_taggable = object() 

66 

67 augment = universal_augment 

68 

69 filter_registry = pg_cluster_filters 

70 action_registry = pg_cluster_actions 

71 

72 

73class PGMixin: 

74 

75 def get_pg_name(self, pg): 

76 return pg['DBParameterGroupName'] 

77 

78 

79class PGClusterMixin: 

80 

81 def get_pg_name(self, pg): 

82 return pg['DBClusterParameterGroupName'] 

83 

84 

85class ParameterGroupFilter(ParameterFilter): 

86 """ 

87 Applies value type filter on db parameter values. 

88 

89 """ 

90 

91 schema = type_schema('db-parameter', rinherit=ValueFilter.schema) 

92 schema_alias = False 

93 permissions = ('rds:DescribeDBParameters',) 

94 policy_annotation = 'c7n:MatchedDBParameter' 

95 

96 def get_pg_values(self, param_group): 

97 pgvalues = {} 

98 param_list = self.get_param_list(param_group) 

99 pgvalues = { 

100 p['ParameterName']: ParameterFilter.recast(p['ParameterValue'], p['DataType']) 

101 for p in param_list if 'ParameterValue' in p} 

102 return pgvalues 

103 

104 def process(self, resources, event=None): 

105 results = [] 

106 for resource in resources: 

107 name = self.get_pg_name(resource) 

108 pg_values = self.get_pg_values(name) 

109 

110 if self.match(pg_values): 

111 resource.setdefault(self.policy_annotation, []).append( 

112 self.data.get('key')) 

113 results.append(resource) 

114 

115 return results 

116 

117 

118@pg_filters.register('db-parameter') 

119class PGParameterFilter(PGMixin, ParameterGroupFilter): 

120 """ Filter by parameters. 

121 

122 :example: 

123 

124 .. code-block:: yaml 

125 

126 policies: 

127 - name: rds-param-group-param-filter 

128 resource: rds-param-group 

129 filters: 

130 - type: db-parameter 

131 key: someparam 

132 op: eq 

133 value: someval 

134 """ 

135 

136 def get_param_list(self, pg): 

137 client = local_session(self.manager.session_factory).client('rds') 

138 paginator = client.get_paginator('describe_db_parameters') 

139 param_list = list(itertools.chain(*[p['Parameters'] 

140 for p in paginator.paginate(DBParameterGroupName=pg)])) 

141 

142 return param_list 

143 

144 

145@pg_cluster_filters.register('db-parameter') 

146class PGClusterParameterFilter(PGClusterMixin, ParameterGroupFilter): 

147 """ Filter by parameters. 

148 

149 :example: 

150 

151 .. code-block:: yaml 

152 

153 policies: 

154 - name: rds-cluster-param-group-param-filter 

155 resource: rds-cluster-param-group 

156 filters: 

157 - type: db-parameter 

158 key: someparam 

159 op: eq 

160 value: someval 

161 """ 

162 

163 def get_param_list(self, pg): 

164 client = local_session(self.manager.session_factory).client('rds') 

165 paginator = client.get_paginator('describe_db_cluster_parameters') 

166 param_list = list(itertools.chain(*[p['Parameters'] 

167 for p in paginator.paginate(DBClusterParameterGroupName=pg)])) 

168 

169 return param_list 

170 

171 

172class Copy(BaseAction): 

173 

174 schema = type_schema( 

175 'copy', 

176 **{ 

177 'required': ['name'], 

178 'name': {'type': 'string'}, 

179 'description': {'type': 'string'}, 

180 } 

181 ) 

182 

183 def process(self, param_groups): 

184 client = local_session(self.manager.session_factory).client('rds') 

185 

186 for param_group in param_groups: 

187 name = self.get_pg_name(param_group) 

188 copy_name = self.data.get('name') 

189 copy_desc = self.data.get('description', 'Copy of {}'.format(name)) 

190 self.do_copy(client, name, copy_name, copy_desc) 

191 self.log.info('Copied RDS parameter group %s to %s', name, copy_name) 

192 

193 

194@pg_actions.register('copy') 

195class PGCopy(PGMixin, Copy): 

196 """ Action to copy an RDS parameter group. 

197 

198 :example: 

199 

200 .. code-block:: yaml 

201 

202 policies: 

203 - name: rds-param-group-copy 

204 resource: rds-param-group 

205 filters: 

206 - DBParameterGroupName: original_pg_name 

207 actions: 

208 - type: copy 

209 name: copy_name 

210 """ 

211 

212 permissions = ('rds:CopyDBParameterGroup',) 

213 

214 def do_copy(self, client, name, copy_name, desc): 

215 client.copy_db_parameter_group( 

216 SourceDBParameterGroupIdentifier=name, 

217 TargetDBParameterGroupIdentifier=copy_name, 

218 TargetDBParameterGroupDescription=desc 

219 ) 

220 

221 

222@pg_cluster_actions.register('copy') 

223class PGClusterCopy(PGClusterMixin, Copy): 

224 """ Action to copy an RDS cluster parameter group. 

225 

226 :example: 

227 

228 .. code-block:: yaml 

229 

230 policies: 

231 - name: rds-cluster-param-group-copy 

232 resource: rds-cluster-param-group 

233 filters: 

234 - DBClusterParameterGroupName: original_cluster_pg_name 

235 actions: 

236 - type: copy 

237 name: copy_name 

238 """ 

239 

240 permissions = ('rds:CopyDBClusterParameterGroup',) 

241 

242 def do_copy(self, client, name, copy_name, desc): 

243 client.copy_db_cluster_parameter_group( 

244 SourceDBClusterParameterGroupIdentifier=name, 

245 TargetDBClusterParameterGroupIdentifier=copy_name, 

246 TargetDBClusterParameterGroupDescription=desc 

247 ) 

248 

249 

250class Delete(BaseAction): 

251 

252 schema = type_schema('delete') 

253 

254 def process(self, param_groups): 

255 client = local_session(self.manager.session_factory).client('rds') 

256 

257 for param_group in param_groups: 

258 name = self.get_pg_name(param_group) 

259 try: 

260 self.do_delete(client, name) 

261 except ClientError as e: 

262 if e.response['Error']['Code'] == 'DBParameterGroupNotFoundFault': 

263 self.log.warning('RDS parameter group %s already deleted', name) 

264 continue 

265 raise 

266 self.log.info('Deleted RDS parameter group: %s', name) 

267 

268 

269@pg_actions.register('delete') 

270class PGDelete(PGMixin, Delete): 

271 """Action to delete an RDS parameter group 

272 

273 :example: 

274 

275 .. code-block:: yaml 

276 

277 policies: 

278 - name: rds-param-group-delete 

279 resource: rds-param-group 

280 filters: 

281 - DBParameterGroupName: pg_name 

282 actions: 

283 - type: delete 

284 """ 

285 

286 permissions = ('rds:DeleteDBParameterGroup',) 

287 

288 def do_delete(self, client, name): 

289 client.delete_db_parameter_group(DBParameterGroupName=name) 

290 

291 

292@pg_cluster_actions.register('delete') 

293class PGClusterDelete(PGClusterMixin, Delete): 

294 """Action to delete an RDS cluster parameter group 

295 

296 :example: 

297 

298 .. code-block:: yaml 

299 

300 policies: 

301 - name: rds-cluster-param-group-delete 

302 resource: rds-cluster-param-group 

303 filters: 

304 - DBClusterParameterGroupName: cluster_pg_name 

305 actions: 

306 - type: delete 

307 """ 

308 

309 permissions = ('rds:DeleteDBClusterParameterGroup',) 

310 

311 def do_delete(self, client, name): 

312 client.delete_db_cluster_parameter_group(DBClusterParameterGroupName=name) 

313 

314 

315class Modify(BaseAction): 

316 

317 schema = type_schema( 

318 'modify', 

319 **{ 

320 'required': ['params'], 

321 'params': { 

322 'type': 'array', 

323 'items': { 

324 'type': 'object', 

325 'required': ['name', 'value'], 

326 'name': {'type': 'string'}, 

327 'value': {'type': 'string'}, 

328 'apply-method': {'type': 'string', 'enum': ['immediate', 'pending-reboot']} 

329 }, 

330 }, 

331 } 

332 ) 

333 

334 def process(self, param_groups): 

335 client = local_session(self.manager.session_factory).client('rds') 

336 

337 params = [] 

338 for param in self.data.get('params', []): 

339 params.append({ 

340 'ParameterName': param['name'], 

341 'ParameterValue': param['value'], 

342 'ApplyMethod': param.get('apply-method', 'immediate'), 

343 }) 

344 

345 for param_group in param_groups: 

346 name = self.get_pg_name(param_group) 

347 

348 # Fetch the existing parameters for this DB, so we only try to change the ones that are 

349 # different. 

350 cur_params = self.get_current_params(client, name) 

351 changed_params = [] 

352 for param in params: 

353 param_name = param['ParameterName'] 

354 if (param_name not in cur_params or 

355 cur_params[param_name]['ParameterValue'] != param['ParameterValue']): 

356 changed_params.append(param) 

357 

358 # Can only do 20 elements at a time per docs, so if we have more than that we will 

359 # break it into multiple requests: 

360 # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds.html#RDS.Client.modify_db_parameter_group 

361 for param_set in chunks(changed_params, 5): 

362 self.do_modify(client, name, param_set) 

363 

364 self.log.info('Modified RDS parameter group %s (%i parameters changed, %i unchanged)', 

365 name, len(changed_params), len(params) - len(changed_params)) 

366 

367 

368@pg_actions.register('modify') 

369class PGModify(PGMixin, Modify): 

370 """Action to modify an RDS parameter group 

371 

372 :example: 

373 

374 .. code-block:: yaml 

375 

376 policies: 

377 - name: rds-param-group-modify 

378 resource: rds-param-group 

379 filters: 

380 - DBParameterGroupName: pg_name 

381 actions: 

382 - type: modify 

383 params: 

384 - name: autocommit 

385 value: "1" 

386 - name: max_connections 

387 value: "100" 

388 """ 

389 

390 permissions = ('rds:DescribeDBParameters', 'rds:ModifyDBParameterGroup') 

391 

392 def get_current_params(self, client, name): 

393 params = client.describe_db_parameters(DBParameterGroupName=name) 

394 return {x['ParameterName']: { 

395 'ParameterValue': x.get('ParameterValue'), 

396 'ApplyMethod': x['ApplyMethod']} 

397 for x in params.get('Parameters', [])} 

398 

399 def do_modify(self, client, name, params): 

400 client.modify_db_parameter_group(DBParameterGroupName=name, Parameters=params) 

401 

402 

403@pg_cluster_actions.register('modify') 

404class PGClusterModify(PGClusterMixin, Modify): 

405 """Action to modify an RDS cluster parameter group 

406 

407 :example: 

408 

409 .. code-block:: yaml 

410 

411 policies: 

412 - name: rds-cluster-param-group-modify 

413 resource: rds-cluster-param-group 

414 filters: 

415 - DBClusterParameterGroupName: cluster_pg_name 

416 actions: 

417 - type: modify 

418 params: 

419 - name: lower_case_table_names 

420 value: "1" 

421 - name: master_verify_checksum 

422 value: "1" 

423 """ 

424 

425 permissions = ('rds:DescribeDBClusterParameters', 'rds:ModifyDBClusterParameterGroup') 

426 

427 def get_current_params(self, client, name): 

428 params = client.describe_db_cluster_parameters(DBClusterParameterGroupName=name) 

429 return {x['ParameterName']: { 

430 'ParameterValue': x.get('ParameterValue'), 

431 'ApplyMethod': x['ApplyMethod']} 

432 for x in params.get('Parameters', [])} 

433 

434 def do_modify(self, client, name, params): 

435 client.modify_db_cluster_parameter_group( 

436 DBClusterParameterGroupName=name, 

437 Parameters=params 

438 )