Make Aggregate.save work with the API db

Aggregate.save now updates aggregates if they
are located in the api db.

blueprint cells-aggregate-api-db

Co-Authored-By: Dan Smith <dansmith@redhat.com>
Change-Id: I6066857a421bfe489ae70f0e22f338c434195bf9
This commit is contained in:
Mark Doffman 2016-04-06 14:53:33 -05:00 committed by Dan Smith
parent 32b7526b3c
commit ffe8b92a6f
3 changed files with 132 additions and 8 deletions

View File

@ -159,6 +159,35 @@ def _metadata_delete_from_db(context, aggregate_id, key):
aggregate_id=aggregate_id, metadata_key=key)
@db_api.api_context_manager.writer
def _aggregate_update_to_db(context, aggregate_id, values):
aggregate = _aggregate_get_from_db(context, aggregate_id)
set_delete = True
if "availability_zone" in values:
az = values.pop('availability_zone')
if 'metadata' not in values:
values['metadata'] = {'availability_zone': az}
set_delete = False
else:
values['metadata']['availability_zone'] = az
metadata = values.get('metadata')
if metadata is not None:
_metadata_add_to_db(context, aggregate_id, values.pop('metadata'),
set_delete=set_delete)
aggregate.update(values)
try:
aggregate.save(context.session)
except db_exc.DBDuplicateEntry:
if 'name' in values:
raise exception.AggregateNameExists(
aggregate_name=values['name'])
else:
raise
return _aggregate_get_from_db(context, aggregate_id)
@base.NovaObjectRegistry.register
class Aggregate(base.NovaPersistentObject, base.NovaObject):
# Version 1.0: Initial version
@ -295,7 +324,13 @@ class Aggregate(base.NovaPersistentObject, base.NovaObject):
"updateprop.start",
payload)
updates.pop('id', None)
db_aggregate = db.aggregate_update(self._context, self.id, updates)
db_aggregate = None
try:
db_aggregate = _aggregate_update_to_db(self._context,
self.id, updates)
except exception.AggregateNotFound:
db_aggregate = db.aggregate_update(self._context, self.id, updates)
compute_utils.notify_about_aggregate_update(self._context,
"updateprop.end",
payload)

View File

@ -205,6 +205,74 @@ class AggregateObjectDbTestCase(test.NoDBTestCase):
key='goodkey')
self.assertEqual(2, len(rl1))
def test_aggregate_update(self):
created = _create_aggregate(self.context,
metadata={'availability_zone': 'fake_avail_zone'})
result = aggregate_obj._aggregate_get_from_db(self.context,
created['id'])
self.assertEqual(result['availability_zone'], 'fake_avail_zone')
new_values = deepcopy(_get_fake_aggregate(1, result=False))
new_values['availability_zone'] = 'different_avail_zone'
updated = aggregate_obj._aggregate_update_to_db(self.context,
result['id'], new_values)
self.assertEqual('different_avail_zone', updated['availability_zone'])
def test_aggregate_update_with_metadata(self):
result = _create_aggregate(self.context, metadata=None)
values = deepcopy(_get_fake_aggregate(1, result=False))
values['metadata'] = deepcopy(_get_fake_metadata(1))
values['availability_zone'] = 'different_avail_zone'
expected_metadata = deepcopy(values['metadata'])
expected_metadata['availability_zone'] = values['availability_zone']
aggregate_obj._aggregate_update_to_db(self.context, result['id'],
values)
metadata = _aggregate_metadata_get_all(self.context, result['id'])
updated = aggregate_obj._aggregate_get_from_db(self.context,
result['id'])
self.assertThat(metadata,
matchers.DictMatches(expected_metadata))
self.assertEqual('different_avail_zone', updated['availability_zone'])
def test_aggregate_update_with_existing_metadata(self):
result = _create_aggregate(self.context)
values = deepcopy(_get_fake_aggregate(1, result=False))
values['metadata'] = deepcopy(_get_fake_metadata(1))
values['metadata']['fake_key1'] = 'foo'
expected_metadata = deepcopy(values['metadata'])
aggregate_obj._aggregate_update_to_db(self.context, result['id'],
values)
metadata = _aggregate_metadata_get_all(self.context, result['id'])
self.assertThat(metadata, matchers.DictMatches(expected_metadata))
def test_aggregate_update_zone_with_existing_metadata(self):
result = _create_aggregate(self.context)
new_zone = {'availability_zone': 'fake_avail_zone_2'}
metadata = deepcopy(_get_fake_metadata(1))
metadata.update(new_zone)
aggregate_obj._aggregate_update_to_db(self.context, result['id'],
new_zone)
expected = _aggregate_metadata_get_all(self.context, result['id'])
self.assertThat(metadata, matchers.DictMatches(expected))
def test_aggregate_update_raise_not_found(self):
# this does not exist!
aggregate_id = 2
new_values = deepcopy(_get_fake_aggregate(1, result=False))
self.assertRaises(exception.AggregateNotFound,
aggregate_obj._aggregate_update_to_db,
self.context, aggregate_id, new_values)
def test_aggregate_update_raise_name_exist(self):
_create_aggregate(self.context, values={'name': 'test1'},
metadata={'availability_zone': 'fake_avail_zone'})
_create_aggregate(self.context, values={'name': 'test2'},
metadata={'availability_zone': 'fake_avail_zone'})
aggregate_id = 1
new_values = {'name': 'test2'}
self.assertRaises(exception.AggregateNameExists,
aggregate_obj._aggregate_update_to_db,
self.context, aggregate_id, new_values)
def test_aggregate_host_add_to_db(self):
result = _create_aggregate(self.context, metadata=None)
host = _get_fake_hosts(1)[0]

View File

@ -140,18 +140,39 @@ class _TestAggregateObject(object):
{'name': 'foo', 'uuid': uuidsentinel.fake_agg},
metadata={'one': 'two'})
@mock.patch.object(db, 'aggregate_update')
def test_save(self, mock_aggregate_update):
mock_aggregate_update.return_value = fake_aggregate
@mock.patch('nova.objects.aggregate._aggregate_update_to_db')
@mock.patch('nova.db.aggregate_update')
def test_save(self, update_mock, api_update_mock):
api_update_mock.side_effect = exception.AggregateNotFound(
aggregate_id='foo')
update_mock.return_value = fake_aggregate
agg = aggregate.Aggregate(context=self.context)
agg.id = 123
agg.name = 'baz'
agg.name = 'fake-aggregate'
agg.save()
self.compare_obj(agg, fake_aggregate, subs=SUBS)
update_mock.assert_called_once_with(self.context,
123,
{'name': 'fake-aggregate'})
self.assertTrue(api_update_mock.called)
mock_aggregate_update.aasert_called_once_with(self.context,
123, {'name': 'baz'})
@mock.patch('nova.objects.Aggregate.in_api', return_value=True)
@mock.patch('nova.objects.aggregate._aggregate_update_to_db')
@mock.patch('nova.db.aggregate_update')
def test_save_to_api(self, update_mock, api_update_mock, in_api_mock):
api_update_mock.return_value = fake_aggregate
agg = aggregate.Aggregate(context=self.context)
agg.id = 123
agg.name = 'fake-api-aggregate'
agg.save()
self.compare_obj(agg, fake_aggregate, subs=SUBS)
api_update_mock.assert_called_once_with(self.context,
123,
{'name': 'fake-api-aggregate'})
self.assertFalse(update_mock.called)
api_update_mock.assert_called_once_with(self.context,
123, {'name': 'fake-api-aggregate'})
def test_save_and_create_no_hosts(self):
agg = aggregate.Aggregate(context=self.context)