From: Thanh Ha Date: Fri, 10 Mar 2017 02:51:46 +0000 (-0500) Subject: Add openstack image list and cleanup cmds X-Git-Tag: v0.0.8^2 X-Git-Url: https://gerrit.linuxfoundation.org/infra/gitweb?a=commitdiff_plain;h=8016842244541a15a98446779febefa788548c12;p=releng%2Flftools.git Add openstack image list and cleanup cmds This adds 2 new commands to lftools to manipulate openstack images: list: Prints available images in the cloud. cleanup: Removes old unused images. Also fix ctx not getting passed properly to the cli function. Change-Id: I5c73964f33f4fccb56684910fb08828c747fa562 Signed-off-by: Thanh Ha --- diff --git a/docs/commands/index.rst b/docs/commands/index.rst index f0d01791..5499452a 100644 --- a/docs/commands/index.rst +++ b/docs/commands/index.rst @@ -8,4 +8,6 @@ bash. It supports the following commands. .. toctree:: :maxdepth: 2 + nexus + openstack version diff --git a/docs/commands/openstack.rst b/docs/commands/openstack.rst new file mode 100644 index 00000000..ddf32795 --- /dev/null +++ b/docs/commands/openstack.rst @@ -0,0 +1,31 @@ +********* +OpenStack +********* + +.. program-output:: lftools openstack --help + +Commands +======== + +.. contents:: OpenStack Commands + :local: + +image +----- + +.. program-output:: lftools openstack --os-cloud docs image --help + +cleanup +^^^^^^^ + +The intent of this command is to automatically cleanup old images in the cloud. +The OpenDaylight project has 2 clouds, a Private Cloud and a Public cloud which +needs the `--clouds` option to automatically remove the same images from +multiple clouds simultaniously. + +.. program-output:: lftools openstack --os-cloud docs image cleanup --help + +list +^^^^ + +.. program-output:: lftools openstack --os-cloud docs image list --help diff --git a/lftools/cli/__init__.py b/lftools/cli/__init__.py index b36abded..69c84ad0 100644 --- a/lftools/cli/__init__.py +++ b/lftools/cli/__init__.py @@ -15,6 +15,7 @@ __author__ = 'Thanh Ha' import click from lftools.cli.nexus import nexus from lftools.cli.version import version +from lftools.openstack.cmd import openstack @click.group() @@ -25,9 +26,15 @@ def cli(ctx): pass +cli.add_command(openstack) cli.add_command(nexus) cli.add_command(version) -if __name__ == '__main__': +def main(): + """Entry point for lftools CLI.""" cli(obj={}) + + +if __name__ == '__main__': + main() diff --git a/lftools/openstack/__init__.py b/lftools/openstack/__init__.py new file mode 100644 index 00000000..afb60d87 --- /dev/null +++ b/lftools/openstack/__init__.py @@ -0,0 +1,12 @@ +# @License EPL-1.0 +############################################################################## +# Copyright (c) 2017 The Linux Foundation and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +############################################################################## +"""lftools openstack package.""" + +__author__ = 'Thanh Ha' diff --git a/lftools/openstack/cmd.py b/lftools/openstack/cmd.py new file mode 100644 index 00000000..c693da03 --- /dev/null +++ b/lftools/openstack/cmd.py @@ -0,0 +1,73 @@ +# -*- code: utf-8 -*- +# @License EPL-1.0 +############################################################################## +# Copyright (c) 2017 The Linux Foundation and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +############################################################################## +"""CLI configuration for openstack command.""" + +__author__ = 'Thanh Ha' + + +import click +from lftools.openstack import image as os_image + + +@click.group() +@click.option('--os-cloud', envvar='OS_CLOUD', type=str, required=True) +@click.pass_context +def openstack(ctx, os_cloud): + """Provide an interface to OpenStack.""" + ctx.obj['os_cloud'] = os_cloud + + +@openstack.group() +@click.pass_context +def image(ctx): + """Command for manipulating images.""" + pass + + +@click.command() +@click.option( + '--days', type=int, default=0, + help='Find images older than or equal to days.') +@click.option( + '--hide-public', type=bool, default=False, + help='Ignore public images.') +@click.option( + '--clouds', type=str, default=None, + help=('Clouds (as defined in clouds.yaml) to remove images from. If not' + 'passed will assume from os-cloud parameter. (optional)')) +@click.pass_context +def cleanup(ctx, days, hide_public, clouds): + """Cleanup old images.""" + os_image.cleanup( + ctx.obj['os_cloud'], + days=days, + hide_public=hide_public, + clouds=clouds) + + +@click.command() +@click.option( + '--days', type=int, default=0, + help='Find images older than or equal to days.') +@click.option( + '--hide-public', type=bool, default=False, + help='Ignore public images.') +@click.pass_context +def list(ctx, days, hide_public): + """List cloud images.""" + os_image.list( + ctx.obj['os_cloud'], + days=days, + hide_public=hide_public) + + +image.add_command(cleanup) +image.add_command(list) diff --git a/lftools/openstack/image.py b/lftools/openstack/image.py new file mode 100644 index 00000000..1e99ac1f --- /dev/null +++ b/lftools/openstack/image.py @@ -0,0 +1,90 @@ +# -*- code: utf-8 -*- +# @License EPL-1.0 +############################################################################## +# Copyright (c) 2017 The Linux Foundation and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +############################################################################## +"""Image related sub-commands for openstack command.""" + +__author__ = 'Thanh Ha' + +from datetime import datetime +from datetime import timedelta + +import shade + + +def _filter_images(images, days=0, hide_public=False): + """Filter image data and return list. + + :arg bool hide_public: Whether or not to include public images. + """ + filtered = [] + for image in images: + if hide_public and image.is_public: + continue + if days and ( + datetime.strptime(image.created_at, '%Y-%m-%dT%H:%M:%SZ') + >= datetime.now() - timedelta(days=days)): + continue + + filtered.append(image) + return filtered + + +def list(os_cloud, days=0, hide_public=False): + """List images found according to parameters.""" + cloud = shade.openstack_cloud(cloud=os_cloud) + images = cloud.list_images() + + filtered_images = _filter_images(images, days, hide_public) + for image in filtered_images: + print(image.name) + + +def cleanup(os_cloud, days=0, hide_public=False, clouds=None): + """Remove image from cloud. + + :arg str os_cloud: Cloud name as defined in OpenStack clouds.yaml. + :arg int days: Filter images that are older than number of days. + :arg bool hide_public: If true, will ignore public images. (Default: false) + :arg str clouds: If passed, comma-separated list of clouds to remove image + from. Otherwise os_cloud will be used. + """ + def _remove_images_from_cloud(images, cloud): + print('Removing {} images from {}.'.format(len(images), cloud.cloud_config.name)) + for image in images: + try: + result = cloud.delete_image(image.name) + except shade.exc.OpenStackCloudException as e: + if str(e).startswith('Multiple matches found for'): + print('WARNING: {}. Skipping image...'.format(str(e))) + continue + else: + print('ERROR: Unexpected exception: {}'.format(str(e))) + raise + + if not result: + print('WARNING: Failed to remove \"{}\" from {}. Possibly already deleted.' + .format(image.name, cloud.cloud_config.name)) + else: + print('Removed "{}" from {}.'.format(image.name, cloud.cloud_config.name)) + + cloud = shade.openstack_cloud(cloud=os_cloud) + if clouds: + cloud_list = [] + for c in clouds.split(","): + cloud_list.append(shade.openstack_cloud(cloud=c)) + + images = cloud.list_images() + filtered_images = _filter_images(images, days, hide_public) + + if clouds: + for c in cloud_list: + _remove_images_from_cloud(filtered_images, c) + else: + _remove_images_from_cloud(filtered_images, cloud) diff --git a/requirements.txt b/requirements.txt index 6bfbd0d6..8ddebbf7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ click pyyaml requests +shade sphinx>=1.4.9 sphinxcontrib-programoutput sphinx_bootstrap_theme>=0.4.14 diff --git a/setup.py b/setup.py index 0e7c601b..2f8d4c49 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ setup( tests_require=['pytest'], entry_points=''' [console_scripts] - lftools=lftools.cli:cli + lftools=lftools.cli:main ''', scripts=[ 'shell/version',