from yaml files in the suite.
The 'description' element should contain a list with a dictionary
-of fields, e.g.:
+of key/value pairs for entries, i.e.:
description:
- field1: value1
include [default: desc]
--show-facet [yes|no] List the facet of each file
[default: yes]
+
+options only for describing combinations represented by a suite:
+ -c, --combinations Describe test combinations rather than
+ individual yaml fragments
+ --filter <keywords> Only list jobs whose filenames contain at
+ least one of the keywords in the comma
+ separated keyword string specified.
+ --filter-out <keywords> Do not list jobs whose filenames contain
+ any of the keywords in the comma separated
+ keyword string specified.
+ -l <jobs>, --limit <jobs> List at most this many jobs
+ [default: 0]
+ --subset <index/outof> Instead of listing the entire
+ suite, break the set of jobs into
+ <outof> pieces (each of which
+ will contain each facet at least
+ once) and list piece <index>.
+ Listing 0/<outof>, 1/<outof>,
+ 2/<outof> ... <outof>-1/<outof>
+ will list all jobs in the
+ suite (many more than once).
"""
# -*- coding: utf-8 -*-
+
from prettytable import PrettyTable, FRAME, ALL
import os
import yaml
from teuthology.exceptions import ParseError
+from teuthology.suite import build_matrix, combine_path
def main(args):
suite_dir = os.path.abspath(args["<suite_dir>"])
fields = args["--fields"].split(',')
include_facet = args['--show-facet'] == 'yes'
+ if args['--combinations']:
+ limit = int(args['--limit'])
+ filter_in = None
+ if args['--filter']:
+ filter_in = [f.strip() for f in args['--filter'].split(',')]
+ filter_out = None
+ if args['--filter-out']:
+ filter_out = [f.strip() for f in args['--filter-out'].split(',')]
+ subset = None
+ if args['--subset']:
+ subset = map(int, args['--subset'].split('/'))
+ describe_combinations(suite_dir, fields, subset,
+ limit, filter_in, filter_out,
+ include_facet)
+ else:
+ describe_suite(suite_dir, fields, include_facet)
+
+def get_combinations(suite_dir, fields, subset,
+ limit, filter_in, filter_out,
+ include_facet, _isdir=os.path.isdir, _open=open,
+ _isfile=os.path.isfile, _listdir=os.listdir):
+ configs = [(combine_path(suite_dir, item[0]), item[1]) for item in
+ build_matrix(suite_dir, _isfile, _isdir, _listdir, subset)]
+
+ num_listed = 0
+ rows = []
+
+ facet_headers = set()
+
+ for _, fragment_paths in configs:
+ if limit > 0 and num_listed >= limit:
+ break
+ if filter_in and not any([f in path for f in filter_in
+ for path in fragment_paths]):
+ continue
+ if filter_out and any([f in path for f in filter_out
+ for path in fragment_paths]):
+ continue
+
+ fragment_fields = [extract_info(path, fields, _isdir, _open)
+ for path in fragment_paths]
+
+ # merge fields from multiple fragments by joining their values with \n
+ metadata = {}
+ for fragment_meta in fragment_fields:
+ for field, value in fragment_meta.items():
+ if value == '':
+ continue
+ if field in metadata:
+ metadata[field] += '\n' + str(value)
+ else:
+ metadata[field] = str(value)
+
+ if include_facet:
+ # map final dir (facet) -> filename without the .yaml suffix
+ for path in fragment_paths:
+ facet = os.path.basename(os.path.dirname(path))
+ metadata[facet] = os.path.basename(path)[:-5]
+ facet_headers.add(facet)
+
+ rows.append(metadata)
+ num_listed += 1
+
+ headers = sorted(facet_headers) + fields
+ return headers, [[row.get(field, '') for field in headers] for row in rows]
+
+def describe_combinations(suite_dir, fields, subset,
+ limit, filter_in, filter_out,
+ include_facet):
+ headers, rows = get_combinations(suite_dir, fields, subset,
+ limit, filter_in, filter_out,
+ include_facet)
+
+ table = PrettyTable(headers)
+ table.align = 'l'
+ table.vrules = ALL
+ table.hrules = ALL
+
+ for row in rows:
+ table.add_row(row)
+
+ print(table)
+
+def describe_suite(suite_dir, fields, include_facet):
try:
rows = tree_with_info(suite_dir, fields, include_facet, '', [])
except ParseError:
import pytest
from fake_fs import make_fake_fstools
-from teuthology.describe_tests import tree_with_info, extract_info
+from teuthology.describe_tests import (tree_with_info, extract_info,
+ get_combinations)
from teuthology.exceptions import ParseError
realistic_fs = {
class TestDescribeTests(object):
def setup(self):
- self.fake_listdir, _, self.fake_isdir, self.fake_open = \
+ self.fake_listdir, self.fake_isfile, self.fake_isdir, self.fake_open = \
make_fake_fstools(realistic_fs)
def test_no_filters(self):
expected_rbd_features,
expected_desc))
+ def test_combinations_only_facets(self):
+ headers, rows = get_combinations('basic', [], None, 1, None, None, True,
+ self.fake_isdir, self.fake_open,
+ self.fake_isfile, self.fake_listdir)
+ assert headers == sorted(set(filter(bool, expected_facets)))
+ assert rows == [['install', 'fixed-1', 'rbd_api_tests']]
+
+ def test_combinations_desc_features(self):
+ headers, rows = get_combinations('basic', ['desc', 'rbd_features'],
+ None, 1, None, None, False,
+ self.fake_isdir, self.fake_open,
+ self.fake_isfile, self.fake_listdir)
+ assert headers == ['desc', 'rbd_features']
+ assert rows == [['install ceph\nsingle node cluster\nc/c++ librbd api tests with default settings',
+ 'default']]
+
+ def test_combinations_filter_in(self):
+ headers, rows = get_combinations('basic', [], None, 0, ['old_format'],
+ None, True,
+ self.fake_isdir, self.fake_open,
+ self.fake_isfile, self.fake_listdir)
+ assert headers == sorted(set(filter(bool, expected_facets)))
+ assert rows == [['install', 'fixed-1', 'rbd_api_tests_old_format']]
+
+ def test_combinations_filter_out(self):
+ headers, rows = get_combinations('basic', [], None, 0, None,
+ ['old_format'], True,
+ self.fake_isdir, self.fake_open,
+ self.fake_isfile, self.fake_listdir)
+ assert headers == sorted(set(filter(bool, expected_facets)))
+ assert rows == [['install', 'fixed-1', 'rbd_api_tests']]
+
def test_extract_info_dir():
simple_fs = {'a': {'b.yaml': 'description: [{foo: c}]'}}