Merge "Create a Tempest conf from a Devstack env"
diff --git a/tempest/tools/conf_from_devstack b/tempest/tools/conf_from_devstack
new file mode 100755
index 0000000..f3f1309
--- /dev/null
+++ b/tempest/tools/conf_from_devstack
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack, LLC
+# All Rights Reserved.
+#
+# 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.
+
+"""
+Simple script that analyzes a devstack environment and constructs
+a Tempest configuration file for the devstack environment.
+"""
+
+import optparse
+import os
+import subprocess
+import sys
+
+SUCCESS = 0
+FAILURE = 1
+
+
+def execute(cmd, raise_error=True):
+ """
+ Executes a command in a subprocess. Returns a tuple
+ of (exitcode, out, err), where out is the string output
+ from stdout and err is the string output from stderr when
+ executing the command.
+
+ :param cmd: Command string to execute
+ :param raise_error: If returncode is not 0 (success), then
+ raise a RuntimeError? Default: True)
+ """
+
+ process = subprocess.Popen(cmd,
+ shell=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ result = process.communicate()
+ (out, err) = result
+ exitcode = process.returncode
+ if process.returncode != 0 and raise_error:
+ msg = "Command %(cmd)s did not succeed. Returned an exit "\
+ "code of %(exitcode)d."\
+ "\n\nSTDOUT: %(out)s"\
+ "\n\nSTDERR: %(err)s" % locals()
+ raise RuntimeError(msg)
+ return exitcode, out, err
+
+
+def add_options(parser):
+ """
+ Adds CLI options to a supplied option parser
+
+ :param parser: `optparse.OptionParser`
+ """
+ parser.add_option('-o', '--outfile', metavar="PATH",
+ help=("File to save generated config to. Default: "
+ "prints config to stdout."))
+ parser.add_option('-v', '--verbose', default=False, action="store_true",
+ help="Print more verbose output")
+ parser.add_option('-D', '--devstack-dir', metavar="PATH",
+ default=os.getcwd(),
+ help="Directory to find devstack. Default: $PWD")
+
+
+def get_devstack_localrc(options):
+ """
+ Finds the localrc file in the devstack directory and returns a dict
+ representing the key/value pairs in the localrc file.
+ """
+ localrc_path = os.path.join(os.path.abspath(options.devstack_dir), 'localrc')
+ if not os.path.exists(localrc_path):
+ raise RuntimeError("Failed to find localrc file in devstack dir %s" %
+ options.devstack_dir)
+
+ if options.verbose:
+ print "Reading localrc settings from %s" % localrc_path
+
+ try:
+ settings = dict([line.split('=') for line in
+ open(localrc_path, 'r').readlines()
+ if not line.startswith('#')])
+ return settings
+ except (TypeError, ValueError) as e:
+ raise RuntimeError("Failed to read settings from localrc file %s. "
+ "Got error: %s" % (localrc_path, e))
+
+
+def main():
+ oparser = optparse.OptionParser()
+ add_options(oparser)
+
+ options, args = oparser.parse_args()
+
+ localrc = get_devstack_localrc(options)
+
+ conf_settings = {
+ 'service_host': localrc.get('HOST_IP', '127.0.0.1'),
+ 'service_port': 5000, # Make this configurable when devstack does
+ 'identity_api_version': 'v2.0', # Ditto
+ 'user': localrc.get('USERNAME', 'admin'),
+ 'password': localrc.get('ADMIN_PASSWORD', 'password')
+ }
+
+ # We need to determine the UUID of the base image, so we
+ # query the Glance endpoint for a list of images...
+ cmd = "glance index -A %s" % localrc['SERVICE_TOKEN']
+ retcode, out, err = execute(cmd)
+
+ if retcode != 0:
+ raise RuntimeError("Unable to get list of images from Glance. "
+ "Got error: %s" % err)
+
+ image_lines = out.split('\n')[2:]
+ for line in image_lines:
+ if 'ami' in line:
+ conf_settings['base_image_uuid'] = line.split()[0]
+ break
+
+ if 'base_image_uuid' not in conf_settings:
+ raise RuntimeError("Unable to find any AMI images in glance index")
+
+ if options.verbose:
+ print "Found base image with UUID %s" % conf_settings['base_image_uuid']
+
+ tempest_conf = """
+[nova]
+host=%(service_host)s
+port=%(service_port)s
+apiVer=%(identity_api_version)s
+path=tokens
+user=%(user)s
+api_key=%(password)s
+tenant_name=%(user)s
+ssh_timeout=300
+build_interval=10
+build_timeout=600
+
+[environment]
+image_ref=%(base_image_uuid)s
+image_ref_alt=4
+flavor_ref=1
+flavor_ref_alt=2
+create_image_enabled=true
+resize_available=true
+authentication=keystone_v2""" % conf_settings
+
+ if options.outfile:
+ outfile_path = os.path.abspath(options.outfile)
+ if os.path.exists(outfile_path):
+ confirm = raw_input("Output file already exists. Overwrite? [y/N]")
+ if confirm != 'Y':
+ print "Exiting"
+ return SUCCESS
+ with open(outfile_path, 'wb') as outfile:
+ outfile.write(tempest_conf)
+ if options.verbose:
+ print "Wrote tempest config to %s" % outfile_path
+ else:
+ print tempest_conf
+
+
+if __name__ == '__main__':
+ try:
+ sys.exit(main())
+ except RuntimeError, e:
+ sys.exit("ERROR: %s" % e)