11# This file is part of pycloudlib. See LICENSE file for license information.
22"""Base class for all other clouds to provide consistent set of functions."""
33
4+ import dataclasses
45import enum
56import getpass
67import io
@@ -38,6 +39,14 @@ class ImageType(enum.Enum):
3839 PRO_FIPS = "Pro FIPS"
3940
4041
42+ @dataclasses .dataclass
43+ class ImageInfo :
44+ """Dataclass to hold image information."""
45+
46+ id : str
47+ name : str
48+
49+
4150class BaseCloud (ABC ):
4251 """Base Cloud Class."""
4352
@@ -58,7 +67,8 @@ def __init__(
5867 config_file: path to pycloudlib configuration file
5968 """
6069 self .created_instances : List [BaseInstance ] = []
61- self .created_images : List [str ] = []
70+ self .created_images : List [ImageInfo ] = []
71+ self .preserved_images : List [ImageInfo ] = [] # each dict will hold an id and name
6272
6373 self ._log = logging .getLogger ("{}.{}" .format (__name__ , self .__class__ .__name__ ))
6474 self ._check_and_set_config (config_file , required_values )
@@ -185,12 +195,13 @@ def launch(
185195 raise NotImplementedError
186196
187197 @abstractmethod
188- def snapshot (self , instance , clean = True , ** kwargs ):
198+ def snapshot (self , instance , * , clean = True , keep = False , ** kwargs ):
189199 """Snapshot an instance and generate an image from it.
190200
191201 Args:
192202 instance: Instance to snapshot
193203 clean: run instance clean method before taking snapshot
204+ keep: keep the snapshot after the cloud instance is cleaned up
194205
195206 Returns:
196207 An image id
@@ -212,11 +223,18 @@ def clean(self) -> List[Exception]:
212223 instance .delete ()
213224 except Exception as e :
214225 exceptions .append (e )
215- for image_id in self .created_images :
226+ for image_info in self .created_images :
216227 try :
217- self .delete_image (image_id )
228+ self .delete_image (image_id = image_info . id )
218229 except Exception as e :
219230 exceptions .append (e )
231+ for image_info in self .preserved_images :
232+ # noop - just log that we're not cleaning up these images
233+ self ._log .info (
234+ "Preserved image %s [id:%s] is NOT being cleaned up." ,
235+ image_info .name ,
236+ image_info .id ,
237+ )
220238 return exceptions
221239
222240 def list_keys (self ):
@@ -340,3 +358,43 @@ def _get_ssh_keys(self) -> KeyPair:
340358 private_key_path = private_key_path ,
341359 name = self .config .get ("key_name" , user ),
342360 )
361+
362+ def _store_snapshot_info (
363+ self ,
364+ snapshot_id : str ,
365+ snapshot_name : str ,
366+ keep_snapshot : bool ,
367+ ) -> ImageInfo :
368+ """
369+ Save the snapshot information for later cleanup depending on the keep_snapshot arg.
370+
371+ Will either save the snapshot information to created_images or preserved_images depending
372+ on the keep_snapshot arg. These lists are used by the BaseCloud's clean() method to
373+ cleanup the snapshots when the cloud instance is cleaned up. The snapshot information
374+ is also logged appropriately and in a consistent format.
375+
376+ :param snapshot_id: ID of the snapshot (this is used later to delete the snapshot)
377+ :param snapshot_name: Name of the snapshot (this is for user reference)
378+ :param keep_snapshot: Keep the snapshot after the cloud instance is cleaned up
379+
380+ :return: ImageInfo object with the snapshot information
381+ """
382+ image_info = ImageInfo (
383+ id = snapshot_id ,
384+ name = snapshot_name ,
385+ )
386+ if not keep_snapshot :
387+ self .created_images .append (image_info )
388+ self ._log .info (
389+ "Created temporary snapshot %s [id:%s]" ,
390+ image_info .name ,
391+ image_info .id ,
392+ )
393+ else :
394+ self .preserved_images .append (image_info )
395+ self ._log .info (
396+ "Created permanent snapshot %s [id:%s]" ,
397+ image_info .name ,
398+ image_info .id ,
399+ )
400+ return image_info
0 commit comments