diff --git a/blazar/plugins/instances/instance_plugin.py b/blazar/plugins/instances/instance_plugin.py index 5136a58f..8b2be7b7 100644 --- a/blazar/plugins/instances/instance_plugin.py +++ b/blazar/plugins/instances/instance_plugin.py @@ -257,7 +257,43 @@ class VirtualInstancePlugin(base.BasePlugin, nova.NovaClientWrapper): "support updates of reservation.") def on_start(self, resource_id): - pass + ctx = context.current() + instance_reservation = db_api.instance_reservation_get(resource_id) + reservation_id = instance_reservation['reservation_id'] + + try: + self.nova.flavor_access.add_tenant_access(reservation_id, + ctx.project_id) + except nova_exceptions.ClientException: + LOG.info('Failed to associate flavor %s to project %s' % + (reservation_id, ctx.project_id)) + raise mgr_exceptions.EventError() + + pool = nova.ReservationPool() + for allocation in db_api.host_allocation_get_all_by_values( + reservation_id=reservation_id): + host = db_api.host_get(allocation['compute_host_id']) + pool.add_computehost(instance_reservation['aggregate_id'], + host['service_name'], stay_in=True) def on_end(self, resource_id): - pass + instance_reservation = db_api.instance_reservation_get(resource_id) + ctx = context.current() + + try: + self.nova.flavor_access.remove_tenant_access( + instance_reservation['reservation_id'], ctx.project_id) + except nova_exceptions.NotFound: + pass + + allocations = db_api.host_allocation_get_all_by_values( + reservation_id=instance_reservation['reservation_id']) + for allocation in allocations: + db_api.host_allocation_destroy(allocation['id']) + + for server in self.nova.servers.list(search_opts={ + 'flavor': instance_reservation['reservation_id'], + 'all_tenants': 1}, detailed=False): + server.delete() + + self.cleanup_resources(instance_reservation) diff --git a/blazar/tests/plugins/instances/test_instance_plugin.py b/blazar/tests/plugins/instances/test_instance_plugin.py index 941801f2..b931f05b 100644 --- a/blazar/tests/plugins/instances/test_instance_plugin.py +++ b/blazar/tests/plugins/instances/test_instance_plugin.py @@ -444,3 +444,75 @@ class TestVirtualInstancePlugin(tests.TestCase): metadata={'reservation': 'reservation-id1', 'filter_tenant_id': 'fake-project', 'affinity_id': 'server_group_id1'}) + + def test_on_start(self): + def fake_host_get(host_id): + return {'service_name': 'host' + host_id[-1]} + + self.set_context(context.BlazarContext(project_id='fake-project')) + plugin = instance_plugin.VirtualInstancePlugin() + + mock_inst_get = self.patch(db_api, 'instance_reservation_get') + mock_inst_get.return_value = {'reservation_id': 'reservation-id1', + 'aggregate_id': 'aggregate-id1'} + + mock_nova = mock.MagicMock() + type(plugin).nova = mock_nova + + fake_pool = mock.MagicMock() + mock_pool = self.patch(nova, 'ReservationPool') + mock_pool.return_value = fake_pool + + mock_alloc_get = self.patch(db_api, + 'host_allocation_get_all_by_values') + mock_alloc_get.return_value = [ + {'compute_host_id': 'host-id1'}, {'compute_host_id': 'host-id2'}, + {'compute_host_id': 'host-id3'}] + + mock_host_get = self.patch(db_api, 'host_get') + mock_host_get.side_effect = fake_host_get + + plugin.on_start('resource-id1') + + mock_nova.flavor_access.add_tenant_access.assert_called_once_with( + 'reservation-id1', 'fake-project') + for i in range(3): + fake_pool.add_computehost.assert_any_call('aggregate-id1', + 'host' + str(i + 1), + stay_in=True) + + def test_on_end(self): + self.set_context(context.BlazarContext(project_id='fake-project-id')) + + plugin = instance_plugin.VirtualInstancePlugin() + + fake_instance_reservation = {'reservation_id': 'reservation-id1'} + mock_inst_get = self.patch(db_api, 'instance_reservation_get') + mock_inst_get.return_value = fake_instance_reservation + + mock_alloc_get = self.patch(db_api, + 'host_allocation_get_all_by_values') + mock_alloc_get.return_value = [{'id': 'host-alloc-id1'}, + {'id': 'host-alloc-id2'}] + + self.patch(db_api, 'host_allocation_destroy') + + fake_servers = [mock.MagicMock(method='delete') for i in range(5)] + mock_nova = mock.MagicMock() + type(plugin).nova = mock_nova + mock_nova.servers.list.return_value = fake_servers + + mock_cleanup_resources = self.patch(plugin, 'cleanup_resources') + + plugin.on_end('resource-id1') + + mock_nova.flavor_access.remove_tenant_access.assert_called_once_with( + 'reservation-id1', 'fake-project-id') + + mock_nova.servers.list.assert_called_once_with( + search_opts={'flavor': 'reservation-id1', 'all_tenants': 1}, + detailed=False) + for fake in fake_servers: + fake.delete.assert_called_once() + mock_cleanup_resources.assert_called_once_with( + fake_instance_reservation) diff --git a/blazar/utils/openstack/nova.py b/blazar/utils/openstack/nova.py index 99d022f7..58d07e3e 100644 --- a/blazar/utils/openstack/nova.py +++ b/blazar/utils/openstack/nova.py @@ -323,7 +323,7 @@ class ReservationPool(NovaClientWrapper): except manager_exceptions.AggregateNotFound: return [] - def add_computehost(self, pool, host): + def add_computehost(self, pool, host, stay_in=False): """Add a compute host to an aggregate. The `host` must exist otherwise raise an error @@ -344,7 +344,7 @@ class ReservationPool(NovaClientWrapper): except manager_exceptions.AggregateNotFound: raise manager_exceptions.NoFreePool() - if freepool_agg.id != agg.id: + if freepool_agg.id != agg.id and not stay_in: if host not in freepool_agg.hosts: raise manager_exceptions.HostNotInFreePool( host=host, freepool_name=freepool_agg.name)