Move the ReservationPool class to utils/openstack/nova.py

The new instance reservation feature also uses reservation pools to
handle compute hosts in the freepool. However, the reservation class is
located under oshosts plugin directory.

This commit makes the ReservationPool class more general by moving it to
utils/openstack/nova.py.

Partially implements: blueprint new-instance-reservation
Change-Id: I0887574544ff0456022c90c4dea2c48b588dc3b8
This commit is contained in:
Masahito Muroi 2017-05-22 19:54:03 +09:00 committed by Hiroaki Kobayashi
parent 1547ab0d0f
commit cb3632a80a
7 changed files with 599 additions and 662 deletions

View File

@ -24,7 +24,6 @@ import blazar.manager.service
import blazar.notification.notifier
import blazar.plugins.instances.vm_plugin
import blazar.plugins.oshosts.host_plugin
import blazar.plugins.oshosts.reservation_pool
import blazar.utils.openstack.keystone
import blazar.utils.openstack.nova
@ -49,8 +48,6 @@ def list_opts():
('nova', blazar.utils.openstack.nova.nova_opts),
(blazar.plugins.instances.RESOURCE_TYPE,
blazar.plugins.instances.vm_plugin.plugin_opts),
(blazar.plugins.oshosts.RESOURCE_TYPE, itertools.chain(
blazar.plugins.oshosts.host_plugin.plugin_opts,
blazar.plugins.oshosts.reservation_pool.OPTS)),
(blazar.plugins.oshosts.RESOURCE_TYPE,
blazar.plugins.oshosts.host_plugin.plugin_opts),
]

View File

@ -27,7 +27,6 @@ from blazar.manager import exceptions as manager_ex
from blazar.plugins import base
from blazar.plugins import oshosts as plugin
from blazar.plugins.oshosts import nova_inventory
from blazar.plugins.oshosts import reservation_pool as rp
from blazar.utils.openstack import nova
from blazar.utils import trusts
@ -41,7 +40,12 @@ plugin_opts = [
default='on_start',
deprecated_for_removal=True,
deprecated_since='0.3.0',
help='Actions which we will use at the start of the lease')
help='Actions which we will use at the start of the lease'),
cfg.StrOpt('blazar_az_prefix',
default='blazar:',
deprecated_name='climate_az_prefix',
help='Prefix for Availability Zones created by Blazar'),
]
CONF = cfg.CONF
@ -53,7 +57,7 @@ class PhysicalHostPlugin(base.BasePlugin, nova.NovaClientWrapper):
resource_type = plugin.RESOURCE_TYPE
title = 'Physical Host Plugin'
description = 'This plugin starts and shutdowns the hosts.'
freepool_name = CONF[resource_type].aggregate_freepool_name
freepool_name = CONF.nova.aggregate_freepool_name
pool = None
def __init__(self):
@ -66,8 +70,11 @@ class PhysicalHostPlugin(base.BasePlugin, nova.NovaClientWrapper):
def reserve_resource(self, reservation_id, values):
"""Create reservation."""
pool = rp.ReservationPool()
pool_instance = pool.create(name=reservation_id)
pool = nova.ReservationPool()
pool_name = reservation_id
az_name = "%s%s" % (CONF[self.resource_type].blazar_az_prefix,
pool_name)
pool_instance = pool.create(name=pool_name, az=az_name)
min_hosts = values.get('min')
max_hosts = values.get('max')
if 0 <= min_hosts and min_hosts <= max_hosts:
@ -102,7 +109,6 @@ class PhysicalHostPlugin(base.BasePlugin, nova.NovaClientWrapper):
"""Update reservation."""
reservation = db_api.reservation_get(reservation_id)
lease = db_api.lease_get(reservation['lease_id'])
host_reservation = None
if (values['start_date'] < lease['start_date'] or
values['end_date'] > lease['end_date']):
allocations = []
@ -130,7 +136,7 @@ class PhysicalHostPlugin(base.BasePlugin, nova.NovaClientWrapper):
if allocations:
host_reservation = db_api.host_reservation_get(
reservation['resource_id'])
pool = rp.ReservationPool()
pool = nova.ReservationPool()
hosts_in_pool.extend(pool.get_computehosts(
host_reservation['aggregate_id']))
host_ids = self._matching_hosts(
@ -170,7 +176,7 @@ class PhysicalHostPlugin(base.BasePlugin, nova.NovaClientWrapper):
def on_start(self, resource_id):
"""Add the hosts in the pool."""
host_reservation = db_api.host_reservation_get(resource_id)
pool = rp.ReservationPool()
pool = nova.ReservationPool()
for allocation in db_api.host_allocation_get_all_by_values(
reservation_id=host_reservation['reservation_id']):
host = db_api.host_get(allocation['compute_host_id'])
@ -186,7 +192,7 @@ class PhysicalHostPlugin(base.BasePlugin, nova.NovaClientWrapper):
reservation_id=host_reservation['reservation_id'])
for allocation in allocations:
db_api.host_allocation_destroy(allocation['id'])
pool = rp.ReservationPool()
pool = nova.ReservationPool()
for host in pool.get_computehosts(host_reservation['aggregate_id']):
for server in self.nova.servers.list(
search_opts={"host": host}):
@ -250,7 +256,7 @@ class PhysicalHostPlugin(base.BasePlugin, nova.NovaClientWrapper):
extra_capabilities = dict(
(key, host_values[key]) for key in extra_capabilities_keys
)
pool = rp.ReservationPool()
pool = nova.ReservationPool()
pool.add_computehost(self.freepool_name,
host_details['service_name'])
@ -328,7 +334,7 @@ class PhysicalHostPlugin(base.BasePlugin, nova.NovaClientWrapper):
host=host['hypervisor_hostname'], servers=servers)
try:
pool = rp.ReservationPool()
pool = nova.ReservationPool()
pool.remove_computehost(self.freepool_name,
host['service_name'])
# NOTE(sbauza): Extracapabilities will be destroyed thanks to

View File

