initial version
Change-Id: I699e0ab082657880998d8618fe29eb7f56c6c661
This commit is contained in:
42
watcher/tests/__init__.py
Normal file
42
watcher/tests/__init__.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# 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 unittest
|
||||
|
||||
from oslo_config import cfg
|
||||
import pecan
|
||||
from pecan import testing
|
||||
|
||||
|
||||
cfg.CONF.import_opt('enable_authentication', 'watcher.api.acl')
|
||||
|
||||
|
||||
__all__ = ['FunctionalTest']
|
||||
|
||||
|
||||
class FunctionalTest(unittest.TestCase):
|
||||
"""Functional tests
|
||||
|
||||
Used for functional tests where you need to test your
|
||||
literal application and its integration with the framework.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
cfg.CONF.set_override("enable_authentication", False)
|
||||
self.app = testing.load_test_app(os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'config.py'
|
||||
))
|
||||
|
||||
def tearDown(self):
|
||||
pecan.set_config({}, overwrite=True)
|
||||
0
watcher/tests/api/__init__.py
Normal file
0
watcher/tests/api/__init__.py
Normal file
243
watcher/tests/api/base.py
Normal file
243
watcher/tests/api/base.py
Normal file
@@ -0,0 +1,243 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# 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.
|
||||
"""Base classes for API tests."""
|
||||
|
||||
# NOTE: Ported from ceilometer/tests/api.py (subsequently moved to
|
||||
# ceilometer/tests/api/__init__.py). This should be oslo'ified:
|
||||
# https://bugs.launchpad.net/watcher/+bug/1255115.
|
||||
|
||||
# NOTE(deva): import auth_token so we can override a config option
|
||||
from keystonemiddleware import auth_token # noqa
|
||||
# import mock
|
||||
from oslo_config import cfg
|
||||
import pecan
|
||||
import pecan.testing
|
||||
from six.moves.urllib import parse as urlparse
|
||||
|
||||
from watcher.api import hooks
|
||||
from watcher.tests.db import base
|
||||
|
||||
PATH_PREFIX = '/v1'
|
||||
|
||||
|
||||
class FunctionalTest(base.DbTestCase):
|
||||
"""Pecan controller functional testing class.
|
||||
|
||||
Used for functional tests of Pecan controllers where you need to
|
||||
test your literal application and its integration with the
|
||||
framework.
|
||||
"""
|
||||
|
||||
SOURCE_DATA = {'test_source': {'somekey': '666'}}
|
||||
|
||||
def setUp(self):
|
||||
super(FunctionalTest, self).setUp()
|
||||
cfg.CONF.set_override("auth_version", "v2.0",
|
||||
group='keystone_authtoken')
|
||||
cfg.CONF.set_override("admin_user", "admin",
|
||||
group='keystone_authtoken')
|
||||
self.app = self._make_app()
|
||||
|
||||
def reset_pecan():
|
||||
pecan.set_config({}, overwrite=True)
|
||||
|
||||
self.addCleanup(reset_pecan)
|
||||
|
||||
def _make_app(self, enable_acl=False):
|
||||
# Determine where we are so we can set up paths in the config
|
||||
root_dir = self.path_get()
|
||||
|
||||
self.config = {
|
||||
'app': {
|
||||
'root': 'watcher.api.controllers.root.RootController',
|
||||
'modules': ['watcher.api'],
|
||||
'hooks': [
|
||||
hooks.ContextHook(),
|
||||
hooks.NoExceptionTracebackHook()
|
||||
],
|
||||
'static_root': '%s/public' % root_dir,
|
||||
'template_path': '%s/api/templates' % root_dir,
|
||||
'enable_acl': enable_acl,
|
||||
'acl_public_routes': ['/', '/v1'],
|
||||
},
|
||||
}
|
||||
|
||||
return pecan.testing.load_test_app(self.config)
|
||||
|
||||
def _request_json(self, path, params, expect_errors=False, headers=None,
|
||||
method="post", extra_environ=None, status=None,
|
||||
path_prefix=PATH_PREFIX):
|
||||
"""Sends simulated HTTP request to Pecan test app.
|
||||
|
||||
:param path: url path of target service
|
||||
:param params: content for wsgi.input of request
|
||||
:param expect_errors: Boolean value; whether an error is expected based
|
||||
on request
|
||||
:param headers: a dictionary of headers to send along with the request
|
||||
:param method: Request method type. Appropriate method function call
|
||||
should be used rather than passing attribute in.
|
||||
:param extra_environ: a dictionary of environ variables to send along
|
||||
with the request
|
||||
:param status: expected status code of response
|
||||
:param path_prefix: prefix of the url path
|
||||
"""
|
||||
full_path = path_prefix + path
|
||||
print('%s: %s %s' % (method.upper(), full_path, params))
|
||||
|
||||
response = getattr(self.app, "%s_json" % method)(
|
||||
str(full_path),
|
||||
params=params,
|
||||
headers=headers,
|
||||
status=status,
|
||||
extra_environ=extra_environ,
|
||||
expect_errors=expect_errors
|
||||
)
|
||||
print('GOT:%s' % response)
|
||||
return response
|
||||
|
||||
def put_json(self, path, params, expect_errors=False, headers=None,
|
||||
extra_environ=None, status=None):
|
||||
"""Sends simulated HTTP PUT request to Pecan test app.
|
||||
|
||||
:param path: url path of target service
|
||||
:param params: content for wsgi.input of request
|
||||
:param expect_errors: Boolean value; whether an error is expected based
|
||||
on request
|
||||
:param headers: a dictionary of headers to send along with the request
|
||||
:param extra_environ: a dictionary of environ variables to send along
|
||||
with the request
|
||||
:param status: expected status code of response
|
||||
"""
|
||||
return self._request_json(path=path, params=params,
|
||||
expect_errors=expect_errors,
|
||||
headers=headers, extra_environ=extra_environ,
|
||||
status=status, method="put")
|
||||
|
||||
def post_json(self, path, params, expect_errors=False, headers=None,
|
||||
extra_environ=None, status=None):
|
||||
"""Sends simulated HTTP POST request to Pecan test app.
|
||||
|
||||
:param path: url path of target service
|
||||
:param params: content for wsgi.input of request
|
||||
:param expect_errors: Boolean value; whether an error is expected based
|
||||
on request
|
||||
:param headers: a dictionary of headers to send along with the request
|
||||
:param extra_environ: a dictionary of environ variables to send along
|
||||
with the request
|
||||
:param status: expected status code of response
|
||||
"""
|
||||
return self._request_json(path=path, params=params,
|
||||
expect_errors=expect_errors,
|
||||
headers=headers, extra_environ=extra_environ,
|
||||
status=status, method="post")
|
||||
|
||||
def patch_json(self, path, params, expect_errors=False, headers=None,
|
||||
extra_environ=None, status=None):
|
||||
"""Sends simulated HTTP PATCH request to Pecan test app.
|
||||
|
||||
:param path: url path of target service
|
||||
:param params: content for wsgi.input of request
|
||||
:param expect_errors: Boolean value; whether an error is expected based
|
||||
on request
|
||||
:param headers: a dictionary of headers to send along with the request
|
||||
:param extra_environ: a dictionary of environ variables to send along
|
||||
with the request
|
||||
:param status: expected status code of response
|
||||
"""
|
||||
return self._request_json(path=path, params=params,
|
||||
expect_errors=expect_errors,
|
||||
headers=headers, extra_environ=extra_environ,
|
||||
status=status, method="patch")
|
||||
|
||||
def delete(self, path, expect_errors=False, headers=None,
|
||||
extra_environ=None, status=None, path_prefix=PATH_PREFIX):
|
||||
"""Sends simulated HTTP DELETE request to Pecan test app.
|
||||
|
||||
:param path: url path of target service
|
||||
:param expect_errors: Boolean value; whether an error is expected based
|
||||
on request
|
||||
:param headers: a dictionary of headers to send along with the request
|
||||
:param extra_environ: a dictionary of environ variables to send along
|
||||
with the request
|
||||
:param status: expected status code of response
|
||||
:param path_prefix: prefix of the url path
|
||||
"""
|
||||
full_path = path_prefix + path
|
||||
print('DELETE: %s' % (full_path))
|
||||
response = self.app.delete(str(full_path),
|
||||
headers=headers,
|
||||
status=status,
|
||||
extra_environ=extra_environ,
|
||||
expect_errors=expect_errors)
|
||||
print('GOT:%s' % response)
|
||||
return response
|
||||
|
||||
def get_json(self, path, expect_errors=False, headers=None,
|
||||
extra_environ=None, q=[], path_prefix=PATH_PREFIX, **params):
|
||||
"""Sends simulated HTTP GET request to Pecan test app.
|
||||
|
||||
:param path: url path of target service
|
||||
:param expect_errors: Boolean value;whether an error is expected based
|
||||
on request
|
||||
:param headers: a dictionary of headers to send along with the request
|
||||
:param extra_environ: a dictionary of environ variables to send along
|
||||
with the request
|
||||
:param q: list of queries consisting of: field, value, op, and type
|
||||
keys
|
||||
:param path_prefix: prefix of the url path
|
||||
:param params: content for wsgi.input of request
|
||||
"""
|
||||
full_path = path_prefix + path
|
||||
query_params = {'q.field': [],
|
||||
'q.value': [],
|
||||
'q.op': [],
|
||||
}
|
||||
for query in q:
|
||||
for name in ['field', 'op', 'value']:
|
||||
query_params['q.%s' % name].append(query.get(name, ''))
|
||||
all_params = {}
|
||||
all_params.update(params)
|
||||
if q:
|
||||
all_params.update(query_params)
|
||||
print('GET: %s %r' % (full_path, all_params))
|
||||
|
||||
response = self.app.get(full_path,
|
||||
params=all_params,
|
||||
headers=headers,
|
||||
extra_environ=extra_environ,
|
||||
expect_errors=expect_errors)
|
||||
if not expect_errors:
|
||||
response = response.json
|
||||
print('GOT:%s' % response)
|
||||
return response
|
||||
|
||||
def validate_link(self, link, bookmark=False):
|
||||
"""Checks if the given link can get correct data."""
|
||||
# removes the scheme and net location parts of the link
|
||||
url_parts = list(urlparse.urlparse(link))
|
||||
url_parts[0] = url_parts[1] = ''
|
||||
|
||||
# bookmark link should not have the version in the URL
|
||||
if bookmark and url_parts[2].startswith(PATH_PREFIX):
|
||||
return False
|
||||
|
||||
full_path = urlparse.urlunparse(url_parts)
|
||||
try:
|
||||
self.get_json(full_path, path_prefix='')
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
30
watcher/tests/api/test_base.py
Normal file
30
watcher/tests/api/test_base.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# 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.
|
||||
|
||||
from watcher.tests.api import base
|
||||
|
||||
|
||||
class TestBase(base.FunctionalTest):
|
||||
|
||||
def test_api_setup(self):
|
||||
pass
|
||||
|
||||
def test_bad_uri(self):
|
||||
response = self.get_json('/bad/path',
|
||||
expect_errors=True,
|
||||
headers={"Accept": "application/json"})
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual("application/json", response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
140
watcher/tests/api/test_hooks.py
Normal file
140
watcher/tests/api/test_hooks.py
Normal file
@@ -0,0 +1,140 @@
|
||||
# Copyright 2014
|
||||
# The Cloudscaling Group, Inc.
|
||||
#
|
||||
# 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 mock
|
||||
from oslo_config import cfg
|
||||
import oslo_messaging as messaging
|
||||
|
||||
from watcher.api.controllers import root
|
||||
from watcher.api import hooks
|
||||
from watcher.common import context as watcher_context
|
||||
from watcher.tests.api import base as api_base
|
||||
from watcher.tests import base
|
||||
from watcher.tests import fakes
|
||||
|
||||
|
||||
class TestContextHook(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestContextHook, self).setUp()
|
||||
self.app = fakes.FakeApp()
|
||||
|
||||
def test_context_hook_before_method(self):
|
||||
state = mock.Mock(request=fakes.FakePecanRequest())
|
||||
hook = hooks.ContextHook()
|
||||
hook.before(state)
|
||||
ctx = state.request.context
|
||||
self.assertIsInstance(ctx, watcher_context.RequestContext)
|
||||
self.assertEqual(ctx.auth_token,
|
||||
fakes.fakeAuthTokenHeaders['X-Auth-Token'])
|
||||
self.assertEqual(ctx.project_id,
|
||||
fakes.fakeAuthTokenHeaders['X-Project-Id'])
|
||||
self.assertEqual(ctx.user_id,
|
||||
fakes.fakeAuthTokenHeaders['X-User-Id'])
|
||||
self.assertEqual(ctx.auth_url,
|
||||
fakes.fakeAuthTokenHeaders['X-Auth-Url'])
|
||||
self.assertEqual(ctx.domain_name,
|
||||
fakes.fakeAuthTokenHeaders['X-User-Domain-Name'])
|
||||
self.assertEqual(ctx.domain_id,
|
||||
fakes.fakeAuthTokenHeaders['X-User-Domain-Id'])
|
||||
self.assertIsNone(ctx.auth_token_info)
|
||||
|
||||
def test_context_hook_before_method_auth_info(self):
|
||||
state = mock.Mock(request=fakes.FakePecanRequest())
|
||||
state.request.environ['keystone.token_info'] = 'assert_this'
|
||||
hook = hooks.ContextHook()
|
||||
hook.before(state)
|
||||
ctx = state.request.context
|
||||
self.assertIsInstance(ctx, watcher_context.RequestContext)
|
||||
self.assertEqual(fakes.fakeAuthTokenHeaders['X-Auth-Token'],
|
||||
ctx.auth_token)
|
||||
self.assertEqual('assert_this', ctx.auth_token_info)
|
||||
|
||||
|
||||
class TestNoExceptionTracebackHook(api_base.FunctionalTest):
|
||||
|
||||
TRACE = [
|
||||
u'Traceback (most recent call last):',
|
||||
u' File "/opt/stack/watcher/watcher/openstack/common/rpc/amqp.py",'
|
||||
' line 434, in _process_data\\n **args)',
|
||||
u' File "/opt/stack/watcher/watcher/openstack/common/rpc/'
|
||||
'dispatcher.py", line 172, in dispatch\\n result ='
|
||||
' getattr(proxyobj, method)(context, **kwargs)']
|
||||
MSG_WITHOUT_TRACE = "Test exception message."
|
||||
MSG_WITH_TRACE = MSG_WITHOUT_TRACE + "\n" + "\n".join(TRACE)
|
||||
|
||||
def setUp(self):
|
||||
super(TestNoExceptionTracebackHook, self).setUp()
|
||||
p = mock.patch.object(root.Root, 'convert')
|
||||
self.root_convert_mock = p.start()
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def test_hook_exception_success(self):
|
||||
self.root_convert_mock.side_effect = Exception(self.MSG_WITH_TRACE)
|
||||
|
||||
response = self.get_json('/', path_prefix='', expect_errors=True)
|
||||
|
||||
actual_msg = json.loads(response.json['error_message'])['faultstring']
|
||||
self.assertEqual(self.MSG_WITHOUT_TRACE, actual_msg)
|
||||
|
||||
def test_hook_remote_error_success(self):
|
||||
test_exc_type = 'TestException'
|
||||
self.root_convert_mock.side_effect = messaging.rpc.RemoteError(
|
||||
test_exc_type, self.MSG_WITHOUT_TRACE, self.TRACE)
|
||||
|
||||
response = self.get_json('/', path_prefix='', expect_errors=True)
|
||||
|
||||
# NOTE(max_lobur): For RemoteError the client message will still have
|
||||
# some garbage because in RemoteError traceback is serialized as a list
|
||||
# instead of'\n'.join(trace). But since RemoteError is kind of very
|
||||
# rare thing (happens due to wrong deserialization settings etc.)
|
||||
# we don't care about this garbage.
|
||||
expected_msg = ("Remote error: %s %s"
|
||||
% (test_exc_type, self.MSG_WITHOUT_TRACE) + "\n[u'")
|
||||
actual_msg = json.loads(response.json['error_message'])['faultstring']
|
||||
self.assertEqual(expected_msg, actual_msg)
|
||||
|
||||
def test_hook_without_traceback(self):
|
||||
msg = "Error message without traceback \n but \n multiline"
|
||||
self.root_convert_mock.side_effect = Exception(msg)
|
||||
|
||||
response = self.get_json('/', path_prefix='', expect_errors=True)
|
||||
|
||||
actual_msg = json.loads(response.json['error_message'])['faultstring']
|
||||
self.assertEqual(msg, actual_msg)
|
||||
|
||||
def test_hook_server_debug_on_serverfault(self):
|
||||
cfg.CONF.set_override('debug', True)
|
||||
self.root_convert_mock.side_effect = Exception(self.MSG_WITH_TRACE)
|
||||
|
||||
response = self.get_json('/', path_prefix='', expect_errors=True)
|
||||
|
||||
actual_msg = json.loads(
|
||||
response.json['error_message'])['faultstring']
|
||||
self.assertEqual(self.MSG_WITHOUT_TRACE, actual_msg)
|
||||
|
||||
def test_hook_server_debug_on_clientfault(self):
|
||||
cfg.CONF.set_override('debug', True)
|
||||
client_error = Exception(self.MSG_WITH_TRACE)
|
||||
client_error.code = 400
|
||||
self.root_convert_mock.side_effect = client_error
|
||||
|
||||
response = self.get_json('/', path_prefix='', expect_errors=True)
|
||||
|
||||
actual_msg = json.loads(
|
||||
response.json['error_message'])['faultstring']
|
||||
self.assertEqual(self.MSG_WITH_TRACE, actual_msg)
|
||||
44
watcher/tests/api/test_root.py
Normal file
44
watcher/tests/api/test_root.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
# 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.
|
||||
|
||||
from watcher.tests.api import base
|
||||
|
||||
|
||||
class TestRoot(base.FunctionalTest):
|
||||
|
||||
def test_get_root(self):
|
||||
data = self.get_json('/', path_prefix='')
|
||||
self.assertEqual('v1', data['default_version']['id'])
|
||||
# Check fields are not empty
|
||||
[self.assertNotIn(f, ['', []]) for f in data.keys()]
|
||||
|
||||
|
||||
class TestV1Root(base.FunctionalTest):
|
||||
|
||||
def test_get_v1_root(self):
|
||||
data = self.get_json('/')
|
||||
self.assertEqual('v1', data['id'])
|
||||
# Check fields are not empty
|
||||
for f in data.keys():
|
||||
self.assertNotIn(f, ['', []])
|
||||
# Check if all known resources are present and there are no extra ones.
|
||||
not_resources = ('id', 'links', 'media_types')
|
||||
actual_resources = tuple(set(data.keys()) - set(not_resources))
|
||||
expected_resources = ('audit_templates', 'audits', 'actions',
|
||||
'action_plans')
|
||||
self.assertEqual(sorted(expected_resources), sorted(actual_resources))
|
||||
|
||||
self.assertIn({'type': 'application/vnd.openstack.watcher.v1+json',
|
||||
'base': 'application/json'}, data['media_types'])
|
||||
103
watcher/tests/api/utils.py
Normal file
103
watcher/tests/api/utils.py
Normal file
@@ -0,0 +1,103 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
Utils for testing the API service.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import json
|
||||
|
||||
from watcher.api.controllers.v1 import action as action_ctrl
|
||||
from watcher.api.controllers.v1 import action_plan as action_plan_ctrl
|
||||
from watcher.api.controllers.v1 import audit as audit_ctrl
|
||||
from watcher.api.controllers.v1 import audit_template as audit_template_ctrl
|
||||
from watcher.tests.db import utils as db_utils
|
||||
|
||||
|
||||
ADMIN_TOKEN = '4562138218392831'
|
||||
MEMBER_TOKEN = '4562138218392832'
|
||||
|
||||
|
||||
class FakeMemcache(object):
|
||||
"""Fake cache that is used for keystone tokens lookup."""
|
||||
|
||||
_cache = {
|
||||
'tokens/%s' % ADMIN_TOKEN: {
|
||||
'access': {
|
||||
'token': {'id': ADMIN_TOKEN,
|
||||
'expires': '2100-09-11T00:00:00'},
|
||||
'user': {'id': 'user_id1',
|
||||
'name': 'user_name1',
|
||||
'tenantId': '123i2910',
|
||||
'tenantName': 'mytenant',
|
||||
'roles': [{'name': 'admin'}]
|
||||
},
|
||||
}
|
||||
},
|
||||
'tokens/%s' % MEMBER_TOKEN: {
|
||||
'access': {
|
||||
'token': {'id': MEMBER_TOKEN,
|
||||
'expires': '2100-09-11T00:00:00'},
|
||||
'user': {'id': 'user_id2',
|
||||
'name': 'user-good',
|
||||
'tenantId': 'project-good',
|
||||
'tenantName': 'goodies',
|
||||
'roles': [{'name': 'Member'}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.set_key = None
|
||||
self.set_value = None
|
||||
self.token_expiration = None
|
||||
|
||||
def get(self, key):
|
||||
dt = datetime.datetime.utcnow() + datetime.timedelta(minutes=5)
|
||||
return json.dumps((self._cache.get(key), dt.isoformat()))
|
||||
|
||||
def set(self, key, value, time=0, min_compress_len=0):
|
||||
self.set_value = value
|
||||
self.set_key = key
|
||||
|
||||
|
||||
def remove_internal(values, internal):
|
||||
# NOTE(yuriyz): internal attributes should not be posted, except uuid
|
||||
int_attr = [attr.lstrip('/') for attr in internal if attr != '/uuid']
|
||||
return dict([(k, v) for (k, v) in values.iteritems() if k not in int_attr])
|
||||
|
||||
|
||||
def audit_post_data(**kw):
|
||||
audit = db_utils.get_test_audit(**kw)
|
||||
internal = audit_ctrl.AuditPatchType.internal_attrs()
|
||||
return remove_internal(audit, internal)
|
||||
|
||||
|
||||
def audit_template_post_data(**kw):
|
||||
audit_template = db_utils.get_test_audit_template(**kw)
|
||||
internal = audit_template_ctrl.AuditTemplatePatchType.internal_attrs()
|
||||
return remove_internal(audit_template, internal)
|
||||
|
||||
|
||||
def action_post_data(**kw):
|
||||
action = db_utils.get_test_action(**kw)
|
||||
internal = action_ctrl.ActionPatchType.internal_attrs()
|
||||
return remove_internal(action, internal)
|
||||
|
||||
|
||||
def action_plan_post_data(**kw):
|
||||
act_plan = db_utils.get_test_action_plan(**kw)
|
||||
internal = action_plan_ctrl.ActionPlanPatchType.internal_attrs()
|
||||
return remove_internal(act_plan, internal)
|
||||
0
watcher/tests/api/v1/__init__.py
Normal file
0
watcher/tests/api/v1/__init__.py
Normal file
587
watcher/tests/api/v1/test_actions.py
Normal file
587
watcher/tests/api/v1/test_actions.py
Normal file
@@ -0,0 +1,587 @@
|
||||
# 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 datetime
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import timeutils
|
||||
from wsme import types as wtypes
|
||||
|
||||
from watcher.api.controllers.v1 import action as api_action
|
||||
from watcher.common import utils
|
||||
from watcher.db import api as db_api
|
||||
from watcher import objects
|
||||
from watcher.tests.api import base as api_base
|
||||
from watcher.tests.api import utils as api_utils
|
||||
from watcher.tests import base
|
||||
from watcher.tests.db import utils as db_utils
|
||||
from watcher.tests.objects import utils as obj_utils
|
||||
|
||||
|
||||
def post_get_test_action(**kw):
|
||||
action = api_utils.action_post_data(**kw)
|
||||
action_plan = db_utils.get_test_action_plan()
|
||||
action['action_plan_id'] = None
|
||||
action['action_plan_uuid'] = kw.get('action_plan_uuid',
|
||||
action_plan['uuid'])
|
||||
action['next'] = None
|
||||
return action
|
||||
|
||||
|
||||
class TestActionObject(base.TestCase):
|
||||
|
||||
def test_action_init(self):
|
||||
action_dict = api_utils.action_post_data(action_plan_id=None,
|
||||
next=None)
|
||||
del action_dict['state']
|
||||
action = api_action.Action(**action_dict)
|
||||
self.assertEqual(wtypes.Unset, action.state)
|
||||
|
||||
|
||||
class TestListAction(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestListAction, self).setUp()
|
||||
obj_utils.create_test_action_plan(self.context)
|
||||
|
||||
def test_empty(self):
|
||||
response = self.get_json('/actions')
|
||||
self.assertEqual([], response['actions'])
|
||||
|
||||
def _assert_action_fields(self, action):
|
||||
action_fields = ['uuid', 'state', 'action_plan_uuid', 'action_type']
|
||||
for field in action_fields:
|
||||
self.assertIn(field, action)
|
||||
|
||||
def test_one(self):
|
||||
action = obj_utils.create_test_action(self.context, next=None)
|
||||
response = self.get_json('/actions')
|
||||
self.assertEqual(action.uuid, response['actions'][0]["uuid"])
|
||||
self._assert_action_fields(response['actions'][0])
|
||||
|
||||
def test_one_soft_deleted(self):
|
||||
action = obj_utils.create_test_action(self.context, next=None)
|
||||
action.soft_delete()
|
||||
response = self.get_json('/actions',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(action.uuid, response['actions'][0]["uuid"])
|
||||
self._assert_action_fields(response['actions'][0])
|
||||
|
||||
response = self.get_json('/actions')
|
||||
self.assertEqual([], response['actions'])
|
||||
|
||||
def test_get_one(self):
|
||||
action = obj_utils.create_test_action(self.context, next=None)
|
||||
response = self.get_json('/actions/%s' % action['uuid'])
|
||||
self.assertEqual(action.uuid, response['uuid'])
|
||||
self.assertEqual(action.description, response['description'])
|
||||
self.assertEqual(action.src, response['src'])
|
||||
self.assertEqual(action.dst, response['dst'])
|
||||
self.assertEqual(action.action_type, response['action_type'])
|
||||
self.assertEqual(action.parameter, response['parameter'])
|
||||
self._assert_action_fields(response)
|
||||
|
||||
def test_get_one_soft_deleted(self):
|
||||
action = obj_utils.create_test_action(self.context, next=None)
|
||||
action.soft_delete()
|
||||
response = self.get_json('/actions/%s' % action['uuid'],
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(action.uuid, response['uuid'])
|
||||
self._assert_action_fields(response)
|
||||
|
||||
response = self.get_json('/actions/%s' % action['uuid'],
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
|
||||
def test_detail(self):
|
||||
action = obj_utils.create_test_action(self.context, next=None)
|
||||
response = self.get_json('/actions/detail')
|
||||
self.assertEqual(action.uuid, response['actions'][0]["uuid"])
|
||||
self._assert_action_fields(response['actions'][0])
|
||||
|
||||
def test_detail_soft_deleted(self):
|
||||
action = obj_utils.create_test_action(self.context, next=None)
|
||||
action.soft_delete()
|
||||
response = self.get_json('/actions/detail',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(action.uuid, response['actions'][0]["uuid"])
|
||||
self._assert_action_fields(response['actions'][0])
|
||||
|
||||
response = self.get_json('/actions/detail')
|
||||
self.assertEqual([], response['actions'])
|
||||
|
||||
def test_detail_against_single(self):
|
||||
action = obj_utils.create_test_action(self.context, next=None)
|
||||
response = self.get_json('/actions/%s/detail' % action['uuid'],
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
|
||||
def test_many(self):
|
||||
action_list = []
|
||||
for id_ in range(5):
|
||||
action = obj_utils.create_test_action(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
action_list.append(action.uuid)
|
||||
response = self.get_json('/actions')
|
||||
self.assertEqual(len(action_list), len(response['actions']))
|
||||
uuids = [s['uuid'] for s in response['actions']]
|
||||
self.assertEqual(sorted(action_list), sorted(uuids))
|
||||
|
||||
def test_many_with_action_plan_uuid(self):
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context,
|
||||
id=2,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_id=1)
|
||||
action_list = []
|
||||
for id_ in range(5):
|
||||
action = obj_utils.create_test_action(
|
||||
self.context, id=id_,
|
||||
action_plan_id=2,
|
||||
uuid=utils.generate_uuid())
|
||||
action_list.append(action.uuid)
|
||||
response = self.get_json('/actions')
|
||||
self.assertEqual(len(action_list), len(response['actions']))
|
||||
for action in response['actions']:
|
||||
self.assertEqual(action_plan.uuid, action['action_plan_uuid'])
|
||||
|
||||
def test_filter_by_audit_uuid(self):
|
||||
audit = obj_utils.create_test_audit(self.context,
|
||||
uuid=utils.generate_uuid())
|
||||
action_plan_1 = obj_utils.create_test_action_plan(
|
||||
self.context,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_id=audit.id)
|
||||
action_list = []
|
||||
|
||||
for id_ in range(3):
|
||||
action = obj_utils.create_test_action(
|
||||
self.context, id=id_,
|
||||
action_plan_id=action_plan_1.id,
|
||||
uuid=utils.generate_uuid())
|
||||
action_list.append(action.uuid)
|
||||
|
||||
audit2 = obj_utils.create_test_audit(self.context,
|
||||
uuid=utils.generate_uuid())
|
||||
action_plan_2 = obj_utils.create_test_action_plan(
|
||||
self.context,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_id=audit2.id)
|
||||
|
||||
for id_ in range(4, 5, 6):
|
||||
obj_utils.create_test_action(
|
||||
self.context, id=id_,
|
||||
action_plan_id=action_plan_2.id,
|
||||
uuid=utils.generate_uuid())
|
||||
|
||||
response = self.get_json('/actions?audit_uuid=%s' % audit.uuid)
|
||||
self.assertEqual(len(action_list), len(response['actions']))
|
||||
for action in response['actions']:
|
||||
self.assertEqual(action_plan_1.uuid, action['action_plan_uuid'])
|
||||
|
||||
def test_filter_by_action_plan_uuid(self):
|
||||
audit = obj_utils.create_test_audit(self.context,
|
||||
uuid=utils.generate_uuid())
|
||||
action_plan_1 = obj_utils.create_test_action_plan(
|
||||
self.context,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_id=audit.id)
|
||||
action_list = []
|
||||
|
||||
for id_ in range(3):
|
||||
action = obj_utils.create_test_action(
|
||||
self.context, id=id_,
|
||||
action_plan_id=action_plan_1.id,
|
||||
uuid=utils.generate_uuid())
|
||||
action_list.append(action.uuid)
|
||||
|
||||
action_plan_2 = obj_utils.create_test_action_plan(
|
||||
self.context,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_id=audit.id)
|
||||
|
||||
for id_ in range(4, 5, 6):
|
||||
obj_utils.create_test_action(
|
||||
self.context, id=id_,
|
||||
action_plan_id=action_plan_2.id,
|
||||
uuid=utils.generate_uuid())
|
||||
|
||||
response = self.get_json(
|
||||
'/actions?action_plan_uuid=%s' % action_plan_1.uuid)
|
||||
self.assertEqual(len(action_list), len(response['actions']))
|
||||
for action in response['actions']:
|
||||
self.assertEqual(action_plan_1.uuid, action['action_plan_uuid'])
|
||||
|
||||
response = self.get_json(
|
||||
'/actions?action_plan_uuid=%s' % action_plan_2.uuid)
|
||||
for action in response['actions']:
|
||||
self.assertEqual(action_plan_2.uuid, action['action_plan_uuid'])
|
||||
|
||||
def test_details_and_filter_by_action_plan_uuid(self):
|
||||
audit = obj_utils.create_test_audit(self.context,
|
||||
uuid=utils.generate_uuid())
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_id=audit.id)
|
||||
|
||||
for id_ in range(3):
|
||||
action = obj_utils.create_test_action(
|
||||
self.context, id=id_,
|
||||
action_plan_id=action_plan.id,
|
||||
uuid=utils.generate_uuid())
|
||||
|
||||
response = self.get_json(
|
||||
'/actions/detail?action_plan_uuid=%s' % action_plan.uuid)
|
||||
for action in response['actions']:
|
||||
self.assertEqual(action_plan.uuid, action['action_plan_uuid'])
|
||||
|
||||
def test_details_and_filter_by_audit_uuid(self):
|
||||
audit = obj_utils.create_test_audit(self.context,
|
||||
uuid=utils.generate_uuid())
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_id=audit.id)
|
||||
|
||||
for id_ in range(3):
|
||||
action = obj_utils.create_test_action(
|
||||
self.context, id=id_,
|
||||
action_plan_id=action_plan.id,
|
||||
uuid=utils.generate_uuid())
|
||||
|
||||
response = self.get_json(
|
||||
'/actions/detail?audit_uuid=%s' % audit.uuid)
|
||||
for action in response['actions']:
|
||||
self.assertEqual(action_plan.uuid, action['action_plan_uuid'])
|
||||
|
||||
def test_filter_by_action_plan_and_audit_uuids(self):
|
||||
audit = obj_utils.create_test_audit(
|
||||
self.context, uuid=utils.generate_uuid())
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_id=audit.id)
|
||||
url = '/actions?action_plan_uuid=%s&audit_uuid=%s' % (
|
||||
action_plan.uuid, audit.uuid)
|
||||
response = self.get_json(url, expect_errors=True)
|
||||
self.assertEqual(400, response.status_int)
|
||||
|
||||
def test_many_with_soft_deleted_action_plan_uuid(self):
|
||||
action_plan1 = obj_utils.create_test_action_plan(
|
||||
self.context,
|
||||
id=2,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_id=1)
|
||||
action_plan2 = obj_utils.create_test_action_plan(
|
||||
self.context,
|
||||
id=3,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_id=1)
|
||||
action_list = []
|
||||
|
||||
for id_ in range(0, 2):
|
||||
action = obj_utils.create_test_action(
|
||||
self.context, id=id_,
|
||||
action_plan_id=2,
|
||||
uuid=utils.generate_uuid())
|
||||
action_list.append(action.uuid)
|
||||
|
||||
for id_ in range(2, 4):
|
||||
action = obj_utils.create_test_action(
|
||||
self.context, id=id_,
|
||||
action_plan_id=3,
|
||||
uuid=utils.generate_uuid())
|
||||
action_list.append(action.uuid)
|
||||
|
||||
self.delete('/action_plans/%s' % action_plan1.uuid)
|
||||
|
||||
response = self.get_json('/actions')
|
||||
self.assertEqual(len(action_list), len(response['actions']))
|
||||
for id_ in range(0, 2):
|
||||
action = response['actions'][id_]
|
||||
self.assertEqual(None, action['action_plan_uuid'])
|
||||
|
||||
for id_ in range(2, 4):
|
||||
action = response['actions'][id_]
|
||||
self.assertEqual(action_plan2.uuid, action['action_plan_uuid'])
|
||||
|
||||
def test_many_with_next_uuid(self):
|
||||
action_list = []
|
||||
for id_ in range(5):
|
||||
action = obj_utils.create_test_action(self.context, id=id_,
|
||||
uuid=utils.generate_uuid(),
|
||||
next=id_ + 1)
|
||||
action_list.append(action.uuid)
|
||||
response = self.get_json('/actions')
|
||||
response_actions = response['actions']
|
||||
for id in [0, 1, 2, 3]:
|
||||
self.assertEqual(response_actions[id]['next_uuid'],
|
||||
response_actions[id + 1]['uuid'])
|
||||
|
||||
def test_many_without_soft_deleted(self):
|
||||
action_list = []
|
||||
for id_ in [1, 2, 3]:
|
||||
action = obj_utils.create_test_action(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
action_list.append(action.uuid)
|
||||
for id_ in [4, 5]:
|
||||
action = obj_utils.create_test_action(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
action.soft_delete()
|
||||
response = self.get_json('/actions')
|
||||
self.assertEqual(3, len(response['actions']))
|
||||
uuids = [s['uuid'] for s in response['actions']]
|
||||
self.assertEqual(sorted(action_list), sorted(uuids))
|
||||
|
||||
def test_many_with_soft_deleted(self):
|
||||
action_list = []
|
||||
for id_ in [1, 2, 3]:
|
||||
action = obj_utils.create_test_action(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
action_list.append(action.uuid)
|
||||
for id_ in [4, 5]:
|
||||
action = obj_utils.create_test_action(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
action.soft_delete()
|
||||
action_list.append(action.uuid)
|
||||
response = self.get_json('/actions',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(5, len(response['actions']))
|
||||
uuids = [s['uuid'] for s in response['actions']]
|
||||
self.assertEqual(sorted(action_list), sorted(uuids))
|
||||
|
||||
def test_many_with_sort_key_next_uuid(self):
|
||||
for id_ in range(5):
|
||||
obj_utils.create_test_action(self.context, id=id_,
|
||||
uuid=utils.generate_uuid(),
|
||||
next=id_ + 1)
|
||||
response = self.get_json('/actions/')
|
||||
reference_uuids = [(s['next_uuid'] if 'next_uuid' in s else None)
|
||||
for s in response['actions']]
|
||||
|
||||
response = self.get_json('/actions/?sort_key=next_uuid')
|
||||
|
||||
self.assertEqual(5, len(response['actions']))
|
||||
uuids = [(s['next_uuid'] if 'next_uuid' in s else None)
|
||||
for s in response['actions']]
|
||||
self.assertEqual(sorted(reference_uuids), uuids)
|
||||
|
||||
response = self.get_json('/actions/?sort_key=next_uuid&sort_dir=desc')
|
||||
|
||||
self.assertEqual(5, len(response['actions']))
|
||||
uuids = [(s['next_uuid'] if 'next_uuid' in s else None)
|
||||
for s in response['actions']]
|
||||
self.assertEqual(sorted(reference_uuids, reverse=True), uuids)
|
||||
|
||||
def test_links(self):
|
||||
uuid = utils.generate_uuid()
|
||||
obj_utils.create_test_action(self.context, id=1, uuid=uuid)
|
||||
response = self.get_json('/actions/%s' % uuid)
|
||||
self.assertIn('links', response.keys())
|
||||
self.assertEqual(2, len(response['links']))
|
||||
self.assertIn(uuid, response['links'][0]['href'])
|
||||
for l in response['links']:
|
||||
bookmark = l['rel'] == 'bookmark'
|
||||
self.assertTrue(self.validate_link(l['href'], bookmark=bookmark))
|
||||
|
||||
def test_collection_links(self):
|
||||
next = -1
|
||||
for id_ in range(5):
|
||||
action = obj_utils.create_test_action(self.context, id=id_,
|
||||
uuid=utils.generate_uuid(),
|
||||
next=next)
|
||||
next = action.id
|
||||
response = self.get_json('/actions/?limit=3')
|
||||
self.assertEqual(3, len(response['actions']))
|
||||
|
||||
next_marker = response['actions'][-1]['uuid']
|
||||
self.assertIn(next_marker, response['next'])
|
||||
|
||||
def test_collection_links_default_limit(self):
|
||||
cfg.CONF.set_override('max_limit', 3, 'api')
|
||||
for id_ in range(5):
|
||||
obj_utils.create_test_action(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
response = self.get_json('/actions')
|
||||
self.assertEqual(3, len(response['actions']))
|
||||
|
||||
next_marker = response['actions'][-1]['uuid']
|
||||
self.assertIn(next_marker, response['next'])
|
||||
|
||||
|
||||
class TestPatch(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPatch, self).setUp()
|
||||
obj_utils.create_test_action_plan(self.context)
|
||||
self.action = obj_utils.create_test_action(self.context, next=None)
|
||||
p = mock.patch.object(db_api.Connection, 'update_action')
|
||||
self.mock_action_update = p.start()
|
||||
self.mock_action_update.side_effect = self._simulate_rpc_action_update
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def _simulate_rpc_action_update(self, action):
|
||||
action.save()
|
||||
return action
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_replace_ok(self, mock_utcnow):
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
|
||||
new_state = 'SUBMITTED'
|
||||
response = self.get_json('/actions/%s' % self.action.uuid)
|
||||
self.assertNotEqual(new_state, response['state'])
|
||||
|
||||
response = self.patch_json(
|
||||
'/actions/%s' % self.action.uuid,
|
||||
[{'path': '/state', 'value': new_state,
|
||||
'op': 'replace'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
response = self.get_json('/actions/%s' % self.action.uuid)
|
||||
self.assertEqual(new_state, response['state'])
|
||||
return_updated_at = timeutils.parse_isotime(
|
||||
response['updated_at']).replace(tzinfo=None)
|
||||
self.assertEqual(test_time, return_updated_at)
|
||||
|
||||
def test_replace_non_existent_action(self):
|
||||
response = self.patch_json('/actions/%s' % utils.generate_uuid(),
|
||||
[{'path': '/state', 'value': 'SUBMITTED',
|
||||
'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_add_ok(self):
|
||||
new_state = 'SUCCESS'
|
||||
response = self.patch_json(
|
||||
'/actions/%s' % self.action.uuid,
|
||||
[{'path': '/state', 'value': new_state, 'op': 'add'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_int)
|
||||
|
||||
response = self.get_json('/actions/%s' % self.action.uuid)
|
||||
self.assertEqual(new_state, response['state'])
|
||||
|
||||
def test_add_non_existent_property(self):
|
||||
response = self.patch_json(
|
||||
'/actions/%s' % self.action.uuid,
|
||||
[{'path': '/foo', 'value': 'bar', 'op': 'add'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(400, response.status_int)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_remove_ok(self):
|
||||
response = self.get_json('/actions/%s' % self.action.uuid)
|
||||
self.assertIsNotNone(response['state'])
|
||||
|
||||
response = self.patch_json('/actions/%s' % self.action.uuid,
|
||||
[{'path': '/state', 'op': 'remove'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
response = self.get_json('/actions/%s' % self.action.uuid)
|
||||
self.assertIsNone(response['state'])
|
||||
|
||||
def test_remove_uuid(self):
|
||||
response = self.patch_json('/actions/%s' % self.action.uuid,
|
||||
[{'path': '/uuid', 'op': 'remove'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_remove_non_existent_property(self):
|
||||
response = self.patch_json(
|
||||
'/actions/%s' % self.action.uuid,
|
||||
[{'path': '/non-existent', 'op': 'remove'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
|
||||
# class TestDelete(api_base.FunctionalTest):
|
||||
|
||||
# def setUp(self):
|
||||
# super(TestDelete, self).setUp()
|
||||
# self.action = obj_utils.create_test_action(self.context, next=None)
|
||||
# p = mock.patch.object(db_api.Connection, 'destroy_action')
|
||||
# self.mock_action_delete = p.start()
|
||||
# self.mock_action_delete.side_effect =
|
||||
# self._simulate_rpc_action_delete
|
||||
# self.addCleanup(p.stop)
|
||||
|
||||
# def _simulate_rpc_action_delete(self, action_uuid):
|
||||
# action = objects.Action.get_by_uuid(self.context, action_uuid)
|
||||
# action.destroy()
|
||||
|
||||
# def test_delete_action(self):
|
||||
# self.delete('/actions/%s' % self.action.uuid)
|
||||
# response = self.get_json('/actions/%s' % self.action.uuid,
|
||||
# expect_errors=True)
|
||||
# self.assertEqual(404, response.status_int)
|
||||
# self.assertEqual('application/json', response.content_type)
|
||||
# self.assertTrue(response.json['error_message'])
|
||||
|
||||
# def test_delete_action_not_found(self):
|
||||
# uuid = utils.generate_uuid()
|
||||
# response = self.delete('/actions/%s' % uuid, expect_errors=True)
|
||||
# self.assertEqual(404, response.status_int)
|
||||
# self.assertEqual('application/json', response.content_type)
|
||||
# self.assertTrue(response.json['error_message'])
|
||||
|
||||
class TestDelete(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDelete, self).setUp()
|
||||
obj_utils.create_test_action_plan(self.context)
|
||||
self.action = obj_utils.create_test_action(self.context, next=None)
|
||||
p = mock.patch.object(db_api.Connection, 'update_action')
|
||||
self.mock_action_update = p.start()
|
||||
self.mock_action_update.side_effect = self._simulate_rpc_action_update
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def _simulate_rpc_action_update(self, action):
|
||||
action.save()
|
||||
return action
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_delete_action(self, mock_utcnow):
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
self.delete('/actions/%s' % self.action.uuid)
|
||||
response = self.get_json('/actions/%s' % self.action.uuid,
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
self.context.show_deleted = True
|
||||
action = objects.Action.get_by_uuid(self.context, self.action.uuid)
|
||||
|
||||
return_deleted_at = timeutils.strtime(action['deleted_at'])
|
||||
self.assertEqual(timeutils.strtime(test_time), return_deleted_at)
|
||||
self.assertEqual(action['state'], 'DELETED')
|
||||
|
||||
def test_delete_action_not_found(self):
|
||||
uuid = utils.generate_uuid()
|
||||
response = self.delete('/actions/%s' % uuid, expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
433
watcher/tests/api/v1/test_actions_plans.py
Normal file
433
watcher/tests/api/v1/test_actions_plans.py
Normal file
@@ -0,0 +1,433 @@
|
||||
# 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 datetime
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import timeutils
|
||||
from wsme import types as wtypes
|
||||
|
||||
from watcher.api.controllers.v1 import action_plan as api_action_plan
|
||||
from watcher.applier.framework import rpcapi as aapi
|
||||
from watcher.common import utils
|
||||
from watcher.db import api as db_api
|
||||
from watcher import objects
|
||||
from watcher.tests.api import base as api_base
|
||||
from watcher.tests.api import utils as api_utils
|
||||
from watcher.tests import base
|
||||
from watcher.tests.objects import utils as obj_utils
|
||||
|
||||
|
||||
class TestActionPlanObject(base.TestCase):
|
||||
|
||||
def test_actionPlan_init(self):
|
||||
act_plan_dict = api_utils.action_plan_post_data()
|
||||
del act_plan_dict['state']
|
||||
del act_plan_dict['audit_id']
|
||||
act_plan = api_action_plan.ActionPlan(**act_plan_dict)
|
||||
self.assertEqual(wtypes.Unset, act_plan.state)
|
||||
|
||||
|
||||
class TestListActionPlan(api_base.FunctionalTest):
|
||||
|
||||
def test_empty(self):
|
||||
response = self.get_json('/action_plans')
|
||||
self.assertEqual([], response['action_plans'])
|
||||
|
||||
def _assert_action_plans_fields(self, action_plan):
|
||||
action_plan_fields = ['state']
|
||||
for field in action_plan_fields:
|
||||
self.assertIn(field, action_plan)
|
||||
|
||||
def test_one(self):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(self.context)
|
||||
response = self.get_json('/action_plans')
|
||||
self.assertEqual(action_plan.uuid,
|
||||
response['action_plans'][0]["uuid"])
|
||||
self._assert_action_plans_fields(response['action_plans'][0])
|
||||
|
||||
def test_one_soft_deleted(self):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(self.context)
|
||||
action_plan.soft_delete()
|
||||
response = self.get_json('/action_plans',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(action_plan.uuid,
|
||||
response['action_plans'][0]["uuid"])
|
||||
self._assert_action_plans_fields(response['action_plans'][0])
|
||||
|
||||
response = self.get_json('/action_plans')
|
||||
self.assertEqual([], response['action_plans'])
|
||||
|
||||
def test_get_one(self):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(self.context)
|
||||
response = self.get_json('/action_plans/%s' % action_plan['uuid'])
|
||||
self.assertEqual(action_plan.uuid, response['uuid'])
|
||||
self._assert_action_plans_fields(response)
|
||||
|
||||
def test_get_one_soft_deleted(self):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(self.context)
|
||||
action_plan.soft_delete()
|
||||
response = self.get_json('/action_plans/%s' % action_plan['uuid'],
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(action_plan.uuid, response['uuid'])
|
||||
self._assert_action_plans_fields(response)
|
||||
|
||||
response = self.get_json('/action_plans/%s' % action_plan['uuid'],
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
|
||||
def test_detail(self):
|
||||
action_plan = obj_utils.create_test_action_plan(self.context,
|
||||
audit_id=None)
|
||||
response = self.get_json('/action_plans/detail')
|
||||
self.assertEqual(action_plan.uuid,
|
||||
response['action_plans'][0]["uuid"])
|
||||
self._assert_action_plans_fields(response['action_plans'][0])
|
||||
|
||||
def test_detail_soft_deleted(self):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(self.context)
|
||||
action_plan.soft_delete()
|
||||
response = self.get_json('/action_plans/detail',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(action_plan.uuid,
|
||||
response['action_plans'][0]["uuid"])
|
||||
self._assert_action_plans_fields(response['action_plans'][0])
|
||||
|
||||
response = self.get_json('/action_plans/detail')
|
||||
self.assertEqual([], response['action_plans'])
|
||||
|
||||
def test_detail_against_single(self):
|
||||
action_plan = obj_utils.create_test_action_plan(self.context)
|
||||
response = self.get_json(
|
||||
'/action_plan/%s/detail' % action_plan['uuid'],
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
|
||||
def test_many(self):
|
||||
action_plan_list = []
|
||||
for id_ in range(5):
|
||||
action_plan = obj_utils.create_action_plan_without_audit(
|
||||
self.context, id=id_, uuid=utils.generate_uuid())
|
||||
action_plan_list.append(action_plan.uuid)
|
||||
response = self.get_json('/action_plans')
|
||||
self.assertEqual(len(action_plan_list), len(response['action_plans']))
|
||||
uuids = [s['uuid'] for s in response['action_plans']]
|
||||
self.assertEqual(sorted(action_plan_list), sorted(uuids))
|
||||
|
||||
def test_many_with_soft_deleted_audit_uuid(self):
|
||||
action_plan_list = []
|
||||
audit1 = obj_utils.create_test_audit(self.context,
|
||||
id=1,
|
||||
uuid=utils.generate_uuid())
|
||||
audit2 = obj_utils.create_test_audit(self.context,
|
||||
id=2,
|
||||
uuid=utils.generate_uuid())
|
||||
|
||||
for id_ in range(0, 2):
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
audit_id=audit1.id)
|
||||
action_plan_list.append(action_plan.uuid)
|
||||
|
||||
for id_ in range(2, 4):
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
audit_id=audit2.id)
|
||||
action_plan_list.append(action_plan.uuid)
|
||||
|
||||
self.delete('/audits/%s' % audit1.uuid)
|
||||
|
||||
response = self.get_json('/action_plans')
|
||||
|
||||
self.assertEqual(len(action_plan_list), len(response['action_plans']))
|
||||
|
||||
for id_ in range(0, 2):
|
||||
action_plan = response['action_plans'][id_]
|
||||
self.assertEqual(None, action_plan['audit_uuid'])
|
||||
|
||||
for id_ in range(2, 4):
|
||||
action_plan = response['action_plans'][id_]
|
||||
self.assertEqual(audit2.uuid, action_plan['audit_uuid'])
|
||||
|
||||
def test_many_with_audit_uuid(self):
|
||||
action_plan_list = []
|
||||
audit = obj_utils.create_test_audit(self.context,
|
||||
uuid=utils.generate_uuid())
|
||||
for id_ in range(5):
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
audit_id=audit.id)
|
||||
action_plan_list.append(action_plan.uuid)
|
||||
response = self.get_json('/action_plans')
|
||||
self.assertEqual(len(action_plan_list), len(response['action_plans']))
|
||||
for action in response['action_plans']:
|
||||
self.assertEqual(audit.uuid, action['audit_uuid'])
|
||||
|
||||
def test_many_with_audit_uuid_filter(self):
|
||||
action_plan_list1 = []
|
||||
audit1 = obj_utils.create_test_audit(self.context,
|
||||
uuid=utils.generate_uuid())
|
||||
for id_ in range(5):
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
audit_id=audit1.id)
|
||||
action_plan_list1.append(action_plan.uuid)
|
||||
|
||||
audit2 = obj_utils.create_test_audit(self.context,
|
||||
uuid=utils.generate_uuid())
|
||||
action_plan_list2 = []
|
||||
for id_ in [5, 6, 7]:
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
audit_id=audit2.id)
|
||||
action_plan_list2.append(action_plan.uuid)
|
||||
|
||||
response = self.get_json('/action_plans?audit_uuid=%s' % audit2.uuid)
|
||||
self.assertEqual(len(action_plan_list2), len(response['action_plans']))
|
||||
for action in response['action_plans']:
|
||||
self.assertEqual(audit2.uuid, action['audit_uuid'])
|
||||
|
||||
def test_many_without_soft_deleted(self):
|
||||
action_plan_list = []
|
||||
for id_ in [1, 2, 3]:
|
||||
action_plan = obj_utils.create_action_plan_without_audit(
|
||||
self.context, id=id_, uuid=utils.generate_uuid())
|
||||
action_plan_list.append(action_plan.uuid)
|
||||
for id_ in [4, 5]:
|
||||
action_plan = obj_utils.create_test_action_plan(
|
||||
self.context, id=id_, uuid=utils.generate_uuid())
|
||||
action_plan.soft_delete()
|
||||
response = self.get_json('/action_plans')
|
||||
self.assertEqual(3, len(response['action_plans']))
|
||||
uuids = [s['uuid'] for s in response['action_plans']]
|
||||
self.assertEqual(sorted(action_plan_list), sorted(uuids))
|
||||
|
||||
def test_many_with_soft_deleted(self):
|
||||
action_plan_list = []
|
||||
for id_ in [1, 2, 3]:
|
||||
action_plan = obj_utils.create_action_plan_without_audit(
|
||||
self.context, id=id_, uuid=utils.generate_uuid())
|
||||
action_plan_list.append(action_plan.uuid)
|
||||
for id_ in [4, 5]:
|
||||
action_plan = obj_utils.create_action_plan_without_audit(
|
||||
self.context, id=id_, uuid=utils.generate_uuid())
|
||||
action_plan.soft_delete()
|
||||
action_plan_list.append(action_plan.uuid)
|
||||
response = self.get_json('/action_plans',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(5, len(response['action_plans']))
|
||||
uuids = [s['uuid'] for s in response['action_plans']]
|
||||
self.assertEqual(sorted(action_plan_list), sorted(uuids))
|
||||
|
||||
def test_many_with_sort_key_audit_uuid(self):
|
||||
audit_list = []
|
||||
for id_ in range(5):
|
||||
audit = obj_utils.create_test_audit(self.context,
|
||||
uuid=utils.generate_uuid())
|
||||
obj_utils.create_test_action_plan(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
audit_id=audit.id)
|
||||
audit_list.append(audit.uuid)
|
||||
|
||||
response = self.get_json('/action_plans/?sort_key=audit_uuid')
|
||||
|
||||
self.assertEqual(5, len(response['action_plans']))
|
||||
uuids = [s['audit_uuid'] for s in response['action_plans']]
|
||||
self.assertEqual(sorted(audit_list), uuids)
|
||||
|
||||
def test_links(self):
|
||||
uuid = utils.generate_uuid()
|
||||
obj_utils.create_action_plan_without_audit(self.context,
|
||||
id=1, uuid=uuid)
|
||||
response = self.get_json('/action_plans/%s' % uuid)
|
||||
self.assertIn('links', response.keys())
|
||||
self.assertEqual(2, len(response['links']))
|
||||
self.assertIn(uuid, response['links'][0]['href'])
|
||||
for l in response['links']:
|
||||
bookmark = l['rel'] == 'bookmark'
|
||||
self.assertTrue(self.validate_link(l['href'], bookmark=bookmark))
|
||||
|
||||
def test_collection_links(self):
|
||||
for id_ in range(5):
|
||||
obj_utils.create_action_plan_without_audit(
|
||||
self.context, id=id_, uuid=utils.generate_uuid())
|
||||
response = self.get_json('/action_plans/?limit=3')
|
||||
self.assertEqual(3, len(response['action_plans']))
|
||||
|
||||
next_marker = response['action_plans'][-1]['uuid']
|
||||
self.assertIn(next_marker, response['next'])
|
||||
|
||||
def test_collection_links_default_limit(self):
|
||||
cfg.CONF.set_override('max_limit', 3, 'api')
|
||||
for id_ in range(5):
|
||||
obj_utils.create_action_plan_without_audit(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
audit_id=None)
|
||||
response = self.get_json('/action_plans')
|
||||
self.assertEqual(3, len(response['action_plans']))
|
||||
|
||||
next_marker = response['action_plans'][-1]['uuid']
|
||||
self.assertIn(next_marker, response['next'])
|
||||
|
||||
|
||||
class TestDelete(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDelete, self).setUp()
|
||||
self.action_plan = obj_utils.create_action_plan_without_audit(
|
||||
self.context)
|
||||
p = mock.patch.object(db_api.Connection, 'destroy_action_plan')
|
||||
self.mock_action_plan_delete = p.start()
|
||||
self.mock_action_plan_delete.side_effect = \
|
||||
self._simulate_rpc_action_plan_delete
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def _simulate_rpc_action_plan_delete(self, audit_uuid):
|
||||
action_plan = objects.ActionPlan.get_by_uuid(self.context, audit_uuid)
|
||||
action_plan.destroy()
|
||||
|
||||
def test_delete_action_plan(self):
|
||||
self.delete('/action_plans/%s' % self.action_plan.uuid)
|
||||
response = self.get_json('/action_plans/%s' % self.action_plan.uuid,
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_delete_ction_plan_not_found(self):
|
||||
uuid = utils.generate_uuid()
|
||||
response = self.delete('/action_plans/%s' % uuid, expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
|
||||
class TestPatch(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPatch, self).setUp()
|
||||
self.action_plan = obj_utils.create_action_plan_without_audit(
|
||||
self.context)
|
||||
p = mock.patch.object(db_api.Connection, 'update_action_plan')
|
||||
self.mock_action_plan_update = p.start()
|
||||
self.mock_action_plan_update.side_effect = \
|
||||
self._simulate_rpc_action_plan_update
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def _simulate_rpc_action_plan_update(self, action_plan):
|
||||
action_plan.save()
|
||||
return action_plan
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_replace_ok(self, mock_utcnow):
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
|
||||
new_state = 'CANCELLED'
|
||||
response = self.get_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid)
|
||||
self.assertNotEqual(new_state, response['state'])
|
||||
|
||||
response = self.patch_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid,
|
||||
[{'path': '/state', 'value': new_state,
|
||||
'op': 'replace'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
response = self.get_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid)
|
||||
self.assertEqual(new_state, response['state'])
|
||||
return_updated_at = timeutils.parse_isotime(
|
||||
response['updated_at']).replace(tzinfo=None)
|
||||
self.assertEqual(test_time, return_updated_at)
|
||||
|
||||
def test_replace_non_existent_action_plan(self):
|
||||
response = self.patch_json(
|
||||
'/action_plans/%s' % utils.generate_uuid(),
|
||||
[{'path': '/state', 'value': 'CANCELLED',
|
||||
'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_add_ok(self):
|
||||
new_state = 'CANCELLED'
|
||||
response = self.patch_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid,
|
||||
[{'path': '/state', 'value': new_state, 'op': 'add'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_int)
|
||||
|
||||
response = self.get_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid)
|
||||
self.assertEqual(new_state, response['state'])
|
||||
|
||||
def test_add_non_existent_property(self):
|
||||
response = self.patch_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid,
|
||||
[{'path': '/foo', 'value': 'bar', 'op': 'add'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(400, response.status_int)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_remove_ok(self):
|
||||
response = self.get_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid)
|
||||
self.assertIsNotNone(response['state'])
|
||||
|
||||
response = self.patch_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid,
|
||||
[{'path': '/state', 'op': 'remove'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
response = self.get_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid)
|
||||
self.assertIsNone(response['state'])
|
||||
|
||||
def test_remove_uuid(self):
|
||||
response = self.patch_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid,
|
||||
[{'path': '/uuid', 'op': 'remove'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_remove_non_existent_property(self):
|
||||
response = self.patch_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid,
|
||||
[{'path': '/non-existent', 'op': 'remove'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_replace_ok_state_starting(self):
|
||||
with mock.patch.object(aapi.ApplierAPI,
|
||||
'launch_action_plan') as applier_mock:
|
||||
new_state = 'STARTING'
|
||||
response = self.get_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid)
|
||||
self.assertNotEqual(new_state, response['state'])
|
||||
|
||||
response = self.patch_json(
|
||||
'/action_plans/%s' % self.action_plan.uuid,
|
||||
[{'path': '/state', 'value': new_state,
|
||||
'op': 'replace'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
applier_mock.assert_called_once_with(mock.ANY,
|
||||
self.action_plan.uuid)
|
||||
475
watcher/tests/api/v1/test_audit_templates.py
Normal file
475
watcher/tests/api/v1/test_audit_templates.py
Normal file
@@ -0,0 +1,475 @@
|
||||
# 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 datetime
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import timeutils
|
||||
from six.moves.urllib import parse as urlparse
|
||||
from wsme import types as wtypes
|
||||
|
||||
from watcher.api.controllers.v1 import audit_template as api_audit_template
|
||||
from watcher.common import utils
|
||||
from watcher.db import api as db_api
|
||||
from watcher import objects
|
||||
from watcher.tests.api import base as api_base
|
||||
from watcher.tests.api import utils as api_utils
|
||||
from watcher.tests import base
|
||||
from watcher.tests.objects import utils as obj_utils
|
||||
|
||||
|
||||
class TestAuditTemplateObject(base.TestCase):
|
||||
|
||||
def test_audit_template_init(self):
|
||||
audit_template_dict = api_utils.audit_template_post_data()
|
||||
del audit_template_dict['name']
|
||||
audit_template = api_audit_template.AuditTemplate(
|
||||
**audit_template_dict)
|
||||
self.assertEqual(wtypes.Unset, audit_template.name)
|
||||
|
||||
|
||||
class TestListAuditTemplate(api_base.FunctionalTest):
|
||||
|
||||
def test_empty(self):
|
||||
response = self.get_json('/audit_templates')
|
||||
self.assertEqual([], response['audit_templates'])
|
||||
|
||||
def _assert_audit_template_fields(self, audit_template):
|
||||
audit_template_fields = ['name', 'goal', 'host_aggregate']
|
||||
for field in audit_template_fields:
|
||||
self.assertIn(field, audit_template)
|
||||
|
||||
def test_one(self):
|
||||
audit_template = obj_utils.create_test_audit_template(self.context)
|
||||
response = self.get_json('/audit_templates')
|
||||
self.assertEqual(audit_template.uuid,
|
||||
response['audit_templates'][0]["uuid"])
|
||||
self._assert_audit_template_fields(response['audit_templates'][0])
|
||||
|
||||
def test_one_soft_deleted(self):
|
||||
audit_template = obj_utils.create_test_audit_template(self.context)
|
||||
audit_template.soft_delete()
|
||||
response = self.get_json('/audit_templates',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(audit_template.uuid,
|
||||
response['audit_templates'][0]["uuid"])
|
||||
self._assert_audit_template_fields(response['audit_templates'][0])
|
||||
|
||||
response = self.get_json('/audit_templates')
|
||||
self.assertEqual([], response['audit_templates'])
|
||||
|
||||
def test_get_one_by_uuid(self):
|
||||
audit_template = obj_utils.create_test_audit_template(self.context)
|
||||
response = self.get_json(
|
||||
'/audit_templates/%s' % audit_template['uuid'])
|
||||
self.assertEqual(audit_template.uuid, response['uuid'])
|
||||
self._assert_audit_template_fields(response)
|
||||
|
||||
def test_get_one_by_name(self):
|
||||
audit_template = obj_utils.create_test_audit_template(self.context)
|
||||
response = self.get_json(urlparse.quote(
|
||||
'/audit_templates/%s' % audit_template['name']))
|
||||
self.assertEqual(audit_template.uuid, response['uuid'])
|
||||
self._assert_audit_template_fields(response)
|
||||
|
||||
def test_get_one_soft_deleted(self):
|
||||
audit_template = obj_utils.create_test_audit_template(self.context)
|
||||
audit_template.soft_delete()
|
||||
response = self.get_json(
|
||||
'/audit_templates/%s' % audit_template['uuid'],
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(audit_template.uuid, response['uuid'])
|
||||
self._assert_audit_template_fields(response)
|
||||
|
||||
response = self.get_json(
|
||||
'/audit_templates/%s' % audit_template['uuid'],
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
|
||||
def test_detail(self):
|
||||
audit_template = obj_utils.create_test_audit_template(self.context)
|
||||
response = self.get_json('/audit_templates/detail')
|
||||
self.assertEqual(audit_template.uuid,
|
||||
response['audit_templates'][0]["uuid"])
|
||||
self._assert_audit_template_fields(response['audit_templates'][0])
|
||||
|
||||
def test_detail_soft_deleted(self):
|
||||
audit_template = obj_utils.create_test_audit_template(self.context)
|
||||
audit_template.soft_delete()
|
||||
response = self.get_json('/audit_templates/detail',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(audit_template.uuid,
|
||||
response['audit_templates'][0]["uuid"])
|
||||
self._assert_audit_template_fields(response['audit_templates'][0])
|
||||
|
||||
response = self.get_json('/audit_templates/detail')
|
||||
self.assertEqual([], response['audit_templates'])
|
||||
|
||||
def test_detail_against_single(self):
|
||||
audit_template = obj_utils.create_test_audit_template(self.context)
|
||||
response = self.get_json(
|
||||
'/audit_templates/%s/detail' % audit_template['uuid'],
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
|
||||
def test_many(self):
|
||||
audit_template_list = []
|
||||
for id_ in range(5):
|
||||
audit_template = obj_utils.create_test_audit_template(
|
||||
self.context, id=id_,
|
||||
uuid=utils.generate_uuid(),
|
||||
name='My Audit Template ' + str(id_))
|
||||
audit_template_list.append(audit_template.uuid)
|
||||
response = self.get_json('/audit_templates')
|
||||
self.assertEqual(len(audit_template_list),
|
||||
len(response['audit_templates']))
|
||||
uuids = [s['uuid'] for s in response['audit_templates']]
|
||||
self.assertEqual(sorted(audit_template_list), sorted(uuids))
|
||||
|
||||
def test_many_without_soft_deleted(self):
|
||||
audit_template_list = []
|
||||
for id_ in [1, 2, 3]:
|
||||
audit_template = obj_utils.create_test_audit_template(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
name='My Audit Template ' + str(id_))
|
||||
audit_template_list.append(audit_template.uuid)
|
||||
for id_ in [4, 5]:
|
||||
audit_template = obj_utils.create_test_audit_template(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
name='My Audit Template ' + str(id_))
|
||||
audit_template.soft_delete()
|
||||
response = self.get_json('/audit_templates')
|
||||
self.assertEqual(3, len(response['audit_templates']))
|
||||
uuids = [s['uuid'] for s in response['audit_templates']]
|
||||
self.assertEqual(sorted(audit_template_list), sorted(uuids))
|
||||
|
||||
def test_many_with_soft_deleted(self):
|
||||
audit_template_list = []
|
||||
for id_ in [1, 2, 3]:
|
||||
audit_template = obj_utils.create_test_audit_template(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
name='My Audit Template ' + str(id_))
|
||||
audit_template_list.append(audit_template.uuid)
|
||||
for id_ in [4, 5]:
|
||||
audit_template = obj_utils.create_test_audit_template(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
name='My Audit Template ' + str(id_))
|
||||
audit_template.soft_delete()
|
||||
audit_template_list.append(audit_template.uuid)
|
||||
response = self.get_json('/audit_templates',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(5, len(response['audit_templates']))
|
||||
uuids = [s['uuid'] for s in response['audit_templates']]
|
||||
self.assertEqual(sorted(audit_template_list), sorted(uuids))
|
||||
|
||||
def test_links(self):
|
||||
uuid = utils.generate_uuid()
|
||||
obj_utils.create_test_audit_template(self.context, id=1, uuid=uuid)
|
||||
response = self.get_json('/audit_templates/%s' % uuid)
|
||||
self.assertIn('links', response.keys())
|
||||
self.assertEqual(2, len(response['links']))
|
||||
self.assertIn(uuid, response['links'][0]['href'])
|
||||
for l in response['links']:
|
||||
bookmark = l['rel'] == 'bookmark'
|
||||
self.assertTrue(self.validate_link(l['href'], bookmark=bookmark))
|
||||
|
||||
def test_collection_links(self):
|
||||
for id_ in range(5):
|
||||
obj_utils.create_test_audit_template(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
name='My Audit Template ' + str(id_))
|
||||
response = self.get_json('/audit_templates/?limit=3')
|
||||
self.assertEqual(3, len(response['audit_templates']))
|
||||
|
||||
next_marker = response['audit_templates'][-1]['uuid']
|
||||
self.assertIn(next_marker, response['next'])
|
||||
|
||||
def test_collection_links_default_limit(self):
|
||||
cfg.CONF.set_override('max_limit', 3, 'api')
|
||||
for id_ in range(5):
|
||||
obj_utils.create_test_audit_template(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
name='My Audit Template ' + str(id_))
|
||||
response = self.get_json('/audit_templates')
|
||||
self.assertEqual(3, len(response['audit_templates']))
|
||||
|
||||
next_marker = response['audit_templates'][-1]['uuid']
|
||||
self.assertIn(next_marker, response['next'])
|
||||
|
||||
|
||||
class TestPatch(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPatch, self).setUp()
|
||||
self.audit_template = obj_utils.create_test_audit_template(
|
||||
self.context)
|
||||
p = mock.patch.object(db_api.Connection, 'update_audit_template')
|
||||
self.mock_audit_template_update = p.start()
|
||||
self.mock_audit_template_update.side_effect = \
|
||||
self._simulate_rpc_audit_template_update
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def _simulate_rpc_audit_template_update(self, audit_template):
|
||||
audit_template.save()
|
||||
return audit_template
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_replace_ok(self, mock_utcnow):
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
|
||||
new_goal = 'BALANCE_LOAD'
|
||||
response = self.get_json(
|
||||
'/audit_templates/%s' % self.audit_template.uuid)
|
||||
self.assertNotEqual(new_goal, response['goal'])
|
||||
|
||||
response = self.patch_json(
|
||||
'/audit_templates/%s' % self.audit_template.uuid,
|
||||
[{'path': '/goal', 'value': new_goal,
|
||||
'op': 'replace'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
response = self.get_json(
|
||||
'/audit_templates/%s' % self.audit_template.uuid)
|
||||
self.assertEqual(new_goal, response['goal'])
|
||||
return_updated_at = timeutils.parse_isotime(
|
||||
response['updated_at']).replace(tzinfo=None)
|
||||
self.assertEqual(test_time, return_updated_at)
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_replace_ok_by_name(self, mock_utcnow):
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
|
||||
new_goal = 'BALANCE_LOAD'
|
||||
response = self.get_json(urlparse.quote(
|
||||
'/audit_templates/%s' % self.audit_template.name))
|
||||
self.assertNotEqual(new_goal, response['goal'])
|
||||
|
||||
response = self.patch_json(
|
||||
'/audit_templates/%s' % self.audit_template.name,
|
||||
[{'path': '/goal', 'value': new_goal,
|
||||
'op': 'replace'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
response = self.get_json(
|
||||
'/audit_templates/%s' % self.audit_template.name)
|
||||
self.assertEqual(new_goal, response['goal'])
|
||||
return_updated_at = timeutils.parse_isotime(
|
||||
response['updated_at']).replace(tzinfo=None)
|
||||
self.assertEqual(test_time, return_updated_at)
|
||||
|
||||
def test_replace_non_existent_audit_template(self):
|
||||
response = self.patch_json(
|
||||
'/audit_templates/%s' % utils.generate_uuid(),
|
||||
[{'path': '/goal', 'value': 'BALANCE_LOAD',
|
||||
'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_add_ok(self):
|
||||
new_goal = 'BALANCE_LOAD'
|
||||
response = self.patch_json(
|
||||
'/audit_templates/%s' % self.audit_template.uuid,
|
||||
[{'path': '/goal', 'value': new_goal, 'op': 'add'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_int)
|
||||
|
||||
response = self.get_json(
|
||||
'/audit_templates/%s' % self.audit_template.uuid)
|
||||
self.assertEqual(new_goal, response['goal'])
|
||||
|
||||
def test_add_non_existent_property(self):
|
||||
response = self.patch_json(
|
||||
'/audit_templates/%s' % self.audit_template.uuid,
|
||||
[{'path': '/foo', 'value': 'bar', 'op': 'add'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(400, response.status_int)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_remove_ok(self):
|
||||
response = self.get_json(
|
||||
'/audit_templates/%s' % self.audit_template.uuid)
|
||||
self.assertIsNotNone(response['goal'])
|
||||
|
||||
response = self.patch_json(
|
||||
'/audit_templates/%s' % self.audit_template.uuid,
|
||||
[{'path': '/goal', 'op': 'remove'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
response = self.get_json(
|
||||
'/audit_templates/%s' % self.audit_template.uuid)
|
||||
self.assertIsNone(response['goal'])
|
||||
|
||||
def test_remove_uuid(self):
|
||||
response = self.patch_json(
|
||||
'/audit_templates/%s' % self.audit_template.uuid,
|
||||
[{'path': '/uuid', 'op': 'remove'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_remove_non_existent_property(self):
|
||||
response = self.patch_json(
|
||||
'/audit_templates/%s' % self.audit_template.uuid,
|
||||
[{'path': '/non-existent', 'op': 'remove'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
|
||||
class TestPost(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPost, self).setUp()
|
||||
p = mock.patch.object(db_api.Connection, 'create_audit_template')
|
||||
self.mock_create_audit_template = p.start()
|
||||
self.mock_create_audit_template.side_effect = (
|
||||
self._simulate_rpc_audit_template_create)
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def _simulate_rpc_audit_template_create(self, audit_template):
|
||||
audit_template.create()
|
||||
return audit_template
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_create_audit_template(self, mock_utcnow):
|
||||
audit_template_dict = api_utils.audit_template_post_data()
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
|
||||
response = self.post_json('/audit_templates', audit_template_dict)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(201, response.status_int)
|
||||
# Check location header
|
||||
self.assertIsNotNone(response.location)
|
||||
expected_location = \
|
||||
'/v1/audit_templates/%s' % audit_template_dict['uuid']
|
||||
self.assertEqual(urlparse.urlparse(response.location).path,
|
||||
expected_location)
|
||||
self.assertEqual(audit_template_dict['uuid'], response.json['uuid'])
|
||||
self.assertNotIn('updated_at', response.json.keys)
|
||||
self.assertNotIn('deleted_at', response.json.keys)
|
||||
return_created_at = timeutils.parse_isotime(
|
||||
response.json['created_at']).replace(tzinfo=None)
|
||||
self.assertEqual(test_time, return_created_at)
|
||||
|
||||
def test_create_audit_template_doesnt_contain_id(self):
|
||||
with mock.patch.object(
|
||||
self.dbapi,
|
||||
'create_audit_template',
|
||||
wraps=self.dbapi.create_audit_template
|
||||
) as cn_mock:
|
||||
audit_template_dict = api_utils.audit_template_post_data(
|
||||
goal='SERVERS_CONSOLIDATION')
|
||||
response = self.post_json('/audit_templates', audit_template_dict)
|
||||
self.assertEqual(audit_template_dict['goal'],
|
||||
response.json['goal'])
|
||||
cn_mock.assert_called_once_with(mock.ANY)
|
||||
# Check that 'id' is not in first arg of positional args
|
||||
self.assertNotIn('id', cn_mock.call_args[0][0])
|
||||
|
||||
def test_create_audit_template_generate_uuid(self):
|
||||
audit_template_dict = api_utils.audit_template_post_data()
|
||||
del audit_template_dict['uuid']
|
||||
|
||||
response = self.post_json('/audit_templates', audit_template_dict)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(201, response.status_int)
|
||||
self.assertEqual(audit_template_dict['goal'], response.json['goal'])
|
||||
self.assertTrue(utils.is_uuid_like(response.json['uuid']))
|
||||
|
||||
def test_create_audit_template_with_invalid_goal(self):
|
||||
with mock.patch.object(
|
||||
self.dbapi,
|
||||
'create_audit_template',
|
||||
wraps=self.dbapi.create_audit_template
|
||||
) as cn_mock:
|
||||
audit_template_dict = api_utils.audit_template_post_data(
|
||||
goal='INVALID_GOAL')
|
||||
response = self.post_json('/audit_templates',
|
||||
audit_template_dict, expect_errors=True)
|
||||
self.assertEqual(400, response.status_int)
|
||||
assert not cn_mock.called
|
||||
|
||||
|
||||
class TestDelete(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDelete, self).setUp()
|
||||
self.audit_template = obj_utils.create_test_audit_template(
|
||||
self.context)
|
||||
p = mock.patch.object(db_api.Connection, 'update_audit_template')
|
||||
self.mock_audit_template_update = p.start()
|
||||
self.mock_audit_template_update.side_effect = \
|
||||
self._simulate_rpc_audit_template_update
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def _simulate_rpc_audit_template_update(self, audit_template):
|
||||
audit_template.save()
|
||||
return audit_template
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_delete_audit_template_by_uuid(self, mock_utcnow):
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
self.delete('/audit_templates/%s' % self.audit_template.uuid)
|
||||
response = self.get_json(
|
||||
'/audit_templates/%s' % self.audit_template.uuid,
|
||||
expect_errors=True)
|
||||
# self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
self.context.show_deleted = True
|
||||
audit_template = objects.AuditTemplate.get_by_uuid(
|
||||
self.context, self.audit_template.uuid)
|
||||
|
||||
return_deleted_at = timeutils.strtime(audit_template['deleted_at'])
|
||||
self.assertEqual(timeutils.strtime(test_time), return_deleted_at)
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_delete_audit_template_by_name(self, mock_utcnow):
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
self.delete(urlparse.quote('/audit_templates/%s' %
|
||||
self.audit_template.name))
|
||||
response = self.get_json(urlparse.quote(
|
||||
'/audit_templates/%s' % self.audit_template.name),
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
self.context.show_deleted = True
|
||||
audit_template = objects.AuditTemplate.get_by_name(
|
||||
self.context, self.audit_template.name)
|
||||
|
||||
return_deleted_at = timeutils.strtime(audit_template['deleted_at'])
|
||||
self.assertEqual(timeutils.strtime(test_time), return_deleted_at)
|
||||
|
||||
def test_delete_audit_template_not_found(self):
|
||||
uuid = utils.generate_uuid()
|
||||
response = self.delete(
|
||||
'/audit_templates/%s' % uuid, expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
555
watcher/tests/api/v1/test_audits.py
Normal file
555
watcher/tests/api/v1/test_audits.py
Normal file
@@ -0,0 +1,555 @@
|
||||
# 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 datetime
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import timeutils
|
||||
from wsme import types as wtypes
|
||||
|
||||
from six.moves.urllib import parse as urlparse
|
||||
from watcher.api.controllers.v1 import audit as api_audit
|
||||
from watcher.common import utils
|
||||
from watcher.db import api as db_api
|
||||
from watcher.decision_engine.framework import rpcapi as deapi
|
||||
from watcher import objects
|
||||
from watcher.tests.api import base as api_base
|
||||
from watcher.tests.api import utils as api_utils
|
||||
from watcher.tests import base
|
||||
from watcher.tests.db import utils as db_utils
|
||||
from watcher.tests.objects import utils as obj_utils
|
||||
|
||||
|
||||
def post_get_test_audit(**kw):
|
||||
audit = api_utils.audit_post_data(**kw)
|
||||
audit_template = db_utils.get_test_audit_template()
|
||||
audit['audit_template_id'] = None
|
||||
audit['audit_template_uuid'] = kw.get('audit_template_uuid',
|
||||
audit_template['uuid'])
|
||||
return audit
|
||||
|
||||
|
||||
class TestAuditObject(base.TestCase):
|
||||
|
||||
def test_audit_init(self):
|
||||
audit_dict = api_utils.audit_post_data(audit_template_id=None)
|
||||
del audit_dict['state']
|
||||
audit = api_audit.Audit(**audit_dict)
|
||||
self.assertEqual(wtypes.Unset, audit.state)
|
||||
|
||||
|
||||
class TestListAudit(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestListAudit, self).setUp()
|
||||
obj_utils.create_test_audit_template(self.context)
|
||||
|
||||
def test_empty(self):
|
||||
response = self.get_json('/audits')
|
||||
self.assertEqual([], response['audits'])
|
||||
|
||||
def _assert_audit_fields(self, audit):
|
||||
audit_fields = ['type', 'deadline', 'state']
|
||||
for field in audit_fields:
|
||||
self.assertIn(field, audit)
|
||||
|
||||
def test_one(self):
|
||||
audit = obj_utils.create_test_audit(self.context)
|
||||
response = self.get_json('/audits')
|
||||
self.assertEqual(audit.uuid, response['audits'][0]["uuid"])
|
||||
self._assert_audit_fields(response['audits'][0])
|
||||
|
||||
def test_one_soft_deleted(self):
|
||||
audit = obj_utils.create_test_audit(self.context)
|
||||
audit.soft_delete()
|
||||
response = self.get_json('/audits',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(audit.uuid, response['audits'][0]["uuid"])
|
||||
self._assert_audit_fields(response['audits'][0])
|
||||
|
||||
response = self.get_json('/audits')
|
||||
self.assertEqual([], response['audits'])
|
||||
|
||||
def test_get_one(self):
|
||||
audit = obj_utils.create_test_audit(self.context)
|
||||
response = self.get_json('/audits/%s' % audit['uuid'])
|
||||
self.assertEqual(audit.uuid, response['uuid'])
|
||||
self._assert_audit_fields(response)
|
||||
|
||||
def test_get_one_soft_deleted(self):
|
||||
audit = obj_utils.create_test_audit(self.context)
|
||||
audit.soft_delete()
|
||||
response = self.get_json('/audits/%s' % audit['uuid'],
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(audit.uuid, response['uuid'])
|
||||
self._assert_audit_fields(response)
|
||||
|
||||
response = self.get_json('/audits/%s' % audit['uuid'],
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
|
||||
def test_detail(self):
|
||||
audit = obj_utils.create_test_audit(self.context)
|
||||
response = self.get_json('/audits/detail')
|
||||
self.assertEqual(audit.uuid, response['audits'][0]["uuid"])
|
||||
self._assert_audit_fields(response['audits'][0])
|
||||
|
||||
def test_detail_soft_deleted(self):
|
||||
audit = obj_utils.create_test_audit(self.context)
|
||||
audit.soft_delete()
|
||||
response = self.get_json('/audits/detail',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(audit.uuid, response['audits'][0]["uuid"])
|
||||
self._assert_audit_fields(response['audits'][0])
|
||||
|
||||
response = self.get_json('/audits/detail')
|
||||
self.assertEqual([], response['audits'])
|
||||
|
||||
def test_detail_against_single(self):
|
||||
audit = obj_utils.create_test_audit(self.context)
|
||||
response = self.get_json('/audits/%s/detail' % audit['uuid'],
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
|
||||
def test_many(self):
|
||||
audit_list = []
|
||||
for id_ in range(5):
|
||||
audit = obj_utils.create_test_audit(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
audit_list.append(audit.uuid)
|
||||
response = self.get_json('/audits')
|
||||
self.assertEqual(len(audit_list), len(response['audits']))
|
||||
uuids = [s['uuid'] for s in response['audits']]
|
||||
self.assertEqual(sorted(audit_list), sorted(uuids))
|
||||
|
||||
def test_many_without_soft_deleted(self):
|
||||
audit_list = []
|
||||
for id_ in [1, 2, 3]:
|
||||
audit = obj_utils.create_test_audit(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
audit_list.append(audit.uuid)
|
||||
for id_ in [4, 5]:
|
||||
audit = obj_utils.create_test_audit(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
audit.soft_delete()
|
||||
response = self.get_json('/audits')
|
||||
self.assertEqual(3, len(response['audits']))
|
||||
uuids = [s['uuid'] for s in response['audits']]
|
||||
self.assertEqual(sorted(audit_list), sorted(uuids))
|
||||
|
||||
def test_many_with_soft_deleted(self):
|
||||
audit_list = []
|
||||
for id_ in [1, 2, 3]:
|
||||
audit = obj_utils.create_test_audit(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
audit_list.append(audit.uuid)
|
||||
for id_ in [4, 5]:
|
||||
audit = obj_utils.create_test_audit(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
audit.soft_delete()
|
||||
audit_list.append(audit.uuid)
|
||||
response = self.get_json('/audits',
|
||||
headers={'X-Show-Deleted': 'True'})
|
||||
self.assertEqual(5, len(response['audits']))
|
||||
uuids = [s['uuid'] for s in response['audits']]
|
||||
self.assertEqual(sorted(audit_list), sorted(uuids))
|
||||
|
||||
def test_many_with_sort_key_audit_template_uuid(self):
|
||||
audit_template_list = []
|
||||
for id_ in range(5):
|
||||
audit_template = obj_utils.create_test_audit_template(
|
||||
self.context,
|
||||
name='at' + str(id_),
|
||||
uuid=utils.generate_uuid())
|
||||
obj_utils.create_test_audit(
|
||||
self.context, id=id_, uuid=utils.generate_uuid(),
|
||||
audit_template_id=audit_template.id)
|
||||
audit_template_list.append(audit_template.uuid)
|
||||
|
||||
response = self.get_json('/audits/?sort_key=audit_template_uuid')
|
||||
|
||||
self.assertEqual(5, len(response['audits']))
|
||||
uuids = [s['audit_template_uuid'] for s in response['audits']]
|
||||
self.assertEqual(sorted(audit_template_list), uuids)
|
||||
|
||||
def test_links(self):
|
||||
uuid = utils.generate_uuid()
|
||||
obj_utils.create_test_audit(self.context, id=1, uuid=uuid)
|
||||
response = self.get_json('/audits/%s' % uuid)
|
||||
self.assertIn('links', response.keys())
|
||||
self.assertEqual(2, len(response['links']))
|
||||
self.assertIn(uuid, response['links'][0]['href'])
|
||||
for l in response['links']:
|
||||
bookmark = l['rel'] == 'bookmark'
|
||||
self.assertTrue(self.validate_link(l['href'], bookmark=bookmark))
|
||||
|
||||
def test_collection_links(self):
|
||||
for id_ in range(5):
|
||||
obj_utils.create_test_audit(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
response = self.get_json('/audits/?limit=3')
|
||||
self.assertEqual(3, len(response['audits']))
|
||||
|
||||
next_marker = response['audits'][-1]['uuid']
|
||||
self.assertIn(next_marker, response['next'])
|
||||
|
||||
def test_collection_links_default_limit(self):
|
||||
cfg.CONF.set_override('max_limit', 3, 'api')
|
||||
for id_ in range(5):
|
||||
obj_utils.create_test_audit(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
response = self.get_json('/audits')
|
||||
self.assertEqual(3, len(response['audits']))
|
||||
|
||||
next_marker = response['audits'][-1]['uuid']
|
||||
self.assertIn(next_marker, response['next'])
|
||||
|
||||
def test_filter_by_audit_template_uuid(self):
|
||||
audit_template_uuid = utils.generate_uuid()
|
||||
audit_template_name = 'My_Audit_Template'
|
||||
|
||||
audit_template = obj_utils.create_test_audit_template(
|
||||
self.context,
|
||||
uuid=audit_template_uuid,
|
||||
name=audit_template_name)
|
||||
number_of_audits_with_audit_template_id = 5
|
||||
for id_ in range(number_of_audits_with_audit_template_id):
|
||||
obj_utils.create_test_audit(self.context, id=id_,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_template_id=audit_template.id)
|
||||
for id_ in range(6, 8):
|
||||
obj_utils.create_test_audit(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
|
||||
response = self.get_json('/audits/?audit_template=%s'
|
||||
% audit_template_uuid)
|
||||
|
||||
audits = response['audits']
|
||||
self.assertEqual(5, len(audits))
|
||||
for audit in audits:
|
||||
self.assertEqual(audit_template_uuid,
|
||||
audit['audit_template_uuid'])
|
||||
|
||||
def test_filter_by_audit_template_name(self):
|
||||
audit_template_uuid = utils.generate_uuid()
|
||||
audit_template_name = 'My_Audit_Template'
|
||||
|
||||
audit_template = obj_utils.create_test_audit_template(
|
||||
self.context,
|
||||
uuid=audit_template_uuid,
|
||||
name=audit_template_name)
|
||||
|
||||
number_of_audits_with_audit_template_id = 5
|
||||
for id_ in range(number_of_audits_with_audit_template_id):
|
||||
obj_utils.create_test_audit(self.context, id=id_,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_template_id=audit_template.id)
|
||||
for id_ in range(6, 8):
|
||||
obj_utils.create_test_audit(self.context, id=id_,
|
||||
uuid=utils.generate_uuid())
|
||||
|
||||
response = self.get_json('/audits/?audit_template=%s'
|
||||
% audit_template_name)
|
||||
|
||||
audits = response['audits']
|
||||
self.assertEqual(5, len(audits))
|
||||
for audit in audits:
|
||||
self.assertEqual(audit_template_uuid,
|
||||
audit['audit_template_uuid'])
|
||||
|
||||
def test_many_by_soft_deleted_audit_template(self):
|
||||
audit_list = []
|
||||
audit_template1 = obj_utils.create_test_audit_template(
|
||||
self.context,
|
||||
uuid=utils.generate_uuid(),
|
||||
name='at1',
|
||||
id=3,
|
||||
)
|
||||
|
||||
audit_template2 = obj_utils.create_test_audit_template(
|
||||
self.context,
|
||||
uuid=utils.generate_uuid(),
|
||||
name='at2',
|
||||
id=4,
|
||||
)
|
||||
|
||||
for id_ in range(0, 2):
|
||||
audit = obj_utils.create_test_audit(
|
||||
self.context, id=id_,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_template_id=audit_template1.id)
|
||||
audit_list.append(audit.uuid)
|
||||
|
||||
for id_ in range(2, 4):
|
||||
audit = obj_utils.create_test_audit(
|
||||
self.context, id=id_,
|
||||
uuid=utils.generate_uuid(),
|
||||
audit_template_id=audit_template2.id)
|
||||
audit_list.append(audit.uuid)
|
||||
|
||||
self.delete('/audit_templates/%s' % audit_template1.uuid)
|
||||
|
||||
response = self.get_json('/audits')
|
||||
|
||||
self.assertEqual(len(audit_list), len(response['audits']))
|
||||
|
||||
for id_ in range(0, 2):
|
||||
audit = response['audits'][id_]
|
||||
self.assertEqual(None, audit['audit_template_uuid'])
|
||||
|
||||
for id_ in range(2, 4):
|
||||
audit = response['audits'][id_]
|
||||
self.assertEqual(audit_template2.uuid,
|
||||
audit['audit_template_uuid'])
|
||||
|
||||
|
||||
class TestPatch(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPatch, self).setUp()
|
||||
obj_utils.create_test_audit_template(self.context)
|
||||
self.audit = obj_utils.create_test_audit(self.context)
|
||||
p = mock.patch.object(db_api.Connection, 'update_audit')
|
||||
self.mock_audit_update = p.start()
|
||||
self.mock_audit_update.side_effect = self._simulate_rpc_audit_update
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def _simulate_rpc_audit_update(self, audit):
|
||||
audit.save()
|
||||
return audit
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_replace_ok(self, mock_utcnow):
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
|
||||
new_state = 'SUBMITTED'
|
||||
response = self.get_json('/audits/%s' % self.audit.uuid)
|
||||
self.assertNotEqual(new_state, response['state'])
|
||||
|
||||
response = self.patch_json(
|
||||
'/audits/%s' % self.audit.uuid,
|
||||
[{'path': '/state', 'value': new_state,
|
||||
'op': 'replace'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
response = self.get_json('/audits/%s' % self.audit.uuid)
|
||||
self.assertEqual(new_state, response['state'])
|
||||
return_updated_at = timeutils.parse_isotime(
|
||||
response['updated_at']).replace(tzinfo=None)
|
||||
self.assertEqual(test_time, return_updated_at)
|
||||
|
||||
def test_replace_non_existent_audit(self):
|
||||
response = self.patch_json('/audits/%s' % utils.generate_uuid(),
|
||||
[{'path': '/state', 'value': 'SUBMITTED',
|
||||
'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_add_ok(self):
|
||||
new_state = 'SUCCESS'
|
||||
response = self.patch_json(
|
||||
'/audits/%s' % self.audit.uuid,
|
||||
[{'path': '/state', 'value': new_state, 'op': 'add'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_int)
|
||||
|
||||
response = self.get_json('/audits/%s' % self.audit.uuid)
|
||||
self.assertEqual(new_state, response['state'])
|
||||
|
||||
def test_add_non_existent_property(self):
|
||||
response = self.patch_json(
|
||||
'/audits/%s' % self.audit.uuid,
|
||||
[{'path': '/foo', 'value': 'bar', 'op': 'add'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(400, response.status_int)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_remove_ok(self):
|
||||
response = self.get_json('/audits/%s' % self.audit.uuid)
|
||||
self.assertIsNotNone(response['state'])
|
||||
|
||||
response = self.patch_json('/audits/%s' % self.audit.uuid,
|
||||
[{'path': '/state', 'op': 'remove'}])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
response = self.get_json('/audits/%s' % self.audit.uuid)
|
||||
self.assertIsNone(response['state'])
|
||||
|
||||
def test_remove_uuid(self):
|
||||
response = self.patch_json('/audits/%s' % self.audit.uuid,
|
||||
[{'path': '/uuid', 'op': 'remove'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_remove_non_existent_property(self):
|
||||
response = self.patch_json(
|
||||
'/audits/%s' % self.audit.uuid,
|
||||
[{'path': '/non-existent', 'op': 'remove'}],
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
|
||||
class TestPost(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPost, self).setUp()
|
||||
obj_utils.create_test_audit_template(self.context)
|
||||
p = mock.patch.object(db_api.Connection, 'create_audit')
|
||||
self.mock_create_audit = p.start()
|
||||
self.mock_create_audit.side_effect = (
|
||||
self._simulate_rpc_audit_create)
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def _simulate_rpc_audit_create(self, audit):
|
||||
audit.create()
|
||||
return audit
|
||||
|
||||
@mock.patch.object(deapi.DecisionEngineAPI, 'trigger_audit')
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_create_audit(self, mock_utcnow, mock_trigger_audit):
|
||||
mock_trigger_audit.return_value = mock.ANY
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
|
||||
audit_dict = post_get_test_audit()
|
||||
|
||||
response = self.post_json('/audits', audit_dict)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(201, response.status_int)
|
||||
# Check location header
|
||||
self.assertIsNotNone(response.location)
|
||||
expected_location = '/v1/audits/%s' % audit_dict['uuid']
|
||||
self.assertEqual(urlparse.urlparse(response.location).path,
|
||||
expected_location)
|
||||
self.assertEqual(audit_dict['uuid'], response.json['uuid'])
|
||||
self.assertEqual(objects.audit.AuditStatus.PENDING,
|
||||
response.json['state'])
|
||||
self.assertNotIn('updated_at', response.json.keys)
|
||||
self.assertNotIn('deleted_at', response.json.keys)
|
||||
return_created_at = timeutils.parse_isotime(
|
||||
response.json['created_at']).replace(tzinfo=None)
|
||||
self.assertEqual(test_time, return_created_at)
|
||||
|
||||
@mock.patch.object(deapi.DecisionEngineAPI, 'trigger_audit')
|
||||
def test_create_audit_doesnt_contain_id(self, mock_trigger_audit):
|
||||
mock_trigger_audit.return_value = mock.ANY
|
||||
|
||||
audit_dict = post_get_test_audit(state='ONGOING')
|
||||
with mock.patch.object(self.dbapi, 'create_audit',
|
||||
wraps=self.dbapi.create_audit) as cn_mock:
|
||||
response = self.post_json('/audits', audit_dict)
|
||||
self.assertEqual(audit_dict['state'], response.json['state'])
|
||||
cn_mock.assert_called_once_with(mock.ANY)
|
||||
# Check that 'id' is not in first arg of positional args
|
||||
self.assertNotIn('id', cn_mock.call_args[0][0])
|
||||
|
||||
@mock.patch.object(deapi.DecisionEngineAPI, 'trigger_audit')
|
||||
def test_create_audit_generate_uuid(self, mock_trigger_audit):
|
||||
mock_trigger_audit.return_value = mock.ANY
|
||||
|
||||
audit_dict = post_get_test_audit()
|
||||
del audit_dict['uuid']
|
||||
|
||||
response = self.post_json('/audits', audit_dict)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(201, response.status_int)
|
||||
self.assertEqual(objects.audit.AuditStatus.PENDING,
|
||||
response.json['state'])
|
||||
self.assertTrue(utils.is_uuid_like(response.json['uuid']))
|
||||
|
||||
def test_create_audit_trigger_decision_engine(self):
|
||||
with mock.patch.object(deapi.DecisionEngineAPI,
|
||||
'trigger_audit') as de_mock:
|
||||
audit_dict = post_get_test_audit(state='ONGOING')
|
||||
self.post_json('/audits', audit_dict)
|
||||
de_mock.assert_called_once_with(mock.ANY, audit_dict['uuid'])
|
||||
|
||||
|
||||
# class TestDelete(api_base.FunctionalTest):
|
||||
|
||||
# def setUp(self):
|
||||
# super(TestDelete, self).setUp()
|
||||
# self.audit = obj_utils.create_test_audit(self.context)
|
||||
# p = mock.patch.object(db_api.Connection, 'destroy_audit')
|
||||
# self.mock_audit_delete = p.start()
|
||||
# self.mock_audit_delete.side_effect = self._simulate_rpc_audit_delete
|
||||
# self.addCleanup(p.stop)
|
||||
|
||||
# def _simulate_rpc_audit_delete(self, audit_uuid):
|
||||
# audit = objects.Audit.get_by_uuid(self.context, audit_uuid)
|
||||
# audit.destroy()
|
||||
|
||||
# def test_delete_audit(self):
|
||||
# self.delete('/audits/%s' % self.audit.uuid)
|
||||
# response = self.get_json('/audits/%s' % self.audit.uuid,
|
||||
# expect_errors=True)
|
||||
# self.assertEqual(404, response.status_int)
|
||||
# self.assertEqual('application/json', response.content_type)
|
||||
# self.assertTrue(response.json['error_message'])
|
||||
|
||||
# def test_delete_audit_not_found(self):
|
||||
# uuid = utils.generate_uuid()
|
||||
# response = self.delete('/audits/%s' % uuid, expect_errors=True)
|
||||
# self.assertEqual(404, response.status_int)
|
||||
# self.assertEqual('application/json', response.content_type)
|
||||
# self.assertTrue(response.json['error_message'])
|
||||
|
||||
class TestDelete(api_base.FunctionalTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDelete, self).setUp()
|
||||
obj_utils.create_test_audit_template(self.context)
|
||||
self.audit = obj_utils.create_test_audit(self.context)
|
||||
p = mock.patch.object(db_api.Connection, 'update_audit')
|
||||
self.mock_audit_update = p.start()
|
||||
self.mock_audit_update.side_effect = self._simulate_rpc_audit_update
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def _simulate_rpc_audit_update(self, audit):
|
||||
audit.save()
|
||||
return audit
|
||||
|
||||
@mock.patch('oslo_utils.timeutils.utcnow')
|
||||
def test_delete_audit(self, mock_utcnow):
|
||||
test_time = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
mock_utcnow.return_value = test_time
|
||||
self.delete('/audits/%s' % self.audit.uuid)
|
||||
response = self.get_json('/audits/%s' % self.audit.uuid,
|
||||
expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
self.context.show_deleted = True
|
||||
audit = objects.Audit.get_by_uuid(self.context, self.audit.uuid)
|
||||
|
||||
return_deleted_at = timeutils.strtime(audit['deleted_at'])
|
||||
self.assertEqual(timeutils.strtime(test_time), return_deleted_at)
|
||||
self.assertEqual(audit['state'], 'DELETED')
|
||||
|
||||
def test_delete_audit_not_found(self):
|
||||
uuid = utils.generate_uuid()
|
||||
response = self.delete('/audits/%s' % uuid, expect_errors=True)
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
20
watcher/tests/api/v1/test_root.py
Normal file
20
watcher/tests/api/v1/test_root.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# 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.
|
||||
|
||||
from watcher.tests.api import base as api_base
|
||||
|
||||
|
||||
class TestV1Routing(api_base.FunctionalTest):
|
||||
def setUp(self):
|
||||
super(TestV1Routing, self).setUp()
|
||||
252
watcher/tests/api/v1/test_types.py
Normal file
252
watcher/tests/api/v1/test_types.py
Normal file
@@ -0,0 +1,252 @@
|
||||
# coding: utf-8
|
||||
#
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
import webtest
|
||||
import wsme
|
||||
from wsme import types as wtypes
|
||||
|
||||
from watcher.api.controllers.v1 import types
|
||||
from watcher.common import exception
|
||||
from watcher.common import utils
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestUuidType(base.TestCase):
|
||||
|
||||
def test_valid_uuid(self):
|
||||
test_uuid = '1a1a1a1a-2b2b-3c3c-4d4d-5e5e5e5e5e5e'
|
||||
self.assertEqual(test_uuid, types.UuidType.validate(test_uuid))
|
||||
|
||||
def test_invalid_uuid(self):
|
||||
self.assertRaises(exception.InvalidUUID,
|
||||
types.UuidType.validate, 'invalid-uuid')
|
||||
|
||||
|
||||
class TestNameType(base.TestCase):
|
||||
|
||||
def test_valid_name(self):
|
||||
test_name = 'hal-9000'
|
||||
self.assertEqual(test_name, types.NameType.validate(test_name))
|
||||
|
||||
def test_invalid_name(self):
|
||||
self.assertRaises(exception.InvalidName,
|
||||
types.NameType.validate, '-this is not valid-')
|
||||
|
||||
|
||||
class TestUuidOrNameType(base.TestCase):
|
||||
|
||||
@mock.patch.object(utils, 'is_uuid_like')
|
||||
@mock.patch.object(utils, 'is_hostname_safe')
|
||||
def test_valid_uuid(self, host_mock, uuid_mock):
|
||||
test_uuid = '1a1a1a1a-2b2b-3c3c-4d4d-5e5e5e5e5e5e'
|
||||
host_mock.return_value = False
|
||||
uuid_mock.return_value = True
|
||||
self.assertTrue(types.UuidOrNameType.validate(test_uuid))
|
||||
uuid_mock.assert_called_once_with(test_uuid)
|
||||
|
||||
@mock.patch.object(utils, 'is_uuid_like')
|
||||
@mock.patch.object(utils, 'is_hostname_safe')
|
||||
def test_valid_name(self, host_mock, uuid_mock):
|
||||
test_name = 'dc16-database5'
|
||||
uuid_mock.return_value = False
|
||||
host_mock.return_value = True
|
||||
self.assertTrue(types.UuidOrNameType.validate(test_name))
|
||||
host_mock.assert_called_once_with(test_name)
|
||||
|
||||
def test_invalid_uuid_or_name(self):
|
||||
self.assertRaises(exception.InvalidUuidOrName,
|
||||
types.UuidOrNameType.validate, 'inval#uuid%or*name')
|
||||
|
||||
|
||||
class MyPatchType(types.JsonPatchType):
|
||||
"""Helper class for TestJsonPatchType tests."""
|
||||
|
||||
@staticmethod
|
||||
def mandatory_attrs():
|
||||
return ['/mandatory']
|
||||
|
||||
@staticmethod
|
||||
def internal_attrs():
|
||||
return ['/internal']
|
||||
|
||||
|
||||
class MyRoot(wsme.WSRoot):
|
||||
"""Helper class for TestJsonPatchType tests."""
|
||||
|
||||
@wsme.expose([wsme.types.text], body=[MyPatchType])
|
||||
@wsme.validate([MyPatchType])
|
||||
def test(self, patch):
|
||||
return patch
|
||||
|
||||
|
||||
class TestJsonPatchType(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestJsonPatchType, self).setUp()
|
||||
self.app = webtest.TestApp(MyRoot(['restjson']).wsgiapp())
|
||||
|
||||
def _patch_json(self, params, expect_errors=False):
|
||||
return self.app.patch_json(
|
||||
'/test',
|
||||
params=params,
|
||||
headers={'Accept': 'application/json'},
|
||||
expect_errors=expect_errors
|
||||
)
|
||||
|
||||
def test_valid_patches(self):
|
||||
valid_patches = [{'path': '/extra/foo', 'op': 'remove'},
|
||||
{'path': '/extra/foo', 'op': 'add', 'value': 'bar'},
|
||||
{'path': '/str', 'op': 'replace', 'value': 'bar'},
|
||||
{'path': '/bool', 'op': 'add', 'value': True},
|
||||
{'path': '/int', 'op': 'add', 'value': 1},
|
||||
{'path': '/float', 'op': 'add', 'value': 0.123},
|
||||
{'path': '/list', 'op': 'add', 'value': [1, 2]},
|
||||
{'path': '/none', 'op': 'add', 'value': None},
|
||||
{'path': '/empty_dict', 'op': 'add', 'value': {}},
|
||||
{'path': '/empty_list', 'op': 'add', 'value': []},
|
||||
{'path': '/dict', 'op': 'add',
|
||||
'value': {'cat': 'meow'}}]
|
||||
ret = self._patch_json(valid_patches, False)
|
||||
self.assertEqual(200, ret.status_int)
|
||||
self.assertEqual(sorted(valid_patches), sorted(ret.json))
|
||||
|
||||
def test_cannot_update_internal_attr(self):
|
||||
patch = [{'path': '/internal', 'op': 'replace', 'value': 'foo'}]
|
||||
ret = self._patch_json(patch, True)
|
||||
self.assertEqual(400, ret.status_int)
|
||||
self.assertTrue(ret.json['faultstring'])
|
||||
|
||||
def test_cannot_update_internal_dict_attr(self):
|
||||
patch = [{'path': '/internal', 'op': 'replace',
|
||||
'value': 'foo'}]
|
||||
ret = self._patch_json(patch, True)
|
||||
self.assertEqual(400, ret.status_int)
|
||||
self.assertTrue(ret.json['faultstring'])
|
||||
|
||||
def test_mandatory_attr(self):
|
||||
patch = [{'op': 'replace', 'path': '/mandatory', 'value': 'foo'}]
|
||||
ret = self._patch_json(patch, False)
|
||||
self.assertEqual(200, ret.status_int)
|
||||
self.assertEqual(patch, ret.json)
|
||||
|
||||
def test_cannot_remove_mandatory_attr(self):
|
||||
patch = [{'op': 'remove', 'path': '/mandatory'}]
|
||||
ret = self._patch_json(patch, True)
|
||||
self.assertEqual(400, ret.status_int)
|
||||
self.assertTrue(ret.json['faultstring'])
|
||||
|
||||
def test_missing_required_fields_path(self):
|
||||
missing_path = [{'op': 'remove'}]
|
||||
ret = self._patch_json(missing_path, True)
|
||||
self.assertEqual(400, ret.status_int)
|
||||
self.assertTrue(ret.json['faultstring'])
|
||||
|
||||
def test_missing_required_fields_op(self):
|
||||
missing_op = [{'path': '/foo'}]
|
||||
ret = self._patch_json(missing_op, True)
|
||||
self.assertEqual(400, ret.status_int)
|
||||
self.assertTrue(ret.json['faultstring'])
|
||||
|
||||
def test_invalid_op(self):
|
||||
patch = [{'path': '/foo', 'op': 'invalid'}]
|
||||
ret = self._patch_json(patch, True)
|
||||
self.assertEqual(400, ret.status_int)
|
||||
self.assertTrue(ret.json['faultstring'])
|
||||
|
||||
def test_invalid_path(self):
|
||||
patch = [{'path': 'invalid-path', 'op': 'remove'}]
|
||||
ret = self._patch_json(patch, True)
|
||||
self.assertEqual(400, ret.status_int)
|
||||
self.assertTrue(ret.json['faultstring'])
|
||||
|
||||
def test_cannot_add_with_no_value(self):
|
||||
patch = [{'path': '/extra/foo', 'op': 'add'}]
|
||||
ret = self._patch_json(patch, True)
|
||||
self.assertEqual(400, ret.status_int)
|
||||
self.assertTrue(ret.json['faultstring'])
|
||||
|
||||
def test_cannot_replace_with_no_value(self):
|
||||
patch = [{'path': '/foo', 'op': 'replace'}]
|
||||
ret = self._patch_json(patch, True)
|
||||
self.assertEqual(400, ret.status_int)
|
||||
self.assertTrue(ret.json['faultstring'])
|
||||
|
||||
|
||||
class TestBooleanType(base.TestCase):
|
||||
|
||||
def test_valid_true_values(self):
|
||||
v = types.BooleanType()
|
||||
self.assertTrue(v.validate("true"))
|
||||
self.assertTrue(v.validate("TRUE"))
|
||||
self.assertTrue(v.validate("True"))
|
||||
self.assertTrue(v.validate("t"))
|
||||
self.assertTrue(v.validate("1"))
|
||||
self.assertTrue(v.validate("y"))
|
||||
self.assertTrue(v.validate("yes"))
|
||||
self.assertTrue(v.validate("on"))
|
||||
|
||||
def test_valid_false_values(self):
|
||||
v = types.BooleanType()
|
||||
self.assertFalse(v.validate("false"))
|
||||
self.assertFalse(v.validate("FALSE"))
|
||||
self.assertFalse(v.validate("False"))
|
||||
self.assertFalse(v.validate("f"))
|
||||
self.assertFalse(v.validate("0"))
|
||||
self.assertFalse(v.validate("n"))
|
||||
self.assertFalse(v.validate("no"))
|
||||
self.assertFalse(v.validate("off"))
|
||||
|
||||
def test_invalid_value(self):
|
||||
v = types.BooleanType()
|
||||
self.assertRaises(exception.Invalid, v.validate, "invalid-value")
|
||||
self.assertRaises(exception.Invalid, v.validate, "01")
|
||||
|
||||
|
||||
class TestJsonType(base.TestCase):
|
||||
|
||||
def test_valid_values(self):
|
||||
vt = types.jsontype
|
||||
value = vt.validate("hello")
|
||||
self.assertEqual("hello", value)
|
||||
value = vt.validate(10)
|
||||
self.assertEqual(10, value)
|
||||
value = vt.validate(0.123)
|
||||
self.assertEqual(0.123, value)
|
||||
value = vt.validate(True)
|
||||
self.assertEqual(True, value)
|
||||
value = vt.validate([1, 2, 3])
|
||||
self.assertEqual([1, 2, 3], value)
|
||||
value = vt.validate({'foo': 'bar'})
|
||||
self.assertEqual({'foo': 'bar'}, value)
|
||||
value = vt.validate(None)
|
||||
self.assertEqual(None, value)
|
||||
|
||||
def test_invalid_values(self):
|
||||
vt = types.jsontype
|
||||
self.assertRaises(exception.Invalid, vt.validate, object())
|
||||
|
||||
def test_apimultitype_tostring(self):
|
||||
vts = str(types.jsontype)
|
||||
self.assertIn(str(wtypes.text), vts)
|
||||
self.assertIn(str(int), vts)
|
||||
self.assertIn(str(long), vts)
|
||||
self.assertIn(str(float), vts)
|
||||
self.assertIn(str(types.BooleanType), vts)
|
||||
self.assertIn(str(list), vts)
|
||||
self.assertIn(str(dict), vts)
|
||||
self.assertIn(str(None), vts)
|
||||
49
watcher/tests/api/v1/test_utils.py
Normal file
49
watcher/tests/api/v1/test_utils.py
Normal file
@@ -0,0 +1,49 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
# 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.
|
||||
|
||||
import wsme
|
||||
|
||||
from watcher.api.controllers.v1 import utils
|
||||
from watcher.tests import base
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestApiUtils(base.TestCase):
|
||||
|
||||
def test_validate_limit(self):
|
||||
limit = utils.validate_limit(10)
|
||||
self.assertEqual(10, 10)
|
||||
|
||||
# max limit
|
||||
limit = utils.validate_limit(999999999)
|
||||
self.assertEqual(CONF.api.max_limit, limit)
|
||||
|
||||
# negative
|
||||
self.assertRaises(wsme.exc.ClientSideError, utils.validate_limit, -1)
|
||||
|
||||
# zero
|
||||
self.assertRaises(wsme.exc.ClientSideError, utils.validate_limit, 0)
|
||||
|
||||
def test_validate_sort_dir(self):
|
||||
sort_dir = utils.validate_sort_dir('asc')
|
||||
self.assertEqual('asc', sort_dir)
|
||||
|
||||
# invalid sort_dir parameter
|
||||
self.assertRaises(wsme.exc.ClientSideError,
|
||||
utils.validate_sort_dir,
|
||||
'fake-sort')
|
||||
0
watcher/tests/applier/__init__.py
Normal file
0
watcher/tests/applier/__init__.py
Normal file
0
watcher/tests/applier/demo/__init__.py
Normal file
0
watcher/tests/applier/demo/__init__.py
Normal file
65
watcher/tests/applier/demo/test_applier.py
Normal file
65
watcher/tests/applier/demo/test_applier.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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_config import cfg
|
||||
|
||||
from watcher.applier.framework.default_applier import DefaultApplier
|
||||
|
||||
from watcher.common import utils
|
||||
from watcher.decision_engine.framework.default_planner import DefaultPlanner
|
||||
from watcher.decision_engine.strategies.basic_consolidation import \
|
||||
BasicConsolidation
|
||||
from watcher.openstack.common import log
|
||||
from watcher.tests.db import base
|
||||
from watcher.tests.db import utils as db_utils
|
||||
from watcher.tests.decision_engine.faker_cluster_state import \
|
||||
FakerStateCollector
|
||||
from watcher.tests.decision_engine.faker_metrics_collector import \
|
||||
FakerMetricsCollector
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
CONF = cfg.CONF
|
||||
""
|
||||
class TestApplier(base.DbTestCase):
|
||||
default_planner = DefaultPlanner()
|
||||
|
||||
def create_solution(self):
|
||||
metrics = FakerMetricsCollector()
|
||||
current_state_cluster = FakerStateCollector()
|
||||
sercon = BasicConsolidation()
|
||||
sercon.set_metrics_resource_collector(metrics)
|
||||
return sercon.execute(current_state_cluster.generate_scenario_1())
|
||||
|
||||
def test_scheduler_w(self):
|
||||
CONF.debug = True
|
||||
log.setup('watcher-sercon-demo')
|
||||
|
||||
CONF.keystone_authtoken.auth_uri = "http://10.50.0.105:5000/v3"
|
||||
CONF.keystone_authtoken.admin_user = "admin"
|
||||
CONF.keystone_authtoken.admin_password = "openstacktest"
|
||||
CONF.keystone_authtoken.admin_tenant_name = "test"
|
||||
|
||||
audit = db_utils.create_test_audit(uuid=utils.generate_uuid())
|
||||
|
||||
action_plan = self.default_planner.schedule(self.context,
|
||||
audit.id,
|
||||
self.create_solution())
|
||||
|
||||
applier = DefaultApplier()
|
||||
applier.execute(self.context, action_plan.uuid)
|
||||
"""""
|
||||
99
watcher/tests/applier/demo/test_migrate.py
Normal file
99
watcher/tests/applier/demo/test_migrate.py
Normal file
@@ -0,0 +1,99 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 keystoneclient import session
|
||||
|
||||
from keystoneclient.auth.identity import v3
|
||||
|
||||
import cinderclient.v2.client as ciclient
|
||||
import glanceclient.v2.client as glclient
|
||||
import keystoneclient.v3.client as ksclient
|
||||
import neutronclient.neutron.client as netclient
|
||||
import novaclient.v2.client as nvclient
|
||||
|
||||
from watcher.common.utils import CONF
|
||||
from oslo_config import cfg
|
||||
from watcher.applier.framework.command.migrate_command import MigrateCommand
|
||||
from watcher.applier.framework.command.wrapper.nova_wrapper import NovaWrapper
|
||||
from watcher.decision_engine.framework.default_planner import Primitives
|
||||
from watcher.openstack.common import log
|
||||
|
||||
cfg.CONF.import_opt('auth_uri', 'keystoneclient.middleware.auth_token',
|
||||
group='keystone_authtoken')
|
||||
cfg.CONF.import_opt('admin_user', 'keystoneclient.middleware.auth_token',
|
||||
group='keystone_authtoken')
|
||||
cfg.CONF.import_opt('admin_password', 'keystoneclient.middleware.auth_token',
|
||||
group='keystone_authtoken')
|
||||
cfg.CONF.import_opt('admin_tenant_name',
|
||||
'keystoneclient.middleware.auth_token',
|
||||
group='keystone_authtoken')
|
||||
|
||||
cfg.CONF.keystone_authtoken.auth_uri = "http://10.50.0.105:5000/v3/"
|
||||
cfg.CONF.keystone_authtoken.admin_user = "admin"
|
||||
cfg.CONF.keystone_authtoken.admin_password = "openstacktest"
|
||||
cfg.CONF.keystone_authtoken.admin_tenant_name = "test"
|
||||
|
||||
try:
|
||||
cfg.CONF.debug = True
|
||||
log.setup('watcher-sercon-demo')
|
||||
creds = \
|
||||
{'auth_url': CONF.keystone_authtoken.auth_uri,
|
||||
'username': CONF.keystone_authtoken.admin_user,
|
||||
'password': CONF.keystone_authtoken.admin_password,
|
||||
'project_name': CONF.keystone_authtoken.admin_tenant_name,
|
||||
'user_domain_name': "default",
|
||||
'project_domain_name': "default"}
|
||||
auth = v3.Password(auth_url=creds['auth_url'],
|
||||
username=creds['username'],
|
||||
password=creds['password'],
|
||||
project_name=creds['project_name'],
|
||||
user_domain_name=creds[
|
||||
'user_domain_name'],
|
||||
project_domain_name=creds[
|
||||
'project_domain_name'])
|
||||
sess = session.Session(auth=auth)
|
||||
nova = nvclient.Client("3", session=sess)
|
||||
neutron = netclient.Client('2.0', session=sess)
|
||||
neutron.format = 'json'
|
||||
keystone = ksclient.Client(**creds)
|
||||
|
||||
glance_endpoint = keystone. \
|
||||
service_catalog.url_for(service_type='image',
|
||||
endpoint_type='publicURL')
|
||||
glance = glclient.Client(glance_endpoint,
|
||||
token=keystone.auth_token)
|
||||
|
||||
cinder = ciclient.Client('2', session=sess)
|
||||
wrapper = NovaWrapper(user=creds['username'], nova=nova,
|
||||
neutron=neutron, glance=glance,
|
||||
cinder=cinder)
|
||||
instance = wrapper. \
|
||||
create_instance(hypervisor_id='ldev-indeedsrv006',
|
||||
inst_name="demo_instance_1",
|
||||
keypair_name='admin',
|
||||
image_id=
|
||||
"2b958331-379b-4618-b2ba-fbe8a608b2bb")
|
||||
|
||||
cmd = MigrateCommand(instance.id, Primitives.COLD_MIGRATE,
|
||||
'ldev-indeedsrv006',
|
||||
'ldev-indeedsrv005')
|
||||
resu = cmd.execute(cmd)
|
||||
resu.result()
|
||||
# wrapper.delete_instance(instance.id)
|
||||
except Exception as e:
|
||||
print("rollback " + unicode(e))
|
||||
"""""
|
||||
0
watcher/tests/applier/framework/__init__.py
Normal file
0
watcher/tests/applier/framework/__init__.py
Normal file
0
watcher/tests/applier/framework/command/__init__.py
Normal file
0
watcher/tests/applier/framework/command/__init__.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock import call
|
||||
from mock import MagicMock
|
||||
|
||||
|
||||
from watcher.applier.framework.messaging.events import Events
|
||||
from watcher.applier.framework.messaging.launch_action_plan import \
|
||||
LaunchActionPlanCommand
|
||||
|
||||
from watcher.objects.action_plan import Status
|
||||
from watcher.objects import ActionPlan
|
||||
from watcher.tests.db.base import DbTestCase
|
||||
from watcher.tests.objects import utils as obj_utils
|
||||
|
||||
|
||||
class TestLaunchActionPlanCommand(DbTestCase):
|
||||
def setUp(self):
|
||||
super(TestLaunchActionPlanCommand, self).setUp()
|
||||
self.action_plan = obj_utils.create_test_action_plan(
|
||||
self.context)
|
||||
|
||||
def test_launch_action_plan_wihout_errors(self):
|
||||
try:
|
||||
|
||||
command = LaunchActionPlanCommand(self.context, MagicMock(),
|
||||
self.action_plan.uuid)
|
||||
command.execute()
|
||||
except Exception as e:
|
||||
self.fail(
|
||||
"The ActionPlan should be trigged wihtour error" + unicode(e))
|
||||
|
||||
def test_launch_action_plan_state_failed(self):
|
||||
command = LaunchActionPlanCommand(self.context, MagicMock(),
|
||||
self.action_plan.uuid)
|
||||
command.execute()
|
||||
action_plan = ActionPlan.get_by_uuid(self.context,
|
||||
self.action_plan.uuid)
|
||||
self.assertEqual(Status.SUCCESS, action_plan.state)
|
||||
|
||||
def test_trigger_audit_send_notification(self):
|
||||
messaging = MagicMock()
|
||||
command = LaunchActionPlanCommand(self.context, messaging,
|
||||
self.action_plan.uuid)
|
||||
command.execute()
|
||||
|
||||
call_on_going = call(Events.LAUNCH_ACTION_PLAN.name, {
|
||||
'action_plan_status': Status.ONGOING,
|
||||
'action_plan__uuid': self.action_plan.uuid})
|
||||
call_success = call(Events.LAUNCH_ACTION_PLAN.name, {
|
||||
'action_plan_status': Status.SUCCESS,
|
||||
'action_plan__uuid': self.action_plan.uuid})
|
||||
|
||||
calls = [call_on_going, call_success]
|
||||
messaging.topic_status.publish_event.assert_has_calls(calls)
|
||||
self.assertEqual(2, messaging.topic_status.publish_event.call_count)
|
||||
@@ -0,0 +1,64 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock
|
||||
import time
|
||||
from watcher.applier.framework.command.wrapper.nova_wrapper import NovaWrapper
|
||||
from watcher.common import utils
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestNovaWrapper(base.TestCase):
|
||||
@mock.patch('keystoneclient.v3.client.Client')
|
||||
def setUp(self, mock_ksclient):
|
||||
super(TestNovaWrapper, self).setUp()
|
||||
self.instance_uuid = "fb5311b7-37f3-457e-9cde-6494a3c59bfe"
|
||||
self.source_hypervisor = "ldev-indeedsrv005"
|
||||
self.destination_hypervisor = "ldev-indeedsrv006"
|
||||
|
||||
self.creds = mock.MagicMock()
|
||||
self.session = mock.MagicMock()
|
||||
self.wrapper = NovaWrapper(creds=self.creds, session=self.session)
|
||||
|
||||
def test_stop_instance(self):
|
||||
instance_id = utils.generate_uuid()
|
||||
server = mock.MagicMock()
|
||||
server.id = instance_id
|
||||
setattr(server, 'OS-EXT-STS:vm_state', 'stopped')
|
||||
self.wrapper.nova.servers = mock.MagicMock()
|
||||
self.wrapper.nova.servers.find.return_value = server
|
||||
self.wrapper.nova.servers.list.return_value = [server]
|
||||
|
||||
result = self.wrapper.stop_instance(instance_id)
|
||||
self.assertEqual(result, True)
|
||||
|
||||
def test_set_host_offline(self):
|
||||
host = mock.MagicMock()
|
||||
self.wrapper.nova.hosts = mock.MagicMock()
|
||||
self.wrapper.nova.hosts.get.return_value = host
|
||||
result = self.wrapper.set_host_offline("rennes")
|
||||
self.assertEqual(result, True)
|
||||
|
||||
def test_live_migrate_instance(self):
|
||||
server = mock.MagicMock()
|
||||
server.id = self.instance_uuid
|
||||
self.wrapper.nova.servers = mock.MagicMock()
|
||||
self.wrapper.nova.servers.list.return_value = [server]
|
||||
with mock.patch.object(time, 'sleep'):
|
||||
instance = self.wrapper.live_migrate_instance(
|
||||
self.instance_uuid,
|
||||
self.destination_hypervisor)
|
||||
self.assertIsNotNone(instance)
|
||||
@@ -0,0 +1,38 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock import MagicMock
|
||||
from watcher.applier.framework.messaging.trigger_action_plan import \
|
||||
TriggerActionPlan
|
||||
from watcher.common import utils
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestTriggerActionPlan(base.TestCase):
|
||||
def __init__(self, *args, **kwds):
|
||||
super(TestTriggerActionPlan, self).__init__(*args, **kwds)
|
||||
self.applier = MagicMock()
|
||||
self.endpoint = TriggerActionPlan(self.applier)
|
||||
|
||||
def setUp(self):
|
||||
super(TestTriggerActionPlan, self).setUp()
|
||||
|
||||
def test_launch_action_plan(self):
|
||||
action_plan_uuid = utils.generate_uuid()
|
||||
expected_uuid = self.endpoint.launch_action_plan(self.context,
|
||||
action_plan_uuid)
|
||||
self.assertEqual(action_plan_uuid, expected_uuid)
|
||||
29
watcher/tests/applier/framework/test_applier_manager.py
Normal file
29
watcher/tests/applier/framework/test_applier_manager.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 watcher.applier.framework.manager_applier import ApplierManager
|
||||
from watcher.common.messaging.events.event import Event
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestApplierManager(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestApplierManager, self).setUp()
|
||||
self.applier = ApplierManager()
|
||||
|
||||
def test_evt(self):
|
||||
e = Event()
|
||||
self.applier.event_receive(e)
|
||||
60
watcher/tests/applier/framework/test_command_executor.py
Normal file
60
watcher/tests/applier/framework/test_command_executor.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock
|
||||
|
||||
from watcher.applier.framework.command_executor import CommandExecutor
|
||||
from watcher import objects
|
||||
|
||||
from watcher.common import utils
|
||||
from watcher.decision_engine.framework.default_planner import Primitives
|
||||
from watcher.objects.action import Action
|
||||
from watcher.objects.action import Status
|
||||
from watcher.tests.db.base import DbTestCase
|
||||
|
||||
|
||||
class TestCommandExecutor(DbTestCase):
|
||||
def setUp(self):
|
||||
super(TestCommandExecutor, self).setUp()
|
||||
self.applier = mock.MagicMock()
|
||||
self.executor = CommandExecutor(self.applier, self.context)
|
||||
|
||||
def test_execute(self):
|
||||
actions = mock.MagicMock()
|
||||
result = self.executor.execute(actions)
|
||||
self.assertEqual(result, True)
|
||||
|
||||
def test_execute_with_actions(self):
|
||||
actions = []
|
||||
action = {
|
||||
'uuid': utils.generate_uuid(),
|
||||
'action_plan_id': 0,
|
||||
'action_type': Primitives.NOP.value,
|
||||
'applies_to': '',
|
||||
'src': '',
|
||||
'dst': '',
|
||||
'parameter': '',
|
||||
'description': '',
|
||||
'state': Status.PENDING,
|
||||
'alarm': None,
|
||||
'next': None,
|
||||
}
|
||||
new_action = objects.Action(self.context, **action)
|
||||
new_action.create(self.context)
|
||||
new_action.save()
|
||||
actions.append(Action.get_by_uuid(self.context, action['uuid']))
|
||||
result = self.executor.execute(actions)
|
||||
self.assertEqual(result, True)
|
||||
56
watcher/tests/applier/framework/test_command_mapper.py
Normal file
56
watcher/tests/applier/framework/test_command_mapper.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock
|
||||
from watcher.applier.framework.default_command_mapper import \
|
||||
DefaultCommandMapper
|
||||
from watcher.decision_engine.framework.default_planner import Primitives
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestCommandMapper(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestCommandMapper, self).setUp()
|
||||
self.mapper = DefaultCommandMapper()
|
||||
|
||||
def test_build_command_cold(self):
|
||||
action = mock.MagicMock()
|
||||
action.action_type = Primitives.COLD_MIGRATE.value
|
||||
cmd = self.mapper.build_primitive_command(action)
|
||||
self.assertIsNotNone(cmd)
|
||||
|
||||
def test_build_command_live(self):
|
||||
action = mock.MagicMock()
|
||||
action.action_type = Primitives.LIVE_MIGRATE.value
|
||||
cmd = self.mapper.build_primitive_command(action)
|
||||
self.assertIsNotNone(cmd)
|
||||
|
||||
def test_build_command_h_s(self):
|
||||
action = mock.MagicMock()
|
||||
action.action_type = Primitives.HYPERVISOR_STATE.value
|
||||
cmd = self.mapper.build_primitive_command(action)
|
||||
self.assertIsNotNone(cmd)
|
||||
|
||||
def test_build_command_p_s(self):
|
||||
action = mock.MagicMock()
|
||||
action.action_type = Primitives.POWER_STATE.value
|
||||
cmd = self.mapper.build_primitive_command(action)
|
||||
self.assertIsNotNone(cmd)
|
||||
|
||||
def test_build_command_exception_attribute(self):
|
||||
action = mock.MagicMock
|
||||
self.assertRaises(AttributeError, self.mapper.build_primitive_command,
|
||||
action)
|
||||
31
watcher/tests/applier/framework/test_manager.py
Normal file
31
watcher/tests/applier/framework/test_manager.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 watcher.decision_engine.framework.manager_decision_engine import \
|
||||
DecisionEngineManager
|
||||
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestApplierdManager(base.TestCase):
|
||||
manager = DecisionEngineManager()
|
||||
|
||||
def setUp(self):
|
||||
super(TestApplierdManager, self).setUp()
|
||||
|
||||
def test_event_receive(self):
|
||||
pass
|
||||
58
watcher/tests/applier/framework/test_rpcapi.py
Normal file
58
watcher/tests/applier/framework/test_rpcapi.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock
|
||||
import oslo.messaging as om
|
||||
from watcher.applier.framework.rpcapi import ApplierAPI
|
||||
|
||||
from watcher.common import exception
|
||||
from watcher.common import utils
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestApplierAPI(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestApplierAPI, self).setUp()
|
||||
|
||||
api = ApplierAPI()
|
||||
|
||||
def test_get_version(self):
|
||||
expected_version = self.api.API_VERSION
|
||||
self.assertEqual(expected_version, self.api.get_version())
|
||||
|
||||
def test_get_api_version(self):
|
||||
with mock.patch.object(om.RPCClient, 'call') as mock_call:
|
||||
expected_context = self.context
|
||||
self.api.check_api_version(expected_context)
|
||||
mock_call.assert_called_once_with(
|
||||
expected_context.to_dict(),
|
||||
'check_api_version',
|
||||
api_version=ApplierAPI().API_VERSION)
|
||||
|
||||
def test_execute_action_plan_throw_exception(self):
|
||||
action_plan_uuid = "uuid"
|
||||
self.assertRaises(exception.InvalidUuidOrName,
|
||||
self.api.launch_action_plan,
|
||||
action_plan_uuid)
|
||||
|
||||
def test_execute_audit_without_error(self):
|
||||
with mock.patch.object(om.RPCClient, 'call') as mock_call:
|
||||
action_plan_uuid = utils.generate_uuid()
|
||||
self.api.launch_action_plan(self.context, action_plan_uuid)
|
||||
mock_call.assert_called_once_with(
|
||||
self.context.to_dict(),
|
||||
'launch_action_plan',
|
||||
action_plan_uuid=action_plan_uuid)
|
||||
119
watcher/tests/base.py
Normal file
119
watcher/tests/base.py
Normal file
@@ -0,0 +1,119 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2010-2011 OpenStack Foundation
|
||||
# Copyright (c) 2013 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 copy
|
||||
import os
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslotest import base
|
||||
import pecan
|
||||
from pecan import testing
|
||||
import testscenarios
|
||||
|
||||
from watcher.common import context as watcher_context
|
||||
from watcher.objects import base as objects_base
|
||||
from watcher.tests import conf_fixture
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.set_override('use_stderr', False)
|
||||
|
||||
|
||||
class BaseTestCase(testscenarios.WithScenarios, base.BaseTestCase):
|
||||
"""Test base class."""
|
||||
|
||||
def setUp(self):
|
||||
super(BaseTestCase, self).setUp()
|
||||
self.addCleanup(cfg.CONF.reset)
|
||||
|
||||
|
||||
class TestCase(base.BaseTestCase):
|
||||
"""Test case base class for all unit tests."""
|
||||
|
||||
def setUp(self):
|
||||
super(TestCase, self).setUp()
|
||||
self.app = testing.load_test_app(os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'config.py'
|
||||
))
|
||||
token_info = {
|
||||
'token': {
|
||||
'project': {
|
||||
'id': 'fake_project'
|
||||
},
|
||||
'user': {
|
||||
'id': 'fake_user'
|
||||
}
|
||||
}
|
||||
}
|
||||
self.context = watcher_context.RequestContext(
|
||||
auth_token_info=token_info,
|
||||
project_id='fake_project',
|
||||
user_id='fake_user')
|
||||
|
||||
def make_context(*args, **kwargs):
|
||||
# If context hasn't been constructed with token_info
|
||||
if not kwargs.get('auth_token_info'):
|
||||
kwargs['auth_token_info'] = copy.deepcopy(token_info)
|
||||
if not kwargs.get('project_id'):
|
||||
kwargs['project_id'] = 'fake_project'
|
||||
if not kwargs.get('user_id'):
|
||||
kwargs['user_id'] = 'fake_user'
|
||||
|
||||
context = watcher_context.RequestContext(*args, **kwargs)
|
||||
return watcher_context.RequestContext.from_dict(context.to_dict())
|
||||
|
||||
p = mock.patch.object(watcher_context, 'make_context',
|
||||
side_effect=make_context)
|
||||
self.mock_make_context = p.start()
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
self.useFixture(conf_fixture.ConfFixture(cfg.CONF))
|
||||
|
||||
self._base_test_obj_backup = copy.copy(
|
||||
objects_base.WatcherObject._obj_classes)
|
||||
self.addCleanup(self._restore_obj_registry)
|
||||
|
||||
def _restore_obj_registry(self):
|
||||
objects_base.WatcherObject._obj_classes = self._base_test_obj_backup
|
||||
|
||||
def tearDown(self):
|
||||
super(TestCase, self).tearDown()
|
||||
pecan.set_config({}, overwrite=True)
|
||||
|
||||
def config(self, **kw):
|
||||
"""Override config options for a test."""
|
||||
group = kw.pop('group', None)
|
||||
for k, v in kw.iteritems():
|
||||
CONF.set_override(k, v, group)
|
||||
|
||||
def path_get(self, project_file=None):
|
||||
"""Get the absolute path to a file. Used for testing the API.
|
||||
|
||||
:param project_file: File whose path to return. Default: None.
|
||||
:returns: path to the specified file, or path to project root.
|
||||
"""
|
||||
root = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
'..',
|
||||
'..',
|
||||
)
|
||||
)
|
||||
if project_file:
|
||||
return os.path.join(root, project_file)
|
||||
else:
|
||||
return root
|
||||
0
watcher/tests/common/__init__.py
Normal file
0
watcher/tests/common/__init__.py
Normal file
0
watcher/tests/common/messaging/__init__.py
Normal file
0
watcher/tests/common/messaging/__init__.py
Normal file
1
watcher/tests/common/messaging/event/__init__.py
Normal file
1
watcher/tests/common/messaging/event/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
__author__ = 'bcom'
|
||||
@@ -0,0 +1,80 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock import call
|
||||
from mock import MagicMock
|
||||
from watcher.common.messaging.events.event import Event
|
||||
from watcher.common.messaging.events.event_dispatcher import EventDispatcher
|
||||
from watcher.decision_engine.framework.messaging.events import Events
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestEventDispatcher(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestEventDispatcher, self).setUp()
|
||||
self.event_dispatcher = EventDispatcher()
|
||||
|
||||
def fake_listener(self):
|
||||
return MagicMock()
|
||||
|
||||
def fake_event(self, event_type):
|
||||
event = Event()
|
||||
event.set_type(event_type)
|
||||
return event
|
||||
|
||||
def test_add_listener(self):
|
||||
listener = self.fake_listener()
|
||||
self.event_dispatcher.add_event_listener(Events.ALL,
|
||||
listener)
|
||||
|
||||
self.assertEqual(True, self.event_dispatcher.has_listener(
|
||||
Events.ALL, listener))
|
||||
|
||||
def test_remove_listener(self):
|
||||
listener = self.fake_listener()
|
||||
self.event_dispatcher.add_event_listener(Events.ALL,
|
||||
listener)
|
||||
self.event_dispatcher.remove_event_listener(Events.ALL, listener)
|
||||
|
||||
self.assertEqual(False, self.event_dispatcher.has_listener(
|
||||
Events.TRIGGER_AUDIT, listener))
|
||||
|
||||
def test_dispatch_event(self):
|
||||
listener = self.fake_listener()
|
||||
event = self.fake_event(Events.TRIGGER_AUDIT)
|
||||
self.event_dispatcher.add_event_listener(Events.TRIGGER_AUDIT,
|
||||
listener)
|
||||
|
||||
self.event_dispatcher.dispatch_event(event)
|
||||
listener.assert_has_calls(call(event))
|
||||
|
||||
def test_dispatch_event_to_all_listener(self):
|
||||
event = self.fake_event(Events.ACTION_PLAN)
|
||||
listener_all = self.fake_listener()
|
||||
listener_action_plan = self.fake_listener()
|
||||
listener_trigger_audit = self.fake_listener()
|
||||
|
||||
self.event_dispatcher.add_event_listener(Events.ALL, listener_all)
|
||||
self.event_dispatcher.add_event_listener(Events.ACTION_PLAN,
|
||||
listener_action_plan)
|
||||
|
||||
self.event_dispatcher.add_event_listener(Events.TRIGGER_AUDIT,
|
||||
listener_trigger_audit)
|
||||
|
||||
self.event_dispatcher.dispatch_event(event)
|
||||
listener_all.assert_has_calls(call(event))
|
||||
listener_action_plan.assert_has_calls(call(event))
|
||||
listener_trigger_audit.assert_has_calls([])
|
||||
77
watcher/tests/common/messaging/test_messaging_core.py
Normal file
77
watcher/tests/common/messaging/test_messaging_core.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock
|
||||
from oslo_config import cfg
|
||||
|
||||
from watcher.common.messaging.messaging_core import MessagingCore
|
||||
from watcher.common.messaging.messaging_handler import MessagingHandler
|
||||
from watcher.common.rpc import RequestContextSerializer
|
||||
from watcher.tests import base
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestMessagingCore(base.TestCase):
|
||||
messaging = MessagingCore("", "", "")
|
||||
|
||||
def fake_topic_name(self):
|
||||
topic_name = "MyTopic"
|
||||
return topic_name
|
||||
|
||||
def test_build_topic(self):
|
||||
topic_name = self.fake_topic_name()
|
||||
messaging_handler = self.messaging.build_topic(topic_name)
|
||||
self.assertIsNotNone(messaging_handler)
|
||||
|
||||
def test_init_messaging_core(self):
|
||||
self.assertIsInstance(self.messaging.serializer,
|
||||
RequestContextSerializer)
|
||||
self.assertIsInstance(self.messaging.topic_control, MessagingHandler)
|
||||
self.assertIsInstance(self.messaging.topic_status, MessagingHandler)
|
||||
|
||||
def test_publish_control(self):
|
||||
with mock.patch.object(MessagingCore, 'publish_control') as mock_call:
|
||||
payload = {
|
||||
"name": "value",
|
||||
}
|
||||
event = "MyEvent"
|
||||
self.messaging.publish_control(event, payload)
|
||||
mock_call.assert_called_once_with(event, payload)
|
||||
|
||||
def test_publish_status(self):
|
||||
with mock.patch.object(MessagingCore, 'publish_status') as mock_call:
|
||||
payload = {
|
||||
"name": "value",
|
||||
}
|
||||
event = "MyEvent"
|
||||
self.messaging.publish_status(event, payload)
|
||||
mock_call.assert_called_once_with(event, payload)
|
||||
|
||||
def test_response(self):
|
||||
with mock.patch.object(MessagingCore, 'publish_status') as mock_call:
|
||||
event = "My event"
|
||||
context = {'request_id': 12}
|
||||
message = "My Message"
|
||||
|
||||
self.messaging.response(event, context, message)
|
||||
|
||||
expected_payload = {
|
||||
'request_id': context['request_id'],
|
||||
'msg': message
|
||||
}
|
||||
mock_call.assert_called_once_with(event, expected_payload)
|
||||
55
watcher/tests/common/messaging/test_notification_handler.py
Normal file
55
watcher/tests/common/messaging/test_notification_handler.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock
|
||||
from oslo import messaging
|
||||
from watcher.common.messaging.notification_handler import NotificationHandler
|
||||
from watcher.common.messaging.utils.observable import Observable
|
||||
from watcher.tests import base
|
||||
|
||||
PUBLISHER_ID = 'TEST_API'
|
||||
|
||||
|
||||
class TestNotificationHandler(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNotificationHandler, self).setUp()
|
||||
self.notification_handler = NotificationHandler(PUBLISHER_ID)
|
||||
|
||||
def _test_notify(self, level_to_call):
|
||||
ctx = {}
|
||||
publisher_id = PUBLISHER_ID
|
||||
event_type = 'Test'
|
||||
payload = {}
|
||||
metadata = {}
|
||||
|
||||
with mock.patch.object(Observable, 'notify') as mock_call:
|
||||
notification_result = level_to_call(ctx, publisher_id, event_type,
|
||||
payload, metadata)
|
||||
self.assertEqual(messaging.NotificationResult.HANDLED,
|
||||
notification_result)
|
||||
mock_call.assert_called_once_with(ctx, publisher_id, event_type,
|
||||
metadata, payload)
|
||||
|
||||
def test_notify_info(self):
|
||||
self._test_notify(self.notification_handler.info)
|
||||
|
||||
def test_notify_warn(self):
|
||||
self._test_notify(self.notification_handler.warn)
|
||||
|
||||
def test_notify_error(self):
|
||||
self._test_notify(self.notification_handler.error)
|
||||
0
watcher/tests/common/messaging/utils/__init__.py
Normal file
0
watcher/tests/common/messaging/utils/__init__.py
Normal file
@@ -0,0 +1,47 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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_config import cfg
|
||||
import re
|
||||
from watcher.common.messaging.utils.transport_url_builder import \
|
||||
TransportUrlBuilder
|
||||
from watcher.tests import base
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestTransportUrlBuilder(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestTransportUrlBuilder, self).setUp()
|
||||
|
||||
def test_transport_url_not_none(self):
|
||||
url = TransportUrlBuilder().url
|
||||
print(url)
|
||||
self.assertIsNotNone(url, "The transport url must not be none")
|
||||
|
||||
def test_transport_url_valid_pattern(self):
|
||||
url = TransportUrlBuilder().url
|
||||
url_pattern = r'(\D+)://(\D+):(\D+)@(\D+):(\d+)'
|
||||
pattern = re.compile(url_pattern)
|
||||
match = re.search(url_pattern, url)
|
||||
self.assertEqual('rabbit', match.group(1))
|
||||
self.assertEqual('guest', match.group(2))
|
||||
self.assertEqual('guest', match.group(3))
|
||||
self.assertEqual('localhost', match.group(4))
|
||||
self.assertEqual('5672', match.group(5))
|
||||
self.assertIsNotNone(pattern.match(url))
|
||||
39
watcher/tests/conf_fixture.py
Normal file
39
watcher/tests/conf_fixture.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# 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.
|
||||
|
||||
import fixtures
|
||||
from oslo_config import cfg
|
||||
|
||||
from watcher.common import config
|
||||
|
||||
cfg.CONF.register_opt(cfg.StrOpt('host', default='localhost', help='host'))
|
||||
|
||||
|
||||
class ConfFixture(fixtures.Fixture):
|
||||
"""Fixture to manage global conf settings."""
|
||||
|
||||
def __init__(self, conf):
|
||||
self.conf = conf
|
||||
|
||||
def setUp(self):
|
||||
super(ConfFixture, self).setUp()
|
||||
|
||||
self.conf.set_default('host', 'fake-mini')
|
||||
self.conf.set_default('connection', "sqlite://", group='database')
|
||||
self.conf.set_default('sqlite_synchronous', False, group='database')
|
||||
self.conf.set_default('verbose', True)
|
||||
config.parse_args([], default_config_files=[])
|
||||
self.addCleanup(self.conf.reset)
|
||||
38
watcher/tests/config.py
Normal file
38
watcher/tests/config.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# 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 watcher.api import hooks
|
||||
|
||||
# Server Specific Configurations
|
||||
server = {
|
||||
'port': '9322',
|
||||
'host': '0.0.0.0'
|
||||
}
|
||||
|
||||
# Pecan Application Configurations
|
||||
app = {
|
||||
'root': 'watcher.api.controllers.root.RootController',
|
||||
'modules': ['watcher.api'],
|
||||
'hooks': [
|
||||
hooks.ContextHook(),
|
||||
],
|
||||
'acl_public_routes': [
|
||||
'/'
|
||||
],
|
||||
}
|
||||
|
||||
# Custom Configurations must be in Python dictionary format::
|
||||
#
|
||||
# foo = {'bar':'baz'}
|
||||
#
|
||||
# All configurations are accessible at::
|
||||
# pecan.conf
|
||||
0
watcher/tests/db/__init__.py
Normal file
0
watcher/tests/db/__init__.py
Normal file
104
watcher/tests/db/base.py
Normal file
104
watcher/tests/db/base.py
Normal file
@@ -0,0 +1,104 @@
|
||||
# Copyright (c) 2012 NTT DOCOMO, INC.
|
||||
# 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.
|
||||
|
||||
"""Watcher DB test base class."""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import fixtures
|
||||
from oslo_config import cfg
|
||||
|
||||
from watcher.common import paths
|
||||
from watcher.db import api as dbapi
|
||||
from watcher.db.sqlalchemy import api as sqla_api
|
||||
from watcher.db.sqlalchemy import migration
|
||||
from watcher.db.sqlalchemy import models
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
CONF.import_opt('enable_authentication', 'watcher.api.acl')
|
||||
|
||||
_DB_CACHE = None
|
||||
|
||||
|
||||
class Database(fixtures.Fixture):
|
||||
|
||||
def __init__(self, db_api, db_migrate, sql_connection,
|
||||
sqlite_db, sqlite_clean_db):
|
||||
self.sql_connection = sql_connection
|
||||
self.sqlite_db = sqlite_db
|
||||
self.sqlite_clean_db = sqlite_clean_db
|
||||
|
||||
self.engine = db_api.get_engine()
|
||||
self.engine.dispose()
|
||||
conn = self.engine.connect()
|
||||
if sql_connection == "sqlite://":
|
||||
self.setup_sqlite(db_migrate)
|
||||
elif sql_connection.startswith('sqlite:///'):
|
||||
testdb = paths.state_path_rel(sqlite_db)
|
||||
if os.path.exists(testdb):
|
||||
return
|
||||
self.setup_sqlite(db_migrate)
|
||||
else:
|
||||
db_migrate.upgrade('head')
|
||||
self.post_migrations()
|
||||
if sql_connection == "sqlite://":
|
||||
conn = self.engine.connect()
|
||||
self._DB = "".join(line for line in conn.connection.iterdump())
|
||||
self.engine.dispose()
|
||||
else:
|
||||
cleandb = paths.state_path_rel(sqlite_clean_db)
|
||||
shutil.copyfile(testdb, cleandb)
|
||||
|
||||
def setup_sqlite(self, db_migrate):
|
||||
if db_migrate.version():
|
||||
return
|
||||
models.Base.metadata.create_all(self.engine)
|
||||
db_migrate.stamp('head')
|
||||
|
||||
def setUp(self):
|
||||
super(Database, self).setUp()
|
||||
|
||||
if self.sql_connection == "sqlite://":
|
||||
conn = self.engine.connect()
|
||||
conn.connection.executescript(self._DB)
|
||||
self.addCleanup(self.engine.dispose)
|
||||
else:
|
||||
shutil.copyfile(paths.state_path_rel(self.sqlite_clean_db),
|
||||
paths.state_path_rel(self.sqlite_db))
|
||||
self.addCleanup(os.unlink, self.sqlite_db)
|
||||
|
||||
def post_migrations(self):
|
||||
"""Any addition steps that are needed outside of the migrations."""
|
||||
|
||||
|
||||
class DbTestCase(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
cfg.CONF.set_override("enable_authentication", False)
|
||||
super(DbTestCase, self).setUp()
|
||||
|
||||
self.dbapi = dbapi.get_instance()
|
||||
|
||||
global _DB_CACHE
|
||||
if not _DB_CACHE:
|
||||
_DB_CACHE = Database(sqla_api, migration,
|
||||
sql_connection=CONF.database.connection,
|
||||
sqlite_db=CONF.database.sqlite_db,
|
||||
sqlite_clean_db='clean.sqlite')
|
||||
self.useFixture(_DB_CACHE)
|
||||
0
watcher/tests/db/sqlalchemy/__init__.py
Normal file
0
watcher/tests/db/sqlalchemy/__init__.py
Normal file
70
watcher/tests/db/sqlalchemy/test_types.py
Normal file
70
watcher/tests/db/sqlalchemy/test_types.py
Normal file
@@ -0,0 +1,70 @@
|
||||
# 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.
|
||||
|
||||
"""Tests for custom SQLAlchemy types via Magnum DB."""
|
||||
|
||||
from oslo_db import exception as db_exc
|
||||
|
||||
from watcher.common import utils as w_utils
|
||||
from watcher.db import api as dbapi
|
||||
import watcher.db.sqlalchemy.api as sa_api
|
||||
from watcher.db.sqlalchemy import models
|
||||
from watcher.tests.db import base
|
||||
|
||||
|
||||
class SqlAlchemyCustomTypesTestCase(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SqlAlchemyCustomTypesTestCase, self).setUp()
|
||||
self.dbapi = dbapi.get_instance()
|
||||
|
||||
def test_JSONEncodedDict_default_value(self):
|
||||
# Create audit_template w/o extra
|
||||
audit_template1_id = w_utils.generate_uuid()
|
||||
self.dbapi.create_audit_template({'uuid': audit_template1_id})
|
||||
audit_template1 = sa_api.model_query(models.AuditTemplate) \
|
||||
.filter_by(uuid=audit_template1_id).one()
|
||||
self.assertEqual({}, audit_template1.extra)
|
||||
|
||||
# Create audit_template with extra
|
||||
audit_template2_id = w_utils.generate_uuid()
|
||||
self.dbapi.create_audit_template({'uuid': audit_template2_id,
|
||||
'extra': {'bar': 'foo'}})
|
||||
audit_template2 = sa_api.model_query(models.AuditTemplate) \
|
||||
.filter_by(uuid=audit_template2_id).one()
|
||||
self.assertEqual('foo', audit_template2.extra['bar'])
|
||||
|
||||
def test_JSONEncodedDict_type_check(self):
|
||||
self.assertRaises(db_exc.DBError,
|
||||
self.dbapi.create_audit_template,
|
||||
{'extra': ['this is not a dict']})
|
||||
|
||||
# def test_JSONEncodedList_default_value(self):
|
||||
# # Create audit_template w/o images
|
||||
# audit_template1_id = w_utils.generate_uuid()
|
||||
# self.dbapi.create_audit_template({'uuid': audit_template1_id})
|
||||
# audit_template1 = sa_api.model_query(models.AuditTemplate) \
|
||||
# .filter_by(uuid=audit_template1_id).one()
|
||||
# self.assertEqual([], audit_template1.images)
|
||||
|
||||
# # Create audit_template with images
|
||||
# audit_template2_id = w_utils.generate_uuid()
|
||||
# self.dbapi.create_audit_template({'uuid': audit_template2_id,
|
||||
# 'images': ['myimage1', 'myimage2']})
|
||||
# audit_template2 = sa_api.model_query(models.AuditTemplate) \
|
||||
# .filter_by(uuid=audit_template2_id).one()
|
||||
# self.assertEqual(['myimage1', 'myimage2'], audit_template2.images)
|
||||
|
||||
# def test_JSONEncodedList_type_check(self):
|
||||
# self.assertRaises(db_exc.DBError,
|
||||
# self.dbapi.create_audit_template,
|
||||
# {'images': {'this is not a list': 'test'}})
|
||||
158
watcher/tests/db/test_action.py
Normal file
158
watcher/tests/db/test_action.py
Normal file
@@ -0,0 +1,158 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
"""Tests for manipulating Action via the DB API"""
|
||||
|
||||
import six
|
||||
from watcher.common import exception
|
||||
from watcher.common import utils as w_utils
|
||||
from watcher.tests.db import base
|
||||
from watcher.tests.db import utils
|
||||
|
||||
|
||||
class DbActionTestCase(base.DbTestCase):
|
||||
|
||||
def _create_test_action(self, **kwargs):
|
||||
action = utils.get_test_action(**kwargs)
|
||||
self.dbapi.create_action(action)
|
||||
return action
|
||||
|
||||
def _create_test_action_plan(self, **kwargs):
|
||||
action_plan = utils.get_test_action_plan(**kwargs)
|
||||
self.dbapi.create_action_plan(action_plan)
|
||||
return action_plan
|
||||
|
||||
def test_get_action_list(self):
|
||||
uuids = []
|
||||
for i in range(1, 6):
|
||||
action = utils.create_test_action(uuid=w_utils.generate_uuid())
|
||||
uuids.append(six.text_type(action['uuid']))
|
||||
res = self.dbapi.get_action_list(self.context)
|
||||
res_uuids = [r.uuid for r in res]
|
||||
self.assertEqual(uuids.sort(), res_uuids.sort())
|
||||
|
||||
def test_get_action_list_with_filters(self):
|
||||
audit = utils.create_test_audit(uuid=w_utils.generate_uuid())
|
||||
action_plan = self._create_test_action_plan(
|
||||
id=1,
|
||||
uuid=w_utils.generate_uuid(),
|
||||
audit_id=audit.id,
|
||||
first_action_id=None,
|
||||
state='RECOMMENDED')
|
||||
action1 = self._create_test_action(
|
||||
id=1,
|
||||
action_plan_id=1,
|
||||
description='description action 1',
|
||||
uuid=w_utils.generate_uuid(),
|
||||
next=None,
|
||||
state='PENDING',
|
||||
alarm=None)
|
||||
action2 = self._create_test_action(
|
||||
id=2,
|
||||
action_plan_id=2,
|
||||
description='description action 2',
|
||||
uuid=w_utils.generate_uuid(),
|
||||
next=action1['uuid'],
|
||||
state='PENDING',
|
||||
alarm=None)
|
||||
action3 = self._create_test_action(
|
||||
id=3,
|
||||
action_plan_id=1,
|
||||
description='description action 3',
|
||||
uuid=w_utils.generate_uuid(),
|
||||
next=action2['uuid'],
|
||||
state='ONGOING',
|
||||
alarm=None)
|
||||
res = self.dbapi.get_action_list(self.context,
|
||||
filters={'state': 'ONGOING'})
|
||||
self.assertEqual([action3['id']], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_action_list(self.context,
|
||||
filters={'state': 'bad-state'})
|
||||
self.assertEqual([], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_action_list(
|
||||
self.context,
|
||||
filters={'action_plan_id': 2})
|
||||
self.assertEqual([action2['id']], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_action_list(
|
||||
self.context,
|
||||
filters={'action_plan_uuid': action_plan['uuid']})
|
||||
self.assertEqual(
|
||||
[action1['id'], action3['id']].sort(),
|
||||
[r.id for r in res].sort())
|
||||
|
||||
res = self.dbapi.get_action_list(
|
||||
self.context,
|
||||
filters={'audit_uuid': audit.uuid})
|
||||
for action in res:
|
||||
self.assertEqual(action_plan['id'], action.action_plan_id)
|
||||
|
||||
def test_get_action_by_id(self):
|
||||
action = self._create_test_action()
|
||||
action = self.dbapi.get_action_by_id(self.context, action['id'])
|
||||
self.assertEqual(action['uuid'], action.uuid)
|
||||
|
||||
def test_get_action_by_uuid(self):
|
||||
action = self._create_test_action()
|
||||
action = self.dbapi.get_action_by_uuid(self.context, action['uuid'])
|
||||
self.assertEqual(action['id'], action.id)
|
||||
|
||||
def test_get_action_that_does_not_exist(self):
|
||||
self.assertRaises(exception.ActionNotFound,
|
||||
self.dbapi.get_action_by_id, self.context, 1234)
|
||||
|
||||
def test_update_action(self):
|
||||
action = self._create_test_action()
|
||||
res = self.dbapi.update_action(action['id'], {'state': 'CANCELLED'})
|
||||
self.assertEqual('CANCELLED', res.state)
|
||||
|
||||
def test_update_action_that_does_not_exist(self):
|
||||
self.assertRaises(exception.ActionNotFound,
|
||||
self.dbapi.update_action, 1234, {'state': ''})
|
||||
|
||||
def test_update_action_uuid(self):
|
||||
action = self._create_test_action()
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.dbapi.update_action, action['id'],
|
||||
{'uuid': 'hello'})
|
||||
|
||||
def test_destroy_action(self):
|
||||
action = self._create_test_action()
|
||||
self.dbapi.destroy_action(action['id'])
|
||||
self.assertRaises(exception.ActionNotFound,
|
||||
self.dbapi.get_action_by_id,
|
||||
self.context, action['id'])
|
||||
|
||||
def test_destroy_action_by_uuid(self):
|
||||
uuid = w_utils.generate_uuid()
|
||||
self._create_test_action(uuid=uuid)
|
||||
self.assertIsNotNone(self.dbapi.get_action_by_uuid(self.context,
|
||||
uuid))
|
||||
self.dbapi.destroy_action(uuid)
|
||||
self.assertRaises(exception.ActionNotFound,
|
||||
self.dbapi.get_action_by_uuid, self.context, uuid)
|
||||
|
||||
def test_destroy_action_that_does_not_exist(self):
|
||||
self.assertRaises(exception.ActionNotFound,
|
||||
self.dbapi.destroy_action, 1234)
|
||||
|
||||
def test_create_action_already_exists(self):
|
||||
uuid = w_utils.generate_uuid()
|
||||
self._create_test_action(id=1, uuid=uuid)
|
||||
self.assertRaises(exception.ActionAlreadyExists,
|
||||
self._create_test_action,
|
||||
id=2, uuid=uuid)
|
||||
148
watcher/tests/db/test_action_plan.py
Normal file
148
watcher/tests/db/test_action_plan.py
Normal file
@@ -0,0 +1,148 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
"""Tests for manipulating ActionPlan via the DB API"""
|
||||
|
||||
import six
|
||||
from watcher.common import exception
|
||||
from watcher.common import utils as w_utils
|
||||
from watcher.tests.db import base
|
||||
from watcher.tests.db import utils
|
||||
|
||||
|
||||
class DbActionPlanTestCase(base.DbTestCase):
|
||||
|
||||
def _create_test_audit(self, **kwargs):
|
||||
audit = utils.get_test_audit(**kwargs)
|
||||
self.dbapi.create_audit(audit)
|
||||
return audit
|
||||
|
||||
def _create_test_action_plan(self, **kwargs):
|
||||
action_plan = utils.get_test_action_plan(**kwargs)
|
||||
self.dbapi.create_action_plan(action_plan)
|
||||
return action_plan
|
||||
|
||||
def test_get_action_plan_list(self):
|
||||
uuids = []
|
||||
for i in range(1, 6):
|
||||
audit = utils.create_test_action_plan(uuid=w_utils.generate_uuid())
|
||||
uuids.append(six.text_type(audit['uuid']))
|
||||
res = self.dbapi.get_action_plan_list(self.context)
|
||||
res_uuids = [r.uuid for r in res]
|
||||
self.assertEqual(uuids.sort(), res_uuids.sort())
|
||||
|
||||
def test_get_action_plan_list_with_filters(self):
|
||||
audit = self._create_test_audit(
|
||||
id=1,
|
||||
type='ONESHOT',
|
||||
uuid=w_utils.generate_uuid(),
|
||||
deadline=None,
|
||||
state='ONGOING')
|
||||
action_plan1 = self._create_test_action_plan(
|
||||
id=1,
|
||||
uuid=w_utils.generate_uuid(),
|
||||
audit_id=audit['id'],
|
||||
first_action_id=None,
|
||||
state='RECOMMENDED')
|
||||
action_plan2 = self._create_test_action_plan(
|
||||
id=2,
|
||||
uuid=w_utils.generate_uuid(),
|
||||
audit_id=audit['id'],
|
||||
first_action_id=action_plan1['id'],
|
||||
state='ONGOING')
|
||||
|
||||
res = self.dbapi.get_action_plan_list(
|
||||
self.context,
|
||||
filters={'state': 'RECOMMENDED'})
|
||||
self.assertEqual([action_plan1['id']], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_action_plan_list(
|
||||
self.context,
|
||||
filters={'state': 'ONGOING'})
|
||||
self.assertEqual([action_plan2['id']], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_action_plan_list(
|
||||
self.context,
|
||||
filters={'audit_uuid': audit['uuid']})
|
||||
|
||||
for r in res:
|
||||
self.assertEqual(audit['id'], r.audit_id)
|
||||
|
||||
def test_get_action_plan_by_id(self):
|
||||
action_plan = self._create_test_action_plan()
|
||||
action_plan = self.dbapi.get_action_plan_by_id(
|
||||
self.context, action_plan['id'])
|
||||
self.assertEqual(action_plan['uuid'], action_plan.uuid)
|
||||
|
||||
def test_get_action_plan_by_uuid(self):
|
||||
action_plan = self._create_test_action_plan()
|
||||
action_plan = self.dbapi.get_action_plan_by_uuid(
|
||||
self.context, action_plan['uuid'])
|
||||
self.assertEqual(action_plan['id'], action_plan.id)
|
||||
|
||||
def test_get_action_plan_that_does_not_exist(self):
|
||||
self.assertRaises(exception.ActionPlanNotFound,
|
||||
self.dbapi.get_action_plan_by_id, self.context, 1234)
|
||||
|
||||
def test_update_action_plan(self):
|
||||
action_plan = self._create_test_action_plan()
|
||||
res = self.dbapi.update_action_plan(
|
||||
action_plan['id'], {'name': 'updated-model'})
|
||||
self.assertEqual('updated-model', res.name)
|
||||
|
||||
def test_update_action_plan_that_does_not_exist(self):
|
||||
self.assertRaises(exception.ActionPlanNotFound,
|
||||
self.dbapi.update_action_plan, 1234, {'name': ''})
|
||||
|
||||
def test_update_action_plan_uuid(self):
|
||||
action_plan = self._create_test_action_plan()
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.dbapi.update_action_plan, action_plan['id'],
|
||||
{'uuid': 'hello'})
|
||||
|
||||
def test_destroy_action_plan(self):
|
||||
action_plan = self._create_test_action_plan()
|
||||
self.dbapi.destroy_action_plan(action_plan['id'])
|
||||
self.assertRaises(exception.ActionPlanNotFound,
|
||||
self.dbapi.get_action_plan_by_id,
|
||||
self.context, action_plan['id'])
|
||||
|
||||
def test_destroy_action_plan_by_uuid(self):
|
||||
uuid = w_utils.generate_uuid()
|
||||
self._create_test_action_plan(uuid=uuid)
|
||||
self.assertIsNotNone(self.dbapi.get_action_plan_by_uuid(
|
||||
self.context, uuid))
|
||||
self.dbapi.destroy_action_plan(uuid)
|
||||
self.assertRaises(exception.ActionPlanNotFound,
|
||||
self.dbapi.get_action_plan_by_uuid,
|
||||
self.context, uuid)
|
||||
|
||||
def test_destroy_action_plan_that_does_not_exist(self):
|
||||
self.assertRaises(exception.ActionPlanNotFound,
|
||||
self.dbapi.destroy_action_plan, 1234)
|
||||
|
||||
def test_destroy_action_plan_that_referenced_by_actions(self):
|
||||
action_plan = self._create_test_action_plan()
|
||||
action = utils.create_test_action(action_plan_id=action_plan['id'])
|
||||
self.assertEqual(action_plan['id'], action.action_plan_id)
|
||||
self.assertRaises(exception.ActionPlanReferenced,
|
||||
self.dbapi.destroy_action_plan, action_plan['id'])
|
||||
|
||||
def test_create_action_plan_already_exists(self):
|
||||
uuid = w_utils.generate_uuid()
|
||||
self._create_test_action_plan(id=1, uuid=uuid)
|
||||
self.assertRaises(exception.ActionPlanAlreadyExists,
|
||||
self._create_test_action_plan,
|
||||
id=2, uuid=uuid)
|
||||
186
watcher/tests/db/test_audit.py
Normal file
186
watcher/tests/db/test_audit.py
Normal file
@@ -0,0 +1,186 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
"""Tests for manipulating Audit via the DB API"""
|
||||
|
||||
import six
|
||||
from watcher.common import exception
|
||||
from watcher.common import utils as w_utils
|
||||
from watcher.tests.db import base
|
||||
from watcher.tests.db import utils
|
||||
|
||||
|
||||
class DbAuditTestCase(base.DbTestCase):
|
||||
|
||||
def _create_test_audit(self, **kwargs):
|
||||
audit = utils.get_test_audit(**kwargs)
|
||||
self.dbapi.create_audit(audit)
|
||||
return audit
|
||||
|
||||
def test_get_audit_list(self):
|
||||
uuids = []
|
||||
for i in range(1, 6):
|
||||
audit = utils.create_test_audit(uuid=w_utils.generate_uuid())
|
||||
uuids.append(six.text_type(audit['uuid']))
|
||||
res = self.dbapi.get_audit_list(self.context)
|
||||
res_uuids = [r.uuid for r in res]
|
||||
self.assertEqual(uuids.sort(), res_uuids.sort())
|
||||
|
||||
def test_get_audit_list_with_filters(self):
|
||||
audit1 = self._create_test_audit(
|
||||
id=1,
|
||||
type='ONESHOT',
|
||||
uuid=w_utils.generate_uuid(),
|
||||
deadline=None,
|
||||
state='ONGOING')
|
||||
audit2 = self._create_test_audit(
|
||||
id=2,
|
||||
type='CONTINUOUS',
|
||||
uuid=w_utils.generate_uuid(),
|
||||
deadline=None,
|
||||
state='PENDING')
|
||||
|
||||
res = self.dbapi.get_audit_list(self.context,
|
||||
filters={'type': 'ONESHOT'})
|
||||
self.assertEqual([audit1['id']], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_audit_list(self.context,
|
||||
filters={'type': 'bad-type'})
|
||||
self.assertEqual([], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_audit_list(
|
||||
self.context,
|
||||
filters={'state': 'ONGOING'})
|
||||
self.assertEqual([audit1['id']], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_audit_list(
|
||||
self.context,
|
||||
filters={'state': 'PENDING'})
|
||||
self.assertEqual([audit2['id']], [r.id for r in res])
|
||||
|
||||
def test_get_audit_by_id(self):
|
||||
audit = self._create_test_audit()
|
||||
audit = self.dbapi.get_audit_by_id(self.context, audit['id'])
|
||||
self.assertEqual(audit['uuid'], audit.uuid)
|
||||
|
||||
def test_get_audit_by_uuid(self):
|
||||
audit = self._create_test_audit()
|
||||
audit = self.dbapi.get_audit_by_uuid(self.context, audit['uuid'])
|
||||
self.assertEqual(audit['id'], audit.id)
|
||||
|
||||
def test_get_audit_that_does_not_exist(self):
|
||||
self.assertRaises(exception.AuditNotFound,
|
||||
self.dbapi.get_audit_by_id, self.context, 1234)
|
||||
|
||||
def test_get_audit_list_with_filter_by_audit_template_uuid(self):
|
||||
|
||||
audit_template = self.dbapi.create_audit_template(
|
||||
utils.get_test_audit_template(
|
||||
uuid=w_utils.generate_uuid(),
|
||||
name='My Audit Template 1',
|
||||
description='Description of my audit template 1',
|
||||
host_aggregate=5,
|
||||
goal='SERVERS_CONSOLIDATION',
|
||||
extra={'automatic': True})
|
||||
)
|
||||
|
||||
audit = self._create_test_audit(
|
||||
type='ONESHOT',
|
||||
uuid=w_utils.generate_uuid(),
|
||||
deadline=None,
|
||||
state='ONGOING',
|
||||
audit_template_id=audit_template.id)
|
||||
|
||||
res = self.dbapi.get_audit_list(
|
||||
self.context,
|
||||
filters={'audit_template_uuid': audit_template.uuid})
|
||||
|
||||
for r in res:
|
||||
self.assertEqual(audit['audit_template_id'], r.audit_template_id)
|
||||
|
||||
def test_get_audit_list_with_filter_by_audit_template_name(self):
|
||||
|
||||
audit_template = self.dbapi.create_audit_template(
|
||||
utils.get_test_audit_template(
|
||||
uuid=w_utils.generate_uuid(),
|
||||
name='My Audit Template 1',
|
||||
description='Description of my audit template 1',
|
||||
host_aggregate=5,
|
||||
goal='SERVERS_CONSOLIDATION',
|
||||
extra={'automatic': True})
|
||||
)
|
||||
|
||||
audit = self._create_test_audit(
|
||||
type='ONESHOT',
|
||||
uuid=w_utils.generate_uuid(),
|
||||
deadline=None,
|
||||
state='ONGOING',
|
||||
audit_template_id=audit_template.id)
|
||||
|
||||
res = self.dbapi.get_audit_list(
|
||||
self.context,
|
||||
filters={'audit_template_name': audit_template.name})
|
||||
|
||||
for r in res:
|
||||
self.assertEqual(audit['audit_template_id'], r.audit_template_id)
|
||||
|
||||
def test_update_audit(self):
|
||||
audit = self._create_test_audit()
|
||||
res = self.dbapi.update_audit(audit['id'], {'name': 'updated-model'})
|
||||
self.assertEqual('updated-model', res.name)
|
||||
|
||||
def test_update_audit_that_does_not_exist(self):
|
||||
self.assertRaises(exception.AuditNotFound,
|
||||
self.dbapi.update_audit, 1234, {'name': ''})
|
||||
|
||||
def test_update_audit_uuid(self):
|
||||
audit = self._create_test_audit()
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.dbapi.update_audit, audit['id'],
|
||||
{'uuid': 'hello'})
|
||||
|
||||
def test_destroy_audit(self):
|
||||
audit = self._create_test_audit()
|
||||
self.dbapi.destroy_audit(audit['id'])
|
||||
self.assertRaises(exception.AuditNotFound,
|
||||
self.dbapi.get_audit_by_id,
|
||||
self.context, audit['id'])
|
||||
|
||||
def test_destroy_audit_by_uuid(self):
|
||||
uuid = w_utils.generate_uuid()
|
||||
self._create_test_audit(uuid=uuid)
|
||||
self.assertIsNotNone(self.dbapi.get_audit_by_uuid(self.context,
|
||||
uuid))
|
||||
self.dbapi.destroy_audit(uuid)
|
||||
self.assertRaises(exception.AuditNotFound,
|
||||
self.dbapi.get_audit_by_uuid, self.context, uuid)
|
||||
|
||||
def test_destroy_audit_that_does_not_exist(self):
|
||||
self.assertRaises(exception.AuditNotFound,
|
||||
self.dbapi.destroy_audit, 1234)
|
||||
|
||||
def test_destroy_audit_that_referenced_by_action_plans(self):
|
||||
audit = self._create_test_audit()
|
||||
action_plan = utils.create_test_action_plan(audit_id=audit['id'])
|
||||
self.assertEqual(audit['id'], action_plan.audit_id)
|
||||
self.assertRaises(exception.AuditReferenced,
|
||||
self.dbapi.destroy_audit, audit['id'])
|
||||
|
||||
def test_create_audit_already_exists(self):
|
||||
uuid = w_utils.generate_uuid()
|
||||
self._create_test_audit(id=1, uuid=uuid)
|
||||
self.assertRaises(exception.AuditAlreadyExists,
|
||||
self._create_test_audit,
|
||||
id=2, uuid=uuid)
|
||||
171
watcher/tests/db/test_audit_template.py
Normal file
171
watcher/tests/db/test_audit_template.py
Normal file
@@ -0,0 +1,171 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
"""Tests for manipulating AuditTemplate via the DB API"""
|
||||
|
||||
import six
|
||||
from watcher.common import exception
|
||||
from watcher.common import utils as w_utils
|
||||
from watcher.tests.db import base
|
||||
from watcher.tests.db import utils
|
||||
|
||||
|
||||
class DbAuditTemplateTestCase(base.DbTestCase):
|
||||
|
||||
def _create_test_audit_template(self, **kwargs):
|
||||
audit_template = utils.get_test_audit_template(**kwargs)
|
||||
self.dbapi.create_audit_template(audit_template)
|
||||
return audit_template
|
||||
|
||||
def test_get_audit_template_list(self):
|
||||
uuids = []
|
||||
for i in range(1, 6):
|
||||
audit_template = utils.create_test_audit_template(
|
||||
uuid=w_utils.generate_uuid(),
|
||||
name='My Audit Template ' + str(i))
|
||||
uuids.append(six.text_type(audit_template['uuid']))
|
||||
res = self.dbapi.get_audit_template_list(self.context)
|
||||
res_uuids = [r.uuid for r in res]
|
||||
self.assertEqual(uuids.sort(), res_uuids.sort())
|
||||
|
||||
def test_get_audit_template_list_with_filters(self):
|
||||
audit_template1 = self._create_test_audit_template(
|
||||
id=1,
|
||||
uuid=w_utils.generate_uuid(),
|
||||
name='My Audit Template 1',
|
||||
description='Description of my audit template 1',
|
||||
host_aggregate=5,
|
||||
goal='SERVERS_CONSOLIDATION',
|
||||
extra={'automatic': True})
|
||||
audit_template2 = self._create_test_audit_template(
|
||||
id=2,
|
||||
uuid=w_utils.generate_uuid(),
|
||||
name='My Audit Template 2',
|
||||
description='Description of my audit template 2',
|
||||
host_aggregate=3,
|
||||
goal='SERVERS_CONSOLIDATION',
|
||||
extra={'automatic': True})
|
||||
|
||||
res = self.dbapi.get_audit_template_list(self.context,
|
||||
filters={'host_aggregate': 5})
|
||||
self.assertEqual([audit_template1['id']], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_audit_template_list(self.context,
|
||||
filters={'host_aggregate': 1})
|
||||
self.assertEqual([], [r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_audit_template_list(
|
||||
self.context,
|
||||
filters={'goal': 'SERVERS_CONSOLIDATION'})
|
||||
self.assertEqual([audit_template1['id'], audit_template2['id']],
|
||||
[r.id for r in res])
|
||||
|
||||
res = self.dbapi.get_audit_template_list(
|
||||
self.context,
|
||||
filters={'name': 'My Audit Template 2'})
|
||||
self.assertEqual([audit_template2['id']], [r.id for r in res])
|
||||
|
||||
def test_get_audit_template_by_id(self):
|
||||
audit_template = self._create_test_audit_template()
|
||||
audit_template = self.dbapi.get_audit_template_by_id(
|
||||
self.context, audit_template['id'])
|
||||
self.assertEqual(audit_template['uuid'], audit_template.uuid)
|
||||
|
||||
def test_get_audit_template_by_uuid(self):
|
||||
audit_template = self._create_test_audit_template()
|
||||
audit_template = self.dbapi.get_audit_template_by_uuid(
|
||||
self.context, audit_template['uuid'])
|
||||
self.assertEqual(audit_template['id'], audit_template.id)
|
||||
|
||||
def test_get_audit_template_that_does_not_exist(self):
|
||||
self.assertRaises(exception.AuditTemplateNotFound,
|
||||
self.dbapi.get_audit_template_by_id,
|
||||
self.context, 1234)
|
||||
|
||||
def test_update_audit_template(self):
|
||||
audit_template = self._create_test_audit_template()
|
||||
res = self.dbapi.update_audit_template(audit_template['id'],
|
||||
{'name': 'updated-model'})
|
||||
self.assertEqual('updated-model', res.name)
|
||||
|
||||
def test_update_audit_template_that_does_not_exist(self):
|
||||
self.assertRaises(exception.AuditTemplateNotFound,
|
||||
self.dbapi.update_audit_template, 1234, {'name': ''})
|
||||
|
||||
def test_update_audit_template_uuid(self):
|
||||
audit_template = self._create_test_audit_template()
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
self.dbapi.update_audit_template,
|
||||
audit_template['id'],
|
||||
{'uuid': 'hello'})
|
||||
|
||||
def test_destroy_audit_template(self):
|
||||
audit_template = self._create_test_audit_template()
|
||||
self.dbapi.destroy_audit_template(audit_template['id'])
|
||||
self.assertRaises(exception.AuditTemplateNotFound,
|
||||
self.dbapi.get_audit_template_by_id,
|
||||
self.context, audit_template['id'])
|
||||
|
||||
def test_destroy_audit_template_by_uuid(self):
|
||||
uuid = w_utils.generate_uuid()
|
||||
self._create_test_audit_template(uuid=uuid)
|
||||
self.assertIsNotNone(self.dbapi.get_audit_template_by_uuid(
|
||||
self.context, uuid))
|
||||
self.dbapi.destroy_audit_template(uuid)
|
||||
self.assertRaises(exception.AuditTemplateNotFound,
|
||||
self.dbapi.get_audit_template_by_uuid,
|
||||
self.context, uuid)
|
||||
|
||||
def test_destroy_audit_template_that_does_not_exist(self):
|
||||
self.assertRaises(exception.AuditTemplateNotFound,
|
||||
self.dbapi.destroy_audit_template, 1234)
|
||||
|
||||
# def test_destroy_audit_template_that_referenced_by_goals(self):
|
||||
# audit_template = self._create_test_audit_template()
|
||||
# goal = utils.create_test_goal(audit_template=audit_template['uuid'])
|
||||
# self.assertEqual(audit_template['uuid'], goal.audit_template)
|
||||
# self.assertRaises(exception.AuditTemplateReferenced,
|
||||
# self.dbapi.destroy_audit_template,
|
||||
# audit_template['id'])
|
||||
|
||||
def test_create_audit_template_already_exists(self):
|
||||
uuid = w_utils.generate_uuid()
|
||||
self._create_test_audit_template(id=1, uuid=uuid)
|
||||
self.assertRaises(exception.AuditTemplateAlreadyExists,
|
||||
self._create_test_audit_template,
|
||||
id=2, uuid=uuid)
|
||||
|
||||
def test_audit_template_create_same_name(self):
|
||||
audit_template1 = utils.create_test_audit_template(
|
||||
uuid=w_utils.generate_uuid(),
|
||||
name='audit_template_name')
|
||||
self.assertEqual(audit_template1['uuid'], audit_template1.uuid)
|
||||
self.assertRaises(
|
||||
exception.AuditTemplateAlreadyExists,
|
||||
utils.create_test_audit_template,
|
||||
uuid=w_utils.generate_uuid(),
|
||||
name='audit_template_name')
|
||||
|
||||
def test_audit_template_create_same_uuid(self):
|
||||
uuid = w_utils.generate_uuid()
|
||||
audit_template1 = utils.create_test_audit_template(
|
||||
uuid=uuid,
|
||||
name='audit_template_name_1')
|
||||
self.assertEqual(audit_template1['uuid'], audit_template1.uuid)
|
||||
self.assertRaises(
|
||||
exception.AuditTemplateAlreadyExists,
|
||||
utils.create_test_audit_template,
|
||||
uuid=uuid,
|
||||
name='audit_template_name_2')
|
||||
143
watcher/tests/db/utils.py
Normal file
143
watcher/tests/db/utils.py
Normal file
@@ -0,0 +1,143 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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.
|
||||
"""Magnum test utilities."""
|
||||
|
||||
from watcher.db import api as db_api
|
||||
|
||||
|
||||
def get_test_audit_template(**kwargs):
|
||||
return {
|
||||
'id': kwargs.get('id', 1),
|
||||
'uuid': kwargs.get('uuid', 'e74c40e0-d825-11e2-a28f-0800200c9a66'),
|
||||
'goal': kwargs.get('goal', 'SERVERS_CONSOLIDATION'),
|
||||
'name': kwargs.get('name', 'My Audit Template'),
|
||||
'description': kwargs.get('description', 'Desc. Of My Audit Template'),
|
||||
'extra': kwargs.get('extra', {'automatic': False}),
|
||||
'host_aggregate': kwargs.get('host_aggregate', 1),
|
||||
'version': kwargs.get('version', 'v1'),
|
||||
'created_at': kwargs.get('created_at'),
|
||||
'updated_at': kwargs.get('updated_at'),
|
||||
'deleted_at': kwargs.get('deleted_at'),
|
||||
}
|
||||
|
||||
|
||||
def create_test_audit_template(**kwargs):
|
||||
"""Create test audit template entry in DB and return AuditTemplate DB object.
|
||||
|
||||
Function to be used to create test AuditTemplate objects in the database.
|
||||
:param kwargs: kwargsargs with overriding values for audit template's
|
||||
attributes.
|
||||
:returns: Test AuditTemplate DB object.
|
||||
"""
|
||||
audit_template = get_test_audit_template(**kwargs)
|
||||
# Let DB generate ID if it isn't specified explicitly
|
||||
if 'id' not in kwargs:
|
||||
del audit_template['id']
|
||||
dbapi = db_api.get_instance()
|
||||
return dbapi.create_audit_template(audit_template)
|
||||
|
||||
|
||||
def get_test_audit(**kwargs):
|
||||
return {
|
||||
'id': kwargs.get('id', 1),
|
||||
'uuid': kwargs.get('uuid', '10a47dd1-4874-4298-91cf-eff046dbdb8d'),
|
||||
'type': kwargs.get('type', 'ONESHOT'),
|
||||
'state': kwargs.get('state'),
|
||||
'deadline': kwargs.get('deadline'),
|
||||
'audit_template_id': kwargs.get('audit_template_id', 1),
|
||||
'created_at': kwargs.get('created_at'),
|
||||
'updated_at': kwargs.get('updated_at'),
|
||||
'deleted_at': kwargs.get('deleted_at'),
|
||||
}
|
||||
|
||||
|
||||
def create_test_audit(**kwargs):
|
||||
"""Create test audit entry in DB and return Audit DB object.
|
||||
|
||||
Function to be used to create test Audit objects in the database.
|
||||
:param kwargs: kwargsargs with overriding values for audit's attributes.
|
||||
:returns: Test Audit DB object.
|
||||
"""
|
||||
audit = get_test_audit(**kwargs)
|
||||
# Let DB generate ID if it isn't specified explicitly
|
||||
if 'id' not in kwargs:
|
||||
del audit['id']
|
||||
dbapi = db_api.get_instance()
|
||||
return dbapi.create_audit(audit)
|
||||
|
||||
|
||||
def get_test_action(**kwargs):
|
||||
return {
|
||||
'id': kwargs.get('id', 1),
|
||||
'uuid': kwargs.get('uuid', '10a47dd1-4874-4298-91cf-eff046dbdb8d'),
|
||||
'action_plan_id': kwargs.get('action_plan_id', 1),
|
||||
'action_type': kwargs.get('action_type', 'COLD_MIGRATION'),
|
||||
'applies_to': kwargs.get('applies_to',
|
||||
'10a47dd1-4874-4298-91cf-eff046dbdb8d'),
|
||||
'src': kwargs.get('src', 'rdev-indeedsrv002'),
|
||||
'dst': kwargs.get('dst', 'rdev-indeedsrv001'),
|
||||
'parameter': kwargs.get('parameter', ''),
|
||||
'description': kwargs.get('description', 'Desc. Of The Action'),
|
||||
'state': kwargs.get('state', 'PENDING'),
|
||||
'alarm': kwargs.get('alarm', None),
|
||||
'next': kwargs.get('next', 2),
|
||||
|
||||
'created_at': kwargs.get('created_at'),
|
||||
'updated_at': kwargs.get('updated_at'),
|
||||
'deleted_at': kwargs.get('deleted_at'),
|
||||
}
|
||||
|
||||
|
||||
def create_test_action(**kwargs):
|
||||
"""Create test action entry in DB and return Action DB object.
|
||||
|
||||
Function to be used to create test Action objects in the database.
|
||||
:param kwargs: kwargsargs with overriding values for action's attributes.
|
||||
:returns: Test Action DB object.
|
||||
"""
|
||||
action = get_test_action(**kwargs)
|
||||
# Let DB generate ID if it isn't specified explicitly
|
||||
if 'id' not in kwargs:
|
||||
del action['id']
|
||||
dbapi = db_api.get_instance()
|
||||
return dbapi.create_action(action)
|
||||
|
||||
|
||||
def get_test_action_plan(**kwargs):
|
||||
return {
|
||||
'id': kwargs.get('id', 1),
|
||||
'uuid': kwargs.get('uuid', '76be87bd-3422-43f9-93a0-e85a577e3061'),
|
||||
'state': kwargs.get('state', 'ONGOING'),
|
||||
'audit_id': kwargs.get('audit_id', 1),
|
||||
'first_action_id': kwargs.get('first_action_id', 1),
|
||||
'created_at': kwargs.get('created_at'),
|
||||
'updated_at': kwargs.get('updated_at'),
|
||||
'deleted_at': kwargs.get('deleted_at'),
|
||||
}
|
||||
|
||||
|
||||
def create_test_action_plan(**kwargs):
|
||||
"""Create test action plan entry in DB and return Action DB object.
|
||||
|
||||
Function to be used to create test Action objects in the database.
|
||||
:param kwargs: kwargsargs with overriding values for action's attributes.
|
||||
:returns: Test Action DB object.
|
||||
"""
|
||||
action = get_test_action_plan(**kwargs)
|
||||
# Let DB generate ID if it isn't specified explicitly
|
||||
if 'id' not in kwargs:
|
||||
del action['id']
|
||||
dbapi = db_api.get_instance()
|
||||
return dbapi.create_action_plan(action)
|
||||
1
watcher/tests/decision_engine/__init__.py
Normal file
1
watcher/tests/decision_engine/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
__author__ = 'Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>'
|
||||
0
watcher/tests/decision_engine/demo/__init__.py
Normal file
0
watcher/tests/decision_engine/demo/__init__.py
Normal file
103
watcher/tests/decision_engine/demo/plot_consolidation_basic.py
Normal file
103
watcher/tests/decision_engine/demo/plot_consolidation_basic.py
Normal file
@@ -0,0 +1,103 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
# FIXME(jed): remove this class due jenkins build failed
|
||||
# The following librairies are removed from requirement.txt :
|
||||
# - numpy
|
||||
# - matplotlib
|
||||
# These dependencies required a server x, jenkin's server has no
|
||||
# server x
|
||||
|
||||
# import matplotlib.pyplot as plt
|
||||
# import numpy as np
|
||||
|
||||
|
||||
from watcher.decision_engine.strategies.basic_consolidation import \
|
||||
BasicConsolidation
|
||||
from watcher.tests.decision_engine.faker_cluster_state import \
|
||||
FakerStateCollector
|
||||
from watcher.tests.decision_engine.faker_metrics_collector import \
|
||||
FakerMetricsCollector
|
||||
|
||||
|
||||
class PlotConsolidationBasic(object):
|
||||
def plot(self, sercon, orign_model, solution):
|
||||
pass
|
||||
|
||||
# cluster_size = len(orign_model._hypervisors)
|
||||
# labels = []
|
||||
# before_score = []
|
||||
# after_score = []
|
||||
# for hypevisor_id in orign_model.get_all_hypervisors():
|
||||
# labels.append(hypevisor_id)
|
||||
# hypevisor = orign_model.get_hypervisor_from_id(hypevisor_id)
|
||||
# result_before = sercon.calculate_score_node(hypevisor,
|
||||
# orign_model)
|
||||
# result_after = sercon.calculate_score_node(hypevisor,
|
||||
# solution.get_model())
|
||||
# before_score.append(float(result_before * 100))
|
||||
# if result_after == 0:
|
||||
# result_after = 0
|
||||
# after_score.append(float(result_after * 100))
|
||||
#
|
||||
# ind = np.arange(cluster_size) # the x locations for the groups
|
||||
# width = 0.35 # the width of the bars
|
||||
#
|
||||
# fig, ax = plt.subplots()
|
||||
#
|
||||
# rects1 = ax.bar(ind, before_score, width, color='b')
|
||||
#
|
||||
# rects2 = ax.bar(ind + width, after_score, width, color='r')
|
||||
#
|
||||
# # add some text for labels, title and axes ticks
|
||||
# ax.set_ylabel(
|
||||
# 'Score of each hypervisor that represent their \
|
||||
# utilization level')
|
||||
# ax.set_title('Watcher Basic Server consolidation (efficiency ' + str(
|
||||
# sercon.get_solution().get_efficiency()) + " %)")
|
||||
#
|
||||
# ax.set_xticks(ind + width)
|
||||
# ax.set_xticklabels(labels)
|
||||
# ax.set_ylim([0, 140])
|
||||
|
||||
# ax.legend((rects1[0], rects2[0]),
|
||||
# ('Before Consolidation', 'After Consolidation'))
|
||||
|
||||
# def autolabel(rects):
|
||||
# # attach some text labels
|
||||
# for rect in rects:
|
||||
# height = rect.get_height()
|
||||
# ax.text(rect.get_x() + rect.get_width() / 2., 1.05 * height,
|
||||
# '%d' % int(height),
|
||||
# ha='center', va='bottom')
|
||||
#
|
||||
# autolabel(rects1)
|
||||
# autolabel(rects2)
|
||||
|
||||
# plt.show()
|
||||
|
||||
|
||||
cluster = FakerStateCollector()
|
||||
metrics = FakerMetricsCollector()
|
||||
sercon = BasicConsolidation()
|
||||
sercon.set_metrics_resource_collector(metrics)
|
||||
# try overbooking ? :) 150 % cpu
|
||||
sercon.set_threshold_cores(1)
|
||||
model_cluster = cluster.generate_scenario_1()
|
||||
solution = sercon.execute(model_cluster)
|
||||
plot = PlotConsolidationBasic()
|
||||
plot.plot(sercon, cluster.generate_scenario_1(), solution)
|
||||
45
watcher/tests/decision_engine/demo/test_context_strategy.py
Normal file
45
watcher/tests/decision_engine/demo/test_context_strategy.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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_config import cfg
|
||||
import time
|
||||
from watcher.decision_engine.strategies.basic_consolidation import \
|
||||
BasicConsolidation
|
||||
|
||||
from watcher.openstack.common import log
|
||||
|
||||
from watcher.tests.decision_engine.faker_cluster_state import \
|
||||
FakerStateCollector
|
||||
from watcher.tests.decision_engine.faker_metrics_collector import \
|
||||
FakerMetricsCollector
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
cfg.CONF.debug = True
|
||||
log.setup('metering-controller')
|
||||
|
||||
metrics = FakerMetricsCollector()
|
||||
current_state_cluster = FakerStateCollector()
|
||||
|
||||
sercon = BasicConsolidation("basic", "Basic offline consolidation")
|
||||
sercon.set_metrics_resource_collector(metrics)
|
||||
|
||||
start_time = time.clock()
|
||||
solution = sercon.execute(current_state_cluster.generate_scenario_1())
|
||||
print(time.clock() - start_time, "seconds")
|
||||
print(solution)
|
||||
# planner = DefaultPlanner()
|
||||
# planner.schedule(solution)
|
||||
43
watcher/tests/decision_engine/demo/test_sercon.py
Normal file
43
watcher/tests/decision_engine/demo/test_sercon.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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_config import cfg
|
||||
import time
|
||||
from watcher.decision_engine.strategies.basic_consolidation import \
|
||||
BasicConsolidation
|
||||
|
||||
from watcher.openstack.common import log
|
||||
|
||||
from watcher.tests.decision_engine.faker_cluster_state import \
|
||||
FakerStateCollector
|
||||
from watcher.tests.decision_engine.faker_metrics_collector import \
|
||||
FakerMetricsCollector
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
# debug on
|
||||
cfg.CONF.debug = True
|
||||
log.setup('metering-controller')
|
||||
|
||||
metrics = FakerMetricsCollector()
|
||||
current_state_cluster = FakerStateCollector()
|
||||
|
||||
sercon = BasicConsolidation()
|
||||
sercon.set_metrics_resource_collector(metrics)
|
||||
|
||||
start_time = time.clock()
|
||||
solution = sercon.execute(current_state_cluster.generate_scenario_1())
|
||||
print("duration =" + str((time.clock() - start_time)), "seconds")
|
||||
LOG.debug(solution)
|
||||
255
watcher/tests/decision_engine/faker_cluster_state.py
Normal file
255
watcher/tests/decision_engine/faker_cluster_state.py
Normal file
@@ -0,0 +1,255 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 random
|
||||
|
||||
from watcher.decision_engine.api.collector.cluster_state_collector import \
|
||||
ClusterStateCollector
|
||||
from watcher.decision_engine.framework.model.hypervisor import Hypervisor
|
||||
from watcher.decision_engine.framework.model.model_root import ModelRoot
|
||||
from watcher.decision_engine.framework.model.resource import Resource
|
||||
from watcher.decision_engine.framework.model.resource import ResourceType
|
||||
from watcher.decision_engine.framework.model.vm import VM
|
||||
|
||||
|
||||
class FakerStateCollector(ClusterStateCollector):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def get_latest_state_cluster(self):
|
||||
return self.generate_scenario_1()
|
||||
|
||||
def generate_random(self, count_nodes, number_of_vm_per_node):
|
||||
vms = []
|
||||
|
||||
current_state_cluster = ModelRoot()
|
||||
# number of nodes
|
||||
count_node = count_nodes
|
||||
# number max of vm per hypervisor
|
||||
node_count_vm = number_of_vm_per_node
|
||||
# total number of virtual machine
|
||||
count_vm = (count_node * node_count_vm)
|
||||
|
||||
# define ressouce ( CPU, MEM disk, ... )
|
||||
mem = Resource(ResourceType.memory)
|
||||
# 2199.954 Mhz
|
||||
num_cores = Resource(ResourceType.cpu_cores)
|
||||
disk = Resource(ResourceType.disk)
|
||||
|
||||
current_state_cluster.create_resource(mem)
|
||||
current_state_cluster.create_resource(num_cores)
|
||||
current_state_cluster.create_resource(disk)
|
||||
|
||||
for i in range(0, count_node):
|
||||
node_uuid = "Node_" + str(i)
|
||||
hypervisor = Hypervisor()
|
||||
hypervisor.set_uuid(node_uuid)
|
||||
mem.set_capacity(hypervisor, 132)
|
||||
disk.set_capacity(hypervisor, 250)
|
||||
num_cores.set_capacity(hypervisor, 40)
|
||||
# print("create "+str(hypervisor))
|
||||
current_state_cluster.add_hypervisor(hypervisor)
|
||||
|
||||
for i in range(0, count_vm):
|
||||
vm_uuid = "VM_" + str(i)
|
||||
vm = VM()
|
||||
vm.set_uuid(vm_uuid)
|
||||
# print("create "+str(vm))
|
||||
mem.set_capacity(vm, 8)
|
||||
disk.set_capacity(vm, 10)
|
||||
num_cores.set_capacity(vm, 10)
|
||||
vms.append(vm)
|
||||
current_state_cluster.add_vm(vm)
|
||||
j = 0
|
||||
for node_id in current_state_cluster.get_all_hypervisors():
|
||||
for i in range(0, random.randint(0, node_count_vm)):
|
||||
# todo(jed) check if enough capacity
|
||||
current_state_cluster.get_mapping().map(
|
||||
current_state_cluster.get_hypervisor_from_id(node_id),
|
||||
vms[j])
|
||||
j += 1
|
||||
return current_state_cluster
|
||||
|
||||
def generate_scenario_1(self):
|
||||
vms = []
|
||||
|
||||
current_state_cluster = ModelRoot()
|
||||
# number of nodes
|
||||
count_node = 5
|
||||
# number max of vm per node
|
||||
node_count_vm = 7
|
||||
# total number of virtual machine
|
||||
count_vm = (count_node * node_count_vm)
|
||||
|
||||
# define ressouce ( CPU, MEM disk, ... )
|
||||
mem = Resource(ResourceType.memory)
|
||||
# 2199.954 Mhz
|
||||
num_cores = Resource(ResourceType.cpu_cores)
|
||||
disk = Resource(ResourceType.disk)
|
||||
|
||||
current_state_cluster.create_resource(mem)
|
||||
current_state_cluster.create_resource(num_cores)
|
||||
current_state_cluster.create_resource(disk)
|
||||
|
||||
for i in range(0, count_node):
|
||||
node_uuid = "Node_" + str(i)
|
||||
node = Hypervisor()
|
||||
node.set_uuid(node_uuid)
|
||||
|
||||
mem.set_capacity(node, 132)
|
||||
disk.set_capacity(node, 250)
|
||||
num_cores.set_capacity(node, 40)
|
||||
# print("create "+str(node))
|
||||
current_state_cluster.add_hypervisor(node)
|
||||
|
||||
for i in range(0, count_vm):
|
||||
vm_uuid = "VM_" + str(i)
|
||||
vm = VM()
|
||||
vm.set_uuid(vm_uuid)
|
||||
# print("create "+str(vm))
|
||||
mem.set_capacity(vm, 2)
|
||||
disk.set_capacity(vm, 20)
|
||||
num_cores.set_capacity(vm, 10)
|
||||
vms.append(vm)
|
||||
current_state_cluster.add_vm(vm)
|
||||
|
||||
current_state_cluster.get_mapping().map(
|
||||
current_state_cluster.get_hypervisor_from_id("Node_0"),
|
||||
current_state_cluster.get_vm_from_id("VM_0"))
|
||||
|
||||
current_state_cluster.get_mapping().map(
|
||||
current_state_cluster.get_hypervisor_from_id("Node_0"),
|
||||
current_state_cluster.get_vm_from_id("VM_1"))
|
||||
|
||||
current_state_cluster.get_mapping().map(
|
||||
current_state_cluster.get_hypervisor_from_id("Node_1"),
|
||||
current_state_cluster.get_vm_from_id("VM_2"))
|
||||
|
||||
current_state_cluster.get_mapping().map(
|
||||
current_state_cluster.get_hypervisor_from_id("Node_2"),
|
||||
current_state_cluster.get_vm_from_id("VM_3"))
|
||||
|
||||
current_state_cluster.get_mapping().map(
|
||||
current_state_cluster.get_hypervisor_from_id("Node_2"),
|
||||
current_state_cluster.get_vm_from_id("VM_4"))
|
||||
|
||||
current_state_cluster.get_mapping().map(
|
||||
current_state_cluster.get_hypervisor_from_id("Node_2"),
|
||||
current_state_cluster.get_vm_from_id("VM_5"))
|
||||
|
||||
current_state_cluster.get_mapping().map(
|
||||
current_state_cluster.get_hypervisor_from_id("Node_3"),
|
||||
current_state_cluster.get_vm_from_id("VM_6"))
|
||||
|
||||
current_state_cluster.get_mapping().map(
|
||||
current_state_cluster.get_hypervisor_from_id("Node_4"),
|
||||
current_state_cluster.get_vm_from_id("VM_7"))
|
||||
|
||||
return current_state_cluster
|
||||
|
||||
def generate_scenario_2(self):
|
||||
current_state_cluster = ModelRoot()
|
||||
# number of nodes
|
||||
count_node = 5
|
||||
|
||||
# define ressouce ( CPU, MEM disk, ... )
|
||||
mem = Resource(ResourceType.memory)
|
||||
# 2199.954 Mhz
|
||||
num_cores = Resource(ResourceType.cpu_cores)
|
||||
disk = Resource(ResourceType.disk)
|
||||
|
||||
current_state_cluster.create_resource(mem)
|
||||
current_state_cluster.create_resource(num_cores)
|
||||
current_state_cluster.create_resource(disk)
|
||||
|
||||
for i in range(0, count_node):
|
||||
node_uuid = "Node_" + str(i)
|
||||
node = Hypervisor()
|
||||
node.set_uuid(node_uuid)
|
||||
mem.set_capacity(node, 132)
|
||||
disk.set_capacity(node, 250)
|
||||
num_cores.set_capacity(node, 40)
|
||||
# print("create "+str(node))
|
||||
current_state_cluster.add_hypervisor(node)
|
||||
return current_state_cluster
|
||||
|
||||
def map(self, model, h_id, vm_id):
|
||||
model.get_mapping().map(
|
||||
model.get_hypervisor_from_id(h_id),
|
||||
model.get_vm_from_id(vm_id))
|
||||
|
||||
def generate_scenario_3(self):
|
||||
vms = []
|
||||
|
||||
current_state_cluster = ModelRoot()
|
||||
# number of nodes
|
||||
count_node = 10
|
||||
# number max of vm per node
|
||||
node_count_vm = 7
|
||||
# total number of virtual machine
|
||||
count_vm = (count_node * node_count_vm)
|
||||
|
||||
# define ressouce ( CPU, MEM disk, ... )
|
||||
mem = Resource(ResourceType.memory)
|
||||
# 2199.954 Mhz
|
||||
num_cores = Resource(ResourceType.cpu_cores)
|
||||
disk = Resource(ResourceType.disk)
|
||||
|
||||
current_state_cluster.create_resource(mem)
|
||||
current_state_cluster.create_resource(num_cores)
|
||||
current_state_cluster.create_resource(disk)
|
||||
|
||||
for i in range(0, count_node):
|
||||
node_uuid = "Node_" + str(i)
|
||||
node = Hypervisor()
|
||||
node.set_uuid(node_uuid)
|
||||
mem.set_capacity(node, 132)
|
||||
disk.set_capacity(node, 250)
|
||||
num_cores.set_capacity(node, 40)
|
||||
# print("create "+str(node))
|
||||
current_state_cluster.add_hypervisor(node)
|
||||
|
||||
for i in range(0, count_vm):
|
||||
vm_uuid = "VM_" + str(i)
|
||||
vm = VM()
|
||||
vm.set_uuid(vm_uuid)
|
||||
# print("create "+str(vm))
|
||||
mem.set_capacity(vm, 10)
|
||||
disk.set_capacity(vm, 25)
|
||||
num_cores.set_capacity(vm, 16)
|
||||
vms.append(vm)
|
||||
current_state_cluster.add_vm(vm)
|
||||
print(count_vm)
|
||||
indice = 0
|
||||
for j in range(0, 2):
|
||||
node_uuid = "Node_" + str(j)
|
||||
for i in range(indice, 3):
|
||||
vm_uuid = "VM_" + str(i)
|
||||
self.map(current_state_cluster, node_uuid, vm_uuid)
|
||||
|
||||
for j in range(2, 5):
|
||||
node_uuid = "Node_" + str(j)
|
||||
for i in range(indice, 4):
|
||||
vm_uuid = "VM_" + str(i)
|
||||
self.map(current_state_cluster, node_uuid, vm_uuid)
|
||||
|
||||
for j in range(5, 10):
|
||||
node_uuid = "Node_" + str(j)
|
||||
for i in range(indice, 4):
|
||||
vm_uuid = "VM_" + str(i)
|
||||
self.map(current_state_cluster, node_uuid, vm_uuid)
|
||||
|
||||
return current_state_cluster
|
||||
113
watcher/tests/decision_engine/faker_metrics_collector.py
Normal file
113
watcher/tests/decision_engine/faker_metrics_collector.py
Normal file
@@ -0,0 +1,113 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 random
|
||||
from watcher.decision_engine.api.collector.metrics_resource_collector import \
|
||||
MetricsResourceCollector
|
||||
|
||||
|
||||
class FakerMetricsCollector(MetricsResourceCollector):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def get_average_usage_vm_cpu(self, uuid):
|
||||
"""The last VM CPU usage values to average
|
||||
|
||||
:param uuid:00
|
||||
:return:
|
||||
"""
|
||||
# query influxdb stream
|
||||
|
||||
# compute in stream
|
||||
|
||||
# Normalize
|
||||
mock = {}
|
||||
# node 0
|
||||
mock['VM_0'] = 7
|
||||
mock['VM_1'] = 7
|
||||
# node 1
|
||||
mock['VM_2'] = 10
|
||||
# node 2
|
||||
mock['VM_3'] = 5
|
||||
mock['VM_4'] = 5
|
||||
mock['VM_5'] = 10
|
||||
|
||||
# node 3
|
||||
mock['VM_6'] = 8
|
||||
|
||||
# node 4
|
||||
mock['VM_7'] = 4
|
||||
if uuid not in mock.keys():
|
||||
# mock[uuid] = random.randint(1, 4)
|
||||
mock[uuid] = 8
|
||||
|
||||
return mock[str(uuid)]
|
||||
|
||||
def get_average_usage_vm_memory(self, uuid):
|
||||
mock = {}
|
||||
# node 0
|
||||
mock['VM_0'] = 2
|
||||
mock['VM_1'] = 5
|
||||
# node 1
|
||||
mock['VM_2'] = 5
|
||||
# node 2
|
||||
mock['VM_3'] = 8
|
||||
mock['VM_4'] = 5
|
||||
mock['VM_5'] = 16
|
||||
|
||||
# node 3
|
||||
mock['VM_6'] = 8
|
||||
|
||||
# node 4
|
||||
mock['VM_7'] = 4
|
||||
if uuid not in mock.keys():
|
||||
# mock[uuid] = random.randint(1, 4)
|
||||
mock[uuid] = 10
|
||||
|
||||
return mock[str(uuid)]
|
||||
|
||||
def get_average_usage_vm_disk(self, uuid):
|
||||
mock = {}
|
||||
# node 0
|
||||
mock['VM_0'] = 2
|
||||
mock['VM_1'] = 2
|
||||
# node 1
|
||||
mock['VM_2'] = 2
|
||||
# node 2
|
||||
mock['VM_3'] = 10
|
||||
mock['VM_4'] = 15
|
||||
mock['VM_5'] = 20
|
||||
|
||||
# node 3
|
||||
mock['VM_6'] = 8
|
||||
|
||||
# node 4
|
||||
mock['VM_7'] = 4
|
||||
|
||||
if uuid not in mock.keys():
|
||||
# mock[uuid] = random.randint(1, 4)
|
||||
mock[uuid] = 4
|
||||
|
||||
return mock[str(uuid)]
|
||||
|
||||
def get_virtual_machine_capacity(self, vm_uuid):
|
||||
return random.randint(1, 4)
|
||||
|
||||
def get_average_network_incomming(self, node):
|
||||
pass
|
||||
|
||||
def get_average_network_outcomming(self, node):
|
||||
pass
|
||||
0
watcher/tests/decision_engine/framework/__init__.py
Normal file
0
watcher/tests/decision_engine/framework/__init__.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 exceptions
|
||||
|
||||
from watcher.decision_engine.framework.events.event_consumer_factory import \
|
||||
EventConsumerFactory
|
||||
from watcher.decision_engine.framework.messaging.events import Events
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestEventConsumerFactory(base.TestCase):
|
||||
|
||||
event_consumer_factory = EventConsumerFactory()
|
||||
|
||||
def test_factory_with_unknown_type(self):
|
||||
self.assertRaises(exceptions.AssertionError,
|
||||
self.event_consumer_factory.factory,
|
||||
Events.ALL)
|
||||
@@ -0,0 +1,75 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock import call
|
||||
from mock import MagicMock
|
||||
from watcher.decision_engine.framework.command.trigger_audit_command import \
|
||||
TriggerAuditCommand
|
||||
from watcher.decision_engine.framework.messaging.events import Events
|
||||
from watcher.objects.audit import Audit
|
||||
from watcher.objects.audit import AuditStatus
|
||||
from watcher.tests.db.base import DbTestCase
|
||||
from watcher.tests.decision_engine.faker_cluster_state import \
|
||||
FakerStateCollector
|
||||
from watcher.tests.decision_engine.faker_metrics_collector import \
|
||||
FakerMetricsCollector
|
||||
from watcher.tests.objects import utils as obj_utils
|
||||
|
||||
|
||||
class TestTriggerAuditCommand(DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestTriggerAuditCommand, self).setUp()
|
||||
self.audit_template = obj_utils.create_test_audit_template(
|
||||
self.context)
|
||||
self.audit = obj_utils.create_test_audit(
|
||||
self.context,
|
||||
audit_template_id=self.audit_template.id)
|
||||
|
||||
def test_trigger_audit_wihout_errors(self):
|
||||
try:
|
||||
statedb = FakerStateCollector()
|
||||
ressourcedb = FakerMetricsCollector()
|
||||
command = TriggerAuditCommand(MagicMock(), statedb, ressourcedb)
|
||||
command.execute(self.audit.uuid, self.context)
|
||||
except Exception:
|
||||
self.fail("The audit should be trigged wihtour error")
|
||||
|
||||
def test_trigger_audit_state_succes(self):
|
||||
statedb = FakerStateCollector()
|
||||
ressourcedb = FakerMetricsCollector()
|
||||
command = TriggerAuditCommand(MagicMock(), statedb, ressourcedb)
|
||||
command.execute(self.audit.uuid, self.context)
|
||||
audit = Audit.get_by_uuid(self.context, self.audit.uuid)
|
||||
self.assertEqual(AuditStatus.SUCCESS, audit.state)
|
||||
|
||||
def test_trigger_audit_send_notification(self):
|
||||
messaging = MagicMock()
|
||||
statedb = FakerStateCollector()
|
||||
ressourcedb = FakerMetricsCollector()
|
||||
command = TriggerAuditCommand(messaging, statedb, ressourcedb)
|
||||
command.execute(self.audit.uuid, self.context)
|
||||
|
||||
call_on_going = call(Events.TRIGGER_AUDIT.name, {
|
||||
'audit_status': AuditStatus.ONGOING,
|
||||
'audit_uuid': self.audit.uuid})
|
||||
call_success = call(Events.TRIGGER_AUDIT.name, {
|
||||
'audit_status': AuditStatus.SUCCESS,
|
||||
'audit_uuid': self.audit.uuid})
|
||||
|
||||
calls = [call_on_going, call_success]
|
||||
messaging.topic_status.publish_event.assert_has_calls(calls)
|
||||
self.assertEqual(2, messaging.topic_status.publish_event.call_count)
|
||||
@@ -0,0 +1,42 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock
|
||||
from mock import MagicMock
|
||||
from watcher.common import utils
|
||||
from watcher.decision_engine.framework.command.trigger_audit_command import \
|
||||
TriggerAuditCommand
|
||||
from watcher.decision_engine.framework.messaging.audit_endpoint import \
|
||||
AuditEndpoint
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestAuditEndpoint(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAuditEndpoint, self).setUp()
|
||||
self.endpoint = AuditEndpoint(MagicMock())
|
||||
|
||||
def test_trigger_audit(self):
|
||||
audit_uuid = utils.generate_uuid()
|
||||
# todo() add
|
||||
|
||||
with mock.patch.object(TriggerAuditCommand, 'execute') as mock_call:
|
||||
expected_uuid = self.endpoint.trigger_audit(
|
||||
self.context, audit_uuid)
|
||||
self.assertEqual(audit_uuid, expected_uuid)
|
||||
mock_call.assert_called_once_with(audit_uuid, self.context)
|
||||
"""
|
||||
@@ -0,0 +1,36 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 watcher.decision_engine.framework.strategy.strategy_loader import \
|
||||
StrategyLoader
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestStrategySelector(base.BaseTestCase):
|
||||
|
||||
strategy_loader = StrategyLoader()
|
||||
|
||||
def test_load_strategy_with_empty_model(self):
|
||||
selected_strategy = self.strategy_loader.load(None)
|
||||
self.assertIsNotNone(selected_strategy,
|
||||
'The default strategy be must not none')
|
||||
|
||||
def test_load_strategy_is_basic(self):
|
||||
exptected_strategy = 'basic'
|
||||
selected_strategy = self.strategy_loader.load(exptected_strategy)
|
||||
self.assertEqual(
|
||||
selected_strategy.get_name(),
|
||||
exptected_strategy,
|
||||
'The default strategy should be basic')
|
||||
@@ -0,0 +1,47 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock
|
||||
from oslo_config import cfg
|
||||
from watcher.decision_engine.framework.strategy.strategy_loader import \
|
||||
StrategyLoader
|
||||
from watcher.decision_engine.framework.strategy.strategy_selector import \
|
||||
StrategySelector
|
||||
from watcher.objects.audit_template import Goal
|
||||
from watcher.tests import base
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestStrategySelector(base.BaseTestCase):
|
||||
|
||||
strategy_selector = StrategySelector()
|
||||
|
||||
def test_define_from_with_empty(self):
|
||||
expected_goal = None
|
||||
expected_strategy = \
|
||||
CONF.watcher_goals.goals[Goal.SERVERS_CONSOLIDATION]
|
||||
with mock.patch.object(StrategyLoader, 'load') as \
|
||||
mock_call:
|
||||
self.strategy_selector.define_from_goal(expected_goal)
|
||||
mock_call.assert_called_once_with(expected_strategy)
|
||||
|
||||
def test_define_from_goal(self):
|
||||
expected_goal = Goal.BALANCE_LOAD
|
||||
expected_strategy = CONF.watcher_goals.goals[expected_goal]
|
||||
with mock.patch.object(StrategyLoader, 'load') as \
|
||||
mock_call:
|
||||
self.strategy_selector.define_from_goal(expected_goal)
|
||||
mock_call.assert_called_once_with(expected_strategy)
|
||||
@@ -0,0 +1,75 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock
|
||||
from watcher.common import utils
|
||||
from watcher.db import api as db_api
|
||||
from watcher.decision_engine.framework.default_planner import DefaultPlanner
|
||||
from watcher.decision_engine.strategies.basic_consolidation import \
|
||||
BasicConsolidation
|
||||
from watcher.tests.db import base
|
||||
from watcher.tests.db import utils as db_utils
|
||||
from watcher.tests.decision_engine.faker_cluster_state import \
|
||||
FakerStateCollector
|
||||
from watcher.tests.decision_engine.faker_metrics_collector import \
|
||||
FakerMetricsCollector
|
||||
from watcher.tests.objects import utils as obj_utils
|
||||
|
||||
|
||||
class SolutionFaker(object):
|
||||
@staticmethod
|
||||
def build():
|
||||
metrics = FakerMetricsCollector()
|
||||
current_state_cluster = FakerStateCollector()
|
||||
sercon = BasicConsolidation("basic", "Basic offline consolidation")
|
||||
sercon.set_metrics_resource_collector(metrics)
|
||||
return sercon.execute(current_state_cluster.generate_scenario_1())
|
||||
|
||||
|
||||
class TestDefaultPlanner(base.DbTestCase):
|
||||
default_planner = DefaultPlanner()
|
||||
|
||||
def setUp(self):
|
||||
super(TestDefaultPlanner, self).setUp()
|
||||
obj_utils.create_test_audit_template(self.context)
|
||||
|
||||
p = mock.patch.object(db_api.Connection, 'create_action_plan')
|
||||
self.mock_create_action_plan = p.start()
|
||||
self.mock_create_action_plan.side_effect = (
|
||||
self._simulate_action_plan_create)
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
q = mock.patch.object(db_api.Connection, 'create_action')
|
||||
self.mock_create_action = q.start()
|
||||
self.mock_create_action.side_effect = (
|
||||
self._simulate_action_create)
|
||||
self.addCleanup(q.stop)
|
||||
|
||||
def _simulate_action_plan_create(self, action_plan):
|
||||
action_plan.create()
|
||||
return action_plan
|
||||
|
||||
def _simulate_action_create(self, action):
|
||||
action.create()
|
||||
return action
|
||||
|
||||
def test_scheduler_w(self):
|
||||
audit = db_utils.create_test_audit(uuid=utils.generate_uuid())
|
||||
fake_solution = SolutionFaker.build()
|
||||
action_plan = self.default_planner.schedule(self.context,
|
||||
audit.id, fake_solution)
|
||||
|
||||
self.assertIsNotNone(action_plan.uuid)
|
||||
45
watcher/tests/decision_engine/framework/test_manager.py
Normal file
45
watcher/tests/decision_engine/framework/test_manager.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock
|
||||
|
||||
from watcher.common import utils
|
||||
|
||||
from watcher.decision_engine.framework.events.event_consumer_factory import \
|
||||
EventConsumerFactory
|
||||
|
||||
from watcher.common.messaging.events.event import Event
|
||||
from watcher.decision_engine.framework.manager_decision_engine import \
|
||||
DecisionEngineManager
|
||||
|
||||
from watcher.decision_engine.framework.messaging.events import Events
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestDecisionEngineManager(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestDecisionEngineManager, self).setUp()
|
||||
self.manager = DecisionEngineManager()
|
||||
|
||||
def test_event_receive(self):
|
||||
# todo(jed) remove useless
|
||||
with mock.patch.object(EventConsumerFactory, 'factory') as mock_call:
|
||||
data = {"key1": "value"}
|
||||
request_id = utils.generate_uuid()
|
||||
event_type = Events.TRIGGER_AUDIT
|
||||
event = Event(event_type, data, request_id)
|
||||
self.manager.event_receive(event)
|
||||
mock_call.assert_called_once_with(event_type)
|
||||
57
watcher/tests/decision_engine/framework/test_rpcapi.py
Normal file
57
watcher/tests/decision_engine/framework/test_rpcapi.py
Normal file
@@ -0,0 +1,57 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 mock
|
||||
import oslo.messaging as om
|
||||
from watcher.common import exception
|
||||
from watcher.common import utils
|
||||
from watcher.decision_engine.framework.rpcapi import DecisionEngineAPI
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestDecisionEngineAPI(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDecisionEngineAPI, self).setUp()
|
||||
|
||||
api = DecisionEngineAPI()
|
||||
|
||||
def test_get_version(self):
|
||||
expected_version = self.api.API_VERSION
|
||||
self.assertEqual(expected_version, self.api.get_version())
|
||||
|
||||
def test_get_api_version(self):
|
||||
with mock.patch.object(om.RPCClient, 'call') as mock_call:
|
||||
expected_context = self.context
|
||||
self.api.check_api_version(expected_context)
|
||||
mock_call.assert_called_once_with(
|
||||
expected_context.to_dict(),
|
||||
'check_api_version',
|
||||
api_version=DecisionEngineAPI().API_VERSION)
|
||||
|
||||
def test_execute_audit_throw_exception(self):
|
||||
audit_uuid = "uuid"
|
||||
self.assertRaises(exception.InvalidUuidOrName,
|
||||
self.api.trigger_audit,
|
||||
audit_uuid)
|
||||
|
||||
def test_execute_audit_without_error(self):
|
||||
with mock.patch.object(om.RPCClient, 'call') as mock_call:
|
||||
audit_uuid = utils.generate_uuid()
|
||||
self.api.trigger_audit(self.context, audit_uuid)
|
||||
mock_call.assert_called_once_with(self.context.to_dict(),
|
||||
'trigger_audit',
|
||||
audit_uuid=audit_uuid)
|
||||
184
watcher/tests/decision_engine/test_basic_consolidation.py
Normal file
184
watcher/tests/decision_engine/test_basic_consolidation.py
Normal file
@@ -0,0 +1,184 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 watcher.common import exception
|
||||
|
||||
from watcher.decision_engine.framework.meta_actions.hypervisor_state import \
|
||||
ChangeHypervisorState
|
||||
from watcher.decision_engine.framework.meta_actions.power_state import \
|
||||
ChangePowerState
|
||||
|
||||
from watcher.decision_engine.framework.meta_actions.migrate import Migrate
|
||||
from watcher.decision_engine.framework.model.model_root import ModelRoot
|
||||
from watcher.decision_engine.strategies.basic_consolidation import \
|
||||
BasicConsolidation
|
||||
|
||||
from watcher.tests import base
|
||||
from watcher.tests.decision_engine.faker_cluster_state import \
|
||||
FakerStateCollector
|
||||
from watcher.tests.decision_engine.faker_metrics_collector import \
|
||||
FakerMetricsCollector
|
||||
|
||||
|
||||
class TestBasicConsolidation(base.BaseTestCase):
|
||||
# fake metrics
|
||||
fake_metrics = FakerMetricsCollector()
|
||||
|
||||
# fake cluster
|
||||
fake_cluster = FakerStateCollector()
|
||||
|
||||
def test_cluster_size(self):
|
||||
size_cluster = len(
|
||||
self.fake_cluster.generate_scenario_1().get_all_hypervisors())
|
||||
size_cluster_assert = 5
|
||||
self.assertEqual(size_cluster, size_cluster_assert)
|
||||
|
||||
def test_basic_consolidation_score_hypervisor(self):
|
||||
cluster = self.fake_cluster.generate_scenario_1()
|
||||
sercon = BasicConsolidation()
|
||||
sercon.set_metrics_resource_collector(self.fake_metrics)
|
||||
node_1_score = 0.09862626262626262
|
||||
self.assertEqual(
|
||||
sercon.calculate_score_node(
|
||||
cluster.get_hypervisor_from_id("Node_1"),
|
||||
cluster), node_1_score)
|
||||
node_2_score = 0.29989898989898994
|
||||
self.assertEqual(
|
||||
sercon.calculate_score_node(
|
||||
cluster.get_hypervisor_from_id("Node_2"),
|
||||
cluster), node_2_score)
|
||||
node_0_score = 0.13967676767676765
|
||||
self.assertEqual(
|
||||
sercon.calculate_score_node(
|
||||
cluster.get_hypervisor_from_id("Node_0"),
|
||||
cluster), node_0_score)
|
||||
|
||||
def test_basic_consolidation_score_vm(self):
|
||||
cluster = self.fake_cluster.generate_scenario_1()
|
||||
sercon = BasicConsolidation()
|
||||
sercon.set_metrics_resource_collector(self.fake_metrics)
|
||||
vm_0 = cluster.get_vm_from_id("VM_0")
|
||||
vm_0_score = 0.6
|
||||
self.assertEqual(sercon.calculate_score_vm(vm_0, cluster), vm_0_score)
|
||||
|
||||
vm_1 = cluster.get_vm_from_id("VM_1")
|
||||
vm_1_score = 1.0999999999999999
|
||||
self.assertEqual(sercon.calculate_score_vm(vm_1, cluster),
|
||||
vm_1_score)
|
||||
vm_2 = cluster.get_vm_from_id("VM_2")
|
||||
vm_2_score = 1.2
|
||||
self.assertEqual(sercon.calculate_score_vm(vm_2, cluster), vm_2_score)
|
||||
|
||||
def test_basic_consolidation_weight(self):
|
||||
cluster = self.fake_cluster.generate_scenario_1()
|
||||
sercon = BasicConsolidation()
|
||||
sercon.set_metrics_resource_collector(self.fake_metrics)
|
||||
vm_0 = cluster.get_vm_from_id("VM_0")
|
||||
cores = 16
|
||||
# 80 Go
|
||||
disk = 80
|
||||
# mem 8 Go
|
||||
mem = 8
|
||||
vm_0_weight_assert = 3.1999999999999997
|
||||
self.assertEqual(sercon.calculate_weight(cluster, vm_0, cores, disk,
|
||||
mem),
|
||||
vm_0_weight_assert)
|
||||
|
||||
def test_basic_consolidation_efficiency(self):
|
||||
sercon = BasicConsolidation()
|
||||
sercon.set_metrics_resource_collector(self.fake_metrics)
|
||||
efficient_assert = 100
|
||||
solution = sercon.execute(self.fake_cluster.generate_scenario_1())
|
||||
self.assertEqual(solution.get_efficiency(), efficient_assert)
|
||||
|
||||
def test_exception_model(self):
|
||||
sercon = BasicConsolidation()
|
||||
self.assertRaises(exception.ClusteStateNotDefined, sercon.execute,
|
||||
None)
|
||||
|
||||
def test_exception_cluster_empty(self):
|
||||
sercon = BasicConsolidation()
|
||||
model = ModelRoot()
|
||||
self.assertRaises(exception.ClusterEmpty, sercon.execute,
|
||||
model)
|
||||
|
||||
def test_exception_metric_collector(self):
|
||||
sercon = BasicConsolidation()
|
||||
self.assertRaises(exception.MetricCollectorNotDefined,
|
||||
sercon.calculate_score_vm, "VM_1", None)
|
||||
|
||||
def check_migration(self, array, indice, vm, src, dest):
|
||||
"""Helper to check migration
|
||||
|
||||
:param array:
|
||||
:param indice:
|
||||
:param vm:
|
||||
:param src:
|
||||
:param dest:
|
||||
:return:
|
||||
"""
|
||||
self.assertEqual(array[indice].get_vm().get_uuid(), vm)
|
||||
self.assertEqual(array[indice].get_source_hypervisor().get_uuid(), src)
|
||||
self.assertEqual(array[indice].get_dest_hypervisor().get_uuid(), dest)
|
||||
|
||||
def test_basic_consolidation_migration(self):
|
||||
sercon = BasicConsolidation()
|
||||
sercon.set_metrics_resource_collector(self.fake_metrics)
|
||||
|
||||
solution = sercon.execute(self.fake_cluster.generate_scenario_1())
|
||||
|
||||
count_migration = 0
|
||||
change_hypervisor_state = 0
|
||||
change_power_state = 0
|
||||
migrate = []
|
||||
for action in solution.meta_actions:
|
||||
if isinstance(action, Migrate):
|
||||
count_migration += 1
|
||||
migrate.append(action)
|
||||
if isinstance(action, ChangeHypervisorState):
|
||||
change_hypervisor_state += 1
|
||||
if isinstance(action, ChangePowerState):
|
||||
change_power_state += 1
|
||||
|
||||
self.assertEqual(change_hypervisor_state, 3)
|
||||
self.assertEqual(count_migration, 3)
|
||||
# check migration
|
||||
self.check_migration(migrate, 0, "VM_7", "Node_4", "Node_2")
|
||||
self.check_migration(migrate, 1, "VM_6", "Node_3", "Node_0")
|
||||
self.check_migration(migrate, 2, "VM_2", "Node_1", "Node_0")
|
||||
|
||||
def test_basic_consolidation_random(self):
|
||||
metrics = FakerMetricsCollector()
|
||||
current_state_cluster = FakerStateCollector()
|
||||
|
||||
sercon = BasicConsolidation("sercon", "Basic offline consolidation")
|
||||
sercon.set_metrics_resource_collector(metrics)
|
||||
|
||||
solution = sercon.execute(
|
||||
current_state_cluster.generate_random(25, 2))
|
||||
|
||||
count_migration = 0
|
||||
change_hypervisor_state = 0
|
||||
change_power_state = 0
|
||||
migrate = []
|
||||
for action in solution.meta_actions:
|
||||
if isinstance(action, Migrate):
|
||||
count_migration += 1
|
||||
migrate.append(action)
|
||||
if isinstance(action, ChangeHypervisorState):
|
||||
change_hypervisor_state += 1
|
||||
if isinstance(action, ChangePowerState):
|
||||
change_power_state += 1
|
||||
22
watcher/tests/decision_engine/test_loader.py
Normal file
22
watcher/tests/decision_engine/test_loader.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 watcher.tests import base
|
||||
|
||||
|
||||
class TestLoader(base.BaseTestCase):
|
||||
|
||||
def test_loader(self):
|
||||
pass
|
||||
54
watcher/tests/decision_engine/test_model.py
Normal file
54
watcher/tests/decision_engine/test_model.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 uuid
|
||||
from watcher.common import exception
|
||||
|
||||
from watcher.decision_engine.framework.model.hypervisor import Hypervisor
|
||||
from watcher.decision_engine.framework.model.model_root import ModelRoot
|
||||
|
||||
from watcher.tests.decision_engine.faker_cluster_state import \
|
||||
FakerStateCollector
|
||||
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestModel(base.BaseTestCase):
|
||||
def test_model(self):
|
||||
fake_cluster = FakerStateCollector()
|
||||
model = fake_cluster.generate_scenario_1()
|
||||
|
||||
self.assertEqual(len(model._hypervisors), 5)
|
||||
self.assertEqual(len(model._vms), 35)
|
||||
self.assertEqual(len(model.get_mapping().get_mapping()), 5)
|
||||
|
||||
def test_add_hypervisor(self):
|
||||
model = ModelRoot()
|
||||
id = str(uuid.uuid4())
|
||||
hypervisor = Hypervisor()
|
||||
hypervisor.set_uuid(id)
|
||||
model.add_hypervisor(hypervisor)
|
||||
self.assertEqual(model.get_hypervisor_from_id(id), hypervisor)
|
||||
|
||||
def test_delete_hypervisor(self):
|
||||
model = ModelRoot()
|
||||
id = str(uuid.uuid4())
|
||||
hypervisor = Hypervisor()
|
||||
hypervisor.set_uuid(id)
|
||||
model.add_hypervisor(hypervisor)
|
||||
self.assertEqual(model.get_hypervisor_from_id(id), hypervisor)
|
||||
model.remove_hypervisor(hypervisor)
|
||||
self.assertRaises(exception.HypervisorNotFound,
|
||||
model.get_hypervisor_from_id, id)
|
||||
21
watcher/tests/decision_engine/test_planner.py
Normal file
21
watcher/tests/decision_engine/test_planner.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 watcher.tests import base
|
||||
|
||||
|
||||
class TestPlanner(base.BaseTestCase):
|
||||
def test_planner(self):
|
||||
pass
|
||||
151
watcher/tests/demo_vancouver.py
Normal file
151
watcher/tests/demo_vancouver.py
Normal file
@@ -0,0 +1,151 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright (c) 2015 b<>com
|
||||
#
|
||||
# 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 concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from keystoneclient import session
|
||||
|
||||
from keystoneclient.auth.identity import v3
|
||||
|
||||
import cinderclient.v2.client as ciclient
|
||||
import glanceclient.v2.client as glclient
|
||||
import keystoneclient.v3.client as ksclient
|
||||
import neutronclient.neutron.client as netclient
|
||||
import novaclient.v2.client as nvclient
|
||||
|
||||
from watcher.common.utils import CONF
|
||||
from oslo_config import cfg
|
||||
from watcher.applier.framework.command.migrate_command import MigrateCommand
|
||||
from watcher.applier.framework.command.wrapper.nova_wrapper import NovaWrapper
|
||||
from watcher.decision_engine.framework.default_planner import Primitives
|
||||
from watcher.openstack.common import log
|
||||
import ceilometerclient.v2 as c_client
|
||||
|
||||
cfg.CONF.debug = True
|
||||
log.setup('metering-controller')
|
||||
|
||||
cfg.CONF.import_opt('auth_uri', 'keystoneclient.middleware.auth_token',
|
||||
group='keystone_authtoken')
|
||||
cfg.CONF.import_opt('admin_user', 'keystoneclient.middleware.auth_token',
|
||||
group='keystone_authtoken')
|
||||
cfg.CONF.import_opt('admin_password', 'keystoneclient.middleware.auth_token',
|
||||
group='keystone_authtoken')
|
||||
cfg.CONF.import_opt('admin_tenant_name',
|
||||
'keystoneclient.middleware.auth_token',
|
||||
group='keystone_authtoken')
|
||||
|
||||
cfg.CONF.keystone_authtoken.auth_uri = "http://10.50.0.105:5000/v3/"
|
||||
cfg.CONF.keystone_authtoken.admin_user = "watcher"
|
||||
cfg.CONF.keystone_authtoken.admin_password = "watcher"
|
||||
cfg.CONF.keystone_authtoken.admin_tenant_name = "services"
|
||||
|
||||
|
||||
def make_query(user_id=None, tenant_id=None, resource_id=None,
|
||||
user_ids=None, tenant_ids=None, resource_ids=None):
|
||||
user_ids = user_ids or []
|
||||
tenant_ids = tenant_ids or []
|
||||
resource_ids = resource_ids or []
|
||||
query = []
|
||||
if user_id:
|
||||
user_ids = [user_id]
|
||||
for u_id in user_ids:
|
||||
query.append({"field": "user_id", "op": "eq", "value": u_id})
|
||||
if tenant_id:
|
||||
tenant_ids = [tenant_id]
|
||||
for t_id in tenant_ids:
|
||||
query.append({"field": "project_id", "op": "eq", "value": t_id})
|
||||
if resource_id:
|
||||
resource_ids = [resource_id]
|
||||
for r_id in resource_ids:
|
||||
query.append({"field": "resource_id", "op": "eq", "value": r_id})
|
||||
return query
|
||||
|
||||
|
||||
# nova-manage service enable
|
||||
--host='ldev-indeedsrv005' --service='nova-compute'
|
||||
|
||||
|
||||
def create(wrapper, id, hypervisorid):
|
||||
print("create instance VM_{0} on {1}".format(str(id), str(hypervisorid)))
|
||||
try:
|
||||
|
||||
for image in glance.images.list(name='Cirros'):
|
||||
id_image = image.id
|
||||
|
||||
vm = wrapper.create_instance(hypervisor_id=hypervisorid,
|
||||
inst_name="VM_" + str(id),
|
||||
keypair_name='admin',
|
||||
image_id=id_image,
|
||||
create_new_floating_ip=True,
|
||||
flavor_name='m1.medium')
|
||||
print(vm)
|
||||
except Exception as e:
|
||||
print(unicode(e))
|
||||
|
||||
|
||||
def purge(nova, wrapper):
|
||||
print("Purging the cluster")
|
||||
instances = nova.servers.list()
|
||||
for instance in instances:
|
||||
wrapper.delete_instance(instance.id)
|
||||
|
||||
|
||||
try:
|
||||
executor = ThreadPoolExecutor(max_workers=3)
|
||||
creds = \
|
||||
{'auth_url': CONF.keystone_authtoken.auth_uri,
|
||||
'username': CONF.keystone_authtoken.admin_user,
|
||||
'password': CONF.keystone_authtoken.admin_password,
|
||||
'project_name': CONF.keystone_authtoken.admin_tenant_name,
|
||||
'user_domain_name': "default",
|
||||
'project_domain_name': "default"}
|
||||
auth = v3.Password(auth_url=creds['auth_url'],
|
||||
username=creds['username'],
|
||||
password=creds['password'],
|
||||
project_name=creds['project_name'],
|
||||
user_domain_name=creds[
|
||||
'user_domain_name'],
|
||||
project_domain_name=creds[
|
||||
'project_domain_name'])
|
||||
sess = session.Session(auth=auth)
|
||||
nova = nvclient.Client("3", session=sess)
|
||||
neutron = netclient.Client('2.0', session=sess)
|
||||
neutron.format = 'json'
|
||||
keystone = ksclient.Client(**creds)
|
||||
|
||||
glance_endpoint = keystone. \
|
||||
service_catalog.url_for(service_type='image',
|
||||
endpoint_type='publicURL')
|
||||
glance = glclient.Client(glance_endpoint,
|
||||
token=keystone.auth_token)
|
||||
|
||||
wrapper = NovaWrapper(creds, session=sess)
|
||||
|
||||
wrapper.live_migrate_instance(
|
||||
instance_id="b2aca823-a621-4235-9d56-9f0f75955dc1",
|
||||
dest_hostname="ldev-indeedsrv006", block_migration=True)
|
||||
|
||||
nova-manage service enable --host='ldev-indeedsrv005' \
|
||||
--service='nova-compute'
|
||||
nova-manage service enable --host='ldev-indeedsrv006' \
|
||||
--service='nova-compute'
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print("rollback " + str(e))
|
||||
|
||||
"""
|
||||
41
watcher/tests/fake_policy.py
Normal file
41
watcher/tests/fake_policy.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# Copyright (c) 2012 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
policy_data = """
|
||||
{
|
||||
"admin_api": "role:admin or role:administrator",
|
||||
"public_api": "is_public_api:True",
|
||||
"trusted_call": "rule:admin_api or rule:public_api",
|
||||
"default": "rule:trusted_call",
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
policy_data_compat_juno = """
|
||||
{
|
||||
"admin": "role:admin or role:administrator",
|
||||
"admin_api": "is_admin:True",
|
||||
"default": "rule:admin_api"
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def get_policy_data(compat):
|
||||
if not compat:
|
||||
return policy_data
|
||||
elif compat == 'juno':
|
||||
return policy_data_compat_juno
|
||||
else:
|
||||
raise Exception('Policy data for %s not available' % compat)
|
||||
93
watcher/tests/fakes.py
Normal file
93
watcher/tests/fakes.py
Normal file
@@ -0,0 +1,93 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# 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 mock
|
||||
|
||||
fakeAuthTokenHeaders = {'X-User-Id': u'773a902f022949619b5c2f32cd89d419',
|
||||
'X-Roles': u'admin, ResellerAdmin, _member_',
|
||||
'X-Project-Id': u'5588aebbcdc24e17a061595f80574376',
|
||||
'X-Project-Name': 'test',
|
||||
'X-User-Name': 'test',
|
||||
'X-Auth-Token': u'5588aebbcdc24e17a061595f80574376',
|
||||
'X-Forwarded-For': u'10.10.10.10, 11.11.11.11',
|
||||
'X-Service-Catalog': u'{test: 12345}',
|
||||
'X-Auth-Url': 'fake_auth_url',
|
||||
'X-Identity-Status': 'Confirmed',
|
||||
'X-User-Domain-Name': 'domain',
|
||||
'X-Project-Domain-Id': 'project_domain_id',
|
||||
'X-User-Domain-Id': 'user_domain_id',
|
||||
}
|
||||
|
||||
|
||||
class FakePecanRequest(mock.Mock):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(FakePecanRequest, self).__init__(**kwargs)
|
||||
self.host_url = 'http://test_url:8080/test'
|
||||
self.context = {}
|
||||
self.body = ''
|
||||
self.content_type = 'text/unicode'
|
||||
self.params = {}
|
||||
self.path = '/v1/services'
|
||||
self.headers = fakeAuthTokenHeaders
|
||||
self.environ = {}
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
setattr(self, index, value)
|
||||
|
||||
|
||||
class FakePecanResponse(mock.Mock):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(FakePecanResponse, self).__init__(**kwargs)
|
||||
self.status = None
|
||||
|
||||
|
||||
class FakeApp(object):
|
||||
pass
|
||||
|
||||
|
||||
class FakeService(mock.Mock):
|
||||
def __init__(self, **kwargs):
|
||||
super(FakeService, self).__init__(**kwargs)
|
||||
self.__tablename__ = 'service'
|
||||
self.__resource__ = 'services'
|
||||
self.user_id = 'fake user id'
|
||||
self.project_id = 'fake project id'
|
||||
self.uuid = 'test_uuid'
|
||||
self.id = 8
|
||||
self.name = 'james'
|
||||
self.service_type = 'not_this'
|
||||
self.description = 'amazing'
|
||||
self.tags = ['this', 'and that']
|
||||
self.read_only = True
|
||||
|
||||
def as_dict(self):
|
||||
return dict(service_type=self.service_type,
|
||||
user_id=self.user_id,
|
||||
project_id=self.project_id,
|
||||
uuid=self.uuid,
|
||||
id=self.id,
|
||||
name=self.name,
|
||||
tags=self.tags,
|
||||
read_only=self.read_only,
|
||||
description=self.description)
|
||||
|
||||
|
||||
class FakeAuthProtocol(mock.Mock):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(FakeAuthProtocol, self).__init__(**kwargs)
|
||||
self.app = FakeApp()
|
||||
self.config = ''
|
||||
0
watcher/tests/objects/__init__.py
Normal file
0
watcher/tests/objects/__init__.py
Normal file
118
watcher/tests/objects/test_action.py
Normal file
118
watcher/tests/objects/test_action.py
Normal file
@@ -0,0 +1,118 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
from testtools.matchers import HasLength
|
||||
|
||||
from watcher.common import exception
|
||||
# from watcher.common import utils as w_utils
|
||||
from watcher import objects
|
||||
from watcher.tests.db import base
|
||||
from watcher.tests.db import utils
|
||||
|
||||
|
||||
class TestActionObject(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestActionObject, self).setUp()
|
||||
self.fake_action = utils.get_test_action()
|
||||
|
||||
def test_get_by_id(self):
|
||||
action_id = self.fake_action['id']
|
||||
with mock.patch.object(self.dbapi, 'get_action_by_id',
|
||||
autospec=True) as mock_get_action:
|
||||
mock_get_action.return_value = self.fake_action
|
||||
action = objects.Action.get(self.context, action_id)
|
||||
mock_get_action.assert_called_once_with(self.context,
|
||||
action_id)
|
||||
self.assertEqual(self.context, action._context)
|
||||
|
||||
def test_get_by_uuid(self):
|
||||
uuid = self.fake_action['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_action_by_uuid',
|
||||
autospec=True) as mock_get_action:
|
||||
mock_get_action.return_value = self.fake_action
|
||||
action = objects.Action.get(self.context, uuid)
|
||||
mock_get_action.assert_called_once_with(self.context, uuid)
|
||||
self.assertEqual(self.context, action._context)
|
||||
|
||||
def test_get_bad_id_and_uuid(self):
|
||||
self.assertRaises(exception.InvalidIdentity,
|
||||
objects.Action.get, self.context, 'not-a-uuid')
|
||||
|
||||
def test_list(self):
|
||||
with mock.patch.object(self.dbapi, 'get_action_list',
|
||||
autospec=True) as mock_get_list:
|
||||
mock_get_list.return_value = [self.fake_action]
|
||||
actions = objects.Action.list(self.context)
|
||||
self.assertEqual(mock_get_list.call_count, 1)
|
||||
self.assertThat(actions, HasLength(1))
|
||||
self.assertIsInstance(actions[0], objects.Action)
|
||||
self.assertEqual(self.context, actions[0]._context)
|
||||
|
||||
def test_create(self):
|
||||
with mock.patch.object(self.dbapi, 'create_action',
|
||||
autospec=True) as mock_create_action:
|
||||
mock_create_action.return_value = self.fake_action
|
||||
action = objects.Action(self.context, **self.fake_action)
|
||||
|
||||
action.create()
|
||||
mock_create_action.assert_called_once_with(self.fake_action)
|
||||
self.assertEqual(self.context, action._context)
|
||||
|
||||
def test_destroy(self):
|
||||
uuid = self.fake_action['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_action_by_uuid',
|
||||
autospec=True) as mock_get_action:
|
||||
mock_get_action.return_value = self.fake_action
|
||||
with mock.patch.object(self.dbapi, 'destroy_action',
|
||||
autospec=True) as mock_destroy_action:
|
||||
action = objects.Action.get_by_uuid(self.context, uuid)
|
||||
action.destroy()
|
||||
mock_get_action.assert_called_once_with(self.context, uuid)
|
||||
mock_destroy_action.assert_called_once_with(uuid)
|
||||
self.assertEqual(self.context, action._context)
|
||||
|
||||
def test_save(self):
|
||||
uuid = self.fake_action['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_action_by_uuid',
|
||||
autospec=True) as mock_get_action:
|
||||
mock_get_action.return_value = self.fake_action
|
||||
with mock.patch.object(self.dbapi, 'update_action',
|
||||
autospec=True) as mock_update_action:
|
||||
action = objects.Action.get_by_uuid(self.context, uuid)
|
||||
action.state = 'SUCCESS'
|
||||
action.save()
|
||||
|
||||
mock_get_action.assert_called_once_with(self.context, uuid)
|
||||
mock_update_action.assert_called_once_with(
|
||||
uuid, {'state': 'SUCCESS'})
|
||||
self.assertEqual(self.context, action._context)
|
||||
|
||||
def test_refresh(self):
|
||||
uuid = self.fake_action['uuid']
|
||||
returns = [dict(self.fake_action, state="first state"),
|
||||
dict(self.fake_action, state="second state")]
|
||||
expected = [mock.call(self.context, uuid),
|
||||
mock.call(self.context, uuid)]
|
||||
with mock.patch.object(self.dbapi, 'get_action_by_uuid',
|
||||
side_effect=returns,
|
||||
autospec=True) as mock_get_action:
|
||||
action = objects.Action.get(self.context, uuid)
|
||||
self.assertEqual("first state", action.state)
|
||||
action.refresh()
|
||||
self.assertEqual("second state", action.state)
|
||||
self.assertEqual(expected, mock_get_action.call_args_list)
|
||||
self.assertEqual(self.context, action._context)
|
||||
123
watcher/tests/objects/test_action_plan.py
Normal file
123
watcher/tests/objects/test_action_plan.py
Normal file
@@ -0,0 +1,123 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
from testtools.matchers import HasLength
|
||||
|
||||
from watcher.common import exception
|
||||
# from watcher.common import utils as w_utils
|
||||
from watcher import objects
|
||||
from watcher.tests.db import base
|
||||
from watcher.tests.db import utils
|
||||
|
||||
|
||||
class TestActionPlanObject(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestActionPlanObject, self).setUp()
|
||||
self.fake_action_plan = utils.get_test_action_plan()
|
||||
|
||||
def test_get_by_id(self):
|
||||
action_plan_id = self.fake_action_plan['id']
|
||||
with mock.patch.object(self.dbapi, 'get_action_plan_by_id',
|
||||
autospec=True) as mock_get_action_plan:
|
||||
mock_get_action_plan.return_value = self.fake_action_plan
|
||||
action_plan = objects.ActionPlan.get(self.context, action_plan_id)
|
||||
mock_get_action_plan.assert_called_once_with(
|
||||
self.context, action_plan_id)
|
||||
self.assertEqual(self.context, action_plan._context)
|
||||
|
||||
def test_get_by_uuid(self):
|
||||
uuid = self.fake_action_plan['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_action_plan_by_uuid',
|
||||
autospec=True) as mock_get_action_plan:
|
||||
mock_get_action_plan.return_value = self.fake_action_plan
|
||||
action_plan = objects.ActionPlan.get(self.context, uuid)
|
||||
mock_get_action_plan.assert_called_once_with(self.context, uuid)
|
||||
self.assertEqual(self.context, action_plan._context)
|
||||
|
||||
def test_get_bad_id_and_uuid(self):
|
||||
self.assertRaises(exception.InvalidIdentity,
|
||||
objects.ActionPlan.get, self.context, 'not-a-uuid')
|
||||
|
||||
def test_list(self):
|
||||
with mock.patch.object(self.dbapi, 'get_action_plan_list',
|
||||
autospec=True) as mock_get_list:
|
||||
mock_get_list.return_value = [self.fake_action_plan]
|
||||
action_plans = objects.ActionPlan.list(self.context)
|
||||
self.assertEqual(mock_get_list.call_count, 1)
|
||||
self.assertThat(action_plans, HasLength(1))
|
||||
self.assertIsInstance(action_plans[0], objects.ActionPlan)
|
||||
self.assertEqual(self.context, action_plans[0]._context)
|
||||
|
||||
def test_create(self):
|
||||
with mock.patch.object(self.dbapi, 'create_action_plan',
|
||||
autospec=True) as mock_create_action_plan:
|
||||
mock_create_action_plan.return_value = self.fake_action_plan
|
||||
action_plan = objects.ActionPlan(
|
||||
self.context, **self.fake_action_plan)
|
||||
action_plan.create()
|
||||
mock_create_action_plan.assert_called_once_with(
|
||||
self.fake_action_plan)
|
||||
self.assertEqual(self.context, action_plan._context)
|
||||
|
||||
def test_destroy(self):
|
||||
uuid = self.fake_action_plan['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_action_plan_by_uuid',
|
||||
autospec=True) as mock_get_action_plan:
|
||||
mock_get_action_plan.return_value = self.fake_action_plan
|
||||
with mock.patch.object(self.dbapi, 'destroy_action_plan',
|
||||
autospec=True) as mock_destroy_action_plan:
|
||||
action_plan = objects.ActionPlan.get_by_uuid(
|
||||
self.context, uuid)
|
||||
action_plan.destroy()
|
||||
mock_get_action_plan.assert_called_once_with(
|
||||
self.context, uuid)
|
||||
mock_destroy_action_plan.assert_called_once_with(uuid)
|
||||
self.assertEqual(self.context, action_plan._context)
|
||||
|
||||
def test_save(self):
|
||||
uuid = self.fake_action_plan['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_action_plan_by_uuid',
|
||||
autospec=True) as mock_get_action_plan:
|
||||
mock_get_action_plan.return_value = self.fake_action_plan
|
||||
with mock.patch.object(self.dbapi, 'update_action_plan',
|
||||
autospec=True) as mock_update_action_plan:
|
||||
action_plan = objects.ActionPlan.get_by_uuid(
|
||||
self.context, uuid)
|
||||
action_plan.state = 'SUCCESS'
|
||||
action_plan.save()
|
||||
|
||||
mock_get_action_plan.assert_called_once_with(
|
||||
self.context, uuid)
|
||||
mock_update_action_plan.assert_called_once_with(
|
||||
uuid, {'state': 'SUCCESS'})
|
||||
self.assertEqual(self.context, action_plan._context)
|
||||
|
||||
def test_refresh(self):
|
||||
uuid = self.fake_action_plan['uuid']
|
||||
returns = [dict(self.fake_action_plan, state="first state"),
|
||||
dict(self.fake_action_plan, state="second state")]
|
||||
expected = [mock.call(self.context, uuid),
|
||||
mock.call(self.context, uuid)]
|
||||
with mock.patch.object(self.dbapi, 'get_action_plan_by_uuid',
|
||||
side_effect=returns,
|
||||
autospec=True) as mock_get_action_plan:
|
||||
action_plan = objects.ActionPlan.get(self.context, uuid)
|
||||
self.assertEqual("first state", action_plan.state)
|
||||
action_plan.refresh()
|
||||
self.assertEqual("second state", action_plan.state)
|
||||
self.assertEqual(expected, mock_get_action_plan.call_args_list)
|
||||
self.assertEqual(self.context, action_plan._context)
|
||||
118
watcher/tests/objects/test_audit.py
Normal file
118
watcher/tests/objects/test_audit.py
Normal file
@@ -0,0 +1,118 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
from testtools.matchers import HasLength
|
||||
|
||||
from watcher.common import exception
|
||||
# from watcher.common import utils as w_utils
|
||||
from watcher import objects
|
||||
from watcher.tests.db import base
|
||||
from watcher.tests.db import utils
|
||||
|
||||
|
||||
class TestAuditObject(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAuditObject, self).setUp()
|
||||
self.fake_audit = utils.get_test_audit()
|
||||
|
||||
def test_get_by_id(self):
|
||||
audit_id = self.fake_audit['id']
|
||||
with mock.patch.object(self.dbapi, 'get_audit_by_id',
|
||||
autospec=True) as mock_get_audit:
|
||||
mock_get_audit.return_value = self.fake_audit
|
||||
audit = objects.Audit.get(self.context, audit_id)
|
||||
mock_get_audit.assert_called_once_with(self.context,
|
||||
audit_id)
|
||||
self.assertEqual(self.context, audit._context)
|
||||
|
||||
def test_get_by_uuid(self):
|
||||
uuid = self.fake_audit['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_audit_by_uuid',
|
||||
autospec=True) as mock_get_audit:
|
||||
mock_get_audit.return_value = self.fake_audit
|
||||
audit = objects.Audit.get(self.context, uuid)
|
||||
mock_get_audit.assert_called_once_with(self.context, uuid)
|
||||
self.assertEqual(self.context, audit._context)
|
||||
|
||||
def test_get_bad_id_and_uuid(self):
|
||||
self.assertRaises(exception.InvalidIdentity,
|
||||
objects.Audit.get, self.context, 'not-a-uuid')
|
||||
|
||||
def test_list(self):
|
||||
with mock.patch.object(self.dbapi, 'get_audit_list',
|
||||
autospec=True) as mock_get_list:
|
||||
mock_get_list.return_value = [self.fake_audit]
|
||||
audits = objects.Audit.list(self.context)
|
||||
self.assertEqual(mock_get_list.call_count, 1)
|
||||
self.assertThat(audits, HasLength(1))
|
||||
self.assertIsInstance(audits[0], objects.Audit)
|
||||
self.assertEqual(self.context, audits[0]._context)
|
||||
|
||||
def test_create(self):
|
||||
with mock.patch.object(self.dbapi, 'create_audit',
|
||||
autospec=True) as mock_create_audit:
|
||||
mock_create_audit.return_value = self.fake_audit
|
||||
audit = objects.Audit(self.context, **self.fake_audit)
|
||||
|
||||
audit.create()
|
||||
mock_create_audit.assert_called_once_with(self.fake_audit)
|
||||
self.assertEqual(self.context, audit._context)
|
||||
|
||||
def test_destroy(self):
|
||||
uuid = self.fake_audit['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_audit_by_uuid',
|
||||
autospec=True) as mock_get_audit:
|
||||
mock_get_audit.return_value = self.fake_audit
|
||||
with mock.patch.object(self.dbapi, 'destroy_audit',
|
||||
autospec=True) as mock_destroy_audit:
|
||||
audit = objects.Audit.get_by_uuid(self.context, uuid)
|
||||
audit.destroy()
|
||||
mock_get_audit.assert_called_once_with(self.context, uuid)
|
||||
mock_destroy_audit.assert_called_once_with(uuid)
|
||||
self.assertEqual(self.context, audit._context)
|
||||
|
||||
def test_save(self):
|
||||
uuid = self.fake_audit['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_audit_by_uuid',
|
||||
autospec=True) as mock_get_audit:
|
||||
mock_get_audit.return_value = self.fake_audit
|
||||
with mock.patch.object(self.dbapi, 'update_audit',
|
||||
autospec=True) as mock_update_audit:
|
||||
audit = objects.Audit.get_by_uuid(self.context, uuid)
|
||||
audit.state = 'SUCCESS'
|
||||
audit.save()
|
||||
|
||||
mock_get_audit.assert_called_once_with(self.context, uuid)
|
||||
mock_update_audit.assert_called_once_with(
|
||||
uuid, {'state': 'SUCCESS'})
|
||||
self.assertEqual(self.context, audit._context)
|
||||
|
||||
def test_refresh(self):
|
||||
uuid = self.fake_audit['uuid']
|
||||
returns = [dict(self.fake_audit, state="first state"),
|
||||
dict(self.fake_audit, state="second state")]
|
||||
expected = [mock.call(self.context, uuid),
|
||||
mock.call(self.context, uuid)]
|
||||
with mock.patch.object(self.dbapi, 'get_audit_by_uuid',
|
||||
side_effect=returns,
|
||||
autospec=True) as mock_get_audit:
|
||||
audit = objects.Audit.get(self.context, uuid)
|
||||
self.assertEqual("first state", audit.state)
|
||||
audit.refresh()
|
||||
self.assertEqual("second state", audit.state)
|
||||
self.assertEqual(expected, mock_get_audit.call_args_list)
|
||||
self.assertEqual(self.context, audit._context)
|
||||
155
watcher/tests/objects/test_audit_template.py
Normal file
155
watcher/tests/objects/test_audit_template.py
Normal file
@@ -0,0 +1,155 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
from testtools.matchers import HasLength
|
||||
|
||||
from watcher.common import exception
|
||||
# from watcher.common import utils as w_utils
|
||||
from watcher import objects
|
||||
from watcher.tests.db import base
|
||||
from watcher.tests.db import utils
|
||||
|
||||
|
||||
class TestAuditTemplateObject(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAuditTemplateObject, self).setUp()
|
||||
self.fake_audit_template = utils.get_test_audit_template()
|
||||
|
||||
def test_get_by_id(self):
|
||||
audit_template_id = self.fake_audit_template['id']
|
||||
with mock.patch.object(self.dbapi, 'get_audit_template_by_id',
|
||||
autospec=True) as mock_get_audit_template:
|
||||
mock_get_audit_template.return_value = self.fake_audit_template
|
||||
audit_template = objects.AuditTemplate.get(self.context,
|
||||
audit_template_id)
|
||||
mock_get_audit_template.assert_called_once_with(
|
||||
self.context, audit_template_id)
|
||||
self.assertEqual(self.context, audit_template._context)
|
||||
|
||||
def test_get_by_uuid(self):
|
||||
uuid = self.fake_audit_template['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_audit_template_by_uuid',
|
||||
autospec=True) as mock_get_audit_template:
|
||||
mock_get_audit_template.return_value = self.fake_audit_template
|
||||
audit_template = objects.AuditTemplate.get(self.context, uuid)
|
||||
mock_get_audit_template.assert_called_once_with(self.context, uuid)
|
||||
self.assertEqual(self.context, audit_template._context)
|
||||
|
||||
def test_get_by_name(self):
|
||||
name = self.fake_audit_template['name']
|
||||
with mock.patch.object(self.dbapi, 'get_audit_template_by_name',
|
||||
autospec=True) as mock_get_audit_template:
|
||||
mock_get_audit_template.return_value = self.fake_audit_template
|
||||
audit_template = objects.AuditTemplate.get_by_name(
|
||||
self.context,
|
||||
name)
|
||||
mock_get_audit_template.assert_called_once_with(self.context, name)
|
||||
self.assertEqual(self.context, audit_template._context)
|
||||
|
||||
def test_get_bad_id_and_uuid(self):
|
||||
self.assertRaises(exception.InvalidIdentity,
|
||||
objects.AuditTemplate.get,
|
||||
self.context, 'not-a-uuid')
|
||||
|
||||
def test_list(self):
|
||||
with mock.patch.object(self.dbapi, 'get_audit_template_list',
|
||||
autospec=True) as mock_get_list:
|
||||
mock_get_list.return_value = [self.fake_audit_template]
|
||||
audit_templates = objects.AuditTemplate.list(self.context)
|
||||
self.assertEqual(mock_get_list.call_count, 1)
|
||||
self.assertThat(audit_templates, HasLength(1))
|
||||
self.assertIsInstance(audit_templates[0], objects.AuditTemplate)
|
||||
self.assertEqual(self.context, audit_templates[0]._context)
|
||||
|
||||
def test_create(self):
|
||||
with mock.patch.object(self.dbapi, 'create_audit_template',
|
||||
autospec=True) as mock_create_audit_template:
|
||||
mock_create_audit_template.return_value = self.fake_audit_template
|
||||
audit_template = objects.AuditTemplate(self.context,
|
||||
**self.fake_audit_template)
|
||||
audit_template.create()
|
||||
mock_create_audit_template.assert_called_once_with(
|
||||
self.fake_audit_template)
|
||||
self.assertEqual(self.context, audit_template._context)
|
||||
|
||||
def test_destroy(self):
|
||||
uuid = self.fake_audit_template['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_audit_template_by_uuid',
|
||||
autospec=True) as mock_get_audit_template:
|
||||
mock_get_audit_template.return_value = self.fake_audit_template
|
||||
with mock.patch.object(self.dbapi, 'destroy_audit_template',
|
||||
autospec=True) \
|
||||
as mock_destroy_audit_template:
|
||||
audit_template = objects.AuditTemplate.get_by_uuid(
|
||||
self.context, uuid)
|
||||
audit_template.destroy()
|
||||
mock_get_audit_template.assert_called_once_with(
|
||||
self.context, uuid)
|
||||
mock_destroy_audit_template.assert_called_once_with(uuid)
|
||||
self.assertEqual(self.context, audit_template._context)
|
||||
|
||||
def test_save(self):
|
||||
uuid = self.fake_audit_template['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_audit_template_by_uuid',
|
||||
autospec=True) as mock_get_audit_template:
|
||||
mock_get_audit_template.return_value = self.fake_audit_template
|
||||
with mock.patch.object(self.dbapi, 'update_audit_template',
|
||||
autospec=True) \
|
||||
as mock_update_audit_template:
|
||||
audit_template = objects.AuditTemplate.get_by_uuid(
|
||||
self.context, uuid)
|
||||
audit_template.goal = 'SERVERS_CONSOLIDATION'
|
||||
audit_template.save()
|
||||
|
||||
mock_get_audit_template.assert_called_once_with(
|
||||
self.context, uuid)
|
||||
mock_update_audit_template.assert_called_once_with(
|
||||
uuid, {'goal': 'SERVERS_CONSOLIDATION'})
|
||||
self.assertEqual(self.context, audit_template._context)
|
||||
|
||||
def test_refresh(self):
|
||||
uuid = self.fake_audit_template['uuid']
|
||||
returns = [dict(self.fake_audit_template,
|
||||
goal="SERVERS_CONSOLIDATION"),
|
||||
dict(self.fake_audit_template, goal="BALANCE_LOAD")]
|
||||
expected = [mock.call(self.context, uuid),
|
||||
mock.call(self.context, uuid)]
|
||||
with mock.patch.object(self.dbapi, 'get_audit_template_by_uuid',
|
||||
side_effect=returns,
|
||||
autospec=True) as mock_get_audit_template:
|
||||
audit_template = objects.AuditTemplate.get(self.context, uuid)
|
||||
self.assertEqual("SERVERS_CONSOLIDATION", audit_template.goal)
|
||||
audit_template.refresh()
|
||||
self.assertEqual("BALANCE_LOAD", audit_template.goal)
|
||||
self.assertEqual(expected, mock_get_audit_template.call_args_list)
|
||||
self.assertEqual(self.context, audit_template._context)
|
||||
|
||||
def test_soft_delete(self):
|
||||
uuid = self.fake_audit_template['uuid']
|
||||
with mock.patch.object(self.dbapi, 'get_audit_template_by_uuid',
|
||||
autospec=True) as mock_get_audit_template:
|
||||
mock_get_audit_template.return_value = self.fake_audit_template
|
||||
with mock.patch.object(self.dbapi, 'soft_delete_audit_template',
|
||||
autospec=True) \
|
||||
as mock_soft_delete_audit_template:
|
||||
audit_template = objects.AuditTemplate.get_by_uuid(
|
||||
self.context, uuid)
|
||||
audit_template.soft_delete()
|
||||
mock_get_audit_template.assert_called_once_with(
|
||||
self.context, uuid)
|
||||
mock_soft_delete_audit_template.assert_called_once_with(uuid)
|
||||
self.assertEqual(self.context, audit_template._context)
|
||||
589
watcher/tests/objects/test_objects.py
Normal file
589
watcher/tests/objects/test_objects.py
Normal file
@@ -0,0 +1,589 @@
|
||||
# Copyright 2015 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 datetime
|
||||
import gettext
|
||||
|
||||
import iso8601
|
||||
import netaddr
|
||||
from oslo_utils import timeutils
|
||||
import six
|
||||
|
||||
from watcher.common import context as watcher_context
|
||||
from watcher.common import exception
|
||||
from watcher.objects import base
|
||||
from watcher.objects import utils
|
||||
from watcher.tests import base as test_base
|
||||
|
||||
gettext.install('watcher')
|
||||
|
||||
|
||||
class MyObj(base.WatcherObject):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {'foo': int,
|
||||
'bar': str,
|
||||
'missing': str,
|
||||
}
|
||||
|
||||
def obj_load_attr(self, attrname):
|
||||
setattr(self, attrname, 'loaded!')
|
||||
|
||||
def query(cls, context):
|
||||
obj = cls(context)
|
||||
obj.foo = 1
|
||||
obj.bar = 'bar'
|
||||
obj.obj_reset_changes()
|
||||
return obj
|
||||
|
||||
def marco(self, context):
|
||||
return 'polo'
|
||||
|
||||
def update_test(self, context):
|
||||
if context.project_id == 'alternate':
|
||||
self.bar = 'alternate-context'
|
||||
else:
|
||||
self.bar = 'updated'
|
||||
|
||||
def save(self, context):
|
||||
self.obj_reset_changes()
|
||||
|
||||
def refresh(self, context):
|
||||
self.foo = 321
|
||||
self.bar = 'refreshed'
|
||||
self.obj_reset_changes()
|
||||
|
||||
def modify_save_modify(self, context):
|
||||
self.bar = 'meow'
|
||||
self.save()
|
||||
self.foo = 42
|
||||
|
||||
|
||||
class MyObj2(object):
|
||||
@classmethod
|
||||
def obj_name(cls):
|
||||
return 'MyObj'
|
||||
|
||||
def get(cls, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
class TestSubclassedObject(MyObj):
|
||||
fields = {'new_field': str}
|
||||
|
||||
|
||||
class TestMetaclass(test_base.TestCase):
|
||||
def test_obj_tracking(self):
|
||||
|
||||
@six.add_metaclass(base.WatcherObjectMetaclass)
|
||||
class NewBaseClass(object):
|
||||
fields = {}
|
||||
|
||||
@classmethod
|
||||
def obj_name(cls):
|
||||
return cls.__name__
|
||||
|
||||
class Test1(NewBaseClass):
|
||||
@staticmethod
|
||||
def obj_name():
|
||||
return 'fake1'
|
||||
|
||||
class Test2(NewBaseClass):
|
||||
pass
|
||||
|
||||
class Test2v2(NewBaseClass):
|
||||
@staticmethod
|
||||
def obj_name():
|
||||
return 'Test2'
|
||||
|
||||
expected = {'fake1': [Test1], 'Test2': [Test2, Test2v2]}
|
||||
|
||||
self.assertEqual(expected, NewBaseClass._obj_classes)
|
||||
# The following should work, also.
|
||||
self.assertEqual(expected, Test1._obj_classes)
|
||||
self.assertEqual(expected, Test2._obj_classes)
|
||||
|
||||
|
||||
class TestUtils(test_base.TestCase):
|
||||
|
||||
def test_datetime_or_none(self):
|
||||
naive_dt = datetime.datetime.now()
|
||||
dt = timeutils.parse_isotime(timeutils.isotime(naive_dt))
|
||||
self.assertEqual(utils.datetime_or_none(dt), dt)
|
||||
self.assertEqual(utils.datetime_or_none(dt),
|
||||
naive_dt.replace(tzinfo=iso8601.iso8601.Utc(),
|
||||
microsecond=0))
|
||||
self.assertIsNone(utils.datetime_or_none(None))
|
||||
self.assertRaises(ValueError, utils.datetime_or_none, 'foo')
|
||||
|
||||
def test_datetime_or_str_or_none(self):
|
||||
dts = timeutils.isotime()
|
||||
dt = timeutils.parse_isotime(dts)
|
||||
self.assertEqual(utils.datetime_or_str_or_none(dt), dt)
|
||||
self.assertIsNone(utils.datetime_or_str_or_none(None))
|
||||
self.assertEqual(utils.datetime_or_str_or_none(dts), dt)
|
||||
self.assertRaises(ValueError, utils.datetime_or_str_or_none, 'foo')
|
||||
|
||||
def test_int_or_none(self):
|
||||
self.assertEqual(utils.int_or_none(1), 1)
|
||||
self.assertEqual(utils.int_or_none('1'), 1)
|
||||
self.assertIsNone(utils.int_or_none(None))
|
||||
self.assertRaises(ValueError, utils.int_or_none, 'foo')
|
||||
|
||||
def test_str_or_none(self):
|
||||
class Obj(object):
|
||||
pass
|
||||
self.assertEqual(utils.str_or_none('foo'), 'foo')
|
||||
self.assertEqual(utils.str_or_none(1), '1')
|
||||
self.assertIsNone(utils.str_or_none(None))
|
||||
|
||||
def test_ip_or_none(self):
|
||||
ip4 = netaddr.IPAddress('1.2.3.4', 4)
|
||||
ip6 = netaddr.IPAddress('1::2', 6)
|
||||
self.assertEqual(utils.ip_or_none(4)('1.2.3.4'), ip4)
|
||||
self.assertEqual(utils.ip_or_none(6)('1::2'), ip6)
|
||||
self.assertIsNone(utils.ip_or_none(4)(None))
|
||||
self.assertIsNone(utils.ip_or_none(6)(None))
|
||||
self.assertRaises(netaddr.AddrFormatError, utils.ip_or_none(4), 'foo')
|
||||
self.assertRaises(netaddr.AddrFormatError, utils.ip_or_none(6), 'foo')
|
||||
|
||||
def test_dt_serializer(self):
|
||||
class Obj(object):
|
||||
foo = utils.dt_serializer('bar')
|
||||
|
||||
obj = Obj()
|
||||
obj.bar = timeutils.parse_isotime('1955-11-05T00:00:00Z')
|
||||
self.assertEqual('1955-11-05T00:00:00Z', obj.foo())
|
||||
obj.bar = None
|
||||
self.assertIsNone(obj.foo())
|
||||
obj.bar = 'foo'
|
||||
self.assertRaises(AttributeError, obj.foo)
|
||||
|
||||
def test_dt_deserializer(self):
|
||||
dt = timeutils.parse_isotime('1955-11-05T00:00:00Z')
|
||||
self.assertEqual(utils.dt_deserializer(None, timeutils.isotime(dt)),
|
||||
dt)
|
||||
self.assertIsNone(utils.dt_deserializer(None, None))
|
||||
self.assertRaises(ValueError, utils.dt_deserializer, None, 'foo')
|
||||
|
||||
def test_obj_to_primitive_list(self):
|
||||
class MyList(base.ObjectListBase, base.WatcherObject):
|
||||
pass
|
||||
mylist = MyList(self.context)
|
||||
mylist.objects = [1, 2, 3]
|
||||
self.assertEqual([1, 2, 3], base.obj_to_primitive(mylist))
|
||||
|
||||
def test_obj_to_primitive_dict(self):
|
||||
myobj = MyObj(self.context)
|
||||
myobj.foo = 1
|
||||
myobj.bar = 'foo'
|
||||
self.assertEqual({'foo': 1, 'bar': 'foo'},
|
||||
base.obj_to_primitive(myobj))
|
||||
|
||||
def test_obj_to_primitive_recursive(self):
|
||||
class MyList(base.ObjectListBase, base.WatcherObject):
|
||||
pass
|
||||
|
||||
mylist = MyList(self.context)
|
||||
mylist.objects = [MyObj(self.context), MyObj(self.context)]
|
||||
for i, value in enumerate(mylist):
|
||||
value.foo = i
|
||||
self.assertEqual([{'foo': 0}, {'foo': 1}],
|
||||
base.obj_to_primitive(mylist))
|
||||
|
||||
|
||||
class _TestObject(object):
|
||||
def test_hydration_type_error(self):
|
||||
primitive = {'watcher_object.name': 'MyObj',
|
||||
'watcher_object.namespace': 'watcher',
|
||||
'watcher_object.version': '1.5',
|
||||
'watcher_object.data': {'foo': 'a'}}
|
||||
self.assertRaises(ValueError, MyObj.obj_from_primitive, primitive)
|
||||
|
||||
def test_hydration(self):
|
||||
primitive = {'watcher_object.name': 'MyObj',
|
||||
'watcher_object.namespace': 'watcher',
|
||||
'watcher_object.version': '1.5',
|
||||
'watcher_object.data': {'foo': 1}}
|
||||
obj = MyObj.obj_from_primitive(primitive)
|
||||
self.assertEqual(1, obj.foo)
|
||||
|
||||
def test_hydration_bad_ns(self):
|
||||
primitive = {'watcher_object.name': 'MyObj',
|
||||
'watcher_object.namespace': 'foo',
|
||||
'watcher_object.version': '1.5',
|
||||
'watcher_object.data': {'foo': 1}}
|
||||
self.assertRaises(exception.UnsupportedObjectError,
|
||||
MyObj.obj_from_primitive, primitive)
|
||||
|
||||
def test_dehydration(self):
|
||||
expected = {'watcher_object.name': 'MyObj',
|
||||
'watcher_object.namespace': 'watcher',
|
||||
'watcher_object.version': '1.5',
|
||||
'watcher_object.data': {'foo': 1}}
|
||||
obj = MyObj(self.context)
|
||||
obj.foo = 1
|
||||
obj.obj_reset_changes()
|
||||
self.assertEqual(expected, obj.obj_to_primitive())
|
||||
|
||||
def test_get_updates(self):
|
||||
obj = MyObj(self.context)
|
||||
self.assertEqual({}, obj.obj_get_changes())
|
||||
obj.foo = 123
|
||||
self.assertEqual({'foo': 123}, obj.obj_get_changes())
|
||||
obj.bar = 'test'
|
||||
self.assertEqual({'foo': 123, 'bar': 'test'}, obj.obj_get_changes())
|
||||
obj.obj_reset_changes()
|
||||
self.assertEqual({}, obj.obj_get_changes())
|
||||
|
||||
def test_object_property(self):
|
||||
obj = MyObj(self.context, foo=1)
|
||||
self.assertEqual(1, obj.foo)
|
||||
|
||||
def test_object_property_type_error(self):
|
||||
obj = MyObj(self.context)
|
||||
|
||||
def fail():
|
||||
obj.foo = 'a'
|
||||
self.assertRaises(ValueError, fail)
|
||||
|
||||
def test_object_dict_syntax(self):
|
||||
obj = MyObj(self.context)
|
||||
obj.foo = 123
|
||||
obj.bar = 'bar'
|
||||
self.assertEqual(123, obj['foo'])
|
||||
self.assertEqual([('bar', 'bar'), ('foo', 123)],
|
||||
sorted(obj.items(), key=lambda x: x[0]))
|
||||
self.assertEqual([('bar', 'bar'), ('foo', 123)],
|
||||
sorted(list(obj.iteritems()), key=lambda x: x[0]))
|
||||
|
||||
def test_load(self):
|
||||
obj = MyObj(self.context)
|
||||
self.assertEqual('loaded!', obj.bar)
|
||||
|
||||
def test_load_in_base(self):
|
||||
class Foo(base.WatcherObject):
|
||||
fields = {'foobar': int}
|
||||
obj = Foo(self.context)
|
||||
# NOTE(danms): Can't use assertRaisesRegexp() because of py26
|
||||
raised = False
|
||||
try:
|
||||
obj.foobar
|
||||
except NotImplementedError as ex:
|
||||
raised = True
|
||||
self.assertTrue(raised)
|
||||
self.assertTrue('foobar' in str(ex))
|
||||
|
||||
def test_loaded_in_primitive(self):
|
||||
obj = MyObj(self.context)
|
||||
obj.foo = 1
|
||||
obj.obj_reset_changes()
|
||||
self.assertEqual('loaded!', obj.bar)
|
||||
expected = {'watcher_object.name': 'MyObj',
|
||||
'watcher_object.namespace': 'watcher',
|
||||
'watcher_object.version': '1.0',
|
||||
'watcher_object.changes': ['bar'],
|
||||
'watcher_object.data': {'foo': 1,
|
||||
'bar': 'loaded!'}}
|
||||
self.assertEqual(expected, obj.obj_to_primitive())
|
||||
|
||||
def test_changes_in_primitive(self):
|
||||
obj = MyObj(self.context)
|
||||
obj.foo = 123
|
||||
self.assertEqual(set(['foo']), obj.obj_what_changed())
|
||||
primitive = obj.obj_to_primitive()
|
||||
self.assertTrue('watcher_object.changes' in primitive)
|
||||
obj2 = MyObj.obj_from_primitive(primitive)
|
||||
self.assertEqual(set(['foo']), obj2.obj_what_changed())
|
||||
obj2.obj_reset_changes()
|
||||
self.assertEqual(set(), obj2.obj_what_changed())
|
||||
|
||||
def test_unknown_objtype(self):
|
||||
self.assertRaises(exception.UnsupportedObjectError,
|
||||
base.WatcherObject.obj_class_from_name, 'foo', '1.0')
|
||||
|
||||
def test_with_alternate_context(self):
|
||||
context1 = watcher_context.RequestContext('foo', 'foo')
|
||||
context2 = watcher_context.RequestContext('bar',
|
||||
project_id='alternate')
|
||||
obj = MyObj.query(context1)
|
||||
obj.update_test(context2)
|
||||
self.assertEqual('alternate-context', obj.bar)
|
||||
self.assertRemotes()
|
||||
|
||||
def test_orphaned_object(self):
|
||||
obj = MyObj.query(self.context)
|
||||
obj._context = None
|
||||
self.assertRaises(exception.OrphanedObjectError,
|
||||
obj.update_test)
|
||||
self.assertRemotes()
|
||||
|
||||
def test_changed_1(self):
|
||||
obj = MyObj.query(self.context)
|
||||
obj.foo = 123
|
||||
self.assertEqual(set(['foo']), obj.obj_what_changed())
|
||||
obj.update_test(self.context)
|
||||
self.assertEqual(set(['foo', 'bar']), obj.obj_what_changed())
|
||||
self.assertEqual(123, obj.foo)
|
||||
self.assertRemotes()
|
||||
|
||||
def test_changed_2(self):
|
||||
obj = MyObj.query(self.context)
|
||||
obj.foo = 123
|
||||
self.assertEqual(set(['foo']), obj.obj_what_changed())
|
||||
obj.save()
|
||||
self.assertEqual(set([]), obj.obj_what_changed())
|
||||
self.assertEqual(123, obj.foo)
|
||||
self.assertRemotes()
|
||||
|
||||
def test_changed_3(self):
|
||||
obj = MyObj.query(self.context)
|
||||
obj.foo = 123
|
||||
self.assertEqual(set(['foo']), obj.obj_what_changed())
|
||||
obj.refresh()
|
||||
self.assertEqual(set([]), obj.obj_what_changed())
|
||||
self.assertEqual(321, obj.foo)
|
||||
self.assertEqual('refreshed', obj.bar)
|
||||
self.assertRemotes()
|
||||
|
||||
def test_changed_4(self):
|
||||
obj = MyObj.query(self.context)
|
||||
obj.bar = 'something'
|
||||
self.assertEqual(set(['bar']), obj.obj_what_changed())
|
||||
obj.modify_save_modify(self.context)
|
||||
self.assertEqual(set(['foo']), obj.obj_what_changed())
|
||||
self.assertEqual(42, obj.foo)
|
||||
self.assertEqual('meow', obj.bar)
|
||||
self.assertRemotes()
|
||||
|
||||
def test_static_result(self):
|
||||
obj = MyObj.query(self.context)
|
||||
self.assertEqual('bar', obj.bar)
|
||||
result = obj.marco()
|
||||
self.assertEqual('polo', result)
|
||||
self.assertRemotes()
|
||||
|
||||
def test_updates(self):
|
||||
obj = MyObj.query(self.context)
|
||||
self.assertEqual(1, obj.foo)
|
||||
obj.update_test()
|
||||
self.assertEqual('updated', obj.bar)
|
||||
self.assertRemotes()
|
||||
|
||||
def test_base_attributes(self):
|
||||
dt = datetime.datetime(1955, 11, 5)
|
||||
obj = MyObj(self.context)
|
||||
obj.created_at = dt
|
||||
obj.updated_at = dt
|
||||
expected = {'watcher_object.name': 'MyObj',
|
||||
'watcher_object.namespace': 'watcher',
|
||||
'watcher_object.version': '1.0',
|
||||
'watcher_object.changes':
|
||||
['created_at', 'updated_at'],
|
||||
'watcher_object.data':
|
||||
{'created_at': timeutils.isotime(dt),
|
||||
'updated_at': timeutils.isotime(dt),
|
||||
}
|
||||
}
|
||||
actual = obj.obj_to_primitive()
|
||||
# watcher_object.changes is built from a set and order is undefined
|
||||
self.assertEqual(sorted(expected['watcher_object.changes']),
|
||||
sorted(actual['watcher_object.changes']))
|
||||
del expected['watcher_object.changes'], \
|
||||
actual['watcher_object.changes']
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_contains(self):
|
||||
obj = MyObj(self.context)
|
||||
self.assertFalse('foo' in obj)
|
||||
obj.foo = 1
|
||||
self.assertTrue('foo' in obj)
|
||||
self.assertFalse('does_not_exist' in obj)
|
||||
|
||||
def test_obj_attr_is_set(self):
|
||||
obj = MyObj(self.context, foo=1)
|
||||
self.assertTrue(obj.obj_attr_is_set('foo'))
|
||||
self.assertFalse(obj.obj_attr_is_set('bar'))
|
||||
self.assertRaises(AttributeError, obj.obj_attr_is_set, 'bang')
|
||||
|
||||
def test_get(self):
|
||||
obj = MyObj(self.context, foo=1)
|
||||
# Foo has value, should not get the default
|
||||
self.assertEqual(obj.get('foo', 2), 1)
|
||||
# Foo has value, should return the value without error
|
||||
self.assertEqual(obj.get('foo'), 1)
|
||||
# Bar is not loaded, so we should get the default
|
||||
self.assertEqual(obj.get('bar', 'not-loaded'), 'not-loaded')
|
||||
# Bar without a default should lazy-load
|
||||
self.assertEqual(obj.get('bar'), 'loaded!')
|
||||
# Bar now has a default, but loaded value should be returned
|
||||
self.assertEqual(obj.get('bar', 'not-loaded'), 'loaded!')
|
||||
# Invalid attribute should raise AttributeError
|
||||
self.assertRaises(AttributeError, obj.get, 'nothing')
|
||||
# ...even with a default
|
||||
self.assertRaises(AttributeError, obj.get, 'nothing', 3)
|
||||
|
||||
def test_object_inheritance(self):
|
||||
base_fields = base.WatcherObject.fields.keys()
|
||||
myobj_fields = ['foo', 'bar', 'missing'] + base_fields
|
||||
myobj3_fields = ['new_field']
|
||||
self.assertTrue(issubclass(TestSubclassedObject, MyObj))
|
||||
self.assertEqual(len(myobj_fields), len(MyObj.fields))
|
||||
self.assertEqual(set(myobj_fields), set(MyObj.fields.keys()))
|
||||
self.assertEqual(len(myobj_fields) + len(myobj3_fields),
|
||||
len(TestSubclassedObject.fields))
|
||||
self.assertEqual(set(myobj_fields) | set(myobj3_fields),
|
||||
set(TestSubclassedObject.fields.keys()))
|
||||
|
||||
def test_get_changes(self):
|
||||
obj = MyObj(self.context)
|
||||
self.assertEqual({}, obj.obj_get_changes())
|
||||
obj.foo = 123
|
||||
self.assertEqual({'foo': 123}, obj.obj_get_changes())
|
||||
obj.bar = 'test'
|
||||
self.assertEqual({'foo': 123, 'bar': 'test'}, obj.obj_get_changes())
|
||||
obj.obj_reset_changes()
|
||||
self.assertEqual({}, obj.obj_get_changes())
|
||||
|
||||
def test_obj_fields(self):
|
||||
class TestObj(base.WatcherObject):
|
||||
fields = {'foo': int}
|
||||
obj_extra_fields = ['bar']
|
||||
|
||||
@property
|
||||
def bar(self):
|
||||
return 'this is bar'
|
||||
|
||||
obj = TestObj(self.context)
|
||||
self.assertEqual(set(['created_at', 'updated_at', 'foo', 'bar']),
|
||||
set(obj.obj_fields))
|
||||
|
||||
def test_obj_constructor(self):
|
||||
obj = MyObj(self.context, foo=123, bar='abc')
|
||||
self.assertEqual(123, obj.foo)
|
||||
self.assertEqual('abc', obj.bar)
|
||||
self.assertEqual(set(['foo', 'bar']), obj.obj_what_changed())
|
||||
|
||||
|
||||
class TestObjectListBase(test_base.TestCase):
|
||||
|
||||
def test_list_like_operations(self):
|
||||
class Foo(base.ObjectListBase, base.WatcherObject):
|
||||
pass
|
||||
|
||||
objlist = Foo(self.context)
|
||||
objlist._context = 'foo'
|
||||
objlist.objects = [1, 2, 3]
|
||||
self.assertEqual(list(objlist), objlist.objects)
|
||||
self.assertEqual(3, len(objlist))
|
||||
self.assertIn(2, objlist)
|
||||
self.assertEqual([1], list(objlist[:1]))
|
||||
self.assertEqual('foo', objlist[:1]._context)
|
||||
self.assertEqual(3, objlist[2])
|
||||
self.assertEqual(1, objlist.count(1))
|
||||
self.assertEqual(1, objlist.index(2))
|
||||
|
||||
def test_serialization(self):
|
||||
class Foo(base.ObjectListBase, base.WatcherObject):
|
||||
pass
|
||||
|
||||
class Bar(base.WatcherObject):
|
||||
fields = {'foo': str}
|
||||
|
||||
obj = Foo(self.context)
|
||||
obj.objects = []
|
||||
for i in 'abc':
|
||||
bar = Bar(self.context)
|
||||
bar.foo = i
|
||||
obj.objects.append(bar)
|
||||
|
||||
obj2 = base.WatcherObject.obj_from_primitive(obj.obj_to_primitive())
|
||||
self.assertFalse(obj is obj2)
|
||||
self.assertEqual([x.foo for x in obj],
|
||||
[y.foo for y in obj2])
|
||||
|
||||
def _test_object_list_version_mappings(self, list_obj_class):
|
||||
# Figure out what sort of object this list is for
|
||||
list_field = list_obj_class.fields['objects']
|
||||
item_obj_field = list_field._type._element_type
|
||||
item_obj_name = item_obj_field._type._obj_name
|
||||
|
||||
# Look through all object classes of this type and make sure that
|
||||
# the versions we find are covered by the parent list class
|
||||
for item_class in base.WatcherObject._obj_classes[item_obj_name]:
|
||||
self.assertIn(
|
||||
item_class.VERSION,
|
||||
list_obj_class.child_versions.values())
|
||||
|
||||
def test_object_version_mappings(self):
|
||||
# Find all object list classes and make sure that they at least handle
|
||||
# all the current object versions
|
||||
for obj_classes in base.WatcherObject._obj_classes.values():
|
||||
for obj_class in obj_classes:
|
||||
if issubclass(obj_class, base.ObjectListBase):
|
||||
self._test_object_list_version_mappings(obj_class)
|
||||
|
||||
def test_list_changes(self):
|
||||
class Foo(base.ObjectListBase, base.WatcherObject):
|
||||
pass
|
||||
|
||||
class Bar(base.WatcherObject):
|
||||
fields = {'foo': str}
|
||||
|
||||
obj = Foo(self.context, objects=[])
|
||||
self.assertEqual(set(['objects']), obj.obj_what_changed())
|
||||
obj.objects.append(Bar(self.context, foo='test'))
|
||||
self.assertEqual(set(['objects']), obj.obj_what_changed())
|
||||
obj.obj_reset_changes()
|
||||
# This should still look dirty because the child is dirty
|
||||
self.assertEqual(set(['objects']), obj.obj_what_changed())
|
||||
obj.objects[0].obj_reset_changes()
|
||||
# This should now look clean because the child is clean
|
||||
self.assertEqual(set(), obj.obj_what_changed())
|
||||
|
||||
|
||||
class TestObjectSerializer(test_base.TestCase):
|
||||
|
||||
def test_serialize_entity_primitive(self):
|
||||
ser = base.WatcherObjectSerializer()
|
||||
for thing in (1, 'foo', [1, 2], {'foo': 'bar'}):
|
||||
self.assertEqual(thing, ser.serialize_entity(None, thing))
|
||||
|
||||
def test_deserialize_entity_primitive(self):
|
||||
ser = base.WatcherObjectSerializer()
|
||||
for thing in (1, 'foo', [1, 2], {'foo': 'bar'}):
|
||||
self.assertEqual(thing, ser.deserialize_entity(None, thing))
|
||||
|
||||
def test_object_serialization(self):
|
||||
ser = base.WatcherObjectSerializer()
|
||||
obj = MyObj(self.context)
|
||||
primitive = ser.serialize_entity(self.context, obj)
|
||||
self.assertTrue('watcher_object.name' in primitive)
|
||||
obj2 = ser.deserialize_entity(self.context, primitive)
|
||||
self.assertIsInstance(obj2, MyObj)
|
||||
self.assertEqual(self.context, obj2._context)
|
||||
|
||||
def test_object_serialization_iterables(self):
|
||||
ser = base.WatcherObjectSerializer()
|
||||
obj = MyObj(self.context)
|
||||
for iterable in (list, tuple, set):
|
||||
thing = iterable([obj])
|
||||
primitive = ser.serialize_entity(self.context, thing)
|
||||
self.assertEqual(1, len(primitive))
|
||||
for item in primitive:
|
||||
self.assertFalse(isinstance(item, base.WatcherObject))
|
||||
thing2 = ser.deserialize_entity(self.context, primitive)
|
||||
self.assertEqual(1, len(thing2))
|
||||
for item in thing2:
|
||||
self.assertIsInstance(item, MyObj)
|
||||
137
watcher/tests/objects/utils.py
Normal file
137
watcher/tests/objects/utils.py
Normal file
@@ -0,0 +1,137 @@
|
||||
# Copyright 2014 Rackspace Hosting
|
||||
# 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.
|
||||
"""Watcher object test utilities."""
|
||||
|
||||
from watcher import objects
|
||||
from watcher.tests.db import utils as db_utils
|
||||
|
||||
|
||||
def get_test_audit_template(context, **kw):
|
||||
"""Return a AuditTemplate object with appropriate attributes.
|
||||
|
||||
NOTE: The object leaves the attributes marked as changed, such
|
||||
that a create() could be used to commit it to the DB.
|
||||
"""
|
||||
db_audit_template = db_utils.get_test_audit_template(**kw)
|
||||
# Let DB generate ID if it isn't specified explicitly
|
||||
if 'id' not in kw:
|
||||
del db_audit_template['id']
|
||||
audit_template = objects.AuditTemplate(context)
|
||||
for key in db_audit_template:
|
||||
setattr(audit_template, key, db_audit_template[key])
|
||||
|
||||
return audit_template
|
||||
|
||||
|
||||
def create_test_audit_template(context, **kw):
|
||||
"""Create and return a test audit_template object.
|
||||
|
||||
Create a audit template in the DB and return an AuditTemplate object
|
||||
with appropriate attributes.
|
||||
"""
|
||||
audit_template = get_test_audit_template(context, **kw)
|
||||
audit_template.create()
|
||||
return audit_template
|
||||
|
||||
|
||||
def get_test_audit(context, **kw):
|
||||
"""Return a Audit object with appropriate attributes.
|
||||
|
||||
NOTE: The object leaves the attributes marked as changed, such
|
||||
that a create() could be used to commit it to the DB.
|
||||
"""
|
||||
db_audit = db_utils.get_test_audit(**kw)
|
||||
# Let DB generate ID if it isn't specified explicitly
|
||||
if 'id' not in kw:
|
||||
del db_audit['id']
|
||||
audit = objects.Audit(context)
|
||||
for key in db_audit:
|
||||
setattr(audit, key, db_audit[key])
|
||||
return audit
|
||||
|
||||
|
||||
def create_test_audit(context, **kw):
|
||||
"""Create and return a test audit object.
|
||||
|
||||
Create a audit in the DB and return an Audit object with appropriate
|
||||
attributes.
|
||||
"""
|
||||
audit = get_test_audit(context, **kw)
|
||||
audit.create()
|
||||
return audit
|
||||
|
||||
|
||||
def get_test_action_plan(context, **kw):
|
||||
"""Return a ActionPlan object with appropriate attributes.
|
||||
|
||||
NOTE: The object leaves the attributes marked as changed, such
|
||||
that a create() could be used to commit it to the DB.
|
||||
"""
|
||||
db_action_plan = db_utils.get_test_action_plan(**kw)
|
||||
# Let DB generate ID if it isn't specified explicitly
|
||||
if 'id' not in kw:
|
||||
del db_action_plan['id']
|
||||
action_plan = objects.ActionPlan(context)
|
||||
for key in db_action_plan:
|
||||
setattr(action_plan, key, db_action_plan[key])
|
||||
return action_plan
|
||||
|
||||
|
||||
def create_test_action_plan(context, **kw):
|
||||
"""Create and return a test action_plan object.
|
||||
|
||||
Create a action plan in the DB and return a ActionPlan object with
|
||||
appropriate attributes.
|
||||
"""
|
||||
action_plan = get_test_action_plan(context, **kw)
|
||||
action_plan.create()
|
||||
return action_plan
|
||||
|
||||
|
||||
def create_action_plan_without_audit(context, **kw):
|
||||
"""Create and return a test action_plan object.
|
||||
|
||||
Create a action plan in the DB and return a ActionPlan object with
|
||||
appropriate attributes.
|
||||
"""
|
||||
kw['audit_id'] = None
|
||||
return create_test_action_plan(context, **kw)
|
||||
|
||||
|
||||
def get_test_action(context, **kw):
|
||||
"""Return a Action object with appropriate attributes.
|
||||
|
||||
NOTE: The object leaves the attributes marked as changed, such
|
||||
that a create() could be used to commit it to the DB.
|
||||
"""
|
||||
db_action = db_utils.get_test_action(**kw)
|
||||
# Let DB generate ID if it isn't specified explicitly
|
||||
if 'id' not in kw:
|
||||
del db_action['id']
|
||||
action = objects.Action(context)
|
||||
for key in db_action:
|
||||
setattr(action, key, db_action[key])
|
||||
return action
|
||||
|
||||
|
||||
def create_test_action(context, **kw):
|
||||
"""Create and return a test action object.
|
||||
|
||||
Create a action in the DB and return a Action object with appropriate
|
||||
attributes.
|
||||
"""
|
||||
action = get_test_action(context, **kw)
|
||||
action.create()
|
||||
return action
|
||||
39
watcher/tests/policy_fixture.py
Normal file
39
watcher/tests/policy_fixture.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# Copyright 2012 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 oslo_config import cfg
|
||||
|
||||
from watcher.common import policy as w_policy
|
||||
from watcher.tests import fake_policy
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class PolicyFixture(fixtures.Fixture):
|
||||
def __init__(self, compat=None):
|
||||
self.compat = compat
|
||||
|
||||
def setUp(self):
|
||||
super(PolicyFixture, self).setUp()
|
||||
self.policy_dir = self.useFixture(fixtures.TempDir())
|
||||
self.policy_file_name = os.path.join(self.policy_dir.path,
|
||||
'policy.json')
|
||||
with open(self.policy_file_name, 'w') as policy_file:
|
||||
policy_file.write(fake_policy.get_policy_data(self.compat))
|
||||
CONF.set_override('policy_file', self.policy_file_name)
|
||||
w_policy._ENFORCER = None
|
||||
self.addCleanup(w_policy.get_enforcer().clear)
|
||||
19
watcher/tests/test_units.py
Normal file
19
watcher/tests/test_units.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# 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 unittest
|
||||
|
||||
|
||||
class TestUnits(unittest.TestCase):
|
||||
|
||||
def test_units(self):
|
||||
assert 5 * 5 == 25
|
||||
28
watcher/tests/test_watcher.py
Normal file
28
watcher/tests/test_watcher.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
test_watcher
|
||||
----------------------------------
|
||||
|
||||
Tests for `watcher` module.
|
||||
"""
|
||||
|
||||
from watcher.tests import base
|
||||
|
||||
|
||||
class TestWatcher(base.TestCase):
|
||||
|
||||
def test_something(self):
|
||||
pass
|
||||
Reference in New Issue
Block a user