initial version

Change-Id: I699e0ab082657880998d8618fe29eb7f56c6c661
This commit is contained in:
David TARDIVEL
2015-06-04 15:26:55 +02:00
parent 073c6e49cb
commit d14e057da1
316 changed files with 27260 additions and 0 deletions

42
watcher/tests/__init__.py Normal file
View 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)

View File

243
watcher/tests/api/base.py Normal file
View 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

View 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'])

View 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)

View 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
View 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)

View File

View 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'])

View 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)

View 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'])

View 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'])

View 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()

View 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)

View 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')

View File

View File

View 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)
"""""

View 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))
"""""

View 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)

View File

@@ -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)

View File

@@ -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)

View 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)

View 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)

View 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)

View 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

View 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
View 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

View File

View File

@@ -0,0 +1 @@
__author__ = 'bcom'

View File

@@ -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([])

View 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)

View 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)

View 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))

View 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
View 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

View File

104
watcher/tests/db/base.py Normal file
View 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)

View File

View 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'}})

View 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)

View 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)

View 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)

View 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
View 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)

View File

@@ -0,0 +1 @@
__author__ = 'Jean-Emile DARTOIS <jean-emile.dartois@b-com.com>'

View 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)

View 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)

View 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)

View 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

View 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

View 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)

View File

@@ -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)

View File

@@ -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)
"""

View File

@@ -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')

View 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.
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)

View File

@@ -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)

View 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)

View 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)

View 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

View 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

View 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)

View 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

View 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))
"""

View 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
View 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 = ''

View File

View 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)

View 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)

View 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)

View 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)

View 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)

View 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

View 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)

View 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

View 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