diff --git a/.zuul.yaml b/.zuul.yaml index f196d70..786e634 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -1,3 +1,30 @@ +- job: + name: python-watcherclient-functional + parent: devstack-tox-functional + timeout: 7200 + required-projects: + - openstack/watcher + - openstack/python-watcherclient + vars: + # Run cross-project watcherclient functional tests on watcher repo. + zuul_work_dir: src/opendev.org/openstack/python-watcherclient + openrc_enable_export: true + devstack_plugins: + watcher: https://opendev.org/openstack/watcher + devstack_services: + watcher-api: true + watcher-decision-engine: true + watcher-applier: true + s-account: false + s-container: false + s-object: false + s-proxy: false + irrelevant-files: + - ^.*\.rst$ + - ^doc/.*$ + - ^releasenotes/.*$ + + - project: templates: - openstack-cover-jobs @@ -7,4 +34,4 @@ - openstackclient-plugin-jobs check: jobs: - - watcherclient-tempest-functional + - python-watcherclient-functional diff --git a/test-requirements.txt b/test-requirements.txt index ca03185..875fa8b 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,3 +6,4 @@ python-subunit>=1.0.0 # Apache-2.0/BSD stestr>=2.0.0 # Apache-2.0 testscenarios>=0.4 # Apache-2.0/BSD testtools>=2.2.0 # MIT +tempest>=17.1.0 # Apache-2.0 diff --git a/tox.ini b/tox.ini index fa32b8a..cb8685a 100644 --- a/tox.ini +++ b/tox.ini @@ -77,3 +77,8 @@ commands = python setup.py bdist_wheel [hacking] import_exceptions = watcherclient._i18n + +[testenv:functional] +passenv = OS_* +commands = + stestr --test-path=./watcherclient/tests/client_functional/ run --concurrency=1 {posargs} diff --git a/watcherclient/tests/client_functional/__init__.py b/watcherclient/tests/client_functional/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/watcherclient/tests/client_functional/v1/__init__.py b/watcherclient/tests/client_functional/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/watcherclient/tests/client_functional/v1/base.py b/watcherclient/tests/client_functional/v1/base.py new file mode 100644 index 0000000..69e1240 --- /dev/null +++ b/watcherclient/tests/client_functional/v1/base.py @@ -0,0 +1,141 @@ +# 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 re +import shlex +import subprocess +import testtools + +from tempest.lib.cli import output_parser +from tempest.lib import exceptions + + +def credentials(): + # You can get credentials from OS environment. + creds_dict = { + '--os-username': os.environ.get('OS_USERNAME'), + '--os-password': os.environ.get('OS_PASSWORD'), + '--os-project-name': os.environ.get('OS_PROJECT_NAME'), + '--os-auth-url': os.environ.get('OS_AUTH_URL'), + '--os-project-domain-name': os.environ.get('OS_PROJECT_DOMAIN_ID'), + '--os-user-domain-name': os.environ.get('OS_USER_DOMAIN_ID'), + } + return [x for sub in creds_dict.items() for x in sub] + + +def execute(cmd, fail_ok=False, merge_stderr=False): + """Executes specified command for the given action.""" + cmdlist = shlex.split(cmd) + cmdlist.extend(credentials()) + stdout = subprocess.PIPE + stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE + proc = subprocess.Popen(cmdlist, stdout=stdout, stderr=stderr) + result, result_err = proc.communicate() + result = result.decode('utf-8') + if not fail_ok and proc.returncode != 0: + raise exceptions.CommandFailed(proc.returncode, cmd, result, + result_err) + return result + + +class TestCase(testtools.TestCase): + + delimiter_line = re.compile(r'^\+\-[\+\-]+\-\+$') + + api_version = 1.0 + + @classmethod + def watcher(cls, cmd, fail_ok=False): + """Executes watcherclient command for the given action.""" + return execute( + 'openstack optimize --os-infra-optim-api-version {0} {1}'.format( + cls.api_version, cmd), fail_ok=fail_ok) + + @classmethod + def get_opts(cls, fields, format='value'): + return ' -f {0} {1}'.format(format, + ' '.join(['-c ' + it for it in fields])) + + @classmethod + def assertOutput(cls, expected, actual): + if expected != actual: + raise Exception('{0} != {1}'.format(expected, actual)) + + @classmethod + def assertInOutput(cls, expected, actual): + if expected not in actual: + raise Exception('{0} not in {1}'.format(expected, actual)) + + def assert_table_structure(self, items, field_names): + """Verify that all items have keys listed in field_names.""" + for item in items: + for field in field_names: + self.assertIn(field, item) + + def assert_show_fields(self, items, field_names): + """Verify that all items have keys listed in field_names.""" + for item in items: + for key in item.keys(): + self.assertIn(key, field_names) + + def assert_show_structure(self, items, field_names): + """Verify that all field_names listed in keys of all items.""" + if isinstance(items, list): + o = {} + for d in items: + o.update(d) + else: + o = items + item_keys = o.keys() + for field in field_names: + self.assertIn(field, item_keys) + + @staticmethod + def parse_show_as_object(raw_output): + """Return a dict with values parsed from cli output.""" + items = TestCase.parse_show(raw_output) + o = {} + for item in items: + o.update(item) + return o + + @staticmethod + def parse_show(raw_output): + """Return list of dicts with item values parsed from cli output.""" + + items = [] + table_ = output_parser.table(raw_output) + for row in table_['values']: + item = {} + item[row[0]] = row[1] + items.append(item) + return items + + def parse_listing(self, raw_output): + """Return list of dicts with basic item parsed from cli output.""" + return output_parser.listing(raw_output) + + def has_actionplan_succeeded(self, ap_uuid): + return self.parse_show_as_object( + self.watcher('actionplan show %s' % ap_uuid) + )['State'] == 'SUCCEEDED' + + @classmethod + def has_audit_created(cls, audit_uuid): + audit = cls.parse_show_as_object( + cls.watcher('audit show %s' % audit_uuid)) + if audit['Audit Type'] == 'ONESHOT': + return audit['State'] == 'SUCCEEDED' + else: + return audit['State'] == 'ONGOING' diff --git a/watcherclient/tests/client_functional/v1/test_action.py b/watcherclient/tests/client_functional/v1/test_action.py new file mode 100644 index 0000000..1a12ab2 --- /dev/null +++ b/watcherclient/tests/client_functional/v1/test_action.py @@ -0,0 +1,81 @@ +# Copyright (c) 2016 Servionica +# +# 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. + +from oslo_utils import uuidutils + +import functools + +from tempest.lib.common.utils import test_utils + +from watcherclient.tests.client_functional.v1 import base + + +class ActionTests(base.TestCase): + """Functional tests for action.""" + + dummy_name = 'dummy' + list_fields = ['UUID', 'Parents', 'State', 'Action Plan', 'Action'] + detailed_list_fields = list_fields + ['Created At', 'Updated At', + 'Deleted At', 'Parameters'] + audit_template_name = 'a' + uuidutils.generate_uuid() + audit_uuid = None + + @classmethod + def setUpClass(cls): + template_raw_output = cls.watcher( + 'audittemplate create %s dummy -s dummy' % cls.audit_template_name) + template_output = cls.parse_show_as_object(template_raw_output) + audit_output = cls.parse_show_as_object(cls.watcher( + 'audit create -a %s' % template_output['Name'])) + cls.audit_uuid = audit_output['UUID'] + audit_created = test_utils.call_until_true( + func=functools.partial(cls.has_audit_created, cls.audit_uuid), + duration=600, + sleep_for=2) + if not audit_created: + raise Exception('Audit has not been succeeded') + + @classmethod + def tearDownClass(cls): + # Delete Action Plan and all related actions. + output = cls.parse_show( + cls.watcher('actionplan list --audit %s' % cls.audit_uuid)) + action_plan_uuid = list(output[0])[0] + raw_output = cls.watcher('actionplan delete %s' % action_plan_uuid) + cls.assertOutput('', raw_output) + # Delete audit + raw_output = cls.watcher('audit delete %s' % cls.audit_uuid) + cls.assertOutput('', raw_output) + # Delete Template + raw_output = cls.watcher( + 'audittemplate delete %s' % cls.audit_template_name) + cls.assertOutput('', raw_output) + + def test_action_list(self): + raw_output = self.watcher('action list') + self.assert_table_structure([raw_output], self.list_fields) + + def test_action_detailed_list(self): + raw_output = self.watcher('action list --detail') + self.assert_table_structure([raw_output], self.detailed_list_fields) + + def test_action_show(self): + action_list = self.parse_show(self.watcher('action list --audit %s' + % self.audit_uuid)) + action_uuid = list(action_list[0])[0] + action = self.watcher('action show %s' % action_uuid) + self.assertIn(action_uuid, action) + self.assert_table_structure([action], + self.detailed_list_fields) diff --git a/watcherclient/tests/client_functional/v1/test_action_plan.py b/watcherclient/tests/client_functional/v1/test_action_plan.py new file mode 100644 index 0000000..f2da183 --- /dev/null +++ b/watcherclient/tests/client_functional/v1/test_action_plan.py @@ -0,0 +1,97 @@ +# Copyright (c) 2016 Servionica +# +# 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. + +from oslo_utils import uuidutils + +import functools + +from tempest.lib.common.utils import test_utils + +from watcherclient.tests.client_functional.v1 import base + + +class ActionPlanTests(base.TestCase): + """Functional tests for action plan.""" + + dummy_name = 'dummy' + list_fields = ['UUID', 'Audit', 'State', 'Updated At', 'Global efficacy'] + detailed_list_fields = list_fields + ['Created At', 'Deleted At', + 'Strategy', 'Efficacy indicators', + 'Hostname'] + audit_template_name = 'a' + uuidutils.generate_uuid() + audit_uuid = None + + @classmethod + def setUpClass(cls): + template_raw_output = cls.watcher( + 'audittemplate create %s dummy -s dummy' % cls.audit_template_name) + template_output = cls.parse_show_as_object(template_raw_output) + audit_raw_output = cls.watcher('audit create -a %s' + % template_output['Name']) + audit_output = cls.parse_show_as_object(audit_raw_output) + cls.audit_uuid = audit_output['UUID'] + audit_created = test_utils.call_until_true( + func=functools.partial(cls.has_audit_created, cls.audit_uuid), + duration=600, + sleep_for=2) + if not audit_created: + raise Exception('Audit has not been succeeded') + + @classmethod + def tearDownClass(cls): + # Delete action plan + output = cls.parse_show( + cls.watcher('actionplan list --audit %s' % cls.audit_uuid)) + action_plan_uuid = list(output[0])[0] + raw_output = cls.watcher('actionplan delete %s' % action_plan_uuid) + cls.assertOutput('', raw_output) + # Delete audit + raw_output = cls.watcher('audit delete %s' % cls.audit_uuid) + cls.assertOutput('', raw_output) + # Delete Template + raw_output = cls.watcher( + 'audittemplate delete %s' % cls.audit_template_name) + cls.assertOutput('', raw_output) + + def test_action_plan_list(self): + raw_output = self.watcher('actionplan list') + self.assert_table_structure([raw_output], self.list_fields) + + def test_action_plan_detailed_list(self): + raw_output = self.watcher('actionplan list --detail') + self.assert_table_structure([raw_output], self.detailed_list_fields) + + def test_action_plan_show(self): + action_plan_list = self.parse_show(self.watcher('actionplan list')) + action_plan_uuid = list(action_plan_list[0])[0] + actionplan = self.watcher('actionplan show %s' % action_plan_uuid) + self.assertIn(action_plan_uuid, actionplan) + self.assert_table_structure([actionplan], + self.detailed_list_fields) + + def test_action_plan_start(self): + output = self.parse_show(self.watcher('actionplan list --audit %s' + % self.audit_uuid)) + action_plan_uuid = list(output[0])[0] + self.watcher('actionplan start %s' % action_plan_uuid) + raw_output = self.watcher('actionplan show %s' % action_plan_uuid) + self.assert_table_structure([raw_output], self.detailed_list_fields) + + self.assertTrue(test_utils.call_until_true( + func=functools.partial( + self.has_actionplan_succeeded, action_plan_uuid), + duration=600, + sleep_for=2 + )) diff --git a/watcherclient/tests/client_functional/v1/test_audit.py b/watcherclient/tests/client_functional/v1/test_audit.py new file mode 100644 index 0000000..e4489b1 --- /dev/null +++ b/watcherclient/tests/client_functional/v1/test_audit.py @@ -0,0 +1,212 @@ +# Copyright (c) 2016 Servionica +# +# 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. + +from datetime import datetime +from dateutil import tz +import functools + +from oslo_utils import uuidutils +from tempest.lib.common.utils import test_utils + +from watcherclient.tests.client_functional.v1 import base + + +class AuditTests(base.TestCase): + """Functional tests for audit.""" + + dummy_name = 'dummy' + list_fields = ['UUID', 'Name', 'Audit Type', 'State', 'Goal', 'Strategy'] + detailed_list_fields = list_fields + ['Created At', 'Updated At', + 'Deleted At', 'Parameters', + 'Interval', 'Audit Scope', + 'Next Run Time', 'Hostname'] + audit_template_name = 'a' + uuidutils.generate_uuid() + audit_uuid = None + + @classmethod + def setUpClass(cls): + raw_output = cls.watcher('audittemplate create %s dummy -s dummy' + % cls.audit_template_name) + template_output = cls.parse_show_as_object(raw_output) + audit_raw_output = cls.watcher( + 'audit create -a %s' % template_output['Name']) + audit_output = cls.parse_show_as_object(audit_raw_output) + cls.audit_uuid = audit_output['UUID'] + audit_created = test_utils.call_until_true( + func=functools.partial(cls.has_audit_created, cls.audit_uuid), + duration=600, + sleep_for=2) + if not audit_created: + raise Exception('Audit has not been succeeded') + + @classmethod + def tearDownClass(cls): + output = cls.parse_show( + cls.watcher('actionplan list --audit %s' % cls.audit_uuid)) + action_plan_uuid = list(output[0])[0] + cls.watcher('actionplan delete %s' % action_plan_uuid) + cls.watcher('audit delete %s' % cls.audit_uuid) + cls.watcher('audittemplate delete %s' % cls.audit_template_name) + + def test_audit_list(self): + raw_output = self.watcher('audit list') + self.assert_table_structure([raw_output], self.list_fields) + + def test_audit_detailed_list(self): + raw_output = self.watcher('audit list --detail') + self.assert_table_structure([raw_output], self.detailed_list_fields) + + def test_audit_show(self): + audit = self.watcher('audit show ' + self.audit_uuid) + self.assertIn(self.audit_uuid, audit) + self.assert_table_structure([audit], self.detailed_list_fields) + + def test_audit_update(self): + audit_raw_output = self.watcher('audit update %s add interval=2' + % self.audit_uuid) + audit_output = self.parse_show_as_object(audit_raw_output) + assert int(audit_output['Interval']) == 2 + + +class AuditTestsV11(AuditTests): + """This class tests v1.1 of Watcher API""" + + api_version = 1.1 + + detailed_list_fields = AuditTests.list_fields + [ + 'Created At', 'Updated At', 'Deleted At', 'Parameters', 'Interval', + 'Audit Scope', 'Next Run Time', 'Hostname', 'Start Time', 'End Time'] + + def test_audit_detailed_list(self): + raw_output = self.watcher('audit list --detail') + self.assert_table_structure([raw_output], self.detailed_list_fields) + + def test_audit_show(self): + audit = self.watcher('audit show ' + self.audit_uuid) + self.assertIn(self.audit_uuid, audit) + self.assert_table_structure([audit], self.detailed_list_fields) + + def test_audit_update(self): + local_time = datetime.now(tz.tzlocal()) + local_time_str = local_time.strftime("%Y-%m-%dT%H:%M:%S") + utc_time = (local_time - local_time.utcoffset()) + utc_time_str = utc_time.strftime("%Y-%m-%dT%H:%M:%S") + audit_raw_output = self.watcher( + 'audit update {0} replace end_time="{1}"'.format(self.audit_uuid, + local_time_str)) + audit_output = self.parse_show_as_object(audit_raw_output) + assert audit_output['End Time'] == utc_time_str + + +class AuditTestsV12(AuditTestsV11): + """This class tests v1.2 of Watcher API""" + + api_version = 1.2 + + @classmethod + def setUpClass(cls): + raw_output = cls.watcher('audittemplate create %s dummy -s dummy' + % cls.audit_template_name) + template_output = cls.parse_show_as_object(raw_output) + audit_raw_output = cls.watcher( + 'audit create --force -a %s' % template_output['Name']) + audit_output = cls.parse_show_as_object(audit_raw_output) + cls.audit_uuid = audit_output['UUID'] + audit_created = test_utils.call_until_true( + func=functools.partial(cls.has_audit_created, cls.audit_uuid), + duration=600, + sleep_for=2) + if not audit_created: + raise Exception('Audit has not been succeeded') + + +class AuditActiveTests(base.TestCase): + + list_fields = ['UUID', 'Name', 'Audit Type', 'State', 'Goal', 'Strategy'] + detailed_list_fields = list_fields + ['Created At', 'Updated At', + 'Deleted At', 'Parameters', + 'Interval', 'Audit Scope'] + audit_template_name = 'a' + uuidutils.generate_uuid() + + @classmethod + def setUpClass(cls): + cls.watcher('audittemplate create %s dummy -s dummy' + % cls.audit_template_name) + + @classmethod + def tearDownClass(cls): + cls.watcher('audittemplate delete %s' % cls.audit_template_name) + + def _create_audit(self): + return self.parse_show_as_object( + self.watcher('audit create -a %s' + % self.audit_template_name))['UUID'] + + def _delete_audit(self, audit_uuid): + self.assertTrue(test_utils.call_until_true( + func=functools.partial( + self.has_audit_created, audit_uuid), + duration=600, + sleep_for=2 + )) + output = self.parse_show( + self.watcher('actionplan list --audit %s' % audit_uuid)) + action_plan_uuid = list(output[0])[0] + self.watcher('actionplan delete %s' % action_plan_uuid) + self.watcher('audit delete %s' % audit_uuid) + + def test_create_oneshot_audit(self): + audit = self.watcher('audit create -a %s' % self.audit_template_name) + audit_uuid = self.parse_show_as_object(audit)['UUID'] + self.assert_table_structure([audit], self.detailed_list_fields) + self._delete_audit(audit_uuid) + + def test_delete_oneshot_audit(self): + audit_uuid = self._create_audit() + self.assertTrue(test_utils.call_until_true( + func=functools.partial( + self.has_audit_created, audit_uuid), + duration=600, + sleep_for=2 + )) + raw_output = self.watcher('audit delete %s' % audit_uuid) + self.assertOutput('', raw_output) + output = self.parse_show( + self.watcher('actionplan list --audit %s' % audit_uuid)) + action_plan_uuid = list(output[0])[0] + self.watcher('actionplan delete %s' % action_plan_uuid) + + def test_continuous_audit(self): + audit = self.watcher('audit create -a %s -t CONTINUOUS -i 600' + % self.audit_template_name) + audit_uuid = self.parse_show_as_object(audit)['UUID'] + self.assert_table_structure([audit], self.detailed_list_fields) + self.assertTrue(test_utils.call_until_true( + func=functools.partial( + self.has_audit_created, audit_uuid), + duration=600, + sleep_for=2 + )) + audit_state = self.parse_show_as_object( + self.watcher('audit show %s' % audit_uuid))['State'] + if audit_state == 'ONGOING': + self.watcher('audit update %s replace state=CANCELLED' + % audit_uuid) + raw_output = self.watcher('audit delete %s' % audit_uuid) + self.assertOutput('', raw_output) + outputs = self.parse_listing( + self.watcher('actionplan list --audit %s' % audit_uuid)) + for actionplan in outputs: + self.watcher('actionplan delete %s' % actionplan['UUID']) diff --git a/watcherclient/tests/client_functional/v1/test_audit_template.py b/watcherclient/tests/client_functional/v1/test_audit_template.py new file mode 100644 index 0000000..7224e34 --- /dev/null +++ b/watcherclient/tests/client_functional/v1/test_audit_template.py @@ -0,0 +1,89 @@ +# Copyright (c) 2016 Servionica +# +# 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. + +from oslo_utils import uuidutils + +from watcherclient.tests.client_functional.v1 import base + + +class AuditTemplateTests(base.TestCase): + """Functional tests for audit template.""" + + dummy_name = 'dummy' + list_fields = ['UUID', 'Name', 'Goal', 'Strategy'] + detailed_list_fields = list_fields + ['Created At', 'Updated At', + 'Deleted At', 'Description', + 'Audit Scope'] + audit_template_name = 'a' + uuidutils.generate_uuid() + + @classmethod + def setUpClass(cls): + cls.watcher('audittemplate create %s dummy -s dummy' + % cls.audit_template_name) + + @classmethod + def tearDownClass(cls): + cls.watcher('audittemplate delete %s' % cls.audit_template_name) + + def test_audit_template_list(self): + raw_output = self.watcher('audittemplate list') + self.assert_table_structure([raw_output], self.list_fields) + + def test_audit_template_detailed_list(self): + raw_output = self.watcher('audittemplate list --detail') + self.assert_table_structure([raw_output], self.detailed_list_fields) + + def test_audit_template_show(self): + audit_template = self.watcher( + 'audittemplate show %s' % self.audit_template_name) + self.assertIn(self.audit_template_name, audit_template) + self.assert_table_structure([audit_template], + self.detailed_list_fields) + + def test_audit_template_update(self): + raw_output = self.watcher('audittemplate update %s replace ' + 'description="Updated Desc"' + % self.audit_template_name) + audit_template_output = self.parse_show_as_object(raw_output) + assert audit_template_output['Description'] == 'Updated Desc' + + +class AuditTemplateActiveTests(base.TestCase): + + audit_template_name = 'b' + uuidutils.generate_uuid() + list_fields = ['UUID', 'Name', 'Goal', 'Strategy'] + detailed_list_fields = list_fields + ['Created At', 'Updated At', + 'Deleted At', 'Description', + 'Audit Scope'] + + def _create_audit_template(self): + self.watcher('audittemplate create %s dummy -s dummy ' + '-d "Test Audit Template"' % self.audit_template_name) + + def _delete_audit_template(self): + self.watcher('audittemplate delete %s' % self.audit_template_name) + + def test_create_audit_template(self): + raw_output = self.watcher('audittemplate create %s dummy ' + '-s dummy -d "Test Audit Template"' + % self.audit_template_name) + self.assert_table_structure([raw_output], self.detailed_list_fields) + self._delete_audit_template() + + def test_delete_audit_template(self): + self._create_audit_template() + raw_output = self.watcher('audittemplate delete %s' + % self.audit_template_name) + self.assertOutput('', raw_output) diff --git a/watcherclient/tests/client_functional/v1/test_goal.py b/watcherclient/tests/client_functional/v1/test_goal.py new file mode 100644 index 0000000..1cf8990 --- /dev/null +++ b/watcherclient/tests/client_functional/v1/test_goal.py @@ -0,0 +1,41 @@ +# Copyright (c) 2016 Servionica +# +# 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. + +from watcherclient.tests.client_functional.v1 import base + + +class GoalTests(base.TestCase): + """Functional tests for goal.""" + + dummy_name = 'dummy' + list_fields = ['UUID', 'Name', 'Display name'] + + def test_goal_list(self): + raw_output = self.watcher('goal list') + self.assertIn(self.dummy_name, raw_output) + self.assert_table_structure([raw_output], self.list_fields) + + def test_goal_detailed_list(self): + raw_output = self.watcher('goal list --detail') + self.assertIn(self.dummy_name, raw_output) + self.assert_table_structure( + [raw_output], self.list_fields + ['Efficacy specification']) + + def test_goal_show(self): + raw_output = self.watcher('goal show %s' % self.dummy_name) + self.assertIn(self.dummy_name, raw_output) + self.assert_table_structure( + [raw_output], self.list_fields + ['Efficacy specification']) + self.assertNotIn('server_consolidation', raw_output) diff --git a/watcherclient/tests/client_functional/v1/test_scoring_engine.py b/watcherclient/tests/client_functional/v1/test_scoring_engine.py new file mode 100644 index 0000000..a2c0adf --- /dev/null +++ b/watcherclient/tests/client_functional/v1/test_scoring_engine.py @@ -0,0 +1,40 @@ +# Copyright (c) 2016 Servionica +# +# 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. + +from watcherclient.tests.client_functional.v1 import base + + +class ScoringEngineTests(base.TestCase): + """Functional tests for scoring engine.""" + + dummy_name = 'dummy_scorer' + list_fields = ['UUID', 'Name', 'Description'] + detailed_list_fields = list_fields + ['Metainfo'] + + def test_scoringengine_list(self): + raw_output = self.watcher('scoringengine list') + self.assertIn(self.dummy_name, raw_output) + self.assert_table_structure([raw_output], self.list_fields) + + def test_scoringengine_detailed_list(self): + raw_output = self.watcher('scoringengine list --detail') + self.assertIn(self.dummy_name, raw_output) + self.assert_table_structure([raw_output], self.detailed_list_fields) + + def test_scoringengine_show(self): + raw_output = self.watcher('scoringengine show %s' % self.dummy_name) + self.assertIn(self.dummy_name, raw_output) + self.assert_table_structure([raw_output], self.detailed_list_fields) + self.assertNotIn('dummy_avg_scorer', raw_output) diff --git a/watcherclient/tests/client_functional/v1/test_service.py b/watcherclient/tests/client_functional/v1/test_service.py new file mode 100644 index 0000000..8846341 --- /dev/null +++ b/watcherclient/tests/client_functional/v1/test_service.py @@ -0,0 +1,47 @@ +# Copyright (c) 2016 Servionica +# +# 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. + +from watcherclient.tests.client_functional.v1 import base + + +class ServiceTests(base.TestCase): + """Functional tests for service.""" + + decision_engine_name = 'watcher-decision-engine' + applier_name = 'watcher-applier' + list_fields = ['ID', 'Name', 'Host', 'Status'] + + def test_service_list(self): + raw_output = self.watcher('service list') + self.assertIn(self.decision_engine_name, raw_output) + self.assertIn(self.applier_name, raw_output) + self.assert_table_structure([raw_output], self.list_fields) + + def test_service_detailed_list(self): + raw_output = self.watcher('service list --detail') + self.assertIn(self.decision_engine_name, raw_output) + self.assertIn(self.applier_name, raw_output) + self.assert_table_structure([raw_output], + self.list_fields + ['Last seen up']) + + def test_service_show(self): + # TODO(alexchadin): this method should be refactored since Watcher will + # get HA support soon. + raw_output = self.watcher('service show %s' + % self.decision_engine_name) + self.assertIn(self.decision_engine_name, raw_output) + self.assert_table_structure([raw_output], + self.list_fields + ['Last seen up']) + self.assertNotIn(self.applier_name, raw_output) diff --git a/watcherclient/tests/client_functional/v1/test_strategy.py b/watcherclient/tests/client_functional/v1/test_strategy.py new file mode 100644 index 0000000..20a881a --- /dev/null +++ b/watcherclient/tests/client_functional/v1/test_strategy.py @@ -0,0 +1,48 @@ +# Copyright (c) 2016 Servionica +# +# 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. + +from watcherclient.tests.client_functional.v1 import base + + +class StrategyTests(base.TestCase): + """Functional tests for strategy.""" + + dummy_name = 'dummy' + basic_strategy = 'basic' + list_fields = ['UUID', 'Name', 'Display name', 'Goal'] + state_fields = ['Datasource', 'Metrics', 'CDM', 'Name'] + + def test_strategy_list(self): + raw_output = self.watcher('strategy list') + self.assertIn(self.dummy_name, raw_output) + self.assert_table_structure([raw_output], self.list_fields) + + def test_strategy_detailed_list(self): + raw_output = self.watcher('strategy list --detail') + self.assertIn(self.dummy_name, raw_output) + self.assert_table_structure([raw_output], + self.list_fields + ['Parameters spec']) + + def test_strategy_show(self): + raw_output = self.watcher('strategy show %s' % self.dummy_name) + self.assertIn(self.dummy_name, raw_output) + self.assert_table_structure([raw_output], + self.list_fields + ['Parameters spec']) + self.assertNotIn('basic', raw_output) + + def test_strategy_state(self): + raw_output = self.watcher('strategy state %s' % self.basic_strategy) + self.assertIn(self.basic_strategy, raw_output) + self.assert_table_structure([raw_output], self.state_fields)