Add stack create & delete commands 53/12353/4
authorThanh Ha <thanh.ha@linuxfoundation.org>
Tue, 21 Aug 2018 23:05:31 +0000 (19:05 -0400)
committerThanh Ha <thanh.ha@linuxfoundation.org>
Fri, 24 Aug 2018 00:42:10 +0000 (20:42 -0400)
Add 2 new commands:

    - stack create
    - stack delete

These commands allow lftools the ability to create and delete stacks.

Issue: RELENG-235
Change-Id: Ied7ff51a292199675dc944e36f1d821a5a2d045d
Signed-off-by: Thanh Ha <thanh.ha@linuxfoundation.org>
docs/commands/openstack.rst
lftools/openstack/cmd.py
lftools/openstack/stack.py [new file with mode: 0644]
releasenotes/notes/openstack-stack-08f643f16b75bfb8.yaml [new file with mode: 0644]

index eb33937..fefa86e 100644 (file)
@@ -31,3 +31,41 @@ list
 ^^^^
 
 .. program-output:: lftools openstack --os-cloud docs image list --help
+
+stack
+-----
+
+Command for managing stacks.
+
+.. program-output:: lftools openstack --os-cloud docs stack --help
+
+create
+^^^^^^
+
+Create a new stack.
+
+.. program-output:: lftools openstack --os-cloud docs stack create --help
+
+The create command requires a parameters file in the following format in order
+to build out the stack:
+
+.. code-block: yaml
+   :caption: parameter_file
+
+   parameters:
+     job_name: JOB_NAME
+     silo: SILO
+     vm_0_count: 1
+     vm_0_flavor: odl-highcpu-4
+     vm_0_image: ZZCI - CentOS 7 - builder - 20180802-220823.782
+     vm_1_count: 1
+     vm_1_flavor: odl-standard-4
+     vm_1_image: ZZCI - CentOS 7 - devstack-pike - 20171208-1649
+
+
+delete
+^^^^^^
+
+Delete existing stack.
+
+.. program-output:: lftools openstack --os-cloud docs stack delete --help
index 0a110de..a0b6c5f 100644 (file)
@@ -17,6 +17,7 @@ import click
 
 from lftools.openstack import image as os_image
 from lftools.openstack import server as os_server
+from lftools.openstack import stack as os_stack
 from lftools.openstack import volume as os_volume
 
 
@@ -134,6 +135,53 @@ server.add_command(list)
 server.add_command(remove)
 
 
+@openstack.group()
+@click.pass_context
+def stack(ctx):
+    """Command for manipulating stacks."""
+    pass
+
+
+@click.command()
+@click.argument('name')
+@click.argument('template_file')
+@click.argument('parameter_file')
+@click.option(
+    '--timeout', type=int, default=900,
+    help='Stack create timeout in seconds.')
+@click.option(
+    '--tries', type=int, default=2,
+    help='Number of tries before giving up.')
+@click.pass_context
+def create(ctx, name, template_file, parameter_file, timeout, tries):
+    """Create stack."""
+    os_stack.create(
+        ctx.obj['os_cloud'],
+        name,
+        template_file,
+        parameter_file,
+        timeout,
+        tries)
+
+
+@click.command()
+@click.argument('name_or_id')
+@click.option(
+    '--timeout', type=int, default=900,
+    help='Stack delete timeout in seconds.')
+@click.pass_context
+def delete(ctx, name_or_id, timeout):
+    """Create stack."""
+    os_stack.delete(
+        ctx.obj['os_cloud'],
+        name_or_id,
+        timeout)
+
+
+stack.add_command(create)
+stack.add_command(delete)
+
+
 @openstack.group()
 @click.pass_context
 def volume(ctx):
