]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Move jeos_create into utils so it can be imported
authorJeff Peeler <jpeeler@redhat.com>
Thu, 26 Apr 2012 20:51:00 +0000 (16:51 -0400)
committerJeff Peeler <jpeeler@redhat.com>
Thu, 26 Apr 2012 21:05:26 +0000 (17:05 -0400)
The main reason for this move is so that jeos_create can be tested with
the aide of Mox.

Signed-off-by: Jeff Peeler <jpeeler@redhat.com>
bin/heat
heat/utils.py

index 2b5e01b25d68249c9cd8499b736db7d26b3ee70d..51f112d2e19c186c1177b53670d3ec0f35d3bace 100755 (executable)
--- a/bin/heat
+++ b/bin/heat
@@ -20,16 +20,12 @@ belonging to a user.  It is a convenience application that talks to the heat
 API server.
 """
 
-import functools
 import gettext
 import optparse
 import os
 import sys
 import time
 import json
-import base64
-import libxml2
-import re
 import logging
 
 from urlparse import urlparse
@@ -51,47 +47,14 @@ else:
 
 gettext.install('heat', unicode=1)
 
-from glance import client as glance_client
 from heat import client as heat_client
 from heat import version
 from heat.common import config
 from heat.common import exception
+from heat import utils
 
 
-SUCCESS = 0
-FAILURE = 1
-
-
-def catch_error(action):
-    """Decorator to provide sensible default error handling for actions."""
-    def wrap(func):
-        @functools.wraps(func)
-        def wrapper(*arguments, **kwargs):
-            try:
-                ret = func(*arguments, **kwargs)
-                return SUCCESS if ret is None else ret
-            except exception.NotAuthorized:
-                logging.error("Not authorized to make this request. Check " +\
-                      "your credentials (OS_USERNAME, OS_PASSWORD, " +\
-                      "OS_TENANT_NAME, OS_AUTH_URL and OS_AUTH_STRATEGY).")
-                return FAILURE
-            except exception.ClientConfigurationError:
-                raise
-            except Exception, e:
-                options = arguments[0]
-                if options.debug:
-                    raise
-                logging.error("Failed to %s. Got error:" % action)
-                pieces = unicode(e).split('\n')
-                for piece in pieces:
-                    logging.error(piece)
-                return FAILURE
-
-        return wrapper
-    return wrap
-
-
-@catch_error('validate')
+@utils.catch_error('validate')
 def template_validate(options, arguments):
     '''
     Validate a template.  This command parses a template and verifies that
@@ -110,14 +73,14 @@ def template_validate(options, arguments):
         parameters['TemplateUrl'] = options.template_url
     else:
         logging.error('Please specify a template file or url')
-        return FAILURE
+        return utils.FAILURE
 
     client = get_client(options)
     result = client.validate_template(**parameters)
     print json.dumps(result, indent=2)
 
 
-@catch_error('gettemplate')
+@utils.catch_error('gettemplate')
 def get_template(options, arguments):
     '''
     Gets an existing stack template.
@@ -127,7 +90,7 @@ def get_template(options, arguments):
     pass
 
 
-@catch_error('create')
+@utils.catch_error('create')
 def stack_create(options, arguments):
     '''
     Create a new stack from a template.
@@ -150,7 +113,7 @@ def stack_create(options, arguments):
     except IndexError:
         logging.error("Please specify the stack name you wish to create")
         logging.error("as the first argument")
-        return FAILURE
+        return utils.FAILURE
 
     if options.parameters:
         count = 1
@@ -166,14 +129,14 @@ def stack_create(options, arguments):
         parameters['TemplateUrl'] = options.template_url
     else:
         logging.error('Please specify a template file or url')
-        return FAILURE
+        return utils.FAILURE
 
     c = get_client(options)
     result = c.create_stack(**parameters)
     print json.dumps(result, indent=2)
 
 
-@catch_error('update')
+@utils.catch_error('update')
 def stack_update(options, arguments):
     '''
     Update an existing stack.
@@ -199,7 +162,7 @@ def stack_update(options, arguments):
     except IndexError:
         logging.error("Please specify the stack name you wish to update")
         logging.error("as the first argument")
-        return FAILURE
+        return utils.FAILURE
 
     if options.template_file:
         parameters['TemplateBody'] = open(options.template_file).read()
@@ -219,7 +182,7 @@ def stack_update(options, arguments):
     print json.dumps(result, indent=2)
 
 
-@catch_error('delete')
+@utils.catch_error('delete')
 def stack_delete(options, arguments):
     '''
     Delete an existing stack.  This shuts down all VMs associated with
@@ -234,14 +197,14 @@ def stack_delete(options, arguments):
     except IndexError:
         logging.error("Please specify the stack name you wish to delete")
         logging.error("as the first argument")
-        return FAILURE
+        return utils.FAILURE
 
     c = get_client(options)
     result = c.delete_stack(**parameters)
     print json.dumps(result, indent=2)
 
 
-@catch_error('describe')
+@utils.catch_error('describe')
 def stack_describe(options, arguments):
     '''
     Describes an existing stack.
@@ -254,14 +217,14 @@ def stack_describe(options, arguments):
     except IndexError:
         logging.error("Please specify the stack name you wish to describe")
         logging.error("as the first argument")
-        return FAILURE
+        return utils.FAILURE
 
     c = get_client(options)
     result = c.describe_stacks(**parameters)
     print json.dumps(result, indent=2)
 
 
-@catch_error('events_list')
+@utils.catch_error('events_list')
 def stack_events_list(options, arguments):
     '''
     List events associated with the given stack.
@@ -279,7 +242,7 @@ def stack_events_list(options, arguments):
     print json.dumps(result, indent=2)
 
 
-@catch_error('list')
+@utils.catch_error('list')
 def stack_list(options, arguments):
     '''
     List all running stacks.
@@ -291,7 +254,7 @@ def stack_list(options, arguments):
     print json.dumps(result, indent=2)
 
 
-@catch_error('jeos_create')
+@utils.catch_error('jeos_create')
 def jeos_create(options, arguments):
     '''
     Create a new JEOS (Just Enough Operating System) image.
@@ -307,196 +270,7 @@ def jeos_create(options, arguments):
     The command must be run as root in order for libvirt to have permissions
     to create virtual machines and read the raw DVDs.
     '''
-    global jeos_path
-    global cfntools_path
-
-    # if not running as root, return EPERM to command line
-    if os.geteuid() != 0:
-        logging.error("jeos_create must be run as root")
-        sys.exit(1)
-    if len(arguments) < 3:
-        print '\n  Please provide the distro, arch, and instance type.'
-        print '  Usage:'
-        print '   heat jeos_create <distro> <arch> <instancetype>'
-        print '     instance type can be:'
-        print '     gold builds a base image where userdata is used to' \
-              ' initialize the instance'
-        print '     cfntools builds a base image where AWS CloudFormation' \
-              ' tools are present'
-        sys.exit(1)
-
-    distro = arguments.pop(0)
-    arch = arguments.pop(0)
-    instance_type = arguments.pop(0)
-    images_dir = '/var/lib/libvirt/images'
-
-    arches = ('x86_64', 'i386', 'amd64')
-    arches_str = " | ".join(arches)
-    instance_types = ('gold', 'cfntools')
-    instances_str = " | ".join(instance_types)
-
-    if not arch in arches:
-        logging.error('arch %s not supported' % arch)
-        logging.error('try: heat jeos_create %s [ %s ]' % (distro, arches_str))
-        sys.exit(1)
-
-    if not instance_type in instance_types:
-        logging.error('A JEOS instance type of %s not supported' %\
-            instance_type)
-        logging.error('try: heat jeos_create %s %s [ %s ]' %\
-            (distro, arch, instances_str))
-        sys.exit(1)
-
-    fedora_match = re.match('F(1[6-7])', distro)
-    if fedora_match:
-        version = fedora_match.group(1)
-        iso = '%s/Fedora-%s-%s-DVD.iso' % (images_dir, version, arch)
-    elif distro == 'U10':
-        iso = '%s/ubuntu-10.04.3-server-%s.iso' % (images_dir, arch)
-    else:
-        logging.error('distro %s not supported' % distro)
-        logging.error('try: F16, F17 or U10')
-        sys.exit(1)
-
-    if not os.access(iso, os.R_OK):
-        logging.error('*** %s does not exist.' % (iso))
-        sys.exit(1)
-
-    tdl_path = '%s%s-%s-%s-jeos.tdl' % (jeos_path, distro, arch, instance_type)
-    if options.debug:
-        print "Using tdl: %s" % tdl_path
-
-    # Load the cfntools into the cfntool image by encoding them in base64
-    # and injecting them into the TDL at the appropriate place
-    if instance_type == 'cfntools':
-        tdl_xml = libxml2.parseFile(tdl_path)
-        for cfnname in ['cfn-init', 'cfn-hup', 'cfn-signal', 'cfn_helper.py']:
-            f = open('%s/%s' % (cfntools_path, cfnname), 'r')
-            cfscript_e64 = base64.b64encode(f.read())
-            f.close()
-            cfnpath = "/template/files/file[@name='/opt/aws/bin/%s']" % cfnname
-            tdl_xml.xpathEval(cfnpath)[0].setContent(cfscript_e64)
-
-        # TODO(sdake) INSECURE
-        tdl_xml.saveFormatFile('/tmp/tdl', format=1)
-        tdl_path = '/tmp/tdl'
-
-    dsk_filename = '%s/%s-%s-%s-jeos.dsk' % (images_dir, distro,
-                                             arch, instance_type)
-    qcow2_filename = '%s/%s-%s-%s-jeos.qcow2' % (images_dir, distro,
-                                                 arch, instance_type)
-    image_name = '%s-%s-%s' % (distro, arch, instance_type)
-
-    if not os.access(tdl_path, os.R_OK):
-        logging.error('The tdl for that disto/arch is not available')
-        sys.exit(1)
-
-    creds = dict(username=options.username,
-                 password=options.password,
-                 tenant=options.tenant,
-                 auth_url=options.auth_url,
-                 strategy=options.auth_strategy)
-
-    client = glance_client.Client(host="0.0.0.0", port=9292,
-            use_ssl=False, auth_tok=None, creds=creds)
-
-    parameters = {
-        "filters": {},
-        "limit": 10,
-    }
-    images = client.get_images(**parameters)
-
-    image_registered = False
-    for image in images:
-        if image['name'] == distro + '-' + arch + '-' + instance_type:
-            image_registered = True
-
-    runoz = None
-    if os.access(qcow2_filename, os.R_OK):
-        while runoz not in ('y', 'n'):
-            runoz = raw_input('An existing JEOS was found on disk.' \
-                              ' Do you want to build a fresh JEOS?' \
-                              ' (y/n) ').lower()
-        if runoz == 'y':
-            os.remove(qcow2_filename)
-            os.remove(dsk_filename)
-            if image_registered:
-                client.delete_image(image['id'])
-        elif runoz == 'n':
-            answer = None
-            while answer not in ('y', 'n'):
-                answer = raw_input('Do you want to register your existing' \
-                                   ' JEOS file with glance? (y/n) ').lower()
-                if answer == 'n':
-                    logging.info('No action taken')
-                    sys.exit(0)
-                elif answer == 'y' and image_registered:
-                    answer = None
-                    while answer not in ('y', 'n'):
-                        answer = raw_input('Do you want to delete the ' \
-                                           'existing JEOS in glance?' \
-                                           ' (y/n) ').lower()
-                    if answer == 'n':
-                        logging.info('No action taken')
-                        sys.exit(0)
-                    elif answer == 'y':
-                        client.delete_image(image['id'])
-
-    if runoz == None or runoz == 'y':
-        logging.info('Creating JEOS image (%s) - '\
-                     'this takes approximately 10 minutes.' % image_name)
-        extra_opts = ' '
-        if options.debug:
-            extra_opts = ' -d 3 '
-
-        ozcmd = "oz-install %s -t 50000 -u %s -x /dev/null" % (extra_opts,
-                                                               tdl_path)
-        logging.debug("Running : %s" % ozcmd)
-        res = os.system(ozcmd)
-        if res == 256:
-            sys.exit(1)
-        if not os.access(dsk_filename, os.R_OK):
-            logging.error('oz-install did not create the image,' \
-                          ' check your oz installation.')
-            sys.exit(1)
-
-        logging.info('Converting raw disk image to a qcow2 image.')
-        os.system("qemu-img convert -O qcow2 %s %s" % (dsk_filename,
-                                                       qcow2_filename))
-
-    logging.info('Registering JEOS image (%s) ' \
-                 'with OpenStack Glance.' % image_name)
-
-    image_meta = {'name': image_name,
-                  'is_public': True,
-                  'disk_format': 'qcow2',
-                  'min_disk': 0,
-                  'min_ram': 0,
-                  'owner': options.username,
-                  'container_format': 'bare'}
-
-    try:
-        with open(qcow2_filename) as ifile:
-            image_meta = client.add_image(image_meta, ifile)
-        image_id = image_meta['id']
-        logging.debug(" Added new image with ID: %s" % image_id)
-        logging.debug(" Returned the following metadata for the new image:")
-        for k, v in sorted(image_meta.items()):
-            logging.debug(" %(k)30s => %(v)s" % locals())
-    except exception.ClientConnectionError, e:
-        logging.error((" Failed to connect to the Glance API server." +\
-               " Is the server running?" % locals()))
-        pieces = unicode(e).split('\n')
-        for piece in pieces:
-            logging.error(piece)
-            sys.exit(1)
-    except Exception, e:
-        logging.error(" Failed to add image. Got error:")
-        pieces = unicode(e).split('\n')
-        for piece in pieces:
-            logging.error(piece)
-        logging.warning(" Note: Your image metadata may still be in the " +\
-               "registry, but the image's status will likely be 'killed'.")
+    utils.jeos_create(options, arguments)
 
 
 def get_client(options):
index c71dadcb68c274d0e24c531d77b38f2f40c33950..df4756fe4e768825adc3126a4f47ed7a4d7d57b9 100644 (file)
@@ -1,3 +1,271 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import functools
+import os
+import sys
+import base64
+import libxml2
+import re
+import logging
+
+from glance import client as glance_client
+from heat.common import exception
+
+
+SUCCESS = 0
+FAILURE = 1
+
+
+def catch_error(action):
+    """Decorator to provide sensible default error handling for CLI actions."""
+    def wrap(func):
+        @functools.wraps(func)
+        def wrapper(*arguments, **kwargs):
+            try:
+                ret = func(*arguments, **kwargs)
+                return SUCCESS if ret is None else ret
+            except exception.NotAuthorized:
+                logging.error("Not authorized to make this request. Check " +\
+                      "your credentials (OS_USERNAME, OS_PASSWORD, " +\
+                      "OS_TENANT_NAME, OS_AUTH_URL and OS_AUTH_STRATEGY).")
+                return FAILURE
+            except exception.ClientConfigurationError:
+                raise
+            except Exception, e:
+                options = arguments[0]
+                if options.debug:
+                    raise
+                logging.error("Failed to %s. Got error:" % action)
+                pieces = unicode(e).split('\n')
+                for piece in pieces:
+                    logging.error(piece)
+                return FAILURE
+
+        return wrapper
+    return wrap
+
+
+@catch_error('jeos_create')
+def jeos_create(options, arguments):
+    '''
+    Create a new JEOS (Just Enough Operating System) image.
+
+    Usage: heat jeos_create <distribution> <architecture> <image type>
+
+    Distribution: Distribution such as 'F16', 'F17', 'U10', 'D6'.
+    Architecture: Architecture such as 'i386' 'i686' or 'x86_64'.
+    Image Type: Image type such as 'gold' or 'cfntools'.
+                'gold' is a basic gold JEOS.
+                'cfntools' contains the cfntools helper scripts.
+
+    The command must be run as root in order for libvirt to have permissions
+    to create virtual machines and read the raw DVDs.
+    '''
+    global jeos_path
+    global cfntools_path
+
+    # if not running as root, return EPERM to command line
+    if os.geteuid() != 0:
+        logging.error("jeos_create must be run as root")
+        sys.exit(1)
+    if len(arguments) < 3:
+        print '\n  Please provide the distro, arch, and instance type.'
+        print '  Usage:'
+        print '   heat jeos_create <distro> <arch> <instancetype>'
+        print '     instance type can be:'
+        print '     gold builds a base image where userdata is used to' \
+              ' initialize the instance'
+        print '     cfntools builds a base image where AWS CloudFormation' \
+              ' tools are present'
+        sys.exit(1)
+
+    distro = arguments.pop(0)
+    arch = arguments.pop(0)
+    instance_type = arguments.pop(0)
+    images_dir = '/var/lib/libvirt/images'
+
+    arches = ('x86_64', 'i386', 'amd64')
+    arches_str = " | ".join(arches)
+    instance_types = ('gold', 'cfntools')
+    instances_str = " | ".join(instance_types)
+
+    if not arch in arches:
+        logging.error('arch %s not supported' % arch)
+        logging.error('try: heat jeos_create %s [ %s ]' % (distro, arches_str))
+        sys.exit(1)
+
+    if not instance_type in instance_types:
+        logging.error('A JEOS instance type of %s not supported' %\
+            instance_type)
+        logging.error('try: heat jeos_create %s %s [ %s ]' %\
+            (distro, arch, instances_str))
+        sys.exit(1)
+
+    fedora_match = re.match('F(1[6-7])', distro)
+    if fedora_match:
+        version = fedora_match.group(1)
+        iso = '%s/Fedora-%s-%s-DVD.iso' % (images_dir, version, arch)
+    elif distro == 'U10':
+        iso = '%s/ubuntu-10.04.3-server-%s.iso' % (images_dir, arch)
+    else:
+        logging.error('distro %s not supported' % distro)
+        logging.error('try: F16, F17 or U10')
+        sys.exit(1)
+
+    if not os.access(iso, os.R_OK):
+        logging.error('*** %s does not exist.' % (iso))
+        sys.exit(1)
+
+    tdl_path = '%s%s-%s-%s-jeos.tdl' % (jeos_path, distro, arch, instance_type)
+    if options.debug:
+        print "Using tdl: %s" % tdl_path
+
+    # Load the cfntools into the cfntool image by encoding them in base64
+    # and injecting them into the TDL at the appropriate place
+    if instance_type == 'cfntools':
+        tdl_xml = libxml2.parseFile(tdl_path)
+        for cfnname in ['cfn-init', 'cfn-hup', 'cfn-signal', 'cfn_helper.py']:
+            f = open('%s/%s' % (cfntools_path, cfnname), 'r')
+            cfscript_e64 = base64.b64encode(f.read())
+            f.close()
+            cfnpath = "/template/files/file[@name='/opt/aws/bin/%s']" % cfnname
+            tdl_xml.xpathEval(cfnpath)[0].setContent(cfscript_e64)
+
+        # TODO(sdake) INSECURE
+        tdl_xml.saveFormatFile('/tmp/tdl', format=1)
+        tdl_path = '/tmp/tdl'
+
+    dsk_filename = '%s/%s-%s-%s-jeos.dsk' % (images_dir, distro,
+                                             arch, instance_type)
+    qcow2_filename = '%s/%s-%s-%s-jeos.qcow2' % (images_dir, distro,
+                                                 arch, instance_type)
+    image_name = '%s-%s-%s' % (distro, arch, instance_type)
+
+    if not os.access(tdl_path, os.R_OK):
+        logging.error('The tdl for that disto/arch is not available')
+        sys.exit(1)
+
+    creds = dict(username=options.username,
+                 password=options.password,
+                 tenant=options.tenant,
+                 auth_url=options.auth_url,
+                 strategy=options.auth_strategy)
+
+    client = glance_client.Client(host="0.0.0.0", port=9292,
+            use_ssl=False, auth_tok=None, creds=creds)
+
+    parameters = {
+        "filters": {},
+        "limit": 10,
+    }
+    images = client.get_images(**parameters)
+
+    image_registered = False
+    for image in images:
+        if image['name'] == distro + '-' + arch + '-' + instance_type:
+            image_registered = True
+
+    runoz = None
+    if os.access(qcow2_filename, os.R_OK):
+        while runoz not in ('y', 'n'):
+            runoz = raw_input('An existing JEOS was found on disk.' \
+                              ' Do you want to build a fresh JEOS?' \
+                              ' (y/n) ').lower()
+        if runoz == 'y':
+            os.remove(qcow2_filename)
+            os.remove(dsk_filename)
+            if image_registered:
+                client.delete_image(image['id'])
+        elif runoz == 'n':
+            answer = None
+            while answer not in ('y', 'n'):
+                answer = raw_input('Do you want to register your existing' \
+                                   ' JEOS file with glance? (y/n) ').lower()
+                if answer == 'n':
+                    logging.info('No action taken')
+                    sys.exit(0)
+                elif answer == 'y' and image_registered:
+                    answer = None
+                    while answer not in ('y', 'n'):
+                        answer = raw_input('Do you want to delete the ' \
+                                           'existing JEOS in glance?' \
+                                           ' (y/n) ').lower()
+                    if answer == 'n':
+                        logging.info('No action taken')
+                        sys.exit(0)
+                    elif answer == 'y':
+                        client.delete_image(image['id'])
+
+    if runoz == None or runoz == 'y':
+        logging.info('Creating JEOS image (%s) - '\
+                     'this takes approximately 10 minutes.' % image_name)
+        extra_opts = ' '
+        if options.debug:
+            extra_opts = ' -d 3 '
+
+        ozcmd = "oz-install %s -t 50000 -u %s -x /dev/null" % (extra_opts,
+                                                               tdl_path)
+        logging.debug("Running : %s" % ozcmd)
+        res = os.system(ozcmd)
+        if res == 256:
+            sys.exit(1)
+        if not os.access(dsk_filename, os.R_OK):
+            logging.error('oz-install did not create the image,' \
+                          ' check your oz installation.')
+            sys.exit(1)
+
+        logging.info('Converting raw disk image to a qcow2 image.')
+        os.system("qemu-img convert -O qcow2 %s %s" % (dsk_filename,
+                                                       qcow2_filename))
+
+    logging.info('Registering JEOS image (%s) ' \
+                 'with OpenStack Glance.' % image_name)
+
+    image_meta = {'name': image_name,
+                  'is_public': True,
+                  'disk_format': 'qcow2',
+                  'min_disk': 0,
+                  'min_ram': 0,
+                  'owner': options.username,
+                  'container_format': 'bare'}
+
+    try:
+        with open(qcow2_filename) as ifile:
+            image_meta = client.add_image(image_meta, ifile)
+        image_id = image_meta['id']
+        logging.debug(" Added new image with ID: %s" % image_id)
+        logging.debug(" Returned the following metadata for the new image:")
+        for k, v in sorted(image_meta.items()):
+            logging.debug(" %(k)30s => %(v)s" % locals())
+    except exception.ClientConnectionError, e:
+        logging.error((" Failed to connect to the Glance API server." +\
+               " Is the server running?" % locals()))
+        pieces = unicode(e).split('\n')
+        for piece in pieces:
+            logging.error(piece)
+            sys.exit(1)
+    except Exception, e:
+        logging.error(" Failed to add image. Got error:")
+        pieces = unicode(e).split('\n')
+        for piece in pieces:
+            logging.error(piece)
+        logging.warning(" Note: Your image metadata may still be in the " +\
+               "registry, but the image's status will likely be 'killed'.")
+
+
 class LazyPluggable(object):
     """A pluggable backend loaded lazily based on some value."""