@ -1,286 +0,0 @@
# Copyright (c) 2013 OpenStack Foundation
#
# Author: Swann Croiset <swann.croiset@bull.net>
#
# 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 as uuidgen
from novaclient import exceptions as nova_exceptions
from oslo_config import cfg
from oslo_log import log as logging
from blazar import context
from blazar.manager import exceptions as manager_exceptions
from blazar.plugins import oshosts as plugin
from blazar.utils.openstack import nova
LOG = logging.getLogger(__name__)
OPTS = [
cfg.StrOpt('aggregate_freepool_name',
default='freepool',
help='Name of the special aggregate where all hosts '
'are candidate for physical host reservation'),
cfg.StrOpt('project_id_key',
default='blazar:project',
help='Aggregate metadata value for key matching project_id'),
cfg.StrOpt('blazar_owner',
default='blazar:owner',
deprecated_name='climate_owner',
help='Aggregate metadata key for knowing owner project_id'),
cfg.StrOpt('blazar_az_prefix',
default='blazar:',
deprecated_name='climate_az_prefix',
help='Prefix for Availability Zones created by Blazar'),
]
CONF = cfg.CONF
CONF.register_opts(OPTS, group=plugin.RESOURCE_TYPE)
class ReservationPool(nova.NovaClientWrapper):
def __init__(self):
super(ReservationPool, self).__init__(
username=CONF.os_admin_username,
password=CONF.os_admin_password,
user_domain_name=CONF.os_admin_user_domain_name,
project_name=CONF.os_admin_project_name,
project_domain_name=CONF.os_admin_user_domain_name)
self.config = CONF[plugin.RESOURCE_TYPE]
self.freepool_name = self.config.aggregate_freepool_name
def get_aggregate_from_name_or_id(self, aggregate_obj):
"""Return an aggregate by name or an id."""
aggregate = None
agg_id = None
try:
agg_id = int(aggregate_obj)
except (ValueError, TypeError):
if hasattr(aggregate_obj, 'id') and aggregate_obj.id:
# pool is an aggregate
agg_id = aggregate_obj.id
if agg_id is not None:
try:
aggregate = self.nova.aggregates.get(agg_id)
except nova_exceptions.NotFound:
aggregate = None
else:
# FIXME(scroiset): can't get an aggregate by name
# so iter over all aggregate and check for the good one
all_aggregates = self.nova.aggregates.list()
for agg in all_aggregates:
if aggregate_obj == agg.name:
aggregate = agg
if aggregate:
return aggregate
else:
raise manager_exceptions.AggregateNotFound(pool=aggregate_obj)
@staticmethod
def _generate_aggregate_name():
return str(uuidgen.uuid4())
def create(self, name=None, az=True):
"""Create a Pool (an Aggregate) with or without Availability Zone.
By default expose to user the aggregate with an Availability Zone.
Return an aggregate or raise a nova exception.
"""
name = name or self._generate_aggregate_name()
if az:
az_name = "%s%s" % (self.config.blazar_az_prefix,
name)
LOG.debug('Creating pool aggregate: %s '
'with Availability Zone %s' % (name, az_name))
agg = self.nova.aggregates.create(name, az_name)
else:
LOG.debug('Creating pool aggregate: %s '
'without Availability Zone' % name)
agg = self.nova.aggregates.create(name, None)
project_id = None
try:
ctx = context.current()
project_id = ctx.project_id
except RuntimeError:
e = manager_exceptions.ProjectIdNotFound()
LOG.error(e.message)
raise e
meta = {self.config.blazar_owner: project_id}
self.nova.aggregates.set_metadata(agg, meta)
return agg
def delete(self, pool, force=True):
"""Delete an aggregate.
pool can be an aggregate name or an aggregate id.
Remove all hosts before delete aggregate (default).
If force is False, raise exception if at least one
host is attached to.
"""
agg = self.get_aggregate_from_name_or_id(pool)
hosts = agg.hosts
if len(hosts) > 0 and not force:
raise manager_exceptions.AggregateHaveHost(name=agg.name,
hosts=agg.hosts)
try:
freepool_agg = self.get(self.freepool_name)
except manager_exceptions.AggregateNotFound:
raise manager_exceptions.NoFreePool()
for host in hosts:
LOG.debug("Removing host '%s' from aggregate "
"'%s')" % (host, agg.id))
self.nova.aggregates.remove_host(agg.id, host)
if freepool_agg.id != agg.id:
self.nova.aggregates.add_host(freepool_agg.id, host)
self.nova.aggregates.delete(agg.id)
def get_all(self):
"""Return all aggregate."""
return self.nova.aggregates.list()
def get(self, pool):
"""return details for aggregate pool or raise AggregateNotFound."""
return self.get_aggregate_from_name_or_id(pool)
def get_computehosts(self, pool):
"""Return a list of compute host names for an aggregate."""
try:
agg = self.get_aggregate_from_name_or_id(pool)
return agg.hosts
except manager_exceptions.AggregateNotFound:
return []
def add_computehost(self, pool, host):
"""Add a compute host to an aggregate.
The `host` must exist otherwise raise an error
and the `host` must be in the freepool.
:param pool: Name or UUID of the pool to rattach the host
:param host: Name (not UUID) of the host to associate
:type host: str
Return the related aggregate.
Raise an aggregate exception if something wrong.
"""
agg = self.get_aggregate_from_name_or_id(pool)
try:
freepool_agg = self.get(self.freepool_name)
except manager_exceptions.AggregateNotFound:
raise manager_exceptions.NoFreePool()
if freepool_agg.id != agg.id:
if host not in freepool_agg.hosts:
raise manager_exceptions.HostNotInFreePool(
host=host, freepool_name=freepool_agg.name)
LOG.info("removing host '%s' "
"from aggregate freepool %s" % (host, freepool_agg.name))
try:
self.remove_computehost(freepool_agg.id, host)
except nova_exceptions.NotFound:
raise manager_exceptions.HostNotFound(host=host)
LOG.info("adding host '%s' to aggregate %s" % (host, agg.id))
try:
return self.nova.aggregates.add_host(agg.id, host)
except nova_exceptions.NotFound:
raise manager_exceptions.HostNotFound(host=host)
except nova_exceptions.Conflict:
raise manager_exceptions.AggregateAlreadyHasHost(pool=pool,
host=host)
def remove_all_computehosts(self, pool):
"""Remove all compute hosts attached to an aggregate."""
hosts = self.get_computehosts(pool)
self.remove_computehost(pool, hosts)
def remove_computehost(self, pool, hosts):
"""Remove compute host(s) from an aggregate."""
if not isinstance(hosts, list):
hosts = [hosts]
agg = self.get_aggregate_from_name_or_id(pool)
try:
freepool_agg = self.get(self.freepool_name)
except manager_exceptions.AggregateNotFound:
raise manager_exceptions.NoFreePool()
hosts_failing_to_remove = []
hosts_failing_to_add = []
hosts_not_in_freepool = []
for host in hosts:
if freepool_agg.id == agg.id:
if host not in freepool_agg.hosts:
hosts_not_in_freepool.append(host)
continue
try:
self.nova.aggregates.remove_host(agg.id, host)
except nova_exceptions.ClientException:
hosts_failing_to_remove.append(host)
if freepool_agg.id != agg.id:
# NOTE(sbauza) : We don't want to put again the host in
# freepool if the requested pool is the freepool...
try:
self.nova.aggregates.add_host(freepool_agg.id, host)
except nova_exceptions.ClientException:
hosts_failing_to_add.append(host)
if hosts_failing_to_remove:
raise manager_exceptions.CantRemoveHost(
host=hosts_failing_to_remove, pool=agg)
if hosts_failing_to_add:
raise manager_exceptions.CantAddHost(host=hosts_failing_to_add,
pool=freepool_agg)
if hosts_not_in_freepool:
raise manager_exceptions.HostNotInFreePool(
host=hosts_not_in_freepool, freepool_name=freepool_agg.name)
def add_project(self, pool, project_id):
"""Add a project to an aggregate."""
metadata = {project_id: self.config.project_id_key}
agg = self.get_aggregate_from_name_or_id(pool)
return self.nova.aggregates.set_metadata(agg.id, metadata)
def remove_project(self, pool, project_id):
"""Remove a project from an aggregate."""
agg = self.get_aggregate_from_name_or_id(pool)
metadata = {project_id: None}
return self.nova.aggregates.set_metadata(agg.id, metadata)