diff --git a/lftools/openstack/stack.py b/lftools/openstack/stack.py
new file mode 100644 (file)
index 0000000..5a76498
--- /dev/null
@@ -0,0 +1,103 @@
+# -*- code: utf-8 -*-
+# SPDX-License-Identifier: EPL-1.0
+##############################################################################
+# Copyright (c) 2018 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
+##############################################################################
+"""stack related sub-commands for openstack command."""
+
+__author__ = 'Thanh Ha'
+
+import sys
+import time
+
+import shade
+
+
+def create(os_cloud, name, template_file, parameter_file, timeout=900, tries=2):
+    """Create a heat stack from a template_file and a parameter_file."""
+    cloud = shade.openstack_cloud(cloud=os_cloud)
+    stack_success = False
+
+    print('Creating stack {}'.format(name))
+    for i in range(tries):
+        try:
+            stack = cloud.create_stack(
+                name,
+                template_file=template_file,
+                environment_files=[parameter_file],
+                timeout=timeout,
+                rollback=False)
+        except shade.exc.OpenStackCloudHTTPError as e:
+            if cloud.search_stacks(name):
+                print('Stack with name {} already exists.'.format(name))
+            else:
+                print(e)
+            sys.exit(1)
+
+        stack_id = stack.id
+        t_end = time.time() + timeout
+        while time.time() < t_end:
+            time.sleep(10)
+            stack = cloud.get_stack(stack_id)
+
+            if stack.stack_status == 'CREATE_IN_PROGRESS':
+                print('Waiting to initialize infrastructure...')
+            elif stack.stack_status == 'CREATE_COMPLETE':
+                print('Stack initialization successful.')
+                stack_success = True
+                break
+            elif stack.stack_status == 'CREATE_FAILED':
+                print('WARN: Failed to initialize stack. Reason: {}'.format(
+                    stack.stack_status_reason))
+                if delete(os_cloud, stack_id):
+                    break
+            else:
+                print('Unexpected status: {}'.format(stack.stack_status))
+
+        if stack_success:
+            break
+
+    print('------------------------------------')
+    print('Stack Details')
+    print('------------------------------------')
+    cloud.pprint(stack)
+    print('------------------------------------')
+
+
+def delete(os_cloud, name_or_id, timeout=900):
+    """Delete a stack.
+
+    Return True if delete was successful.
+    """
+    cloud = shade.openstack_cloud(cloud=os_cloud)
+    print('Deleting stack {}'.format(name_or_id))
+    cloud.delete_stack(name_or_id)
+
+    t_end = time.time() + timeout
+    while time.time() < t_end:
+        time.sleep(10)
+        stack = cloud.get_stack(name_or_id)
+
+        if not stack or stack.stack_status == 'DELETE_COMPLETE':
+            print('Successfully deleted stack {}'.format(name_or_id))
+            return True
+        elif stack.stack_status == 'DELETE_IN_PROGRESS':
+            print('Waiting for stack to delete...')
+        elif stack.stack_status == 'DELETE_FAILED':
+            print('WARN: Failed to delete $STACK_NAME. Reason: {}'.format(
+                stack.stack_status_reason))
+            print('Retrying delete...')
+            cloud.delete_stack(name_or_id)
+        else:
+            print('WARN: Unexpected delete status: {}'.format(
+                stack.stack_status))
+            print('Retrying delete...')
+            cloud.delete_stack(name_or_id)
+
+    print('Failed to delete stack.')
+    return False
diff --git a/releasenotes/notes/openstack-stack-08f643f16b75bfb8.yaml b/releasenotes/notes/openstack-stack-08f643f16b75bfb8.yaml
new file mode 100644 (file)
index 0000000..590e6b9
--- /dev/null
@@ -0,0 +1,20 @@
+---
+prelude: >
+    Add new stack command feature to allow creation and deletion of stacks
+    using lftools.
+
+    Usage: lftools openstack stack create|delete <params>
+features:
+  - |
+    Add stack command.
+    https://jira.linuxfoundation.org/browse/RELENG-235
+  - |
+    Add stack create sub-command.
+    https://jira.linuxfoundation.org/browse/RELENG-235
+
+    Usage: lftools openstack stack create NAME TEMPLATE_FILE PARAMETER_FILE
+  - |
+    Add stack delete sub-command.
+    https://jira.linuxfoundation.org/browse/RELENG-235
+
+    Usage: lftools openstack stack create NAME