heat: add cleanup_stacks

If you have a cloud where some of your stacks are stuck in
DELETE_FAILED state and you need to run something to clean
up all the resources, this tool is designed for this.

At the moment, it just handles nested stacks but it can be
extended to handle more things (i.e. network ports, etc). It
does come with destructive behaviour so the operator needs
to be careful using it.

Change-Id: I5c419aa0f45d243b585c9794406086891bfe06c1
This commit is contained in:
Mohammed Naser 2019-05-30 13:59:10 -04:00
parent 7437df6ec0
commit 6dea19b539
1 changed files with 67 additions and 0 deletions

67
heat/cleanup_stacks.py Normal file
View File

@ -0,0 +1,67 @@
# Copyright (c) 2019 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Clean up Heat stacks
This script grabs a list of all stacks in DELETE_FAILED state and tries to
delete them again. For usage, please run the script with `--help`.
"""
import argparse
import openstack
options = argparse.ArgumentParser(description='OpenStack Heat Clean-up')
cloud = openstack.connect(options=options)
def cleanup_stack(stack):
# Skip anything that isn't DELETE_FAILED
if stack.status != 'DELETE_FAILED':
return
# Get a list of all the resources of the stack
resources = list(cloud.orchestration.resources(stack))
# If we don't have any resources, we can consider this stack gone.
if len(resources) == 0:
print('[{}] no resources, deleting stack'.format(stack.id))
cloud.orchestration.delete_stack(stack)
return
# Find resources that are DELETE_FAILED
for resource in resources:
# Skip resources that are not DELETE_FAILED
if resource.status != 'DELETE_FAILED':
continue
# Clean up and nested stacks
if resource.resource_type in ('OS::Heat::ResourceGroup'):
stack_id = resource.physical_resource_id
nested_stack = cloud.orchestration.find_stack(stack_id)
cleanup_stack(nested_stack)
continue
# This is protection to make sure that we only delete once we're sure
# that all resources are gone.
print(stack, resource)
raise
# At this point, the stack should be ready to be deleted
print("[{}] deleting..".format(stack.id))
cloud.orchestration.delete_stack(stack)
for stack in cloud.orchestration.stacks():
cleanup_stack(stack)