View File

@ -1,336 +0,0 @@
# Copyright (c) 2013 Openstack Fondation
# 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 uuid as uuidgen
from novaclient import client as nova_client
from novaclient import exceptions as nova_exceptions
from oslo_config import cfg
from blazar import context
from blazar.manager import exceptions as manager_exceptions
from blazar.plugins import oshosts as host_plugin
from blazar.plugins.oshosts import reservation_pool as rp
from blazar import tests
from blazar.utils.openstack import base
from blazar.utils.openstack import nova
class AggregateFake(object):
def __init__(self, i, name, hosts):
self.id = i
self.name = name
self.hosts = hosts
class ReservationPoolTestCase(tests.TestCase):
def setUp(self):
super(ReservationPoolTestCase, self).setUp()
self.pool_name = 'pool-name-xxx'
self.project_id = 'project-uuid'
self.fake_aggregate = AggregateFake(i=123,
name='fooname',
hosts=['host1', 'host2'])
conf = cfg.CONF[host_plugin.RESOURCE_TYPE]
self.freepool_name = conf.aggregate_freepool_name
self.project_id_key = conf.project_id_key
self.blazar_owner = conf.blazar_owner
self.blazar_az_prefix = conf.blazar_az_prefix
self.fake_freepool = AggregateFake(i=456,
name=self.freepool_name,
hosts=['host3'])
self.set_context(context.BlazarContext(project_id=self.project_id))
self.nova_client = nova_client
self.nova = self.patch(self.nova_client, 'Client').return_value
self.patch(self.nova.aggregates, 'set_metadata')
self.patch(self.nova.aggregates, 'remove_host')
self.patch(base, 'url_for').return_value = 'http://foo.bar'
self.pool = rp.ReservationPool()
self.p_name = self.patch(self.pool, '_generate_aggregate_name')
self.p_name.return_value = self.pool_name
def _patch_get_aggregate_from_name_or_id(self):
def get_fake_aggregate(*args):
if self.freepool_name in args:
return self.fake_freepool
else:
return self.fake_aggregate
patched_pool = self.patch(self.pool, 'get_aggregate_from_name_or_id')
patched_pool.side_effect = get_fake_aggregate
def test_get_aggregate_from_name_or_id(self):
def fake_aggregate_get(id):
if id == self.fake_aggregate.id:
return self.fake_aggregate
else:
raise nova_exceptions.NotFound(id)
self.nova.aggregates.list.return_value = [self.fake_aggregate]
self.nova.aggregates.get.side_effect = fake_aggregate_get
self.assertRaises(manager_exceptions.AggregateNotFound,
self.pool.get_aggregate_from_name_or_id, 'none')
self.assertRaises(manager_exceptions.AggregateNotFound,
self.pool.get_aggregate_from_name_or_id, '3000')
self.assertEqual(self.pool.get_aggregate_from_name_or_id('fooname'),
self.fake_aggregate)
self.assertEqual(
self.pool.get_aggregate_from_name_or_id(self.fake_aggregate),
self.fake_aggregate)
def test_generate_aggregate_name(self):
self.uuidgen = uuidgen
self.patch(uuidgen, 'uuid4').return_value = 'foo'
self.assertEqual('foo', rp.ReservationPool._generate_aggregate_name())
def test_create(self):
self.patch(self.nova.aggregates, 'create').return_value = (
self.fake_aggregate)
agg = self.pool.create()
self.assertEqual(agg, self.fake_aggregate)
az_name = self.blazar_az_prefix + self.pool_name
check0 = self.nova.aggregates.create
check0.assert_called_once_with(self.pool_name, az_name)
meta = {self.blazar_owner: self.project_id}
check1 = self.nova.aggregates.set_metadata
check1.assert_called_once_with(self.fake_aggregate, meta)
def test_create_no_az(self):
self.patch(self.nova.aggregates, 'create').return_value = (
self.fake_aggregate)
self.pool.create(az=False)
self.nova.aggregates.create.assert_called_once_with(self.pool_name,
None)
def test_create_no_project_id(self):
self.patch(self.nova.aggregates, 'create').return_value = (
self.fake_aggregate)
self.nova_wrapper = self.patch(nova.NovaClientWrapper, 'nova')
def raiseRuntimeError():
raise RuntimeError()
self.context_mock.side_effect = raiseRuntimeError
self.assertRaises(manager_exceptions.ProjectIdNotFound,
self.pool.create)
def test_delete_with_host(self):
self._patch_get_aggregate_from_name_or_id()
agg = self.pool.get('foo')
self.pool.delete(agg)
self.nova.aggregates.delete.assert_called_once_with(agg.id)
for host in agg.hosts:
self.nova.aggregates.remove_host.assert_any_call(agg.id, host)
self.nova.aggregates.add_host.assert_any_call(
self.fake_freepool.id, host
)
# can't delete aggregate with hosts
self.assertRaises(manager_exceptions.AggregateHaveHost,
self.pool.delete, 'bar',
force=False)
def test_delete_with_no_host(self):
self._patch_get_aggregate_from_name_or_id()
agg = self.pool.get('foo')
agg.hosts = []
self.pool.delete('foo', force=False)
self.nova.aggregates.delete.assert_called_once_with(agg.id)
def test_delete_with_no_freepool(self):
def get_fake_aggregate_but_no_freepool(*args):
if self.freepool_name in args:
raise manager_exceptions.AggregateNotFound
else:
return self.fake_aggregate
fake_pool = self.patch(self.pool, 'get_aggregate_from_name_or_id')
fake_pool.side_effect = get_fake_aggregate_but_no_freepool
agg = self.pool.get('foo')
agg.hosts = []
self.assertRaises(manager_exceptions.NoFreePool,
self.pool.delete, 'bar',
force=False)
def test_get_all(self):
self.pool.get_all()
self.nova.aggregates.list.assert_called_once_with()
def test_get(self):
self._patch_get_aggregate_from_name_or_id()
agg = self.pool.get('foo')
self.assertEqual(self.fake_aggregate, agg)
def test_add_computehost(self):
self._patch_get_aggregate_from_name_or_id()
self.pool.add_computehost('pool', 'host3')
check0 = self.nova.aggregates.add_host
check0.assert_any_call(self.fake_aggregate.id, 'host3')
check1 = self.nova.aggregates.remove_host
check1.assert_any_call(self.fake_aggregate.id, 'host3')
def test_add_computehost_with_host_id(self):
# NOTE(sbauza): Freepool.hosts only contains names of hosts, not UUIDs
self._patch_get_aggregate_from_name_or_id()
self.assertRaises(manager_exceptions.HostNotInFreePool,
self.pool.add_computehost, 'pool', '3')
def test_add_computehost_not_in_freepool(self):
self._patch_get_aggregate_from_name_or_id()
self.assertRaises(manager_exceptions.HostNotInFreePool,
self.pool.add_computehost,
'foopool',
'ghost-host')
def test_add_computehost_with_no_freepool(self):
def get_fake_aggregate_but_no_freepool(*args):
if self.freepool_name in args:
raise manager_exceptions.AggregateNotFound
else:
return self.fake_aggregate
fake_pool = self.patch(self.pool, 'get_aggregate_from_name_or_id')
fake_pool.side_effect = get_fake_aggregate_but_no_freepool
self.assertRaises(manager_exceptions.NoFreePool,
self.pool.add_computehost,
'pool',
'host3')
def test_add_computehost_with_incorrect_pool(self):
def get_no_aggregate_but_freepool(*args):
if self.freepool_name in args:
return self.freepool_name
else:
raise manager_exceptions.AggregateNotFound
fake_pool = self.patch(self.pool, 'get_aggregate_from_name_or_id')
fake_pool.side_effect = get_no_aggregate_but_freepool
self.assertRaises(manager_exceptions.AggregateNotFound,
self.pool.add_computehost,
'wrong_pool',
'host3')
def test_add_computehost_to_freepool(self):
self._patch_get_aggregate_from_name_or_id()
self.pool.add_computehost(self.freepool_name, 'host2')
check = self.nova.aggregates.add_host
check.assert_called_once_with(self.fake_freepool.id, 'host2')
def test_remove_computehost_from_freepool(self):
self._patch_get_aggregate_from_name_or_id()
self.pool.remove_computehost(self.freepool_name, 'host3')
check = self.nova.aggregates.remove_host
check.assert_called_once_with(self.fake_freepool.id, 'host3')
def test_remove_computehost_not_existing_from_freepool(self):
self._patch_get_aggregate_from_name_or_id()
self.assertRaises(manager_exceptions.HostNotInFreePool,
self.pool.remove_computehost,
self.freepool_name,
'hostXX')
def test_remove_all_computehosts(self):
self._patch_get_aggregate_from_name_or_id()
self.pool.remove_all_computehosts('pool')
for host in self.fake_aggregate.hosts:
check = self.nova.aggregates.remove_host
check.assert_any_call(self.fake_aggregate.id, host)
def test_remove_computehost_with_no_freepool(self):
def get_fake_aggregate_but_no_freepool(*args):
if self.freepool_name in args:
raise manager_exceptions.AggregateNotFound
else:
return self.fake_aggregate
fake_pool = self.patch(self.pool, 'get_aggregate_from_name_or_id')
fake_pool.side_effect = get_fake_aggregate_but_no_freepool
self.assertRaises(manager_exceptions.NoFreePool,
self.pool.remove_computehost,
'pool',
'host3')
def test_remove_computehost_with_incorrect_pool(self):
def get_no_aggregate_but_freepool(*args):
if self.freepool_name in args:
return self.freepool_name
else:
raise manager_exceptions.AggregateNotFound
fake_pool = self.patch(self.pool, 'get_aggregate_from_name_or_id')
fake_pool.side_effect = get_no_aggregate_but_freepool
self.assertRaises(manager_exceptions.AggregateNotFound,
self.pool.remove_computehost,
'wrong_pool',
'host3')
def test_remove_computehost_with_wrong_hosts(self):
self._patch_get_aggregate_from_name_or_id()
self.nova.aggregates.remove_host.side_effect = (
nova_exceptions.NotFound(404))
self.assertRaises(manager_exceptions.CantRemoveHost,
self.pool.remove_computehost,
'pool',
'host3')
def test_remove_computehosts_with_duplicate_host(self):
self._patch_get_aggregate_from_name_or_id()
self.nova.aggregates.add_host.side_effect = (
nova_exceptions.Conflict(409))
self.assertRaises(manager_exceptions.CantAddHost,
self.pool.remove_computehost,
'pool',
'host3')
def test_get_computehosts_with_correct_pool(self):
self._patch_get_aggregate_from_name_or_id()
hosts = self.pool.get_computehosts('foo')
self.assertEqual(hosts, self.fake_aggregate.hosts)
def test_get_computehosts_with_incorrect_pool(self):
self.assertEqual([], self.pool.get_computehosts('wrong_pool'))
def test_add_project(self):
self._patch_get_aggregate_from_name_or_id()
self.pool.add_project('pool', 'projectX')
check = self.nova.aggregates.set_metadata
check.assert_called_once_with(self.fake_aggregate.id,
{'projectX': self.project_id_key})
def test_remove_project(self):
self._patch_get_aggregate_from_name_or_id()
self.pool.remove_project('pool', 'projectY')
check = self.nova.aggregates.set_metadata
check.assert_called_once_with(self.fake_aggregate.id,
{'projectY': None})

