Add tempest init command to tempest cli
This commit adds a new 'tempest init' command to the unified cli.
This command tempest init will be the first step of the new workflow
for running tempest. It is used to initilize a local working directory
to run tempest from. The idea being you can setup different local
working dirs for different tempest envs to test against several
different clouds.
The basic idea for this new workflow after installing tempest is
something like:
1. mkdir tempest-newcloud
2. cd tempest-newcloud
3. tempest init
4. edit local config file
5. tempest run
The next step after this is to start working on the 'tempest run'
command. (which will include the hooks for the tempest plugin
interface) But, until that is added a test runner can be called
directly inside a local tempest working directory.
Partially Implements bp tempest-cli-improvements
Change-Id: I572267e61ba8ca86f92299e174864a7a89597207
diff --git a/setup.cfg b/setup.cfg
index 36b2270..f28c481 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -32,7 +32,8 @@
tempest-cleanup = tempest.cmd.cleanup:main
tempest-account-generator = tempest.cmd.account_generator:main
tempest = tempest.cmd.main:main
-
+tempest.cm =
+ init = tempest.cmd.init:TempestInit
oslo.config.opts =
tempest.config = tempest.config:list_opts
diff --git a/tempest/cmd/init.py b/tempest/cmd/init.py
new file mode 100644
index 0000000..c13fbe5
--- /dev/null
+++ b/tempest/cmd/init.py
@@ -0,0 +1,99 @@
+# Copyright 2015 Hewlett-Packard Development Company, L.P.
+#
+# 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 os
+import shutil
+import subprocess
+
+from cliff import command
+from oslo_log import log as logging
+from six import moves
+
+LOG = logging.getLogger(__name__)
+
+TESTR_CONF = """[DEFAULT]
+test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \\
+ OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \\
+ OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-500} \\
+ ${PYTHON:-python} -m subunit.run discover -t %s %s $LISTOPT $IDOPTION
+test_id_option=--load-list $IDFILE
+test_list_option=--list
+group_regex=([^\.]*\.)*
+"""
+
+
+class TempestInit(command.Command):
+ """Setup a local working environment for running tempest"""
+
+ def get_parser(self, prog_name):
+ parser = super(TempestInit, self).get_parser(prog_name)
+ parser.add_argument('dir', nargs='?', default=os.getcwd())
+ parser.add_argument('--config-dir', '-c', default='/etc/tempest')
+ return parser
+
+ def generate_testr_conf(self, local_path):
+ testr_conf_path = os.path.join(local_path, '.testr.conf')
+ top_level_path = os.path.dirname(os.path.dirname(__file__))
+ discover_path = os.path.join(top_level_path, 'test_discover')
+ testr_conf = TESTR_CONF % (top_level_path, discover_path)
+ with open(testr_conf_path, 'w+') as testr_conf_file:
+ testr_conf_file.write(testr_conf)
+
+ def update_local_conf(self, conf_path, lock_dir, log_dir):
+ config_parse = moves.configparser.SafeConfigParser()
+ config_parse.optionxform = str
+ with open(conf_path, 'w+') as conf_file:
+ config_parse.readfp(conf_file)
+ # Set local lock_dir in tempest conf
+ if not config_parse.has_section('oslo_concurrency'):
+ config_parse.add_section('oslo_concurrency')
+ config_parse.set('oslo_concurrency', 'lock_path', lock_dir)
+ # Set local log_dir in tempest conf
+ config_parse.set('DEFAULT', 'log_dir', log_dir)
+ # Set default log filename to tempest.log
+ config_parse.set('DEFAULT', 'log_file', 'tempest.log')
+
+ def copy_config(self, etc_dir, config_dir):
+ shutil.copytree(config_dir, etc_dir)
+
+ def create_working_dir(self, local_dir, config_dir):
+ # Create local dir if missing
+ if not os.path.isdir(local_dir):
+ LOG.debug('Creating local working dir: %s' % local_dir)
+ os.mkdir(local_dir)
+ lock_dir = os.path.join(local_dir, 'tempest_lock')
+ etc_dir = os.path.join(local_dir, 'etc')
+ config_path = os.path.join(etc_dir, 'tempest.conf')
+ log_dir = os.path.join(local_dir, 'logs')
+ testr_dir = os.path.join(local_dir, '.testrepository')
+ # Create lock dir
+ if not os.path.isdir(lock_dir):
+ LOG.debug('Creating lock dir: %s' % lock_dir)
+ os.mkdir(lock_dir)
+ # Create log dir
+ if not os.path.isdir(log_dir):
+ LOG.debug('Creating log dir: %s' % log_dir)
+ os.mkdir(log_dir)
+ # Create and copy local etc dir
+ self.copy_config(etc_dir, config_dir)
+ # Update local confs to reflect local paths
+ self.update_local_conf(config_path, lock_dir, log_dir)
+ # Generate a testr conf file
+ self.generate_testr_conf(local_dir)
+ # setup local testr working dir
+ if not os.path.isdir(testr_dir):
+ subprocess.call(['testr', 'init'], cwd=local_dir)
+
+ def take_action(self, parsed_args):
+ self.create_working_dir(parsed_args.dir, parsed_args.config_dir)
diff --git a/tempest/tests/cmd/test_tempest_init.py b/tempest/tests/cmd/test_tempest_init.py
new file mode 100644
index 0000000..6b5af7e
--- /dev/null
+++ b/tempest/tests/cmd/test_tempest_init.py
@@ -0,0 +1,66 @@
+# Copyright 2015 Hewlett-Packard Development Company, L.P.
+#
+# 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 os
+
+import fixtures
+
+from tempest.cmd import init
+from tempest.tests import base
+
+
+class TestTempestInit(base.TestCase):
+
+ def test_generate_testr_conf(self):
+ # Create fake conf dir
+ conf_dir = self.useFixture(fixtures.TempDir())
+
+ init_cmd = init.TempestInit(None, None)
+ init_cmd.generate_testr_conf(conf_dir.path)
+
+ # Generate expected file contents
+ top_level_path = os.path.dirname(os.path.dirname(init.__file__))
+ discover_path = os.path.join(top_level_path, 'test_discover')
+ testr_conf_file = init.TESTR_CONF % (top_level_path, discover_path)
+
+ conf_path = conf_dir.join('.testr.conf')
+ conf_file = open(conf_path, 'r')
+ self.addCleanup(conf_file.close)
+ self.assertEqual(conf_file.read(), testr_conf_file)
+
+ def test_create_working_dir(self):
+ fake_local_dir = self.useFixture(fixtures.TempDir())
+ fake_local_conf_dir = self.useFixture(fixtures.TempDir())
+ # Create a fake conf file
+ fake_file = fake_local_conf_dir.join('conf_file.conf')
+ open(fake_file, 'w').close()
+ init_cmd = init.TempestInit(None, None)
+ init_cmd.create_working_dir(fake_local_dir.path,
+ fake_local_conf_dir.path)
+ # Assert directories are created
+ lock_path = os.path.join(fake_local_dir.path, 'tempest_lock')
+ etc_dir = os.path.join(fake_local_dir.path, 'etc')
+ log_dir = os.path.join(fake_local_dir.path, 'logs')
+ testr_dir = os.path.join(fake_local_dir.path, '.testrepository')
+ self.assertTrue(os.path.isdir(lock_path))
+ self.assertTrue(os.path.isdir(etc_dir))
+ self.assertTrue(os.path.isdir(log_dir))
+ self.assertTrue(os.path.isdir(testr_dir))
+ # Assert file creation
+ fake_file_moved = os.path.join(etc_dir, 'conf_file.conf')
+ local_conf_file = os.path.join(etc_dir, 'tempest.conf')
+ local_testr_conf = os.path.join(fake_local_dir.path, '.testr.conf')
+ self.assertTrue(os.path.isfile(fake_file_moved))
+ self.assertTrue(os.path.isfile(local_conf_file))
+ self.assertTrue(os.path.isfile(local_testr_conf))