Add Host Reservation Admin Manager
API extensions are provided using a RPC dispatcher We need to explicitly mention plugin:method when calling. If not, it will stick to core methods for ManagerService. Implements bp:host-provisioning-api Change-Id: Ice5a07eef54664651c22b6b1185be38062c7296e
This commit is contained in:
parent
bac9cf583b
commit
984a43683e
@ -13,7 +13,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from climate import exceptions
|
||||
from climate.manager.oshosts import rpcapi as manager_rpcapi
|
||||
from climate.openstack.common import log as logging
|
||||
|
||||
@ -35,9 +34,6 @@ class API(object):
|
||||
:param data: New computehost characteristics.
|
||||
:type data: dict
|
||||
"""
|
||||
# here API should go to Keystone API v3 and create trust
|
||||
trust = 'trust'
|
||||
data.update({'trust': trust})
|
||||
|
||||
return self.manager_rpcapi.create_computehost(data)
|
||||
|
||||
@ -57,13 +53,6 @@ class API(object):
|
||||
:param data: New computehost characteristics.
|
||||
:type data: dict
|
||||
"""
|
||||
new_name = data.pop('name', None)
|
||||
if len(data) > 0:
|
||||
raise exceptions.ClimateException('Only name changing may be '
|
||||
'proceeded.')
|
||||
data = {}
|
||||
if new_name:
|
||||
data['name'] = new_name
|
||||
return self.manager_rpcapi.update_computehost(host_id, data)
|
||||
|
||||
def delete_computehost(self, host_id):
|
||||
|
@ -334,3 +334,9 @@ def host_extra_capability_destroy(host_extra_capability_id):
|
||||
def host_extra_capability_update(host_extra_capability_id, values):
|
||||
"""Update specific host ExtraCapability."""
|
||||
IMPL.host_extra_capability_update(host_extra_capability_id, values)
|
||||
|
||||
|
||||
def host_extra_capability_get_all_per_name(host_id,
|
||||
extra_capability_name):
|
||||
return IMPL.host_extra_capability_get_all_per_name(host_id,
|
||||
extra_capability_name)
|
||||
|
@ -558,12 +558,12 @@ def host_extra_capability_get(host_extra_capability_id):
|
||||
|
||||
def _host_extra_capability_get_all_per_host(session, host_id):
|
||||
query = model_query(models.ComputeHostExtraCapability, session)
|
||||
return query.filter_by(computehost_id=host_id).all()
|
||||
return query.filter_by(computehost_id=host_id)
|
||||
|
||||
|
||||
def host_extra_capability_get_all_per_host(host_id):
|
||||
return _host_extra_capability_get_all_per_host(get_session(),
|
||||
host_id)
|
||||
host_id).all()
|
||||
|
||||
|
||||
def host_extra_capability_create(values):
|
||||
@ -607,3 +607,11 @@ def host_extra_capability_destroy(host_extra_capability_id):
|
||||
raise RuntimeError("Host Extracapability not found!")
|
||||
|
||||
session.delete(host_extra_capability)
|
||||
|
||||
|
||||
def host_extra_capability_get_all_per_name(host_id, capability_name):
|
||||
session = get_session()
|
||||
|
||||
with session.begin():
|
||||
query = _host_extra_capability_get_all_per_host(session, host_id)
|
||||
return query.filter_by(capability_name=capability_name).all()
|
||||
|
@ -128,7 +128,7 @@ class ComputeHost(mb.ClimateBase):
|
||||
__tablename__ = 'computehosts'
|
||||
|
||||
id = _id_column()
|
||||
vcpu = sa.Column(sa.Integer, nullable=False)
|
||||
vcpus = sa.Column(sa.Integer, nullable=False)
|
||||
cpu_info = sa.Column(MediumText(), nullable=False)
|
||||
hypervisor_type = sa.Column(MediumText(), nullable=False)
|
||||
hypervisor_version = sa.Column(sa.Integer, nullable=False)
|
||||
|
@ -1,31 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright © 2013 Julien Danjou <julien@danjou.info>
|
||||
#
|
||||
# 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 abc
|
||||
|
||||
|
||||
class Plugin(object):
|
||||
"""Base class for inventory plugins."""
|
||||
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_hosts(self):
|
||||
"""List hosts."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_host_details(self, host):
|
||||
"""Get host details."""
|
@ -1,60 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright © 2013 Julien Danjou <julien@danjou.info>
|
||||
#
|
||||
# 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
|
||||
|
||||
from novaclient import client
|
||||
from oslo.config import cfg
|
||||
|
||||
from climate import inventory
|
||||
|
||||
|
||||
CLI_OPTIONS = [
|
||||
cfg.StrOpt('os-username',
|
||||
default=os.environ.get('OS_USERNAME', 'climate'),
|
||||
help='Username to use for OpenStack service access'),
|
||||
cfg.StrOpt('os-password',
|
||||
default=os.environ.get('OS_PASSWORD', 'admin'),
|
||||
help='Password to use for OpenStack service access'),
|
||||
cfg.StrOpt('os-tenant-id',
|
||||
default=os.environ.get('OS_TENANT_ID', ''),
|
||||
help='Tenant ID to use for OpenStack service access'),
|
||||
cfg.StrOpt('os-tenant-name',
|
||||
default=os.environ.get('OS_TENANT_NAME', 'admin'),
|
||||
help='Tenant name to use for OpenStack service access'),
|
||||
cfg.StrOpt('os-auth-url',
|
||||
default=os.environ.get('OS_AUTH_URL',
|
||||
'http://localhost:5000/v2.0'),
|
||||
help='Auth URL to use for openstack service access'),
|
||||
]
|
||||
cfg.CONF.register_cli_opts(CLI_OPTIONS)
|
||||
|
||||
|
||||
class NovaInventory(inventory.Plugin):
|
||||
|
||||
def __init__(self):
|
||||
self.novaclient = client.Client(
|
||||
"2",
|
||||
username=cfg.CONF.os_username,
|
||||
api_key=cfg.CONF.os_password,
|
||||
auth_url=cfg.CONF.os_auth_url,
|
||||
project_id=cfg.CONF.os_tenant_name or cfg.CONF.os_tenant_id)
|
||||
|
||||
def list_hosts(self):
|
||||
return self.novaclient.hypervisors.list()
|
||||
|
||||
def get_host_details(self, host):
|
||||
return self.novaclient.hypervisors.get(host)
|
@ -37,20 +37,23 @@ class ManagerRPCAPI(service.RpcProxy):
|
||||
|
||||
def get_computehost(self, host_id):
|
||||
"""Get detailed info about some computehost."""
|
||||
return self.call('get_computehost', host_id=host_id)
|
||||
return self.call('physical:host:get_computehost', host_id=host_id)
|
||||
|
||||
def list_computehosts(self):
|
||||
"""List all computehosts."""
|
||||
return self.call('list_computehosts')
|
||||
return self.call('physical:host:list_computehosts')
|
||||
|
||||
def create_computehost(self, host_values):
|
||||
"""Create computehost with specified parameters."""
|
||||
return self.call('create_computehost', host_values=host_values)
|
||||
return self.call('physical:host:create_computehost',
|
||||
host_values=host_values)
|
||||
|
||||
def update_computehost(self, host_id, values):
|
||||
"""Update computehost with passes values dictionary."""
|
||||
return self.call('update_computehost', host_id=host_id, values=values)
|
||||
return self.call('physical:host:update_computehost', host_id=host_id,
|
||||
values=values)
|
||||
|
||||
def delete_computehost(self, host_id):
|
||||
"""Delete specified computehost."""
|
||||
return self.cast('delete_computehost', host_id=host_id)
|
||||
return self.cast('physical:host:delete_computehost',
|
||||
host_id=host_id)
|
||||
|
@ -103,7 +103,7 @@ class ManagerService(rpc_service.Service):
|
||||
actions[resource_type] = {}
|
||||
actions[resource_type]['on_start'] = plugin.on_start
|
||||
actions[resource_type]['on_end'] = plugin.on_end
|
||||
|
||||
plugin.setup(None)
|
||||
return actions
|
||||
|
||||
@service_utils.with_empty_context
|
||||
@ -211,3 +211,26 @@ class ManagerService(rpc_service.Service):
|
||||
{'status': reservation_status})
|
||||
|
||||
db_api.event_update(event_id, {'status': 'DONE'})
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""RPC Dispatcher for plugins methods."""
|
||||
|
||||
fn = None
|
||||
try:
|
||||
resource_type, method = name.rsplit(':', 1)
|
||||
except ValueError:
|
||||
# NOTE(sbauza) : the dispatcher needs to know which plugin to use,
|
||||
# raising error if consequently not
|
||||
raise AttributeError(name)
|
||||
try:
|
||||
try:
|
||||
fn = getattr(self.plugins[resource_type], method)
|
||||
except KeyError:
|
||||
LOG.error("Plugin with resource type %s not found",
|
||||
resource_type)
|
||||
except AttributeError:
|
||||
LOG.error("Plugin %s doesn't include method %s",
|
||||
self.plugins[resource_type], method)
|
||||
if fn is not None:
|
||||
return fn
|
||||
raise AttributeError(name)
|
||||
|
106
climate/plugins/oshosts/nova_inventory.py
Normal file
106
climate/plugins/oshosts/nova_inventory.py
Normal file
@ -0,0 +1,106 @@
|
||||
# Copyright (c) 2013 Bull.
|
||||
#
|
||||
# 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 novaclient import client
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
from oslo.config import cfg
|
||||
|
||||
from climate import context
|
||||
from climate import exceptions
|
||||
from climate.openstack.common.gettextutils import _ # noqa
|
||||
|
||||
|
||||
class HostNotFound(exceptions.ClimateException):
|
||||
msg_fmt = _("Host '%(host)s' not found!")
|
||||
|
||||
|
||||
class InvalidHost(exceptions.ClimateException):
|
||||
msg_fmt = _("Invalid values for host %(host)s")
|
||||
|
||||
|
||||
class MultipleHostsFound(exceptions.ClimateException):
|
||||
msg_fmt = _("Multiple Hosts found for pattern '%(host)s'")
|
||||
|
||||
|
||||
class HostHavingServers(exceptions.ClimateException):
|
||||
msg_fmt = _("Servers [%(servers)s] found for host %(host)s")
|
||||
|
||||
|
||||
class NovaInventory(object):
|
||||
def __init__(self):
|
||||
self.ctx = context.current()
|
||||
|
||||
#TODO(sbauza): use catalog to find the url
|
||||
auth_url = "%s://%s:%s/v2.0" % (cfg.CONF.os_auth_protocol,
|
||||
cfg.CONF.os_auth_host,
|
||||
cfg.CONF.os_auth_port)
|
||||
self.nova = client.Client('2',
|
||||
username=cfg.CONF.os_admin_username,
|
||||
api_key=cfg.CONF.os_admin_password,
|
||||
auth_url=auth_url,
|
||||
project_id=cfg.CONF.os_admin_tenant_name)
|
||||
|
||||
def get_host_details(self, host):
|
||||
"""Get Nova capabilities of a single host
|
||||
|
||||
:param host: UUID or name of nova-compute host
|
||||
:return: Dict of capabilities or raise HostNotFound
|
||||
"""
|
||||
try:
|
||||
hypervisor = self.nova.hypervisors.get(host)
|
||||
except nova_exceptions.NotFound:
|
||||
try:
|
||||
hypervisors_list = self.nova.hypervisors.search(host)
|
||||
except nova_exceptions.NotFound:
|
||||
raise HostNotFound(host=host)
|
||||
if len(hypervisors_list) > 1:
|
||||
raise MultipleHostsFound(host)
|
||||
else:
|
||||
hypervisor_id = hypervisors_list[0].id
|
||||
# NOTE(sbauza): No need to catch the exception as we're sure
|
||||
# that the hypervisor exists
|
||||
hypervisor = self.nova.hypervisors.get(hypervisor_id)
|
||||
try:
|
||||
return {'id': hypervisor.id,
|
||||
'hypervisor_hostname': hypervisor.hypervisor_hostname,
|
||||
'vcpus': hypervisor.vcpus,
|
||||
'cpu_info': hypervisor.cpu_info,
|
||||
'hypervisor_type': hypervisor.hypervisor_type,
|
||||
'hypervisor_version': hypervisor.hypervisor_version,
|
||||
'memory_mb': hypervisor.memory_mb,
|
||||
'local_gb': hypervisor.local_gb}
|
||||
except AttributeError:
|
||||
raise InvalidHost(host=host)
|
||||
|
||||
def get_servers_per_host(self, host):
|
||||
"""List all servers of a nova-compute host
|
||||
|
||||
:param host: Name (not UUID) of nova-compute host
|
||||
:return: Dict of servers or None
|
||||
"""
|
||||
try:
|
||||
hypervisors_list = self.nova.hypervisors.search(host, servers=True)
|
||||
except nova_exceptions.NotFound:
|
||||
raise HostNotFound(host=host)
|
||||
if len(hypervisors_list) > 1:
|
||||
raise MultipleHostsFound(host)
|
||||
else:
|
||||
try:
|
||||
return hypervisors_list[0].servers
|
||||
except AttributeError:
|
||||
# NOTE(sbauza): nova.hypervisors.search(servers=True) returns
|
||||
# a list of hosts without 'servers' attribute if no servers
|
||||
# are running on that host
|
||||
return None
|
187
climate/plugins/physical_host_plugin.py
Normal file
187
climate/plugins/physical_host_plugin.py
Normal file
@ -0,0 +1,187 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Author: François Rossigneux <francois.rossigneux@inria.fr>
|
||||
#
|
||||
# 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 climate import context
|
||||
from climate.db import api as db_api
|
||||
from climate import exceptions
|
||||
from climate.openstack.common.gettextutils import _ # noqa
|
||||
from climate.plugins import base
|
||||
from climate.plugins.oshosts import nova_inventory
|
||||
from climate.plugins.oshosts import reservation_pool as rp
|
||||
from climate.utils import service as service_utils
|
||||
|
||||
|
||||
class CantAddExtraCapability(exceptions.ClimateException):
|
||||
msg_fmt = _("Can't add extracapabilities %(keys)s to Host %(host)s")
|
||||
|
||||
|
||||
class PhysicalHostPlugin(base.BasePlugin):
|
||||
"""Plugin for physical host resource."""
|
||||
resource_type = 'physical:host'
|
||||
title = 'Physical Host Plugin'
|
||||
description = 'This plugin starts and shutdowns the hosts.'
|
||||
freepool_name = cfg.CONF[resource_type].aggregate_freepool_name
|
||||
pool = None
|
||||
inventory = None
|
||||
|
||||
def on_start(self, resource_id):
|
||||
"""Add the hosts in the pool."""
|
||||
pass
|
||||
|
||||
def on_end(self, resource_id):
|
||||
"""Remove the hosts from the pool."""
|
||||
pass
|
||||
|
||||
def setup(self, conf):
|
||||
# Create freepool if not exists
|
||||
with context.ClimateContext() as ctx:
|
||||
ctx = ctx.elevated()
|
||||
if self.pool is None:
|
||||
self.pool = rp.ReservationPool()
|
||||
if self.inventory is None:
|
||||
self.inventory = nova_inventory.NovaInventory()
|
||||
if not self._freepool_exists():
|
||||
self.pool.create(name=self.freepool_name, az=None)
|
||||
|
||||
def _freepool_exists(self):
|
||||
try:
|
||||
self.pool.get_aggregate_from_name_or_id(self.freepool_name)
|
||||
return True
|
||||
except rp.AggregateNotFound:
|
||||
return False
|
||||
|
||||
def _get_extra_capabilities(self, host_id):
|
||||
extra_capabilities = {}
|
||||
raw_extra_capabilities = \
|
||||
db_api.host_extra_capability_get_all_per_host(host_id)
|
||||
for capability in raw_extra_capabilities:
|
||||
extra_capabilities[capability['capability_name']] = \
|
||||
capability['capability_value']
|
||||
return extra_capabilities
|
||||
|
||||
@service_utils.export_context
|
||||
def get_computehost(self, host_id):
|
||||
host = db_api.host_get(host_id)
|
||||
extra_capabilities = self._get_extra_capabilities(host_id)
|
||||
if host is not None and extra_capabilities:
|
||||
res = host.copy()
|
||||
res.update(extra_capabilities)
|
||||
return res
|
||||
else:
|
||||
return host
|
||||
|
||||
@service_utils.export_context
|
||||
def list_computehosts(self):
|
||||
raw_host_list = db_api.host_list()
|
||||
host_list = []
|
||||
for host in raw_host_list:
|
||||
host_list.append(self.get_computehost(host['id']))
|
||||
return host_list
|
||||
|
||||
@service_utils.export_context
|
||||
def create_computehost(self, host_values):
|
||||
# TODO(sbauza):
|
||||
# - Exception handling for HostNotFound
|
||||
host_id = host_values.pop('id', None)
|
||||
host_name = host_values.pop('name', None)
|
||||
|
||||
host_ref = host_id or host_name
|
||||
if host_ref is None:
|
||||
raise nova_inventory.InvalidHost(host=host_values)
|
||||
servers = self.inventory.get_servers_per_host(host_ref)
|
||||
if servers:
|
||||
raise nova_inventory.HostHavingServers(host=host_ref,
|
||||
servers=servers)
|
||||
host_details = self.inventory.get_host_details(host_ref)
|
||||
# NOTE(sbauza): Only last duplicate name for same extra capability will
|
||||
# be stored
|
||||
extra_capabilities_keys = \
|
||||
set(host_values.keys()) - set(host_details.keys())
|
||||
extra_capabilities = \
|
||||
dict((key, host_values[key]) for key in extra_capabilities_keys)
|
||||
self.pool.add_computehost(self.freepool_name, host_ref)
|
||||
|
||||
host = None
|
||||
cantaddextracapability = []
|
||||
try:
|
||||
host = db_api.host_create(host_details)
|
||||
except RuntimeError:
|
||||
#We need to rollback
|
||||
# TODO(sbauza): Investigate use of Taskflow for atomic transactions
|
||||
self.pool.remove_computehost(self.freepool_name, host_ref)
|
||||
if host:
|
||||
for key in extra_capabilities:
|
||||
values = {'computehost_id': host['id'],
|
||||
'capability_name': key,
|
||||
'capability_value': extra_capabilities[key]}
|
||||
try:
|
||||
db_api.host_extra_capability_create(values)
|
||||
except RuntimeError:
|
||||
cantaddextracapability.append(key)
|
||||
if cantaddextracapability:
|
||||
raise CantAddExtraCapability(keys=cantaddextracapability,
|
||||
host=host['id'])
|
||||
if host:
|
||||
return self.get_computehost(host['id'])
|
||||
else:
|
||||
return None
|
||||
|
||||
@service_utils.export_context
|
||||
def update_computehost(self, host_id, values):
|
||||
# NOTE (sbauza): Only update existing extra capabilites, don't create
|
||||
# other ones
|
||||
if values:
|
||||
cantupdateextracapability = []
|
||||
for value in values:
|
||||
capabilities = \
|
||||
db_api.host_extra_capability_get_all_per_name(host_id,
|
||||
value)
|
||||
for raw_capability in capabilities:
|
||||
capability = {'capability_name': value,
|
||||
'capability_value': values[value]}
|
||||
try:
|
||||
db_api.host_extra_capability_update(
|
||||
raw_capability['id'], capability)
|
||||
except RuntimeError:
|
||||
cantupdateextracapability.append(
|
||||
raw_capability['capability_name'])
|
||||
if cantupdateextracapability:
|
||||
raise CantAddExtraCapability(host=host_id,
|
||||
keys=cantupdateextracapability)
|
||||
return self.get_computehost(host_id)
|
||||
|
||||
@service_utils.export_context
|
||||
def delete_computehost(self, host_id):
|
||||
# TODO(sbauza):
|
||||
# - Check if no leases having this host scheduled
|
||||
servers = self.inventory.get_servers_per_host(host_id)
|
||||
if servers:
|
||||
raise nova_inventory.HostHavingServers(host=host_id,
|
||||
servers=servers)
|
||||
host = db_api.host_get(host_id)
|
||||
if not host:
|
||||
raise rp.HostNotFound(host=host_id)
|
||||
try:
|
||||
self.pool.remove_computehost(self.freepool_name,
|
||||
host['hypervisor_hostname'])
|
||||
# NOTE(sbauza): Extracapabilities will be destroyed thanks to
|
||||
# the DB FK.
|
||||
db_api.host_destroy(host_id)
|
||||
except RuntimeError:
|
||||
# Nothing so bad, but we need to advert the admin he has to rerun
|
||||
raise rp.CantRemoveHost(host=host_id, pool=self.freepool_name)
|
@ -111,7 +111,7 @@ def _get_fake_cpu_info():
|
||||
|
||||
def _get_fake_host_values(id=_get_fake_random_uuid(), mem=8192, disk=10):
|
||||
return {'id': id,
|
||||
'vcpu': 1,
|
||||
'vcpus': 1,
|
||||
'cpu_info': _get_fake_cpu_info(),
|
||||
'hypervisor_type': 'QEMU',
|
||||
'hypervisor_version': 1000,
|
||||
@ -410,3 +410,12 @@ class SQLAlchemyDBApiTestCase(tests.DBTestCase):
|
||||
self.assertEqual(None, db_api.host_extra_capability_get('1'))
|
||||
self.assertRaises(RuntimeError,
|
||||
db_api.host_extra_capability_destroy, '1')
|
||||
|
||||
def test_host_extra_capability_get_all_per_name(self):
|
||||
db_api.host_extra_capability_create(
|
||||
_get_fake_host_extra_capabilities(id='1', computehost_id='1'))
|
||||
res = db_api.host_extra_capability_get_all_per_name('1', 'vgpu')
|
||||
self.assertEqual(1, len(res))
|
||||
self.assertEqual([],
|
||||
db_api.host_extra_capability_get_all_per_name('1',
|
||||
'bad'))
|
||||
|
@ -1,54 +0,0 @@
|
||||
# Copyright (c) 2013 Julien Danjou <julien@danjou.info>
|
||||
#
|
||||
# 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 climate.inventory import nova
|
||||
from climate import tests
|
||||
|
||||
|
||||
class ServiceTestCase(tests.TestCase):
|
||||
"""This test class should be removed, but is kept as an example for unit
|
||||
testing.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def fake_hypervisors_list():
|
||||
a, b = mock.MagicMock(), mock.MagicMock()
|
||||
a.id = 1
|
||||
b.id = 2
|
||||
return [a, b]
|
||||
|
||||
@staticmethod
|
||||
def fake_hypervisors_get(h):
|
||||
return {'stuff': 'foobar',
|
||||
'cpu_info': {'arch': 'x86'}}
|
||||
|
||||
def setUp(self):
|
||||
super(ServiceTestCase, self).setUp()
|
||||
self.i = nova.NovaInventory()
|
||||
self.patch(self.i.novaclient.hypervisors, "list").\
|
||||
side_effect = self.fake_hypervisors_list
|
||||
|
||||
self.patch(self.i.novaclient.hypervisors, "get").\
|
||||
side_effect = self.fake_hypervisors_get
|
||||
|
||||
def test_list_hosts(self):
|
||||
hosts = self.i.list_hosts()
|
||||
self.assertEqual(len(hosts), 2)
|
||||
|
||||
def test_get_host_detail(self):
|
||||
hosts = self.i.list_hosts()
|
||||
detail = self.i.get_host_details(hosts[0])
|
||||
self.assertEqual(detail['cpu_info']['arch'], 'x86')
|
@ -25,6 +25,7 @@ from climate.db import api as db_api
|
||||
from climate import exceptions
|
||||
from climate.manager import service
|
||||
from climate.plugins import dummy_vm_plugin
|
||||
from climate.plugins import physical_host_plugin
|
||||
from climate import tests
|
||||
|
||||
|
||||
@ -42,9 +43,14 @@ class ServiceTestCase(tests.TestCase):
|
||||
self.db_api = db_api
|
||||
self.dummy_plugin = dummy_vm_plugin
|
||||
|
||||
self.manager = self.service.ManagerService('127.0.0.1')
|
||||
self.fake_plugin = self.patch(self.dummy_plugin, 'DummyVMPlugin')
|
||||
|
||||
self.physical_host_plugin = physical_host_plugin
|
||||
self.fake_phys_plugin = self.patch(self.physical_host_plugin,
|
||||
'PhysicalHostPlugin')
|
||||
|
||||
self.manager = self.service.ManagerService('127.0.0.1')
|
||||
|
||||
self.lease_id = '11-22-33'
|
||||
self.lease = {'id': self.lease_id,
|
||||
'reservations': [{'id': '111',
|
||||
@ -237,3 +243,39 @@ class ServiceTestCase(tests.TestCase):
|
||||
self.reservation_update.assert_called_once_with(
|
||||
'111', {'status': 'IN_USE'})
|
||||
self.event_update.assert_called_once_with('1', {'status': 'DONE'})
|
||||
|
||||
def test_getattr_with_correct_plugin_and_method(self):
|
||||
self.fake_list_computehosts = \
|
||||
self.patch(self.fake_phys_plugin, 'list_computehosts')
|
||||
self.fake_list_computehosts.return_value = 'foo'
|
||||
|
||||
self.manager.plugins = {'physical:host': self.fake_phys_plugin}
|
||||
self.assertEqual('foo', getattr(self.manager,
|
||||
'physical:host:list_computehosts')())
|
||||
|
||||
def test_getattr_with_incorrect_method_name(self):
|
||||
self.fake_list_computehosts = \
|
||||
self.patch(self.fake_phys_plugin, 'list_computehosts')
|
||||
self.fake_list_computehosts.return_value = 'foo'
|
||||
|
||||
self.manager.plugins = {'physical:host': self.fake_phys_plugin}
|
||||
self.assertRaises(AttributeError, getattr, self.manager,
|
||||
'simplefakecallwithValueError')
|
||||
|
||||
def test_getattr_with_missing_plugin(self):
|
||||
self.fake_list_computehosts = \
|
||||
self.patch(self.fake_phys_plugin, 'list_computehosts')
|
||||
self.fake_list_computehosts.return_value = 'foo'
|
||||
|
||||
self.manager.plugins = {'physical:host': self.fake_phys_plugin}
|
||||
self.assertRaises(AttributeError, getattr, self.manager,
|
||||
'plugin:not_present:list_computehosts')
|
||||
|
||||
def test_getattr_with_missing_method_in_plugin(self):
|
||||
self.fake_list_computehosts = \
|
||||
self.patch(self.fake_phys_plugin, 'list_computehosts')
|
||||
self.fake_list_computehosts.return_value = 'foo'
|
||||
|
||||
self.manager.plugins = {'physical:host': None}
|
||||
self.assertRaises(AttributeError, getattr, self.manager,
|
||||
'physical:host:method_not_present')
|
||||
|
136
climate/tests/plugins/oshosts/test_nova_inventory.py
Normal file
136
climate/tests/plugins/oshosts/test_nova_inventory.py
Normal file
@ -0,0 +1,136 @@
|
||||
# Copyright (c) 2013 Bull.
|
||||
#
|
||||
# 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 novaclient import client
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
|
||||
from climate import context
|
||||
from climate.plugins.oshosts import nova_inventory
|
||||
from climate import tests
|
||||
|
||||
|
||||
class FakeNovaHypervisors(object):
|
||||
|
||||
class FakeHost(object):
|
||||
id = 1
|
||||
hypervisor_hostname = 'fake_name'
|
||||
vcpus = 1
|
||||
cpu_info = 'fake_cpu'
|
||||
hypervisor_type = 'fake_type'
|
||||
hypervisor_version = 1000000
|
||||
memory_mb = 8192
|
||||
local_gb = 10
|
||||
|
||||
servers = ['server1', 'server2']
|
||||
|
||||
@classmethod
|
||||
def get(cls, host):
|
||||
try:
|
||||
host = int(host)
|
||||
except ValueError:
|
||||
raise nova_exceptions.NotFound(404)
|
||||
if host == cls.FakeHost.id:
|
||||
return cls.FakeHost
|
||||
else:
|
||||
raise nova_exceptions.NotFound(404)
|
||||
|
||||
@classmethod
|
||||
def search(cls, host, servers=False):
|
||||
if host == 'multiple':
|
||||
return [cls.FakeHost, cls.FakeHost]
|
||||
if host == cls.FakeHost.hypervisor_hostname:
|
||||
return [cls.FakeHost]
|
||||
else:
|
||||
raise nova_exceptions.NotFound(404)
|
||||
|
||||
@classmethod
|
||||
def expected(cls):
|
||||
return {'id': cls.FakeHost.id,
|
||||
'hypervisor_hostname': cls.FakeHost.hypervisor_hostname,
|
||||
'vcpus': cls.FakeHost.vcpus,
|
||||
'cpu_info': cls.FakeHost.cpu_info,
|
||||
'hypervisor_type': cls.FakeHost.hypervisor_type,
|
||||
'hypervisor_version': cls.FakeHost.hypervisor_version,
|
||||
'memory_mb': cls.FakeHost.memory_mb,
|
||||
'local_gb': cls.FakeHost.local_gb}
|
||||
|
||||
|
||||
class NovaInventoryTestCase(tests.TestCase):
|
||||
def setUp(self):
|
||||
super(NovaInventoryTestCase, self).setUp()
|
||||
self.context = context
|
||||
self.patch(self.context, 'ClimateContext')
|
||||
self.nova_inventory = nova_inventory
|
||||
self.client = client
|
||||
self.inventory = self.nova_inventory.NovaInventory()
|
||||
|
||||
self.hypervisors_get = self.patch(self.inventory.nova.hypervisors,
|
||||
'get')
|
||||
self.hypervisors_get.side_effect = FakeNovaHypervisors.get
|
||||
self.hypervisors_search = self.patch(self.inventory.nova.hypervisors,
|
||||
'search')
|
||||
self.hypervisors_search.side_effect = FakeNovaHypervisors.search
|
||||
|
||||
def test_get_host_details_with_host_id(self):
|
||||
host = self.inventory.get_host_details('1')
|
||||
expected = FakeNovaHypervisors.expected()
|
||||
self.assertEqual(expected, host)
|
||||
|
||||
def test_get_host_details_with_host_name(self):
|
||||
host = self.inventory.get_host_details('fake_name')
|
||||
expected = FakeNovaHypervisors.expected()
|
||||
self.assertEqual(expected, host)
|
||||
|
||||
def test_get_host_details_with_host_name_having_multiple_results(self):
|
||||
self.assertRaises(nova_inventory.MultipleHostsFound,
|
||||
self.inventory.get_host_details, 'multiple')
|
||||
|
||||
def test_get_host_details_with_host_id_not_found(self):
|
||||
self.assertRaises(nova_inventory.HostNotFound,
|
||||
self.inventory.get_host_details, '2')
|
||||
|
||||
def test_get_host_details_with_host_name_not_found(self):
|
||||
self.assertRaises(nova_inventory.HostNotFound,
|
||||
self.inventory.get_host_details, 'wrong_name')
|
||||
|
||||
def test_get_host_details_with_invalid_host(self):
|
||||
invalid_host = FakeNovaHypervisors.FakeHost
|
||||
del invalid_host.vcpus
|
||||
self.hypervisors_get.return_value = invalid_host
|
||||
self.assertRaises(nova_inventory.InvalidHost,
|
||||
self.inventory.get_host_details, '1')
|
||||
|
||||
def test_get_servers_per_host(self):
|
||||
servers = self.inventory.get_servers_per_host('fake_name')
|
||||
self.assertEqual(FakeNovaHypervisors.FakeHost.servers, servers)
|
||||
|
||||
def test_get_servers_per_host_with_host_id(self):
|
||||
self.assertRaises(nova_inventory.HostNotFound,
|
||||
self.inventory.get_servers_per_host, '1')
|
||||
|
||||
def test_get_servers_per_host_with_host_not_found(self):
|
||||
self.assertRaises(nova_inventory.HostNotFound,
|
||||
self.inventory.get_servers_per_host, 'wrong_name')
|
||||
|
||||
def test_get_servers_per_host_having_multiple_results(self):
|
||||
self.assertRaises(nova_inventory.MultipleHostsFound,
|
||||
self.inventory.get_servers_per_host, 'multiple')
|
||||
|
||||
def test_get_servers_per_host_with_host_having_no_servers(self):
|
||||
host_with_zero_servers = FakeNovaHypervisors.FakeHost
|
||||
# NOTE(sbauza): We need to simulate a host having zero servers
|
||||
del host_with_zero_servers.servers
|
||||
servers = self.inventory.get_servers_per_host('fake_name')
|
||||
self.assertEqual(None, servers)
|
269
climate/tests/plugins/test_physical_host_plugin.py
Normal file
269
climate/tests/plugins/test_physical_host_plugin.py
Normal file
@ -0,0 +1,269 @@
|
||||
# Copyright (c) 2013 Bull.
|
||||
#
|
||||
# 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 testtools
|
||||
|
||||
from climate import context
|
||||
from climate.db import api as db_api
|
||||
from climate.manager import service
|
||||
from climate.plugins.oshosts import nova_inventory
|
||||
from climate.plugins.oshosts import reservation_pool as rp
|
||||
from climate.plugins import physical_host_plugin
|
||||
from climate import tests
|
||||
|
||||
|
||||
class PhysicalHostPlugingSetupOnlyTestCase(tests.TestCase):
|
||||
def setUp(self):
|
||||
super(PhysicalHostPlugingSetupOnlyTestCase, self).setUp()
|
||||
self.context = context
|
||||
self.patch(self.context, 'ClimateContext')
|
||||
self.physical_host_plugin = physical_host_plugin
|
||||
self.fake_phys_plugin = \
|
||||
self.physical_host_plugin.PhysicalHostPlugin()
|
||||
self.rp = rp
|
||||
self.nova_inventory = nova_inventory
|
||||
self.rp_create = self.patch(self.rp.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')
|
||||
|
||||
def test_setup(self):
|
||||
def fake_setup():
|
||||
freepool = self.patch(self.fake_phys_plugin, '_freepool_exists')
|
||||
freepool.return_value = False
|
||||
|
||||
pool = self.patch(self.rp.ReservationPool, '__init__')
|
||||
pool.side_effect = fake_setup
|
||||
inventory = self.patch(self.nova_inventory.NovaInventory, '__init__')
|
||||
inventory.return_value = None
|
||||
self.fake_phys_plugin.setup(None)
|
||||
pool.assert_called_once_with()
|
||||
inventory.assert_called_once_with()
|
||||
self.rp_create.assert_called_once_with(name='freepool', az=None)
|
||||
|
||||
def test__freepool_exists_with_freepool_present(self):
|
||||
self.patch(self.rp.ReservationPool, 'get_aggregate_from_name_or_id')
|
||||
self.fake_phys_plugin.setup(None)
|
||||
self.assertEqual(self.fake_phys_plugin._freepool_exists(), True)
|
||||
|
||||
def test__freepool_exists_with_freepool_missing(self):
|
||||
def fake_get_aggregate_from_name_or_id(*args, **kwargs):
|
||||
raise rp.AggregateNotFound
|
||||
mock = self.patch(self.rp.ReservationPool,
|
||||
'get_aggregate_from_name_or_id')
|
||||
mock.side_effect = fake_get_aggregate_from_name_or_id
|
||||
self.fake_phys_plugin.setup(None)
|
||||
self.assertEqual(self.fake_phys_plugin._freepool_exists(), False)
|
||||
|
||||
def test__get_extra_capabilities_with_values(self):
|
||||
self.db_host_extra_capability_get_all_per_host.return_value = \
|
||||
[{'id': 1,
|
||||
'capability_name': 'foo',
|
||||
'capability_value': 'bar',
|
||||
'other': 'value',
|
||||
'computehost_id': 1},
|
||||
{'id': 2,
|
||||
'capability_name': 'buzz',
|
||||
'capability_value': 'word',
|
||||
'computehost_id': 1}]
|
||||
res = self.fake_phys_plugin._get_extra_capabilities(1)
|
||||
self.assertEqual({'foo': 'bar', 'buzz': 'word'}, res)
|
||||
|
||||
def test__get_extra_capabilities_with_no_capabilities(self):
|
||||
self.db_host_extra_capability_get_all_per_host.return_value = []
|
||||
res = self.fake_phys_plugin._get_extra_capabilities(1)
|
||||
self.assertEqual({}, res)
|
||||
|
||||
|
||||
class PhysicalHostPluginTestCase(tests.TestCase):
|
||||
def setUp(self):
|
||||
super(PhysicalHostPluginTestCase, self).setUp()
|
||||
|
||||
self.context = context
|
||||
self.patch(self.context, 'ClimateContext')
|
||||
|
||||
self.service = service
|
||||
self.manager = self.service.ManagerService('127.0.0.1')
|
||||
|
||||
self.fake_host_id = '1'
|
||||
self.fake_host = {'id': self.fake_host_id,
|
||||
'hypervisor_hostname': 'foo',
|
||||
'vcpus': 4,
|
||||
'cpu_info': 'foo',
|
||||
'hypervisor_type': 'xen',
|
||||
'hypervisor_version': 1,
|
||||
'memory_mb': 8192,
|
||||
'local_gb': 10}
|
||||
|
||||
self.physical_host_plugin = physical_host_plugin
|
||||
self.fake_phys_plugin = \
|
||||
self.physical_host_plugin.PhysicalHostPlugin()
|
||||
self.db_api = db_api
|
||||
|
||||
self.db_host_get = self.patch(self.db_api, 'host_get')
|
||||
self.db_host_get.return_value = self.fake_host
|
||||
self.db_host_list = self.patch(self.db_api, 'host_list')
|
||||
self.db_host_create = self.patch(self.db_api, 'host_create')
|
||||
self.db_host_update = self.patch(self.db_api, 'host_update')
|
||||
self.db_host_destroy = self.patch(self.db_api, 'host_destroy')
|
||||
|
||||
self.db_host_extra_capability_get_all_per_host = \
|
||||
self.patch(self.db_api, 'host_extra_capability_get_all_per_host')
|
||||
self.db_host_extra_capability_get_all_per_name = \
|
||||
self.patch(self.db_api, 'host_extra_capability_get_all_per_name')
|
||||
self.db_host_extra_capability_create = \
|
||||
self.patch(self.db_api, 'host_extra_capability_create')
|
||||
self.db_host_extra_capability_update = \
|
||||
self.patch(self.db_api, 'host_extra_capability_update')
|
||||
|
||||
self.rp = rp
|
||||
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.patch(self.rp.ReservationPool, 'add_computehost')
|
||||
self.patch(self.rp.ReservationPool, 'remove_computehost')
|
||||
self.get_host_details = self.patch(self.nova_inventory.NovaInventory,
|
||||
'get_host_details')
|
||||
self.get_host_details.return_value = self.fake_host
|
||||
self.get_servers_per_host = self.patch(
|
||||
self.nova_inventory.NovaInventory, 'get_servers_per_host')
|
||||
self.get_servers_per_host.return_value = None
|
||||
self.get_extra_capabilities = self.patch(self.fake_phys_plugin,
|
||||
'_get_extra_capabilities')
|
||||
self.get_extra_capabilities.return_value = {'foo': 'bar',
|
||||
'buzz': 'word'}
|
||||
self.fake_phys_plugin.setup(None)
|
||||
|
||||
def test_get_host(self):
|
||||
host = self.fake_phys_plugin.get_computehost(self.fake_host_id)
|
||||
self.db_host_get.assert_called_once_with('1')
|
||||
expected = self.fake_host.copy()
|
||||
expected.update({'foo': 'bar', 'buzz': 'word'})
|
||||
self.assertEqual(host, expected)
|
||||
|
||||
def test_get_host_without_extracapabilities(self):
|
||||
self.get_extra_capabilities.return_value = {}
|
||||
host = self.fake_phys_plugin.get_computehost(self.fake_host_id)
|
||||
self.db_host_get.assert_called_once_with('1')
|
||||
self.assertEqual(host, self.fake_host)
|
||||
|
||||
@testtools.skip('incorrect decorator')
|
||||
def test_list_hosts(self):
|
||||
self.fake_phys_plugin.list_computehosts()
|
||||
self.db_host_list.assert_called_once_with()
|
||||
del self.service_utils
|
||||
|
||||
def test_create_host_without_extra_capabilities(self):
|
||||
self.get_extra_capabilities.return_value = {}
|
||||
host = self.fake_phys_plugin.create_computehost(self.fake_host)
|
||||
self.db_host_create.assert_called_once_with(self.fake_host)
|
||||
self.assertEqual(host, self.fake_host)
|
||||
|
||||
def test_create_host_with_extra_capabilities(self):
|
||||
fake_host = self.fake_host.copy()
|
||||
fake_host.update({'foo': 'bar'})
|
||||
# NOTE(sbauza): 'id' will be pop'd, we need to keep track of it
|
||||
fake_request = fake_host.copy()
|
||||
fake_capa = {'computehost_id': '1',
|
||||
'capability_name': 'foo',
|
||||
'capability_value': 'bar'}
|
||||
self.get_extra_capabilities.return_value = {'foo': 'bar'}
|
||||
self.db_host_create.return_value = self.fake_host
|
||||
host = self.fake_phys_plugin.create_computehost(fake_request)
|
||||
self.db_host_create.assert_called_once_with(self.fake_host)
|
||||
self.db_host_extra_capability_create.assert_called_once_with(fake_capa)
|
||||
self.assertEqual(host, fake_host)
|
||||
|
||||
def test_create_host_with_invalid_values(self):
|
||||
self.assertRaises(nova_inventory.InvalidHost,
|
||||
self.fake_phys_plugin.create_computehost, {})
|
||||
|
||||
def test_create_host_with_existing_vms(self):
|
||||
self.get_servers_per_host.return_value = ['server1', 'server2']
|
||||
self.assertRaises(nova_inventory.HostHavingServers,
|
||||
self.fake_phys_plugin.create_computehost,
|
||||
self.fake_host)
|
||||
|
||||
def test_create_host_issuing_rollback(self):
|
||||
def fake_db_host_create(*args, **kwargs):
|
||||
raise RuntimeError
|
||||
self.db_host_create.side_effect = fake_db_host_create
|
||||
host = self.fake_phys_plugin.create_computehost(self.fake_host)
|
||||
self.assertEqual(None, host)
|
||||
|
||||
def test_create_host_having_issue_when_storing_extra_capability(self):
|
||||
def fake_db_host_extra_capability_create(*args, **kwargs):
|
||||
raise RuntimeError
|
||||
fake_host = self.fake_host.copy()
|
||||
fake_host.update({'foo': 'bar'})
|
||||
fake_request = fake_host.copy()
|
||||
self.get_extra_capabilities.return_value = {'foo': 'bar'}
|
||||
self.db_host_create.return_value = self.fake_host
|
||||
self.db_host_extra_capability_create.side_effect = \
|
||||
fake_db_host_extra_capability_create
|
||||
self.assertRaises(physical_host_plugin.CantAddExtraCapability,
|
||||
self.fake_phys_plugin.create_computehost,
|
||||
fake_request)
|
||||
|
||||
def test_update_host(self):
|
||||
host_values = {'foo': 'baz'}
|
||||
|
||||
self.db_host_extra_capability_get_all_per_name.return_value = \
|
||||
[{'id': '1',
|
||||
'capability_name': 'foo',
|
||||
'capability_value': 'bar'}]
|
||||
self.fake_phys_plugin.update_computehost(self.fake_host_id,
|
||||
host_values)
|
||||
self.db_host_extra_capability_update.assert_called_once_with(
|
||||
'1', {'capability_name': 'foo', 'capability_value': 'baz'})
|
||||
|
||||
def test_update_host_having_issue_when_storing_extra_capability(self):
|
||||
def fake_db_host_extra_capability_update(*args, **kwargs):
|
||||
raise RuntimeError
|
||||
host_values = {'foo': 'baz'}
|
||||
self.db_host_extra_capability_get_all_per_name.return_value = \
|
||||
[{'id': '1',
|
||||
'capability_name': 'foo',
|
||||
'capability_value': 'bar'}]
|
||||
self.db_host_extra_capability_update.side_effect = \
|
||||
fake_db_host_extra_capability_update
|
||||
self.assertRaises(physical_host_plugin.CantAddExtraCapability,
|
||||
self.fake_phys_plugin.update_computehost,
|
||||
self.fake_host_id, host_values)
|
||||
|
||||
def test_delete_host(self):
|
||||
self.fake_phys_plugin.delete_computehost(self.fake_host_id)
|
||||
|
||||
self.db_host_destroy.assert_called_once_with(self.fake_host_id)
|
||||
|
||||
def test_delete_host_having_vms(self):
|
||||
self.get_servers_per_host.return_value = ['server1', 'server2']
|
||||
self.assertRaises(nova_inventory.HostHavingServers,
|
||||
self.fake_phys_plugin.delete_computehost,
|
||||
self.fake_host_id)
|
||||
|
||||
def test_delete_host_not_existing_in_db(self):
|
||||
self.db_host_get.return_value = None
|
||||
self.assertRaises(rp.HostNotFound,
|
||||
self.fake_phys_plugin.delete_computehost,
|
||||
self.fake_host_id)
|
||||
|
||||
def test_delete_host_issuing_rollback(self):
|
||||
def fake_db_host_destroy(*args, **kwargs):
|
||||
raise RuntimeError
|
||||
self.db_host_destroy.side_effect = fake_db_host_destroy
|
||||
self.assertRaises(rp.CantRemoveHost,
|
||||
self.fake_phys_plugin.delete_computehost,
|
||||
self.fake_host_id)
|
Loading…
Reference in New Issue
Block a user