|  | # vim: tabstop=4 shiftwidth=4 softtabstop=4 | 
|  |  | 
|  | # Copyright 2012 IBM Corp. | 
|  | # | 
|  | #    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 json | 
|  | import os | 
|  | import shutil | 
|  | import sys | 
|  |  | 
|  | from oslo.config import cfg | 
|  |  | 
|  | from tempest.common.rest_client import RestClient | 
|  | from tempest import config | 
|  |  | 
|  | CONF = config.TempestConfig() | 
|  |  | 
|  |  | 
|  | class CoverageClientJSON(RestClient): | 
|  |  | 
|  | def __init__(self, config, username, password, auth_url, tenant_name=None): | 
|  | super(CoverageClientJSON, self).__init__(config, username, password, | 
|  | auth_url, tenant_name) | 
|  | self.service = self.config.compute.catalog_type | 
|  |  | 
|  | def start_coverage(self): | 
|  | post_body = { | 
|  | 'start': {}, | 
|  | } | 
|  | post_body = json.dumps(post_body) | 
|  | return self.post('os-coverage/action', post_body, self.headers) | 
|  |  | 
|  | def start_coverage_combine(self): | 
|  | post_body = { | 
|  | 'start': { | 
|  | 'combine': True, | 
|  | }, | 
|  | } | 
|  | post_body = json.dumps(post_body) | 
|  | return self.post('os-coverage/action', post_body, self.headers) | 
|  |  | 
|  | def stop_coverage(self): | 
|  | post_body = { | 
|  | 'stop': {}, | 
|  | } | 
|  | post_body = json.dumps(post_body) | 
|  | resp, body = self.post('os-coverage/action', post_body, self.headers) | 
|  | body = json.loads(body) | 
|  | return resp, body | 
|  |  | 
|  | def report_coverage_xml(self, file=None): | 
|  | post_body = { | 
|  | 'report': { | 
|  | 'file': 'coverage.report', | 
|  | 'xml': True, | 
|  | }, | 
|  | } | 
|  | if file: | 
|  | post_body['report']['file'] = file | 
|  | post_body = json.dumps(post_body) | 
|  | resp, body = self.post('os-coverage/action', post_body, self.headers) | 
|  | body = json.loads(body) | 
|  | return resp, body | 
|  |  | 
|  | def report_coverage(self, file=None): | 
|  | post_body = { | 
|  | 'report': { | 
|  | 'file': 'coverage.report', | 
|  | }, | 
|  | } | 
|  | if file: | 
|  | post_body['report']['file'] = file | 
|  | post_body = json.dumps(post_body) | 
|  | resp, body = self.post('os-coverage/action', post_body, self.headers) | 
|  | body = json.loads(body) | 
|  | return resp, body | 
|  |  | 
|  | def report_coverage_html(self, file=None): | 
|  | post_body = { | 
|  | 'report': { | 
|  | 'file': 'coverage.report', | 
|  | 'html': True, | 
|  | }, | 
|  | } | 
|  | if file: | 
|  | post_body['report']['file'] = file | 
|  | post_body = json.dumps(post_body) | 
|  | resp, body = self.post('os-coverage/action', post_body, self.headers) | 
|  | body = json.loads(body) | 
|  | return resp, body | 
|  |  | 
|  |  | 
|  | def parse_opts(argv): | 
|  | cli_opts = [ | 
|  | cfg.StrOpt('command', | 
|  | short='c', | 
|  | default='', | 
|  | help="This required argument is used to specify the " | 
|  | "coverage command to run. Only 'start', " | 
|  | "'stop', or 'report' are valid fields."), | 
|  | cfg.StrOpt('filename', | 
|  | default='tempest-coverage', | 
|  | help="Specify a filename to be used for generated report " | 
|  | "files"), | 
|  | cfg.BoolOpt('xml', | 
|  | default=False, | 
|  | help='Generate XML reports instead of text'), | 
|  | cfg.BoolOpt('html', | 
|  | default=False, | 
|  | help='Generate HTML reports instead of text'), | 
|  | cfg.BoolOpt('combine', | 
|  | default=False, | 
|  | help='Generate a single report for all services'), | 
|  | cfg.StrOpt('output', | 
|  | short='o', | 
|  | default=None, | 
|  | help='Optional directory to copy generated coverage data or' | 
|  | ' reports into. This directory must not already exist ' | 
|  | 'it will be created') | 
|  | ] | 
|  | CLI = cfg.ConfigOpts() | 
|  | CLI.register_cli_opts(cli_opts) | 
|  | CLI(argv[1:]) | 
|  | return CLI | 
|  |  | 
|  |  | 
|  | def main(argv): | 
|  | CLI = parse_opts(argv) | 
|  | client_args = (CONF, CONF.identity.admin_username, | 
|  | CONF.identity.admin_password, CONF.identity.uri, | 
|  | CONF.identity.admin_tenant_name) | 
|  | coverage_client = CoverageClientJSON(*client_args) | 
|  |  | 
|  | if CLI.command == 'start': | 
|  | if CLI.combine: | 
|  | coverage_client.start_coverage_combine() | 
|  | else: | 
|  | coverage_client.start_coverage() | 
|  |  | 
|  | elif CLI.command == 'stop': | 
|  | resp, body = coverage_client.stop_coverage() | 
|  | if not resp['status'] == '200': | 
|  | print('coverage stop failed with: %s:' % (resp['status'] + ': ' | 
|  | + body)) | 
|  | exit(int(resp['status'])) | 
|  | path = body['path'] | 
|  | if CLI.output: | 
|  | shutil.copytree(path, CLI.output) | 
|  | else: | 
|  | print("Data files located at: %s" % path) | 
|  |  | 
|  | elif CLI.command == 'report': | 
|  | if CLI.xml: | 
|  | resp, body = coverage_client.report_coverage_xml(file=CLI.filename) | 
|  | elif CLI.html: | 
|  | resp, body = coverage_client.report_coverage_html( | 
|  | file=CLI.filename) | 
|  | else: | 
|  | resp, body = coverage_client.report_coverage(file=CLI.filename) | 
|  | if not resp['status'] == '200': | 
|  | print('coverage report failed with: %s:' % (resp['status'] + ': ' | 
|  | + body)) | 
|  | exit(int(resp['status'])) | 
|  | path = body['path'] | 
|  | if CLI.output: | 
|  | if CLI.html: | 
|  | shutil.copytree(path, CLI.output) | 
|  | else: | 
|  | path = os.path.dirname(path) | 
|  | shutil.copytree(path, CLI.output) | 
|  | else: | 
|  | if not CLI.html: | 
|  | path = os.path.dirname(path) | 
|  | print('Report files located at: %s' % path) | 
|  |  | 
|  | else: | 
|  | print('Invalid command') | 
|  | exit(1) | 
|  |  | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | main(sys.argv) |