Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cloudwash supporting Containers cleanup #140

Merged
merged 1 commit into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ cloudwash supports following cloud providers:
* VMWare vCenter (_Support yet To be added_)
* OCP Clusters deplyed on Public clouds (_Support yet To be added_)

And the Containerization tools:

* Podman

The list of resource types it helps to clean could be found under settings.yaml.template](https://github.com/RedHatQE/cloudwash/blob/master/settings.yaml.template) file for individual cloud providers along with cleanup criteria.

## Installation
Expand Down Expand Up @@ -104,6 +108,7 @@ azure Cleanup Azure provider
aws Cleanup Amazon provider
gce Cleanup GCE provider
openstack Cleanup OSP provider
podman Cleanup Podman provider
rhev Cleanup RHEV provider
vmware Cleanup VMWare provider
```
Expand Down
14 changes: 14 additions & 0 deletions cloudwash/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from cloudwash.providers.aws import cleanup as awsCleanup
from cloudwash.providers.azure import cleanup as azureCleanup
from cloudwash.providers.gce import cleanup as gceCleanup
from cloudwash.providers.podman import cleanup as podmanCleanup

# Adding the pythonpath for importing modules from cloudwash packages
# sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
Expand Down Expand Up @@ -115,6 +116,19 @@ def aws(ctx, vms, discs, nics, images, pips, stacks, _all):
)


@cleanup_providers.command(help="Cleanup Podman provider")
@click.option("--containers", is_flag=True, help="Remove containers from the podman host")
@click.pass_context
def podman(ctx, containers):
# Validate Podman Settings
validate_provider(ctx.command.name)
is_dry_run = ctx.parent.params["dry"]
podmanCleanup(
containers=containers,
dry_run=is_dry_run,
)


@cleanup_providers.command(help="Cleanup VMWare provider")
@common_options
@click.pass_context
Expand Down
7 changes: 6 additions & 1 deletion cloudwash/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,16 @@ def compute_client(compute_resource, **kwargs):
password=settings.aws.auth.secret_key,
region=kwargs['aws_region'],
)
elif compute_resource == "podman":
client = wrapanapi.Podman(
hostname=settings.podman.auth.hostname,
username=settings.podman.auth.username,
port=settings.podman.auth.ssh_port,
)
else:
raise ValueError(
f"{compute_resource} is an incorrect value. It should be one of azure or gce or ec2"
)

try:
yield client
finally:
Expand Down
1 change: 1 addition & 0 deletions cloudwash/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
aws_data = ['VMS', 'NICS', 'DISCS', 'PIPS', 'RESOURCES', 'STACKS']
azure_data = ['VMS', 'NICS', 'DISCS', 'IMAGES', 'PIPS', 'RESOURCES']
gce_data = ['VMS', 'NICS', 'DISCS']
container_data = ['CONTAINERS']
11 changes: 11 additions & 0 deletions cloudwash/entities/providers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from cloudwash.entities.resources.containers import CleanPodmanContainers
from cloudwash.entities.resources.discs import CleanAWSDiscs
from cloudwash.entities.resources.discs import CleanAzureDiscs
from cloudwash.entities.resources.images import CleanAWSImages
Expand Down Expand Up @@ -81,3 +82,13 @@ class GCECleanup(providerCleanup):
def __init__(self, client):
self.client = client
super().__init__(client)


class PodmanCleanup(providerCleanup):
def __init__(self, client):
self.client = client
super().__init__(client)

@property
def containers(self):
return CleanPodmanContainers(client=self.client)
26 changes: 26 additions & 0 deletions cloudwash/entities/resources/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,32 @@ def _set_dry(self):
pass


class ContainerCleanup(ResourceCleanup):
@abstractmethod
def list(self):
pass

@abstractmethod
def cleanup(self):
pass

@abstractmethod
def stop(self):
pass

@abstractmethod
def remove(self):
pass

@abstractmethod
def skip(self):
pass

@abstractmethod
def _set_dry(self):
pass


class ResourceCleanupManager:
def __init__(self):
self.resources = []
Expand Down
60 changes: 60 additions & 0 deletions cloudwash/entities/resources/containers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from cloudwash.config import settings
from cloudwash.entities.resources.base import ContainerCleanup
from cloudwash.logger import logger
from cloudwash.utils import dry_data
from cloudwash.utils import total_running_time


class CleanContainers(ContainerCleanup):
def __init__(self, client):
self.client = client
self._delete = []
self._stop = []
self._skip = []
self.list()

def _set_dry(self):
dry_data['CONTAINERS']['delete'] = self._delete
dry_data['CONTAINERS']['stop'] = self._stop
dry_data['CONTAINERS']['skip'] = self._skip

def list(self):
pass

def stop(self):
for container in self._stop:
self.client.get_container(key=container).stop()
logger.info(f"Stopped Podman Containers: \n{self._stop}")

def remove(self):
for container in self._delete:
self.client.get_container(key=container).delete(force=True)
logger.info(f"Removed Podman Containers: \n{self._delete}")

def skip(self):
logger.info(f"Skipped VMs: \n{self._skip}")

def cleanup(self):
if not settings.dry_run:
self.remove()
self.stop()
self.skip()


class CleanPodmanContainers(CleanContainers):
def list(self):

for container in self.client.containers:
if container.name in settings.podman.exceptions.container.skip_list:
self._skip.append(container.name)
continue
elif total_running_time(container).minutes >= settings.podman.criteria.container.sla:

if container.name in settings.podman.exceptions.container.stop_list:
self._stop.append(container.name)
continue

elif container.name.startswith(settings.podman.criteria.container.name_prefix):
self._delete.append(container.name)

self._set_dry()
19 changes: 19 additions & 0 deletions cloudwash/providers/podman.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Azure CR Cleanup Utilities"""
from cloudwash.client import compute_client
from cloudwash.constants import container_data as data
from cloudwash.entities.providers import PodmanCleanup
from cloudwash.utils import dry_data
from cloudwash.utils import echo_dry


def cleanup(**kwargs):
is_dry_run = kwargs.get("dry_run", False)
for items in data:
dry_data[items]['delete'] = []
with compute_client("podman") as podman_client:
podmancleanup = PodmanCleanup(client=podman_client)
# Actual Cleaning and dry execution
if kwargs["containers"]:
podmancleanup.containers.cleanup()
if is_dry_run:
echo_dry(dry_data)
15 changes: 15 additions & 0 deletions cloudwash/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from cloudwash.logger import logger

_vms_dict = {"VMS": {"delete": [], "stop": [], "skip": []}}
_containers_dict = {"CONTAINERS": {"delete": [], "stop": [], "skip": []}}

dry_data = {
"NICS": {"delete": []},
"DISCS": {"delete": []},
Expand All @@ -16,6 +18,7 @@
"IMAGES": {"delete": []},
}
dry_data.update(_vms_dict)
dry_data.update(_containers_dict)


def echo_dry(dry_data=None) -> None:
Expand All @@ -28,6 +31,9 @@ def echo_dry(dry_data=None) -> None:
deletable_vms = dry_data["VMS"]["delete"]
stopable_vms = dry_data["VMS"]["stop"]
skipped_vms = dry_data["VMS"]["skip"]
deletable_containers = dry_data["CONTAINERS"]["delete"]
stopable_containers = dry_data["CONTAINERS"]["stop"]
skipped_containers = dry_data["CONTAINERS"]["skip"]
deletable_discs = dry_data["DISCS"]["delete"]
deletable_nics = dry_data["NICS"]["delete"]
deletable_images = dry_data["IMAGES"]["delete"]
Expand All @@ -39,6 +45,12 @@ def echo_dry(dry_data=None) -> None:
f"VMs:\n\tDeletable: {deletable_vms}\n\tStoppable: {stopable_vms}\n\t"
f"Skip: {skipped_vms}"
)
if deletable_containers or stopable_containers or skipped_containers:
logger.info(
f"Containers:\n\tDeletable: {deletable_containers}\n\t"
f"Stoppable: {stopable_containers}\n\t"
f"Skip: {skipped_containers}"
)
if deletable_discs:
logger.info(f"DISCs:\n\tDeletable: {deletable_discs}")
if deletable_nics:
Expand All @@ -61,6 +73,9 @@ def echo_dry(dry_data=None) -> None:
deletable_resources,
deletable_stacks,
deletable_images,
deletable_containers,
stopable_containers,
skipped_containers,
]
):
logger.info("\nNo resources are eligible for cleanup!")
Expand Down
17 changes: 17 additions & 0 deletions settings.yaml.template
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,20 @@ AWS:
# CloudFormations names that would be skipped from cleanup
STACK_LIST: []
IMAGES: []
PODMAN:
AUTH:
HOSTNAME:
USERNAME: root
SSH_PORT: 22
CRITERIA:
CONTAINER:
# Container Prepended Test
NAME_PREFIX: 'satci'
# Container running time in minutes
SLA: 720
EXCEPTIONS:
CONTAINER:
# Containers to skip and not to delete
SKIP_LIST: []
# Containers to stop if running but not delete
STOP_LIST: []
Loading