| #!/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 |
| |
| [image] |
| host = %(service_host)s |
| port = 9292 |
| username = %(user)s |
| password = %(password)s |
| tenant = %(user)s |
| auth_url = http://127.0.0.1:5000/v2.0/tokens/ |
| strategy = keystone |
| service_token = servicetoken |
| |
| [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) |