diff --git a/clients/azure/vm.py b/clients/azure/vm.py index b770add..ffa6a6c 100644 --- a/clients/azure/vm.py +++ b/clients/azure/vm.py @@ -51,6 +51,10 @@ class VMNotFound(Exception): pass +class ImageDefinitionNotFound(Exception): + pass + + @dataclass class AzureVMDeploymentProperties: project_name: str @@ -382,6 +386,39 @@ def list_vm_image_definitions(self) -> list[str]: # all images except the default one ] + def delete_vm_image_definition(self, image_definition: str) -> LROPoller: + """Delete image definition.""" + try: + return self._compute_mgmt_client.gallery_images.begin_delete( + resource_group_name=self.resource_group_name, + gallery_name=self.template_specs_image_gallery, + gallery_image_name=image_definition, + ) + except ResourceNotFoundError as error: + raise ImageDefinitionNotFound() from error + + def delete_vm_image_definition_versions( + self, + image_definition: str, + ) -> list[tuple[str, LROPoller]]: + """Delete image definition.""" + return [ + ( + version.name, + self._compute_mgmt_client.gallery_image_versions.begin_delete( + resource_group_name=self.resource_group_name, + gallery_name=self.template_specs_image_gallery, + gallery_image_name=image_definition, + gallery_image_version_name=version.name, + ), + ) + for version in self._compute_mgmt_client.gallery_image_versions.list_by_gallery_image( + resource_group_name=self.resource_group_name, + gallery_name=self.template_specs_image_gallery, + gallery_image_name=image_definition, + ) + ] + def _get_image_versions( self, gallery_name: str, gallery_image_name: str ) -> list[str]: diff --git a/scripts/delete_image_definition.py b/scripts/delete_image_definition.py new file mode 100644 index 0000000..9fd1739 --- /dev/null +++ b/scripts/delete_image_definition.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +""" +Delete an Azure image definition +""" +import argparse +import time + +from clients.azure.vm import VMAzureClient, ImageDefinitionNotFound + +from . import get_logger + +logger = get_logger(__name__) + + +def create_vm(): + parser = argparse.ArgumentParser() + parser.add_argument( + dest="image_definition", + help="Image definition to delete.", + ) + args = parser.parse_args() + + poller = None + + confirm = input( + f"Are you sure you want to delete image definition {args.image_definition}? (y/n): " + ) + if confirm.lower() != "y": + logger.info("Aborting.") + return + + try: + logger.info("Deleting image definition versions...") + pollers = VMAzureClient().delete_vm_image_definition_versions( + args.image_definition + ) + logger.info("%s image definition versions to delete.", len(pollers)) + for version, poller in pollers: + logger.info("Deleting image definition version %s.", version) + while not poller.done(): + poller.result() + logger.info("Done deleting image definition version %s.", version) + + logger.info("Deleting image definition...") + time.sleep( + 7 + ) # sleep a bit, otheriwse azure will say image versions are not deleted yet. + + poller = VMAzureClient().delete_vm_image_definition(args.image_definition) + + except ImageDefinitionNotFound: + logger.error("Image definition %s not found.", args.image_definition) + logger.error( + "Choices are: %s", ", ".join(VMAzureClient().list_vm_image_definitions()) + ) + + if poller: + poller.result() + logger.info("Done.") + + +if __name__ == "__main__": + create_vm()