731 lines
34 KiB
Python
731 lines
34 KiB
Python
# Copyright (c) 2012 Citrix Systems, 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.
|
|
|
|
"""Tests for the aggregates admin api."""
|
|
|
|
import mock
|
|
from oslo_utils.fixture import uuidsentinel
|
|
from webob import exc
|
|
|
|
from nova.api.openstack import api_version_request
|
|
from nova.api.openstack.compute import aggregates as aggregates_v21
|
|
from nova.compute import api as compute_api
|
|
from nova import context
|
|
from nova import exception
|
|
from nova import objects
|
|
from nova.objects import base as obj_base
|
|
from nova import test
|
|
from nova.tests.unit.api.openstack import fakes
|
|
|
|
|
|
def _make_agg_obj(agg_dict):
|
|
return objects.Aggregate(**agg_dict)
|
|
|
|
|
|
def _make_agg_list(agg_list):
|
|
return objects.AggregateList(objects=[_make_agg_obj(a) for a in agg_list])
|
|
|
|
|
|
def _transform_aggregate_az(agg_dict):
|
|
# the Aggregate object looks for availability_zone within metadata,
|
|
# so if availability_zone is in the top-level dict, move it down into
|
|
# metadata. We also have to delete the key from the top-level dict because
|
|
# availability_zone is a read-only property on the Aggregate object
|
|
md = agg_dict.get('metadata', {})
|
|
if 'availability_zone' in agg_dict:
|
|
md['availability_zone'] = agg_dict['availability_zone']
|
|
del agg_dict['availability_zone']
|
|
agg_dict['metadata'] = md
|
|
return agg_dict
|
|
|
|
|
|
def _transform_aggregate_list_azs(agg_list):
|
|
for agg_dict in agg_list:
|
|
yield _transform_aggregate_az(agg_dict)
|
|
|
|
|
|
AGGREGATE_LIST = [
|
|
{"name": "aggregate1", "id": "1",
|
|
"metadata": {"availability_zone": "nova1"}},
|
|
{"name": "aggregate2", "id": "2",
|
|
"metadata": {"availability_zone": "nova1"}},
|
|
{"name": "aggregate3", "id": "3",
|
|
"metadata": {"availability_zone": "nova2"}},
|
|
{"name": "aggregate1", "id": "4",
|
|
"metadata": {"availability_zone": "nova1"}}]
|
|
AGGREGATE_LIST = _make_agg_list(AGGREGATE_LIST)
|
|
|
|
AGGREGATE = {"name": "aggregate1",
|
|
"id": "1",
|
|
"metadata": {"foo": "bar",
|
|
"availability_zone": "nova1"},
|
|
"hosts": ["host1", "host2"]}
|
|
AGGREGATE = _make_agg_obj(AGGREGATE)
|
|
|
|
FORMATTED_AGGREGATE = {"name": "aggregate1",
|
|
"id": "1",
|
|
"metadata": {"availability_zone": "nova1"}}
|
|
FORMATTED_AGGREGATE = _make_agg_obj(FORMATTED_AGGREGATE)
|
|
|
|
|
|
class FakeRequest(object):
|
|
environ = {"nova.context": context.get_admin_context()}
|
|
|
|
|
|
class AggregateTestCaseV21(test.NoDBTestCase):
|
|
"""Test Case for aggregates admin api."""
|
|
|
|
add_host = 'self.controller._add_host'
|
|
remove_host = 'self.controller._remove_host'
|
|
set_metadata = 'self.controller._set_metadata'
|
|
bad_request = exception.ValidationError
|
|
|
|
def _set_up(self):
|
|
self.controller = aggregates_v21.AggregateController()
|
|
self.req = fakes.HTTPRequest.blank('/v2/os-aggregates',
|
|
use_admin_context=True)
|
|
self.user_req = fakes.HTTPRequest.blank('/v2/os-aggregates')
|
|
self.context = self.req.environ['nova.context']
|
|
|
|
def setUp(self):
|
|
super(AggregateTestCaseV21, self).setUp()
|
|
self._set_up()
|
|
|
|
def test_index(self):
|
|
def _list_aggregates(context):
|
|
if context is None:
|
|
raise Exception()
|
|
return AGGREGATE_LIST
|
|
|
|
with mock.patch.object(self.controller.api, 'get_aggregate_list',
|
|
side_effect=_list_aggregates) as mock_list:
|
|
result = self.controller.index(self.req)
|
|
result = _transform_aggregate_list_azs(result['aggregates'])
|
|
self._assert_agg_data(AGGREGATE_LIST, _make_agg_list(result))
|
|
self.assertTrue(mock_list.called)
|
|
|
|
def test_index_no_admin(self):
|
|
self.assertRaises(exception.PolicyNotAuthorized,
|
|
self.controller.index,
|
|
self.user_req)
|
|
|
|
def test_create(self):
|
|
with mock.patch.object(self.controller.api, 'create_aggregate',
|
|
return_value=AGGREGATE) as mock_create:
|
|
result = self.controller.create(self.req,
|
|
body={"aggregate": {"name": "test",
|
|
"availability_zone": "nova1"}})
|
|
result = _transform_aggregate_az(result['aggregate'])
|
|
self._assert_agg_data(FORMATTED_AGGREGATE, _make_agg_obj(result))
|
|
mock_create.assert_called_once_with(self.context, 'test', 'nova1')
|
|
|
|
def test_create_no_admin(self):
|
|
self.assertRaises(exception.PolicyNotAuthorized,
|
|
self.controller.create, self.user_req,
|
|
body={"aggregate": {"name": "test",
|
|
"availability_zone": "nova1"}})
|
|
|
|
def test_create_with_duplicate_aggregate_name(self):
|
|
side_effect = exception.AggregateNameExists(aggregate_name="test")
|
|
with mock.patch.object(self.controller.api, 'create_aggregate',
|
|
side_effect=side_effect) as mock_create:
|
|
self.assertRaises(exc.HTTPConflict, self.controller.create,
|
|
self.req, body={"aggregate": {"name": "test",
|
|
"availability_zone": "nova1"}})
|
|
mock_create.assert_called_once_with(self.context, 'test', 'nova1')
|
|
|
|
@mock.patch.object(compute_api.AggregateAPI, 'create_aggregate')
|
|
def test_create_with_unmigrated_aggregates(self, mock_create_aggregate):
|
|
mock_create_aggregate.side_effect = \
|
|
exception.ObjectActionError(action='create',
|
|
reason='main database still contains aggregates')
|
|
|
|
self.assertRaises(exc.HTTPConflict, self.controller.create,
|
|
self.req, body={"aggregate":
|
|
{"name": "test",
|
|
"availability_zone": "nova1"}})
|
|
|
|
def test_create_with_incorrect_availability_zone(self):
|
|
side_effect = exception.InvalidAggregateAction(
|
|
action='create_aggregate',
|
|
aggregate_id="'N/A'",
|
|
reason='invalid zone')
|
|
with mock.patch.object(self.controller.api, 'create_aggregate',
|
|
side_effect=side_effect) as mock_create:
|
|
self.assertRaises(exc.HTTPBadRequest, self.controller.create,
|
|
self.req,
|
|
body={"aggregate": {"name": "test",
|
|
"availability_zone": "nova_bad"}})
|
|
mock_create.assert_called_once_with(self.context, 'test',
|
|
'nova_bad')
|
|
|
|
def test_create_with_no_aggregate(self):
|
|
self.assertRaises(self.bad_request, self.controller.create,
|
|
self.req, body={"foo":
|
|
{"name": "test",
|
|
"availability_zone": "nova1"}})
|
|
|
|
def test_create_with_no_name(self):
|
|
self.assertRaises(self.bad_request, self.controller.create,
|
|
self.req, body={"aggregate":
|
|
{"foo": "test",
|
|
"availability_zone": "nova1"}})
|
|
|
|
def test_create_name_with_leading_trailing_spaces(self):
|
|
self.assertRaises(self.bad_request, self.controller.create,
|
|
self.req, body={"aggregate":
|
|
{"name": " test ",
|
|
"availability_zone": "nova1"}})
|
|
|
|
def test_create_name_with_leading_trailing_spaces_compat_mode(self):
|
|
|
|
def fake_mock_aggs(context, name, az):
|
|
self.assertEqual('test', name)
|
|
return AGGREGATE
|
|
|
|
with mock.patch.object(compute_api.AggregateAPI,
|
|
'create_aggregate') as mock_aggs:
|
|
mock_aggs.side_effect = fake_mock_aggs
|
|
self.req.set_legacy_v2()
|
|
self.controller.create(self.req,
|
|
body={"aggregate":
|
|
{"name": " test ",
|
|
"availability_zone": "nova1"}})
|
|
|
|
def test_create_with_no_availability_zone(self):
|
|
with mock.patch.object(self.controller.api, 'create_aggregate',
|
|
return_value=AGGREGATE) as mock_create:
|
|
result = self.controller.create(self.req,
|
|
body={"aggregate": {"name": "test"}})
|
|
|
|
result = _transform_aggregate_az(result['aggregate'])
|
|
self._assert_agg_data(FORMATTED_AGGREGATE, _make_agg_obj(result))
|
|
mock_create.assert_called_once_with(self.context, 'test', None)
|
|
|
|
def test_create_with_null_name(self):
|
|
self.assertRaises(self.bad_request, self.controller.create,
|
|
self.req, body={"aggregate":
|
|
{"name": "",
|
|
"availability_zone": "nova1"}})
|
|
|
|
def test_create_with_name_too_long(self):
|
|
self.assertRaises(self.bad_request, self.controller.create,
|
|
self.req, body={"aggregate":
|
|
{"name": "x" * 256,
|
|
"availability_zone": "nova1"}})
|
|
|
|
def test_create_with_availability_zone_too_long(self):
|
|
self.assertRaises(self.bad_request, self.controller.create,
|
|
self.req, body={"aggregate":
|
|
{"name": "test",
|
|
"availability_zone": "x" * 256}})
|
|
|
|
def test_create_with_availability_zone_invalid(self):
|
|
self.assertRaises(self.bad_request, self.controller.create,
|
|
self.req, body={"aggregate":
|
|
{"name": "test",
|
|
"availability_zone": "bad:az"}})
|
|
|
|
def test_create_availability_zone_with_leading_trailing_spaces(self):
|
|
self.assertRaises(self.bad_request, self.controller.create,
|
|
self.req, body={"aggregate":
|
|
{"name": "test",
|
|
"availability_zone": " nova1 "}})
|
|
|
|
def test_create_availability_zone_with_leading_trailing_spaces_compat_mode(
|
|
self):
|
|
|
|
def fake_mock_aggs(context, name, az):
|
|
self.assertEqual('nova1', az)
|
|
return AGGREGATE
|
|
|
|
with mock.patch.object(compute_api.AggregateAPI,
|
|
'create_aggregate') as mock_aggs:
|
|
mock_aggs.side_effect = fake_mock_aggs
|
|
self.req.set_legacy_v2()
|
|
self.controller.create(self.req,
|
|
body={"aggregate":
|
|
{"name": "test",
|
|
"availability_zone": " nova1 "}})
|
|
|
|
def test_create_with_empty_availability_zone(self):
|
|
self.assertRaises(self.bad_request, self.controller.create,
|
|
self.req, body={"aggregate":
|
|
{"name": "test",
|
|
"availability_zone": ""}})
|
|
|
|
@mock.patch('nova.compute.api.AggregateAPI.create_aggregate')
|
|
def test_create_with_none_availability_zone(self, mock_create_agg):
|
|
mock_create_agg.return_value = objects.Aggregate(
|
|
self.context,
|
|
name='test',
|
|
uuid=uuidsentinel.aggregate,
|
|
hosts=[],
|
|
metadata={})
|
|
body = {"aggregate": {"name": "test",
|
|
"availability_zone": None}}
|
|
result = self.controller.create(self.req, body=body)
|
|
mock_create_agg.assert_called_once_with(self.context, 'test', None)
|
|
self.assertEqual(result['aggregate']['name'], 'test')
|
|
|
|
def test_create_with_extra_invalid_arg(self):
|
|
self.assertRaises(self.bad_request, self.controller.create,
|
|
self.req, body={"name": "test",
|
|
"availability_zone": "nova1",
|
|
"foo": 'bar'})
|
|
|
|
def test_show(self):
|
|
with mock.patch.object(self.controller.api, 'get_aggregate',
|
|
return_value=AGGREGATE) as mock_get:
|
|
aggregate = self.controller.show(self.req, "1")
|
|
aggregate = _transform_aggregate_az(aggregate['aggregate'])
|
|
self._assert_agg_data(AGGREGATE, _make_agg_obj(aggregate))
|
|
mock_get.assert_called_once_with(self.context, '1')
|
|
|
|
def test_show_no_admin(self):
|
|
self.assertRaises(exception.PolicyNotAuthorized,
|
|
self.controller.show,
|
|
self.user_req, "1")
|
|
|
|
def test_show_with_invalid_id(self):
|
|
side_effect = exception.AggregateNotFound(aggregate_id='2')
|
|
with mock.patch.object(self.controller.api, 'get_aggregate',
|
|
side_effect=side_effect) as mock_get:
|
|
self.assertRaises(exc.HTTPNotFound, self.controller.show,
|
|
self.req, "2")
|
|
mock_get.assert_called_once_with(self.context, '2')
|
|
|
|
def test_update(self):
|
|
body = {"aggregate": {"name": "new_name",
|
|
"availability_zone": "nova1"}}
|
|
with mock.patch.object(self.controller.api, 'update_aggregate',
|
|
return_value=AGGREGATE) as mock_update:
|
|
result = self.controller.update(self.req, "1", body=body)
|
|
|
|
result = _transform_aggregate_az(result['aggregate'])
|
|
self._assert_agg_data(AGGREGATE, _make_agg_obj(result))
|
|
mock_update.assert_called_once_with(self.context, '1',
|
|
body["aggregate"])
|
|
|
|
def test_update_no_admin(self):
|
|
body = {"aggregate": {"availability_zone": "nova"}}
|
|
self.assertRaises(exception.PolicyNotAuthorized,
|
|
self.controller.update,
|
|
self.user_req, "1", body=body)
|
|
|
|
def test_update_with_only_name(self):
|
|
body = {"aggregate": {"name": "new_name"}}
|
|
with mock.patch.object(self.controller.api, 'update_aggregate',
|
|
return_value=AGGREGATE) as mock_update:
|
|
result = self.controller.update(self.req, "1", body=body)
|
|
|
|
result = _transform_aggregate_az(result['aggregate'])
|
|
self._assert_agg_data(AGGREGATE, _make_agg_obj(result))
|
|
mock_update.assert_called_once_with(self.context, '1',
|
|
body["aggregate"])
|
|
|
|
def test_update_with_only_availability_zone(self):
|
|
body = {"aggregate": {"availability_zone": "nova1"}}
|
|
with mock.patch.object(self.controller.api, 'update_aggregate',
|
|
return_value=AGGREGATE) as mock_update:
|
|
result = self.controller.update(self.req, "1", body=body)
|
|
|
|
result = _transform_aggregate_az(result['aggregate'])
|
|
self._assert_agg_data(AGGREGATE, _make_agg_obj(result))
|
|
mock_update.assert_called_once_with(self.context, '1',
|
|
body["aggregate"])
|
|
|
|
def test_update_with_no_updates(self):
|
|
test_metadata = {"aggregate": {}}
|
|
self.assertRaises(self.bad_request, self.controller.update,
|
|
self.req, "2", body=test_metadata)
|
|
|
|
def test_update_with_no_update_key(self):
|
|
test_metadata = {"asdf": {}}
|
|
self.assertRaises(self.bad_request, self.controller.update,
|
|
self.req, "2", body=test_metadata)
|
|
|
|
def test_update_with_wrong_updates(self):
|
|
test_metadata = {"aggregate": {"status": "disable",
|
|
"foo": "bar"}}
|
|
self.assertRaises(self.bad_request, self.controller.update,
|
|
self.req, "2", body=test_metadata)
|
|
|
|
def test_update_with_null_name(self):
|
|
test_metadata = {"aggregate": {"name": ""}}
|
|
self.assertRaises(self.bad_request, self.controller.update,
|
|
self.req, "2", body=test_metadata)
|
|
|
|
def test_update_with_name_too_long(self):
|
|
test_metadata = {"aggregate": {"name": "x" * 256}}
|
|
self.assertRaises(self.bad_request, self.controller.update,
|
|
self.req, "2", body=test_metadata)
|
|
|
|
def test_update_with_availability_zone_too_long(self):
|
|
test_metadata = {"aggregate": {"availability_zone": "x" * 256}}
|
|
self.assertRaises(self.bad_request, self.controller.update,
|
|
self.req, "2", body=test_metadata)
|
|
|
|
def test_update_with_availability_zone_invalid(self):
|
|
test_metadata = {"aggregate": {"availability_zone": "bad:az"}}
|
|
self.assertRaises(self.bad_request, self.controller.update,
|
|
self.req, "2", body=test_metadata)
|
|
|
|
def test_update_with_empty_availability_zone(self):
|
|
test_metadata = {"aggregate": {"availability_zone": ""}}
|
|
self.assertRaises(self.bad_request, self.controller.update,
|
|
self.req, "2", body=test_metadata)
|
|
|
|
@mock.patch('nova.compute.api.AggregateAPI.update_aggregate')
|
|
def test_update_with_none_availability_zone(self, mock_update_agg):
|
|
agg_id = uuidsentinel.aggregate
|
|
mock_update_agg.return_value = objects.Aggregate(self.context,
|
|
name='test',
|
|
uuid=agg_id,
|
|
hosts=[],
|
|
metadata={})
|
|
body = {"aggregate": {"name": "test",
|
|
"availability_zone": None}}
|
|
result = self.controller.update(self.req, agg_id, body=body)
|
|
mock_update_agg.assert_called_once_with(self.context, agg_id,
|
|
body['aggregate'])
|
|
self.assertEqual(result['aggregate']['name'], 'test')
|
|
|
|
def test_update_with_bad_aggregate(self):
|
|
body = {"aggregate": {"name": "test_name"}}
|
|
side_effect = exception.AggregateNotFound(aggregate_id=2)
|
|
|
|
with mock.patch.object(self.controller.api, 'update_aggregate',
|
|
side_effect=side_effect) as mock_update:
|
|
self.assertRaises(exc.HTTPNotFound, self.controller.update,
|
|
self.req, "2", body=body)
|
|
mock_update.assert_called_once_with(self.context, '2',
|
|
body["aggregate"])
|
|
|
|
def test_update_with_duplicated_name(self):
|
|
body = {"aggregate": {"name": "test_name"}}
|
|
side_effect = exception.AggregateNameExists(aggregate_name="test_name")
|
|
|
|
with mock.patch.object(self.controller.api, 'update_aggregate',
|
|
side_effect=side_effect) as mock_update:
|
|
self.assertRaises(exc.HTTPConflict, self.controller.update,
|
|
self.req, "2", body=body)
|
|
mock_update.assert_called_once_with(self.context, '2',
|
|
body["aggregate"])
|
|
|
|
def test_invalid_action(self):
|
|
body = {"append_host": {"host": "host1"}}
|
|
self.assertRaises(self.bad_request,
|
|
eval(self.add_host), self.req, "1", body=body)
|
|
|
|
def test_update_with_invalid_action(self):
|
|
with mock.patch.object(self.controller.api, "update_aggregate",
|
|
side_effect=exception.InvalidAggregateAction(
|
|
action='invalid', aggregate_id='agg1', reason= "not empty")):
|
|
body = {"aggregate": {"availability_zone": "nova"}}
|
|
self.assertRaises(exc.HTTPBadRequest, self.controller.update,
|
|
self.req, "1", body=body)
|
|
|
|
def test_add_host(self):
|
|
with mock.patch.object(self.controller.api, 'add_host_to_aggregate',
|
|
return_value=AGGREGATE) as mock_add:
|
|
aggregate = eval(self.add_host)(self.req, "1",
|
|
body={"add_host": {"host":
|
|
"host1"}})
|
|
|
|
aggregate = _transform_aggregate_az(aggregate['aggregate'])
|
|
self._assert_agg_data(AGGREGATE, _make_agg_obj(aggregate))
|
|
mock_add.assert_called_once_with(self.context, "1", "host1")
|
|
|
|
def test_add_host_no_admin(self):
|
|
self.assertRaises(exception.PolicyNotAuthorized,
|
|
eval(self.add_host),
|
|
self.user_req, "1",
|
|
body={"add_host": {"host": "host1"}})
|
|
|
|
def test_add_host_with_already_added_host(self):
|
|
side_effect = exception.AggregateHostExists(aggregate_id="1",
|
|
host="host1")
|
|
with mock.patch.object(self.controller.api, 'add_host_to_aggregate',
|
|
side_effect=side_effect) as mock_add:
|
|
self.assertRaises(exc.HTTPConflict, eval(self.add_host),
|
|
self.req, "1",
|
|
body={"add_host": {"host": "host1"}})
|
|
mock_add.assert_called_once_with(self.context, "1", "host1")
|
|
|
|
def test_add_host_with_bad_aggregate(self):
|
|
side_effect = exception.AggregateNotFound(
|
|
aggregate_id="bogus_aggregate")
|
|
with mock.patch.object(self.controller.api, 'add_host_to_aggregate',
|
|
side_effect=side_effect) as mock_add:
|
|
self.assertRaises(exc.HTTPNotFound, eval(self.add_host),
|
|
self.req, "bogus_aggregate",
|
|
body={"add_host": {"host": "host1"}})
|
|
mock_add.assert_called_once_with(self.context, "bogus_aggregate",
|
|
"host1")
|
|
|
|
def test_add_host_with_bad_host(self):
|
|
side_effect = exception.ComputeHostNotFound(host="bogus_host")
|
|
with mock.patch.object(self.controller.api, 'add_host_to_aggregate',
|
|
side_effect=side_effect) as mock_add:
|
|
self.assertRaises(exc.HTTPNotFound, eval(self.add_host),
|
|
self.req, "1",
|
|
body={"add_host": {"host": "bogus_host"}})
|
|
mock_add.assert_called_once_with(self.context, "1", "bogus_host")
|
|
|
|
def test_add_host_with_missing_host(self):
|
|
self.assertRaises(self.bad_request, eval(self.add_host),
|
|
self.req, "1", body={"add_host": {"asdf": "asdf"}})
|
|
|
|
def test_add_host_with_invalid_format_host(self):
|
|
self.assertRaises(self.bad_request, eval(self.add_host),
|
|
self.req, "1", body={"add_host": {"host": "a" * 300}})
|
|
|
|
def test_add_host_with_invalid_name_host(self):
|
|
self.assertRaises(self.bad_request, eval(self.add_host),
|
|
self.req, "1", body={"add_host": {"host": "bad:host"}})
|
|
|
|
def test_add_host_with_multiple_hosts(self):
|
|
self.assertRaises(self.bad_request, eval(self.add_host),
|
|
self.req, "1", body={"add_host": {"host": ["host1", "host2"]}})
|
|
|
|
def test_add_host_raises_key_error(self):
|
|
with mock.patch.object(self.controller.api, 'add_host_to_aggregate',
|
|
side_effect=KeyError) as mock_add:
|
|
self.assertRaises(exc.HTTPInternalServerError,
|
|
eval(self.add_host), self.req, "1",
|
|
body={"add_host": {"host": "host1"}})
|
|
mock_add.assert_called_once_with(self.context, "1", "host1")
|
|
|
|
def test_add_host_with_invalid_request(self):
|
|
self.assertRaises(self.bad_request, eval(self.add_host),
|
|
self.req, "1", body={"add_host": "1"})
|
|
|
|
def test_add_host_with_non_string(self):
|
|
self.assertRaises(self.bad_request, eval(self.add_host),
|
|
self.req, "1", body={"add_host": {"host": 1}})
|
|
|
|
def test_remove_host(self):
|
|
return_value = _make_agg_obj({'metadata': {}})
|
|
with mock.patch.object(self.controller.api,
|
|
'remove_host_from_aggregate',
|
|
return_value=return_value) as mock_rem:
|
|
eval(self.remove_host)(self.req, "1",
|
|
body={"remove_host": {"host": "host1"}})
|
|
mock_rem.assert_called_once_with(self.context, "1", "host1")
|
|
|
|
def test_remove_host_no_admin(self):
|
|
self.assertRaises(exception.PolicyNotAuthorized,
|
|
eval(self.remove_host),
|
|
self.user_req, "1",
|
|
body={"remove_host": {"host": "host1"}})
|
|
|
|
def test_remove_host_with_bad_aggregate(self):
|
|
side_effect = exception.AggregateNotFound(
|
|
aggregate_id="bogus_aggregate")
|
|
with mock.patch.object(self.controller.api,
|
|
'remove_host_from_aggregate',
|
|
side_effect=side_effect) as mock_rem:
|
|
self.assertRaises(exc.HTTPNotFound, eval(self.remove_host),
|
|
self.req, "bogus_aggregate",
|
|
body={"remove_host": {"host": "host1"}})
|
|
mock_rem.assert_called_once_with(self.context, "bogus_aggregate",
|
|
"host1")
|
|
|
|
def test_remove_host_with_host_not_in_aggregate(self):
|
|
side_effect = exception.AggregateHostNotFound(aggregate_id="1",
|
|
host="host1")
|
|
with mock.patch.object(self.controller.api,
|
|
'remove_host_from_aggregate',
|
|
side_effect=side_effect) as mock_rem:
|
|
self.assertRaises(exc.HTTPNotFound, eval(self.remove_host),
|
|
self.req, "1",
|
|
body={"remove_host": {"host": "host1"}})
|
|
mock_rem.assert_called_once_with(self.context, "1", "host1")
|
|
|
|
def test_remove_host_with_bad_host(self):
|
|
side_effect = exception.ComputeHostNotFound(host="bogushost")
|
|
with mock.patch.object(self.controller.api,
|
|
'remove_host_from_aggregate',
|
|
side_effect=side_effect) as mock_rem:
|
|
self.assertRaises(exc.HTTPNotFound, eval(self.remove_host),
|
|
self.req, "1", body={"remove_host": {"host": "bogushost"}})
|
|
mock_rem.assert_called_once_with(self.context, "1", "bogushost")
|
|
|
|
def test_remove_host_with_missing_host(self):
|
|
self.assertRaises(self.bad_request, eval(self.remove_host),
|
|
self.req, "1", body={"asdf": "asdf"})
|
|
|
|
def test_remove_host_with_multiple_hosts(self):
|
|
self.assertRaises(self.bad_request, eval(self.remove_host),
|
|
self.req, "1", body={"remove_host": {"host":
|
|
["host1", "host2"]}})
|
|
|
|
def test_remove_host_with_extra_param(self):
|
|
self.assertRaises(self.bad_request, eval(self.remove_host),
|
|
self.req, "1", body={"remove_host": {"asdf": "asdf",
|
|
"host": "asdf"}})
|
|
|
|
def test_remove_host_with_invalid_request(self):
|
|
self.assertRaises(self.bad_request,
|
|
eval(self.remove_host),
|
|
self.req, "1", body={"remove_host": "1"})
|
|
|
|
def test_remove_host_with_missing_host_empty(self):
|
|
self.assertRaises(self.bad_request,
|
|
eval(self.remove_host),
|
|
self.req, "1", body={"remove_host": {}})
|
|
|
|
def test_set_metadata(self):
|
|
body = {"set_metadata": {"metadata": {"foo": "bar"}}}
|
|
with mock.patch.object(self.controller.api,
|
|
'update_aggregate_metadata',
|
|
return_value=AGGREGATE) as mock_update:
|
|
result = eval(self.set_metadata)(self.req, "1", body=body)
|
|
result = _transform_aggregate_az(result['aggregate'])
|
|
self._assert_agg_data(AGGREGATE, _make_agg_obj(result))
|
|
mock_update.assert_called_once_with(self.context, "1",
|
|
body["set_metadata"]['metadata'])
|
|
|
|
def test_set_metadata_delete(self):
|
|
body = {"set_metadata": {"metadata": {"foo": None}}}
|
|
|
|
with mock.patch.object(self.controller.api,
|
|
'update_aggregate_metadata') as mocked:
|
|
mocked.return_value = AGGREGATE
|
|
result = eval(self.set_metadata)(self.req, "1", body=body)
|
|
|
|
result = _transform_aggregate_az(result['aggregate'])
|
|
self._assert_agg_data(AGGREGATE, _make_agg_obj(result))
|
|
mocked.assert_called_once_with(self.context, "1",
|
|
body["set_metadata"]["metadata"])
|
|
|
|
def test_set_metadata_no_admin(self):
|
|
self.assertRaises(exception.PolicyNotAuthorized,
|
|
eval(self.set_metadata),
|
|
self.user_req, "1",
|
|
body={"set_metadata": {"metadata":
|
|
{"foo": "bar"}}})
|
|
|
|
def test_set_metadata_with_bad_aggregate(self):
|
|
body = {"set_metadata": {"metadata": {"foo": "bar"}}}
|
|
side_effect = exception.AggregateNotFound(aggregate_id="bad_aggregate")
|
|
|
|
with mock.patch.object(self.controller.api,
|
|
'update_aggregate_metadata',
|
|
side_effect=side_effect) as mock_update:
|
|
self.assertRaises(exc.HTTPNotFound, eval(self.set_metadata),
|
|
self.req, "bad_aggregate", body=body)
|
|
mock_update.assert_called_once_with(self.context, "bad_aggregate",
|
|
body["set_metadata"]['metadata'])
|
|
|
|
def test_set_metadata_with_missing_metadata(self):
|
|
body = {"asdf": {"foo": "bar"}}
|
|
self.assertRaises(self.bad_request, eval(self.set_metadata),
|
|
self.req, "1", body=body)
|
|
|
|
def test_set_metadata_with_extra_params(self):
|
|
body = {"metadata": {"foo": "bar"}, "asdf": {"foo": "bar"}}
|
|
self.assertRaises(self.bad_request, eval(self.set_metadata),
|
|
self.req, "1", body=body)
|
|
|
|
def test_set_metadata_without_dict(self):
|
|
body = {"set_metadata": {'metadata': 1}}
|
|
self.assertRaises(self.bad_request, eval(self.set_metadata),
|
|
self.req, "1", body=body)
|
|
|
|
def test_set_metadata_with_empty_key(self):
|
|
body = {"set_metadata": {"metadata": {"": "value"}}}
|
|
self.assertRaises(self.bad_request, eval(self.set_metadata),
|
|
self.req, "1", body=body)
|
|
|
|
def test_set_metadata_with_key_too_long(self):
|
|
body = {"set_metadata": {"metadata": {"x" * 256: "value"}}}
|
|
self.assertRaises(self.bad_request, eval(self.set_metadata),
|
|
self.req, "1", body=body)
|
|
|
|
def test_set_metadata_with_value_too_long(self):
|
|
body = {"set_metadata": {"metadata": {"key": "x" * 256}}}
|
|
self.assertRaises(self.bad_request, eval(self.set_metadata),
|
|
self.req, "1", body=body)
|
|
|
|
def test_set_metadata_with_string(self):
|
|
body = {"set_metadata": {"metadata": "test"}}
|
|
self.assertRaises(self.bad_request, eval(self.set_metadata),
|
|
self.req, "1", body=body)
|
|
|
|
def test_delete_aggregate(self):
|
|
with mock.patch.object(self.controller.api,
|
|
'delete_aggregate') as mock_del:
|
|
self.controller.delete(self.req, "1")
|
|
mock_del.assert_called_once_with(self.context, "1")
|
|
|
|
def test_delete_aggregate_no_admin(self):
|
|
self.assertRaises(exception.PolicyNotAuthorized,
|
|
self.controller.delete,
|
|
self.user_req, "1")
|
|
|
|
def test_delete_aggregate_with_bad_aggregate(self):
|
|
side_effect = exception.AggregateNotFound(
|
|
aggregate_id="bogus_aggregate")
|
|
with mock.patch.object(self.controller.api, 'delete_aggregate',
|
|
side_effect=side_effect) as mock_del:
|
|
self.assertRaises(exc.HTTPNotFound, self.controller.delete,
|
|
self.req, "bogus_aggregate")
|
|
mock_del.assert_called_once_with(self.context, "bogus_aggregate")
|
|
|
|
def test_delete_aggregate_with_host(self):
|
|
with mock.patch.object(self.controller.api, "delete_aggregate",
|
|
side_effect=exception.InvalidAggregateAction(
|
|
action="delete", aggregate_id="agg1",
|
|
reason="not empty")):
|
|
self.assertRaises(exc.HTTPBadRequest,
|
|
self.controller.delete,
|
|
self.req, "agg1")
|
|
|
|
def test_marshall_aggregate(self):
|
|
# _marshall_aggregate() just basically turns the aggregate returned
|
|
# from the AggregateAPI into a dict, so this tests that transform.
|
|
# We would expect the dictionary that comes out is the same one
|
|
# that we pump into the aggregate object in the first place
|
|
agg = {'name': 'aggregate1',
|
|
'id': 1, 'uuid': uuidsentinel.aggregate,
|
|
'metadata': {'foo': 'bar', 'availability_zone': 'nova'},
|
|
'hosts': ['host1', 'host2']}
|
|
agg_obj = _make_agg_obj(agg)
|
|
|
|
# _marshall_aggregate() puts all fields and obj_extra_fields in the
|
|
# top-level dict, so we need to put availability_zone at the top also
|
|
agg['availability_zone'] = 'nova'
|
|
|
|
avr_v240 = api_version_request.APIVersionRequest("2.40")
|
|
avr_v241 = api_version_request.APIVersionRequest("2.41")
|
|
|
|
req = mock.MagicMock(api_version_request=avr_v241)
|
|
marshalled_agg = self.controller._marshall_aggregate(req, agg_obj)
|
|
|
|
self.assertEqual(agg, marshalled_agg['aggregate'])
|
|
|
|
req = mock.MagicMock(api_version_request=avr_v240)
|
|
marshalled_agg = self.controller._marshall_aggregate(req, agg_obj)
|
|
|
|
# UUID isn't in microversion 2.40 and before
|
|
del agg['uuid']
|
|
self.assertEqual(agg, marshalled_agg['aggregate'])
|
|
|
|
def _assert_agg_data(self, expected, actual):
|
|
self.assertTrue(obj_base.obj_equal_prims(expected, actual),
|
|
"The aggregate objects were not equal")
|