View File

@ -28,10 +28,9 @@ from blazar.manager import service
from blazar.plugins import oshosts as plugin
from blazar.plugins.oshosts import host_plugin
from blazar.plugins.oshosts import nova_inventory
from blazar.plugins.oshosts import reservation_pool as rp
from blazar import tests
from blazar.utils.openstack import base
from blazar.utils.openstack.nova import ServerManager
from blazar.utils.openstack import nova
from blazar.utils import trusts
@ -52,9 +51,9 @@ class PhysicalHostPlugingSetupOnlyTestCase(tests.TestCase):
self.patch(base, 'url_for').return_value = 'http://foo.bar'
self.host_plugin = host_plugin
self.fake_phys_plugin = self.host_plugin.PhysicalHostPlugin()
self.rp = rp
self.nova = nova
self.nova_inventory = nova_inventory
self.rp_create = self.patch(self.rp.ReservationPool, 'create')
self.rp_create = self.patch(self.nova.ReservationPool, 'create')
self.db_api = db_api
self.db_host_extra_capability_get_all_per_host = (
self.patch(self.db_api, 'host_extra_capability_get_all_per_host'))
@ -90,7 +89,7 @@ class PhysicalHostPluginTestCase(tests.TestCase):
self.patch(self.context, 'BlazarContext')
self.nova_client = nova_client
self.nova = self.patch(self.nova_client, 'Client').return_value
self.nova_client = self.patch(self.nova_client, 'Client').return_value
self.service = service
self.manager = self.service.ManagerService()
@ -134,13 +133,13 @@ class PhysicalHostPluginTestCase(tests.TestCase):
self.db_host_extra_capability_update = self.patch(
self.db_api, 'host_extra_capability_update')
self.rp = rp
self.nova = nova
self.nova_inventory = nova_inventory
self.rp_create = self.patch(self.rp.ReservationPool, 'create')
self.patch(self.rp.ReservationPool, 'get_aggregate_from_name_or_id')
self.add_compute_host = self.patch(self.rp.ReservationPool,
self.rp_create = self.patch(self.nova.ReservationPool, 'create')
self.patch(self.nova.ReservationPool, 'get_aggregate_from_name_or_id')
self.add_compute_host = self.patch(self.nova.ReservationPool,
'add_computehost')
self.remove_compute_host = self.patch(self.rp.ReservationPool,
self.remove_compute_host = self.patch(self.nova.ReservationPool,
'remove_computehost')
self.get_host_details = self.patch(self.nova_inventory.NovaInventory,
'get_host_details')
@ -161,7 +160,7 @@ class PhysicalHostPluginTestCase(tests.TestCase):
self.trust_ctx = self.patch(self.trusts, 'create_ctx_from_trust')
self.trust_create = self.patch(self.trusts, 'create_trust')
self.ServerManager = ServerManager
self.ServerManager = nova.ServerManager
def test_get_host(self):
host = self.fake_phys_plugin.get_computehost(self.fake_host_id)
@ -316,7 +315,7 @@ class PhysicalHostPluginTestCase(tests.TestCase):
'host_reservation_create')
matching_hosts = self.patch(self.fake_phys_plugin, '_matching_hosts')
matching_hosts.return_value = []
pool_delete = self.patch(self.rp.ReservationPool, 'delete')
pool_delete = self.patch(self.nova.ReservationPool, 'delete')
self.assertRaises(manager_exceptions.NotEnoughHostsAvailable,
self.fake_phys_plugin.reserve_resource,
u'f9894fcf-e2ed-41e9-8a4c-92fac332608e',
@ -403,7 +402,7 @@ class PhysicalHostPluginTestCase(tests.TestCase):
host_reservation_get.return_value = {
'aggregate_id': 1
}
get_computehosts = self.patch(self.rp.ReservationPool,
get_computehosts = self.patch(self.nova.ReservationPool,
'get_computehosts')
get_computehosts.return_value = ['host1']
host_allocation_get_all = self.patch(
@ -486,13 +485,13 @@ class PhysicalHostPluginTestCase(tests.TestCase):
(datetime.datetime(2013, 12, 20, 20, 30),
datetime.datetime(2013, 12, 20, 21, 00))
]
get_computehosts = self.patch(self.rp.ReservationPool,
get_computehosts = self.patch(self.nova.ReservationPool,
'get_computehosts')
get_computehosts.return_value = ['host1']
matching_hosts = self.patch(self.fake_phys_plugin, '_matching_hosts')
matching_hosts.return_value = ['host2']
self.patch(self.fake_phys_plugin, '_get_hypervisor_from_name_or_id')
get_hypervisors = self.patch(self.nova.hypervisors, 'get')
get_hypervisors = self.patch(self.nova_client.hypervisors, 'get')
get_hypervisors.return_value = mock.MagicMock(running_vms=1)
self.assertRaises(
manager_exceptions.NotEnoughHostsAvailable,
@ -533,7 +532,7 @@ class PhysicalHostPluginTestCase(tests.TestCase):
(datetime.datetime(2013, 12, 19, 20, 30),
datetime.datetime(2013, 12, 19, 21, 00))
]
get_computehosts = self.patch(self.rp.ReservationPool,
get_computehosts = self.patch(self.nova.ReservationPool,
'get_computehosts')
get_computehosts.return_value = []
self.fake_phys_plugin.update_reservation(
@ -586,11 +585,11 @@ class PhysicalHostPluginTestCase(tests.TestCase):
(datetime.datetime(2013, 12, 20, 20, 30),
datetime.datetime(2013, 12, 20, 21, 00))
]
get_computehosts = self.patch(self.rp.ReservationPool,
get_computehosts = self.patch(self.nova.ReservationPool,
'get_computehosts')
get_computehosts.return_value = ['host1']
self.patch(self.fake_phys_plugin, '_get_hypervisor_from_name_or_id')
get_hypervisors = self.patch(self.nova.hypervisors, 'get')
get_hypervisors = self.patch(self.nova_client.hypervisors, 'get')
get_hypervisors.return_value = mock.MagicMock(running_vms=0)
matching_hosts = self.patch(self.fake_phys_plugin, '_matching_hosts')
matching_hosts.return_value = ['host2']
@ -630,7 +629,7 @@ class PhysicalHostPluginTestCase(tests.TestCase):
host_get = self.patch(self.db_api, 'host_get')
host_get.return_value = {'service_name': 'host1_hostname'}
add_computehost = self.patch(
self.rp.ReservationPool, 'add_computehost')
self.nova.ReservationPool, 'add_computehost')
self.fake_phys_plugin.on_start(u'04de74e8-193a-49d2-9ab8-cba7b49e45e8')
@ -658,13 +657,13 @@ class PhysicalHostPluginTestCase(tests.TestCase):
host_allocation_destroy = self.patch(
self.db_api,
'host_allocation_destroy')
get_computehosts = self.patch(self.rp.ReservationPool,
get_computehosts = self.patch(self.nova.ReservationPool,
'get_computehosts')
get_computehosts.return_value = ['host']
list_servers = self.patch(self.ServerManager, 'list')
list_servers.return_value = ['server1', 'server2']
delete_server = self.patch(self.ServerManager, 'delete')
delete_pool = self.patch(self.rp.ReservationPool, 'delete')
delete_pool = self.patch(self.nova.ReservationPool, 'delete')
self.fake_phys_plugin.on_end(u'04de74e8-193a-49d2-9ab8-cba7b49e45e8')
host_reservation_update.assert_called_with(
u'04de74e8-193a-49d2-9ab8-cba7b49e45e8', {'status': 'completed'})
@ -695,13 +694,13 @@ class PhysicalHostPluginTestCase(tests.TestCase):
host_allocation_destroy = self.patch(
self.db_api,
'host_allocation_destroy')
get_computehosts = self.patch(self.rp.ReservationPool,
get_computehosts = self.patch(self.nova.ReservationPool,
'get_computehosts')
get_computehosts.return_value = ['host']
list_servers = self.patch(self.ServerManager, 'list')
list_servers.return_value = []
delete_server = self.patch(self.ServerManager, 'delete')
delete_pool = self.patch(self.rp.ReservationPool, 'delete')
delete_pool = self.patch(self.nova.ReservationPool, 'delete')
self.fake_phys_plugin.on_end(u'04de74e8-193a-49d2-9ab8-cba7b49e45e8')
host_reservation_update.assert_called_with(
u'04de74e8-193a-49d2-9ab8-cba7b49e45e8', {'status': 'completed'})

View File

@ -12,13 +12,17 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import uuid as uuidgen
from keystoneauth1 import session
from keystoneauth1 import token_endpoint
from novaclient import client as nova_client
from novaclient import exceptions as nova_exceptions
from oslo_config import cfg
from blazar import context
from blazar.manager import exceptions as manager_exceptions
from blazar.plugins import oshosts as host_plugin
from blazar import tests
from blazar.utils.openstack import base
from blazar.utils.openstack import nova
@ -90,3 +94,314 @@ class TestCNClient(tests.TestCase):
def test_getattr(self):
# TODO(n.s.): Will be done as soon as pypi package will be updated
pass
class AggregateFake(object):
def __init__(self, i, name, hosts):
self.id = i
self.name = name
self.hosts = hosts
class ReservationPoolTestCase(tests.TestCase):
def setUp(self):
super(ReservationPoolTestCase, self).setUp()
self.pool_name = 'pool-name-xxx'
self.project_id = 'project-uuid'
self.fake_aggregate = AggregateFake(i=123,
name='fooname',
hosts=['host1', 'host2'])
physical_host_conf = cfg.CONF[host_plugin.RESOURCE_TYPE]
nova_conf = cfg.CONF.nova
self.freepool_name = nova_conf.aggregate_freepool_name
self.project_id_key = nova_conf.project_id_key
self.blazar_owner = nova_conf.blazar_owner
self.blazar_az_prefix = physical_host_conf.blazar_az_prefix
self.fake_freepool = AggregateFake(i=456,
name=self.freepool_name,
hosts=['host3'])
self.set_context(context.BlazarContext(project_id=self.project_id))
self.nova_client = nova_client
self.nova = self.patch(self.nova_client, 'Client').return_value
self.patch(self.nova.aggregates, 'set_metadata')
self.patch(self.nova.aggregates, 'remove_host')
self.patch(base, 'url_for').return_value = 'http://foo.bar'
self.pool = nova.ReservationPool()
self.p_name = self.patch(self.pool, '_generate_aggregate_name')
self.p_name.return_value = self.pool_name
def _patch_get_aggregate_from_name_or_id(self):
def get_fake_aggregate(*args):
if self.freepool_name in args:
return self.fake_freepool
else:
return self.fake_aggregate
patched_pool = self.patch(self.pool, 'get_aggregate_from_name_or_id')
patched_pool.side_effect = get_fake_aggregate
def test_get_aggregate_from_name_or_id(self):
def fake_aggregate_get(id):
if id == self.fake_aggregate.id:
return self.fake_aggregate
else:
raise nova_exceptions.NotFound(id)
self.nova.aggregates.list.return_value = [self.fake_aggregate]
self.nova.aggregates.get.side_effect = fake_aggregate_get
self.assertRaises(manager_exceptions.AggregateNotFound,
self.pool.get_aggregate_from_name_or_id, 'none')
self.assertRaises(manager_exceptions.AggregateNotFound,
self.pool.get_aggregate_from_name_or_id, '3000')
self.assertEqual(self.pool.get_aggregate_from_name_or_id('fooname'),
self.fake_aggregate)
self.assertEqual(
self.pool.get_aggregate_from_name_or_id(self.fake_aggregate),
self.fake_aggregate)
def test_generate_aggregate_name(self):
self.uuidgen = uuidgen
self.patch(uuidgen, 'uuid4').return_value = 'foo'
self.assertEqual('foo',
nova.ReservationPool._generate_aggregate_name())
def test_create(self):
self.patch(self.nova.aggregates, 'create').return_value = (
self.fake_aggregate)
az_name = self.blazar_az_prefix + self.pool_name
agg = self.pool.create(az=az_name)
self.assertEqual(agg, self.fake_aggregate)
check0 = self.nova.aggregates.create
check0.assert_called_once_with(self.pool_name, az_name)
meta = {self.blazar_owner: self.project_id}
check1 = self.nova.aggregates.set_metadata
check1.assert_called_once_with(self.fake_aggregate, meta)
def test_create_no_az(self):
self.patch(self.nova.aggregates, 'create').return_value = (
self.fake_aggregate)
self.pool.create()
self.nova.aggregates.create.assert_called_once_with(self.pool_name,
None)
def test_create_no_project_id(self):
self.patch(self.nova.aggregates, 'create').return_value = (
self.fake_aggregate)
self.nova_wrapper = self.patch(nova.NovaClientWrapper, 'nova')
def raiseRuntimeError():
raise RuntimeError()
self.context_mock.side_effect = raiseRuntimeError
self.assertRaises(manager_exceptions.ProjectIdNotFound,
self.pool.create)
def test_delete_with_host(self):
self._patch_get_aggregate_from_name_or_id()
agg = self.pool.get('foo')
self.pool.delete(agg)
self.nova.aggregates.delete.assert_called_once_with(agg.id)
for host in agg.hosts:
self.nova.aggregates.remove_host.assert_any_call(agg.id, host)
self.nova.aggregates.add_host.assert_any_call(
self.fake_freepool.id, host
)
# can't delete aggregate with hosts
self.assertRaises(manager_exceptions.AggregateHaveHost,
self.pool.delete, 'bar',
force=False)
def test_delete_with_no_host(self):
self._patch_get_aggregate_from_name_or_id()
agg = self.pool.get('foo')
agg.hosts = []
self.pool.delete('foo', force=False)
self.nova.aggregates.delete.assert_called_once_with(agg.id)
def test_delete_with_no_freepool(self):
def get_fake_aggregate_but_no_freepool(*args):
if self.freepool_name in args:
raise manager_exceptions.AggregateNotFound
else:
return self.fake_aggregate
fake_pool = self.patch(self.pool, 'get_aggregate_from_name_or_id')
fake_pool.side_effect = get_fake_aggregate_but_no_freepool
agg = self.pool.get('foo')
agg.hosts = []
self.assertRaises(manager_exceptions.NoFreePool,
self.pool.delete, 'bar',
force=False)
def test_get_all(self):
self.pool.get_all()
self.nova.aggregates.list.assert_called_once_with()
def test_get(self):
self._patch_get_aggregate_from_name_or_id()
agg = self.pool.get('foo')
self.assertEqual(self.fake_aggregate, agg)
def test_add_computehost(self):
self._patch_get_aggregate_from_name_or_id()
self.pool.add_computehost('pool', 'host3')
check0 = self.nova.aggregates.add_host
check0.assert_any_call(self.fake_aggregate.id, 'host3')
check1 = self.nova.aggregates.remove_host
check1.assert_any_call(self.fake_aggregate.id, 'host3')
def test_add_computehost_with_host_id(self):
# NOTE(sbauza): Freepool.hosts only contains names of hosts, not UUIDs
self._patch_get_aggregate_from_name_or_id()
self.assertRaises(manager_exceptions.HostNotInFreePool,
self.pool.add_computehost, 'pool', '3')
def test_add_computehost_not_in_freepool(self):
self._patch_get_aggregate_from_name_or_id()
self.assertRaises(manager_exceptions.HostNotInFreePool,
self.pool.add_computehost,
'foopool',
'ghost-host')
def test_add_computehost_with_no_freepool(self):
def get_fake_aggregate_but_no_freepool(*args):
if self.freepool_name in args:
raise manager_exceptions.AggregateNotFound
else:
return self.fake_aggregate
fake_pool = self.patch(self.pool, 'get_aggregate_from_name_or_id')
fake_pool.side_effect = get_fake_aggregate_but_no_freepool
self.assertRaises(manager_exceptions.NoFreePool,
self.pool.add_computehost,
'pool',
'host3')
def test_add_computehost_with_incorrect_pool(self):
def get_no_aggregate_but_freepool(*args):
if self.freepool_name in args:
return self.freepool_name
else:
raise manager_exceptions.AggregateNotFound
fake_pool = self.patch(self.pool, 'get_aggregate_from_name_or_id')
fake_pool.side_effect = get_no_aggregate_but_freepool
self.assertRaises(manager_exceptions.AggregateNotFound,
self.pool.add_computehost,
'wrong_pool',
'host3')
def test_add_computehost_to_freepool(self):
self._patch_get_aggregate_from_name_or_id()
self.pool.add_computehost(self.freepool_name, 'host2')
check = self.nova.aggregates.add_host
check.assert_called_once_with(self.fake_freepool.id, 'host2')
def test_remove_computehost_from_freepool(self):
self._patch_get_aggregate_from_name_or_id()
self.pool.remove_computehost(self.freepool_name, 'host3')
check = self.nova.aggregates.remove_host
check.assert_called_once_with(self.fake_freepool.id, 'host3')
def test_remove_computehost_not_existing_from_freepool(self):
self._patch_get_aggregate_from_name_or_id()
self.assertRaises(manager_exceptions.HostNotInFreePool,
self.pool.remove_computehost,
self.freepool_name,
'hostXX')
def test_remove_all_computehosts(self):
self._patch_get_aggregate_from_name_or_id()
self.pool.remove_all_computehosts('pool')
for host in self.fake_aggregate.hosts:
check = self.nova.aggregates.remove_host
check.assert_any_call(self.fake_aggregate.id, host)
def test_remove_computehost_with_no_freepool(self):
def get_fake_aggregate_but_no_freepool(*args):
if self.freepool_name in args:
raise manager_exceptions.AggregateNotFound
else:
return self.fake_aggregate
fake_pool = self.patch(self.pool, 'get_aggregate_from_name_or_id')
fake_pool.side_effect = get_fake_aggregate_but_no_freepool
self.assertRaises(manager_exceptions.NoFreePool,
self.pool.remove_computehost,
'pool',
'host3')
def test_remove_computehost_with_incorrect_pool(self):
def get_no_aggregate_but_freepool(*args):
if self.freepool_name in args:
return self.freepool_name
else:
raise manager_exceptions.AggregateNotFound
fake_pool = self.patch(self.pool, 'get_aggregate_from_name_or_id')
fake_pool.side_effect = get_no_aggregate_but_freepool
self.assertRaises(manager_exceptions.AggregateNotFound,
self.pool.remove_computehost,
'wrong_pool',
'host3')
def test_remove_computehost_with_wrong_hosts(self):
self._patch_get_aggregate_from_name_or_id()
self.nova.aggregates.remove_host.side_effect = (
nova_exceptions.NotFound(404))
self.assertRaises(manager_exceptions.CantRemoveHost,
self.pool.remove_computehost,
'pool',
'host3')
def test_remove_computehosts_with_duplicate_host(self):
self._patch_get_aggregate_from_name_or_id()
self.nova.aggregates.add_host.side_effect = (
nova_exceptions.Conflict(409))
self.assertRaises(manager_exceptions.CantAddHost,
self.pool.remove_computehost,
'pool',
'host3')
def test_get_computehosts_with_correct_pool(self):
self._patch_get_aggregate_from_name_or_id()
hosts = self.pool.get_computehosts('foo')
self.assertEqual(hosts, self.fake_aggregate.hosts)
def test_get_computehosts_with_incorrect_pool(self):
self.assertEqual([], self.pool.get_computehosts('wrong_pool'))
def test_add_project(self):
self._patch_get_aggregate_from_name_or_id()
self.pool.add_project('pool', 'projectX')
check = self.nova.aggregates.set_metadata
check.assert_called_once_with(self.fake_aggregate.id,
{'projectX': self.project_id_key})
def test_remove_project(self):
self._patch_get_aggregate_from_name_or_id()
self.pool.remove_project('pool', 'projectY')
check = self.nova.aggregates.set_metadata
check.assert_called_once_with(self.fake_aggregate.id,
{'projectY': None})

View File

@ -12,6 +12,7 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import uuid as uuidgen
from keystoneauth1 import session
from keystoneauth1 import token_endpoint
@ -19,8 +20,11 @@ from novaclient import client as nova_client
from novaclient import exceptions as nova_exception
from novaclient.v2 import servers
from oslo_config import cfg
from oslo_log import log as logging
from blazar import context
from blazar.manager import exceptions as manager_exceptions
from blazar.plugins import oshosts
from blazar.utils.openstack import base
@ -36,12 +40,27 @@ nova_opts = [
cfg.StrOpt('image_prefix',
default='reserved_',
deprecated_group='DEFAULT',
help='Prefix for VM images if you want to create snapshots')
help='Prefix for VM images if you want to create snapshots'),
cfg.StrOpt('aggregate_freepool_name',
default='freepool',
deprecated_group=oshosts.RESOURCE_TYPE,
help='Name of the special aggregate where all hosts '
'are candidate for physical host reservation'),
cfg.StrOpt('project_id_key',
default='blazar:project',
deprecated_group=oshosts.RESOURCE_TYPE,
help='Aggregate metadata value for key matching project_id'),
cfg.StrOpt('blazar_owner',
default='blazar:owner',
deprecated_group=oshosts.RESOURCE_TYPE,
help='Aggregate metadata key for knowing owner project_id')
]
CONF = cfg.CONF
CONF.register_opts(nova_opts, group='nova')
CONF.import_opt('identity_service', 'blazar.utils.openstack.keystone')
LOG = logging.getLogger(__name__)
class BlazarNovaClient(object):
@ -179,3 +198,226 @@ class NovaClientWrapper(object):
project_name=self.project_name,
project_domain_name=self.project_domain_name)
return nova
class ReservationPool(NovaClientWrapper):
def __init__(self):
super(ReservationPool, self).__init__()
self.config = CONF.nova
self.freepool_name = self.config.aggregate_freepool_name
def get_aggregate_from_name_or_id(self, aggregate_obj):
"""Return an aggregate by name or an id."""
aggregate = None
agg_id = None
try:
agg_id = int(aggregate_obj)
except (ValueError, TypeError):
if hasattr(aggregate_obj, 'id') and aggregate_obj.id:
# pool is an aggregate
agg_id = aggregate_obj.id
if agg_id is not None:
try:
aggregate = self.nova.aggregates.get(agg_id)
except nova_exception.NotFound:
aggregate = None
else:
# FIXME(scroiset): can't get an aggregate by name
# so iter over all aggregate and check for the good one
all_aggregates = self.nova.aggregates.list()
for agg in all_aggregates:
if aggregate_obj == agg.name:
aggregate = agg
if aggregate:
return aggregate
else:
raise manager_exceptions.AggregateNotFound(pool=aggregate_obj)
@staticmethod
def _generate_aggregate_name():
return str(uuidgen.uuid4())
def create(self, name=None, az=None):
"""Create a Pool (an Aggregate) with or without Availability Zone.
By default expose to user the aggregate with an Availability Zone.
Return an aggregate or raise a nova exception.
"""
name = name or self._generate_aggregate_name()
LOG.debug('Creating pool aggregate: %s with Availability Zone %s'
% (name, az))
agg = self.nova.aggregates.create(name, az)
try:
ctx = context.current()
project_id = ctx.project_id
except RuntimeError:
e = manager_exceptions.ProjectIdNotFound()
LOG.error(e.message)
raise e
meta = {self.config.blazar_owner: project_id}
self.nova.aggregates.set_metadata(agg, meta)
return agg
def delete(self, pool, force=True):
"""Delete an aggregate.
pool can be an aggregate name or an aggregate id.
Remove all hosts before delete aggregate (default).
If force is False, raise exception if at least one
host is attached to.
"""
agg = self.get_aggregate_from_name_or_id(pool)
hosts = agg.hosts
if len(hosts) > 0 and not force:
raise manager_exceptions.AggregateHaveHost(name=agg.name,
hosts=agg.hosts)
try:
freepool_agg = self.get(self.freepool_name)
except manager_exceptions.AggregateNotFound:
raise manager_exceptions.NoFreePool()
for host in hosts:
LOG.debug("Removing host '%s' from aggregate "
"'%s')" % (host, agg.id))
self.nova.aggregates.remove_host(agg.id, host)
if freepool_agg.id != agg.id:
self.nova.aggregates.add_host(freepool_agg.id, host)
self.nova.aggregates.delete(agg.id)
def get_all(self):
"""Return all aggregate."""
return self.nova.aggregates.list()
def get(self, pool):
"""return details for aggregate pool or raise AggregateNotFound."""
return self.get_aggregate_from_name_or_id(pool)
def get_computehosts(self, pool):
"""Return a list of compute host names for an aggregate."""
try:
agg = self.get_aggregate_from_name_or_id(pool)
return agg.hosts
except manager_exceptions.AggregateNotFound:
return []
def add_computehost(self, pool, host):
"""Add a compute host to an aggregate.
The `host` must exist otherwise raise an error
and the `host` must be in the freepool.
:param pool: Name or UUID of the pool to rattach the host
:param host: Name (not UUID) of the host to associate
:type host: str
Return the related aggregate.
Raise an aggregate exception if something wrong.
"""
agg = self.get_aggregate_from_name_or_id(pool)
try:
freepool_agg = self.get(self.freepool_name)
except manager_exceptions.AggregateNotFound:
raise manager_exceptions.NoFreePool()
if freepool_agg.id != agg.id:
if host not in freepool_agg.hosts:
raise manager_exceptions.HostNotInFreePool(
host=host, freepool_name=freepool_agg.name)
LOG.info("removing host '%s' "
"from aggregate freepool %s" % (host, freepool_agg.name))
try:
self.remove_computehost(freepool_agg.id, host)
except nova_exception.NotFound:
raise manager_exceptions.HostNotFound(host=host)
LOG.info("adding host '%s' to aggregate %s" % (host, agg.id))
try:
return self.nova.aggregates.add_host(agg.id, host)
except nova_exception.NotFound:
raise manager_exceptions.HostNotFound(host=host)
except nova_exception.Conflict:
raise manager_exceptions.AggregateAlreadyHasHost(pool=pool,
host=host)
def remove_all_computehosts(self, pool):
"""Remove all compute hosts attached to an aggregate."""
hosts = self.get_computehosts(pool)
self.remove_computehost(pool, hosts)
def remove_computehost(self, pool, hosts):
"""Remove compute host(s) from an aggregate."""
if not isinstance(hosts, list):
hosts = [hosts]
agg = self.get_aggregate_from_name_or_id(pool)
try:
freepool_agg = self.get(self.freepool_name)
except manager_exceptions.AggregateNotFound:
raise manager_exceptions.NoFreePool()
hosts_failing_to_remove = []
hosts_failing_to_add = []
hosts_not_in_freepool = []
for host in hosts:
if freepool_agg.id == agg.id:
if host not in freepool_agg.hosts:
hosts_not_in_freepool.append(host)
continue
try:
self.nova.aggregates.remove_host(agg.id, host)
except nova_exception.ClientException:
hosts_failing_to_remove.append(host)
if freepool_agg.id != agg.id:
# NOTE(sbauza) : We don't want to put again the host in
# freepool if the requested pool is the freepool...
try:
self.nova.aggregates.add_host(freepool_agg.id, host)
except nova_exception.ClientException:
hosts_failing_to_add.append(host)
if hosts_failing_to_remove:
raise manager_exceptions.CantRemoveHost(
host=hosts_failing_to_remove, pool=agg)
if hosts_failing_to_add:
raise manager_exceptions.CantAddHost(host=hosts_failing_to_add,
pool=freepool_agg)
if hosts_not_in_freepool:
raise manager_exceptions.HostNotInFreePool(
host=hosts_not_in_freepool, freepool_name=freepool_agg.name)
def add_project(self, pool, project_id):
"""Add a project to an aggregate."""
metadata = {project_id: self.config.project_id_key}
agg = self.get_aggregate_from_name_or_id(pool)
return self.nova.aggregates.set_metadata(agg.id, metadata)
def remove_project(self, pool, project_id):
"""Remove a project from an aggregate."""
agg = self.get_aggregate_from_name_or_id(pool)
metadata = {project_id: None}
return self.nova.aggregates.set_metadata(agg.id, metadata)