Load cell data from a configuration file
Cells currently keeps all inter-cell communication data, including usernames and passwords, in the database. This is undesirable and unnecessary, since cells data isn't updated very frequently. This change allows cells data to be drawn from a JSON file specified via a new [cells]cells_config option. When specified, the database is no longer consulted when reloading cells data. Implements blueprint eliminate-clear-passwords-from-cells-table. DocImpact: Cells may now optionally be configured through a JSON- formatted file. The file will need the columns present in the Cell model (excluding common database fields and the 'id' column). The queue connection information must be specified through a 'transport_url' field, instead of 'username', 'password', etc. The transport_url has the following form: rabbit://<username>:<password>@<hostname>:<port>/<virtual_host> The scheme may be either 'rabbit' (shown above) or 'qpid'. Change-Id: I7046ce55a0a294293c1b1a5fb0f092aeb891ee01
This commit is contained in:
parent
fe1e15678e
commit
88ab9369e3
26
etc/nova/cells.json
Normal file
26
etc/nova/cells.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"parent": {
|
||||
"name": "parent",
|
||||
"api_url": "http://api.example.com:8774",
|
||||
"transport_url": "rabbit://rabbit.example.com",
|
||||
"weight_offset": 0.0,
|
||||
"weight_scale": 1.0,
|
||||
"is_parent": true,
|
||||
},
|
||||
"cell1": {
|
||||
"name": "cell1",
|
||||
"api_url": "http://api.example.com:8774",
|
||||
"transport_url": "rabbit://rabbit1.example.com",
|
||||
"weight_offset": 0.0,
|
||||
"weight_scale": 1.0,
|
||||
"is_parent": false,
|
||||
},
|
||||
"cell2": {
|
||||
"name": "cell2",
|
||||
"api_url": "http://api.example.com:8774",
|
||||
"transport_url": "rabbit://rabbit2.example.com",
|
||||
"weight_offset": 0.0,
|
||||
"weight_scale": 1.0,
|
||||
"is_parent": false,
|
||||
}
|
||||
}
|
@ -27,7 +27,6 @@ from nova.api.openstack import xmlutil
|
||||
from nova.cells import rpc_driver
|
||||
from nova.cells import rpcapi as cells_rpcapi
|
||||
from nova.compute import api as compute
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import timeutils
|
||||
@ -251,7 +250,7 @@ class Controller(object):
|
||||
context = req.environ['nova.context']
|
||||
authorize(context)
|
||||
try:
|
||||
cell = db.cell_get(context, id)
|
||||
cell = self.cells_rpcapi.cell_get(context, id)
|
||||
except exception.CellNotFound:
|
||||
raise exc.HTTPNotFound()
|
||||
return dict(cell=_scrub_cell(cell))
|
||||
@ -260,7 +259,10 @@ class Controller(object):
|
||||
"""Delete a child or parent cell entry. 'id' is a cell name."""
|
||||
context = req.environ['nova.context']
|
||||
authorize(context)
|
||||
num_deleted = db.cell_delete(context, id)
|
||||
try:
|
||||
num_deleted = self.cells_rpcapi.cell_delete(context, id)
|
||||
except exception.CellsUpdateUnsupported as e:
|
||||
raise exc.HTTPForbidden(explanation=e.format_message())
|
||||
if num_deleted == 0:
|
||||
raise exc.HTTPNotFound()
|
||||
return {}
|
||||
@ -342,7 +344,10 @@ class Controller(object):
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
self._validate_cell_name(cell['name'])
|
||||
self._normalize_cell(cell)
|
||||
cell = db.cell_create(context, cell)
|
||||
try:
|
||||
cell = self.cells_rpcapi.cell_create(context, cell)
|
||||
except exception.CellsUpdateUnsupported as e:
|
||||
raise exc.HTTPForbidden(explanation=e.format_message())
|
||||
return dict(cell=_scrub_cell(cell))
|
||||
|
||||
@wsgi.serializers(xml=CellTemplate)
|
||||
@ -366,14 +371,16 @@ class Controller(object):
|
||||
# operation is administrative in nature, and
|
||||
# will be going away in the future, I don't see
|
||||
# it as much of a problem...
|
||||
existing = db.cell_get(context, id)
|
||||
existing = self.cells_rpcapi.cell_get(context, id)
|
||||
except exception.CellNotFound:
|
||||
raise exc.HTTPNotFound()
|
||||
self._normalize_cell(cell, existing)
|
||||
try:
|
||||
cell = db.cell_update(context, id, cell)
|
||||
cell = self.cells_rpcapi.cell_update(context, id, cell)
|
||||
except exception.CellNotFound:
|
||||
raise exc.HTTPNotFound()
|
||||
except exception.CellsUpdateUnsupported as e:
|
||||
raise exc.HTTPForbidden(explanation=e.format_message())
|
||||
return dict(cell=_scrub_cell(cell))
|
||||
|
||||
def sync_instances(self, req, body):
|
||||
|
@ -27,7 +27,6 @@ from nova.api.openstack import xmlutil
|
||||
from nova.cells import rpc_driver
|
||||
from nova.cells import rpcapi as cells_rpcapi
|
||||
from nova.compute import api as compute
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import timeutils
|
||||
@ -248,7 +247,7 @@ class CellsController(object):
|
||||
context = req.environ['nova.context']
|
||||
authorize(context)
|
||||
try:
|
||||
cell = db.cell_get(context, id)
|
||||
cell = self.cells_rpcapi.cell_get(context, id)
|
||||
except exception.CellNotFound:
|
||||
raise exc.HTTPNotFound()
|
||||
return dict(cell=_scrub_cell(cell))
|
||||
@ -257,7 +256,10 @@ class CellsController(object):
|
||||
"""Delete a child or parent cell entry. 'id' is a cell name."""
|
||||
context = req.environ['nova.context']
|
||||
authorize(context)
|
||||
num_deleted = db.cell_delete(context, id)
|
||||
try:
|
||||
num_deleted = self.cells_rpcapi.cell_delete(context, id)
|
||||
except exception.CellsUpdateUnsupported as e:
|
||||
raise exc.HTTPForbidden(explanation=e.format_message())
|
||||
if num_deleted == 0:
|
||||
raise exc.HTTPNotFound()
|
||||
return {}
|
||||
@ -339,7 +341,10 @@ class CellsController(object):
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
self._validate_cell_name(cell['name'])
|
||||
self._normalize_cell(cell)
|
||||
cell = db.cell_create(context, cell)
|
||||
try:
|
||||
cell = self.cells_rpcapi.cell_create(context, cell)
|
||||
except exception.CellsUpdateUnsupported as e:
|
||||
raise exc.HTTPForbidden(explanation=e.format_message())
|
||||
return dict(cell=_scrub_cell(cell))
|
||||
|
||||
@wsgi.serializers(xml=CellTemplate)
|
||||
@ -363,14 +368,16 @@ class CellsController(object):
|
||||
# operation is administrative in nature, and
|
||||
# will be going away in the future, I don't see
|
||||
# it as much of a problem...
|
||||
existing = db.cell_get(context, id)
|
||||
existing = self.cells_rpcapi.cell_get(context, id)
|
||||
except exception.CellNotFound:
|
||||
raise exc.HTTPNotFound()
|
||||
self._normalize_cell(cell, existing)
|
||||
try:
|
||||
cell = db.cell_update(context, id, cell)
|
||||
cell = self.cells_rpcapi.cell_update(context, id, cell)
|
||||
except exception.CellNotFound:
|
||||
raise exc.HTTPNotFound()
|
||||
except exception.CellsUpdateUnsupported as e:
|
||||
raise exc.HTTPForbidden(explanation=e.format_message())
|
||||
return dict(cell=_scrub_cell(cell))
|
||||
|
||||
def sync_instances(self, req, body):
|
||||
|
@ -65,7 +65,7 @@ class CellsManager(manager.Manager):
|
||||
|
||||
Scheduling requests get passed to the scheduler class.
|
||||
"""
|
||||
RPC_API_VERSION = '1.12'
|
||||
RPC_API_VERSION = '1.13'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Mostly for tests.
|
||||
@ -428,3 +428,15 @@ class CellsManager(manager.Manager):
|
||||
do_cast=do_cast)
|
||||
if not do_cast:
|
||||
return response.value_or_raise()
|
||||
|
||||
def cell_create(self, ctxt, values):
|
||||
return self.state_manager.cell_create(ctxt, values)
|
||||
|
||||
def cell_update(self, ctxt, cell_name, values):
|
||||
return self.state_manager.cell_update(ctxt, cell_name, values)
|
||||
|
||||
def cell_delete(self, ctxt, cell_name):
|
||||
return self.state_manager.cell_delete(ctxt, cell_name)
|
||||
|
||||
def cell_get(self, ctxt, cell_name):
|
||||
return self.state_manager.cell_get(ctxt, cell_name)
|
||||
|
@ -68,6 +68,8 @@ class CellsAPI(rpc_proxy.RpcProxy):
|
||||
1.10 - Adds bdm_update_or_create_at_top(), and bdm_destroy_at_top()
|
||||
1.11 - Adds get_migrations()
|
||||
1.12 - Adds instance_start() and instance_stop()
|
||||
1.13 - Adds cell_create(), cell_update(), cell_delete(), and
|
||||
cell_get()
|
||||
'''
|
||||
BASE_RPC_API_VERSION = '1.0'
|
||||
|
||||
@ -382,3 +384,25 @@ class CellsAPI(rpc_proxy.RpcProxy):
|
||||
self.make_msg('stop_instance', instance=instance,
|
||||
do_cast=do_cast),
|
||||
version='1.12')
|
||||
|
||||
def cell_create(self, ctxt, values):
|
||||
return self.call(ctxt,
|
||||
self.make_msg('cell_create', values=values),
|
||||
version='1.13')
|
||||
|
||||
def cell_update(self, ctxt, cell_name, values):
|
||||
return self.call(ctxt,
|
||||
self.make_msg('cell_update',
|
||||
cell_name=cell_name,
|
||||
values=values),
|
||||
version='1.13')
|
||||
|
||||
def cell_delete(self, ctxt, cell_name):
|
||||
return self.call(ctxt,
|
||||
self.make_msg('cell_delete', cell_name=cell_name),
|
||||
version='1.13')
|
||||
|
||||
def cell_get(self, ctxt, cell_name):
|
||||
return self.call(ctxt,
|
||||
self.make_msg('cell_get', cell_name=cell_name),
|
||||
version='1.13')
|
||||
|
@ -26,6 +26,8 @@ from nova.cells import rpc_driver
|
||||
from nova import context
|
||||
from nova.db import base
|
||||
from nova import exception
|
||||
from nova.openstack.common import fileutils
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import timeutils
|
||||
from nova import utils
|
||||
@ -34,6 +36,11 @@ cell_state_manager_opts = [
|
||||
cfg.IntOpt('db_check_interval',
|
||||
default=60,
|
||||
help='Seconds between getting fresh cell info from db.'),
|
||||
cfg.StrOpt('cells_config',
|
||||
default=None,
|
||||
help='Configuration file from which to read cells '
|
||||
'configuration. If given, overrides reading cells '
|
||||
'from the database.'),
|
||||
]
|
||||
|
||||
|
||||
@ -107,19 +114,48 @@ class CellState(object):
|
||||
return "Cell '%s' (%s)" % (self.name, me)
|
||||
|
||||
|
||||
def sync_from_db(f):
|
||||
def sync_before(f):
|
||||
"""Use as a decorator to wrap methods that use cell information to
|
||||
make sure they sync the latest information from the DB periodically.
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if self._time_to_sync():
|
||||
self._cell_db_sync()
|
||||
self._cell_data_sync()
|
||||
return f(self, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
def sync_after(f):
|
||||
"""Use as a decorator to wrap methods that update cell information
|
||||
in the database to make sure the data is synchronized immediately.
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
result = f(self, *args, **kwargs)
|
||||
self._cell_data_sync(force=True)
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
|
||||
_unset = object()
|
||||
|
||||
|
||||
class CellStateManager(base.Base):
|
||||
def __new__(cls, cell_state_cls=None, cells_config=_unset):
|
||||
if cls is not CellStateManager:
|
||||
return super(CellStateManager, cls).__new__(cls)
|
||||
|
||||
if cells_config is _unset:
|
||||
cells_config = CONF.cells.cells_config
|
||||
|
||||
if cells_config:
|
||||
config_path = CONF.find_file(cells_config)
|
||||
if not config_path:
|
||||
raise cfg.ConfigFilesNotFoundError(path=config_path)
|
||||
return CellStateManagerFile(cell_state_cls, config_path)
|
||||
|
||||
return CellStateManagerDB(cell_state_cls)
|
||||
|
||||
def __init__(self, cell_state_cls=None):
|
||||
super(CellStateManager, self).__init__()
|
||||
if not cell_state_cls:
|
||||
@ -129,7 +165,9 @@ class CellStateManager(base.Base):
|
||||
self.parent_cells = {}
|
||||
self.child_cells = {}
|
||||
self.last_cell_db_check = datetime.datetime.min
|
||||
self._cell_db_sync()
|
||||
|
||||
self._cell_data_sync(force=True)
|
||||
|
||||
my_cell_capabs = {}
|
||||
for cap in CONF.cells.capabilities:
|
||||
name, value = cap.split('=', 1)
|
||||
@ -140,11 +178,8 @@ class CellStateManager(base.Base):
|
||||
my_cell_capabs[name] = values
|
||||
self.my_cell_state.update_capabilities(my_cell_capabs)
|
||||
|
||||
def _refresh_cells_from_db(self, ctxt):
|
||||
def _refresh_cells_from_dict(self, db_cells_dict):
|
||||
"""Make our cell info map match the db."""
|
||||
# Add/update existing cells ...
|
||||
db_cells = self.db.cell_get_all(ctxt)
|
||||
db_cells_dict = dict([(cell['name'], cell) for cell in db_cells])
|
||||
|
||||
# Update current cells. Delete ones that disappeared
|
||||
for cells_dict in (self.parent_cells, self.child_cells):
|
||||
@ -171,7 +206,7 @@ class CellStateManager(base.Base):
|
||||
diff = timeutils.utcnow() - self.last_cell_db_check
|
||||
return diff.seconds >= CONF.cells.db_check_interval
|
||||
|
||||
def _update_our_capacity(self, context):
|
||||
def _update_our_capacity(self, ctxt=None):
|
||||
"""Update our capacity in the self.my_cell_state CellState.
|
||||
|
||||
This will add/update 2 entries in our CellState.capacities,
|
||||
@ -208,11 +243,14 @@ class CellStateManager(base.Base):
|
||||
available per instance_type.
|
||||
"""
|
||||
|
||||
if not ctxt:
|
||||
ctxt = context.get_admin_context()
|
||||
|
||||
reserve_level = CONF.cells.reserve_percent / 100.0
|
||||
compute_hosts = {}
|
||||
|
||||
def _get_compute_hosts():
|
||||
compute_nodes = self.db.compute_node_get_all(context)
|
||||
compute_nodes = self.db.compute_node_get_all(ctxt)
|
||||
for compute in compute_nodes:
|
||||
service = compute['service']
|
||||
if not service or service['disabled']:
|
||||
@ -255,7 +293,7 @@ class CellStateManager(base.Base):
|
||||
ram_mb_free_units[str(memory_mb)] += ram_free_units
|
||||
disk_mb_free_units[str(disk_mb)] += disk_free_units
|
||||
|
||||
instance_types = self.db.instance_type_get_all(context)
|
||||
instance_types = self.db.instance_type_get_all(ctxt)
|
||||
|
||||
for compute_values in compute_hosts.values():
|
||||
total_ram_mb_free += compute_values['free_ram_mb']
|
||||
@ -269,22 +307,7 @@ class CellStateManager(base.Base):
|
||||
'units_by_mb': disk_mb_free_units}}
|
||||
self.my_cell_state.update_capacities(capacities)
|
||||
|
||||
@utils.synchronized('cell-db-sync')
|
||||
def _cell_db_sync(self):
|
||||
"""Update status for all cells if it's time. Most calls to
|
||||
this are from the check_for_update() decorator that checks
|
||||
the time, but it checks outside of a lock. The duplicate
|
||||
check here is to prevent multiple threads from pulling the
|
||||
information simultaneously.
|
||||
"""
|
||||
if self._time_to_sync():
|
||||
LOG.debug(_("Updating cell cache from db."))
|
||||
self.last_cell_db_check = timeutils.utcnow()
|
||||
ctxt = context.get_admin_context()
|
||||
self._refresh_cells_from_db(ctxt)
|
||||
self._update_our_capacity(ctxt)
|
||||
|
||||
@sync_from_db
|
||||
@sync_before
|
||||
def get_cell_info_for_neighbors(self):
|
||||
"""Return cell information for all neighbor cells."""
|
||||
cell_list = [cell.get_cell_info()
|
||||
@ -293,30 +316,30 @@ class CellStateManager(base.Base):
|
||||
for cell in self.parent_cells.itervalues()])
|
||||
return cell_list
|
||||
|
||||
@sync_from_db
|
||||
@sync_before
|
||||
def get_my_state(self):
|
||||
"""Return information for my (this) cell."""
|
||||
return self.my_cell_state
|
||||
|
||||
@sync_from_db
|
||||
@sync_before
|
||||
def get_child_cells(self):
|
||||
"""Return list of child cell_infos."""
|
||||
return self.child_cells.values()
|
||||
|
||||
@sync_from_db
|
||||
@sync_before
|
||||
def get_parent_cells(self):
|
||||
"""Return list of parent cell_infos."""
|
||||
return self.parent_cells.values()
|
||||
|
||||
@sync_from_db
|
||||
@sync_before
|
||||
def get_parent_cell(self, cell_name):
|
||||
return self.parent_cells.get(cell_name)
|
||||
|
||||
@sync_from_db
|
||||
@sync_before
|
||||
def get_child_cell(self, cell_name):
|
||||
return self.child_cells.get(cell_name)
|
||||
|
||||
@sync_from_db
|
||||
@sync_before
|
||||
def update_cell_capabilities(self, cell_name, capabilities):
|
||||
"""Update capabilities for a cell."""
|
||||
cell = self.child_cells.get(cell_name)
|
||||
@ -332,7 +355,7 @@ class CellStateManager(base.Base):
|
||||
capabilities[capab_name] = set(values)
|
||||
cell.update_capabilities(capabilities)
|
||||
|
||||
@sync_from_db
|
||||
@sync_before
|
||||
def update_cell_capacities(self, cell_name, capacities):
|
||||
"""Update capacities for a cell."""
|
||||
cell = self.child_cells.get(cell_name)
|
||||
@ -345,7 +368,7 @@ class CellStateManager(base.Base):
|
||||
return
|
||||
cell.update_capacities(capacities)
|
||||
|
||||
@sync_from_db
|
||||
@sync_before
|
||||
def get_our_capabilities(self, include_children=True):
|
||||
capabs = copy.deepcopy(self.my_cell_state.capabilities)
|
||||
if include_children:
|
||||
@ -368,7 +391,7 @@ class CellStateManager(base.Base):
|
||||
target.setdefault(key, 0)
|
||||
target[key] += value
|
||||
|
||||
@sync_from_db
|
||||
@sync_before
|
||||
def get_our_capacities(self, include_children=True):
|
||||
capacities = copy.deepcopy(self.my_cell_state.capacities)
|
||||
if include_children:
|
||||
@ -376,10 +399,85 @@ class CellStateManager(base.Base):
|
||||
self._add_to_dict(capacities, cell.capacities)
|
||||
return capacities
|
||||
|
||||
@sync_from_db
|
||||
@sync_before
|
||||
def get_capacities(self, cell_name=None):
|
||||
if not cell_name or cell_name == self.my_cell_state.name:
|
||||
return self.get_our_capacities()
|
||||
if cell_name in self.child_cells:
|
||||
return self.child_cells[cell_name].capacities
|
||||
raise exception.CellNotFound(cell_name=cell_name)
|
||||
|
||||
@sync_before
|
||||
def cell_get(self, ctxt, cell_name):
|
||||
for cells_dict in (self.parent_cells, self.child_cells):
|
||||
if cell_name in cells_dict:
|
||||
return cells_dict[cell_name]
|
||||
|
||||
raise exception.CellNotFound(cell_name=cell_name)
|
||||
|
||||
|
||||
class CellStateManagerDB(CellStateManager):
|
||||
@utils.synchronized('cell-db-sync')
|
||||
def _cell_data_sync(self, force=False):
|
||||
"""
|
||||
Update cell status for all cells from the backing data store
|
||||
when necessary.
|
||||
|
||||
:param force: If True, cell status will be updated regardless
|
||||
of whether it's time to do so.
|
||||
"""
|
||||
if force or self._time_to_sync():
|
||||
LOG.debug(_("Updating cell cache from db."))
|
||||
self.last_cell_db_check = timeutils.utcnow()
|
||||
ctxt = context.get_admin_context()
|
||||
db_cells = self.db.cell_get_all(ctxt)
|
||||
db_cells_dict = dict((cell['name'], cell) for cell in db_cells)
|
||||
self._refresh_cells_from_dict(db_cells_dict)
|
||||
self._update_our_capacity(ctxt)
|
||||
|
||||
@sync_after
|
||||
def cell_create(self, ctxt, values):
|
||||
return self.db.cell_create(ctxt, values)
|
||||
|
||||
@sync_after
|
||||
def cell_update(self, ctxt, cell_name, values):
|
||||
return self.db.cell_update(ctxt, cell_name, values)
|
||||
|
||||
@sync_after
|
||||
def cell_delete(self, ctxt, cell_name):
|
||||
return self.db.cell_delete(ctxt, cell_name)
|
||||
|
||||
|
||||
class CellStateManagerFile(CellStateManager):
|
||||
def __init__(self, cell_state_cls, cells_config_path):
|
||||
self.cells_config_path = cells_config_path
|
||||
super(CellStateManagerFile, self).__init__(cell_state_cls)
|
||||
|
||||
def _cell_data_sync(self, force=False):
|
||||
"""
|
||||
Update cell status for all cells from the backing data store
|
||||
when necessary.
|
||||
|
||||
:param force: If True, cell status will be updated regardless
|
||||
of whether it's time to do so.
|
||||
"""
|
||||
reloaded, data = fileutils.read_cached_file(self.cells_config_path,
|
||||
force_reload=force)
|
||||
|
||||
if reloaded:
|
||||
LOG.debug(_("Updating cell cache from config file."))
|
||||
self.cells_config_data = jsonutils.loads(data)
|
||||
self._refresh_cells_from_dict(self.cells_config_data)
|
||||
|
||||
if force or self._time_to_sync():
|
||||
self.last_cell_db_check = timeutils.utcnow()
|
||||
self._update_our_capacity()
|
||||
|
||||
def cell_create(self, ctxt, values):
|
||||
raise exception.CellsUpdateProhibited()
|
||||
|
||||
def cell_update(self, ctxt, cell_name, values):
|
||||
raise exception.CellsUpdateProhibited()
|
||||
|
||||
def cell_delete(self, ctxt, cell_name):
|
||||
raise exception.CellsUpdateProhibited()
|
||||
|
@ -884,6 +884,10 @@ class CellError(NovaException):
|
||||
message = _("Exception received during cell processing: %(exc_name)s.")
|
||||
|
||||
|
||||
class CellsUpdateUnsupported(NovaException):
|
||||
message = _("Cannot update cells configuration file.")
|
||||
|
||||
|
||||
class InstanceUnknownCell(NotFound):
|
||||
message = _("Cell is not known for instance %(instance_uuid)s")
|
||||
|
||||
|
@ -24,7 +24,6 @@ from nova.api.openstack import xmlutil
|
||||
from nova.cells import rpc_driver
|
||||
from nova.cells import rpcapi as cells_rpcapi
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova.openstack.common import timeutils
|
||||
from nova import test
|
||||
@ -46,7 +45,7 @@ FAKE_CAPABILITIES = [
|
||||
{'cap3': '4,5', 'cap4': '5,6'}]
|
||||
|
||||
|
||||
def fake_db_cell_get(context, cell_name):
|
||||
def fake_cell_get(self, context, cell_name):
|
||||
for cell in FAKE_CELLS:
|
||||
if cell_name == cell['name']:
|
||||
return cell
|
||||
@ -54,14 +53,14 @@ def fake_db_cell_get(context, cell_name):
|
||||
raise exception.CellNotFound(cell_name=cell_name)
|
||||
|
||||
|
||||
def fake_db_cell_create(context, values):
|
||||
def fake_cell_create(self, context, values):
|
||||
cell = dict(id=1)
|
||||
cell.update(values)
|
||||
return cell
|
||||
|
||||
|
||||
def fake_db_cell_update(context, cell_id, values):
|
||||
cell = fake_db_cell_get(context, cell_id)
|
||||
def fake_cell_update(self, context, cell_id, values):
|
||||
cell = fake_cell_get(self, context, cell_id)
|
||||
cell.update(values)
|
||||
return cell
|
||||
|
||||
@ -82,17 +81,12 @@ def fake_cells_api_get_all_cell_info(*args):
|
||||
return cells
|
||||
|
||||
|
||||
def fake_db_cell_get_all(context):
|
||||
return FAKE_CELLS
|
||||
|
||||
|
||||
class CellsTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(CellsTest, self).setUp()
|
||||
self.stubs.Set(db, 'cell_get', fake_db_cell_get)
|
||||
self.stubs.Set(db, 'cell_get_all', fake_db_cell_get_all)
|
||||
self.stubs.Set(db, 'cell_update', fake_db_cell_update)
|
||||
self.stubs.Set(db, 'cell_create', fake_db_cell_create)
|
||||
self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_get', fake_cell_get)
|
||||
self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_update', fake_cell_update)
|
||||
self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_create', fake_cell_create)
|
||||
self.stubs.Set(cells_rpcapi.CellsAPI, 'get_cell_info_for_neighbors',
|
||||
fake_cells_api_get_all_cell_info)
|
||||
|
||||
@ -139,17 +133,22 @@ class CellsTest(test.TestCase):
|
||||
def test_cell_delete(self):
|
||||
call_info = {'delete_called': 0}
|
||||
|
||||
def fake_db_cell_delete(context, cell_name):
|
||||
def fake_cell_delete(inst, context, cell_name):
|
||||
self.assertEqual(cell_name, 'cell999')
|
||||
call_info['delete_called'] += 1
|
||||
|
||||
self.stubs.Set(db, 'cell_delete', fake_db_cell_delete)
|
||||
self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_delete', fake_cell_delete)
|
||||
|
||||
req = self._get_request("cells/cell999")
|
||||
self.controller.delete(req, 'cell999')
|
||||
self.assertEqual(call_info['delete_called'], 1)
|
||||
|
||||
def test_delete_bogus_cell_raises(self):
|
||||
def fake_cell_delete(inst, context, cell_name):
|
||||
return 0
|
||||
|
||||
self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_delete', fake_cell_delete)
|
||||
|
||||
req = self._get_request("cells/cell999")
|
||||
req.environ['nova.context'] = self.context
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.delete, req,
|
||||
|
@ -23,7 +23,6 @@ from nova.api.openstack import xmlutil
|
||||
from nova.cells import rpc_driver
|
||||
from nova.cells import rpcapi as cells_rpcapi
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova.openstack.common import timeutils
|
||||
from nova import test
|
||||
@ -45,7 +44,7 @@ FAKE_CAPABILITIES = [
|
||||
{'cap3': '4,5', 'cap4': '5,6'}]
|
||||
|
||||
|
||||
def fake_db_cell_get(context, cell_name):
|
||||
def fake_cell_get(self, context, cell_name):
|
||||
for cell in FAKE_CELLS:
|
||||
if cell_name == cell['name']:
|
||||
return cell
|
||||
@ -53,14 +52,14 @@ def fake_db_cell_get(context, cell_name):
|
||||
raise exception.CellNotFound(cell_name=cell_name)
|
||||
|
||||
|
||||
def fake_db_cell_create(context, values):
|
||||
def fake_cell_create(self, context, values):
|
||||
cell = dict(id=1)
|
||||
cell.update(values)
|
||||
return cell
|
||||
|
||||
|
||||
def fake_db_cell_update(context, cell_id, values):
|
||||
cell = fake_db_cell_get(context, cell_id)
|
||||
def fake_cell_update(self, context, cell_id, values):
|
||||
cell = fake_cell_get(self, context, cell_id)
|
||||
cell.update(values)
|
||||
return cell
|
||||
|
||||
@ -81,17 +80,12 @@ def fake_cells_api_get_all_cell_info(*args):
|
||||
return cells
|
||||
|
||||
|
||||
def fake_db_cell_get_all(context):
|
||||
return FAKE_CELLS
|
||||
|
||||
|
||||
class CellsTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(CellsTest, self).setUp()
|
||||
self.stubs.Set(db, 'cell_get', fake_db_cell_get)
|
||||
self.stubs.Set(db, 'cell_get_all', fake_db_cell_get_all)
|
||||
self.stubs.Set(db, 'cell_update', fake_db_cell_update)
|
||||
self.stubs.Set(db, 'cell_create', fake_db_cell_create)
|
||||
self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_get', fake_cell_get)
|
||||
self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_update', fake_cell_update)
|
||||
self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_create', fake_cell_create)
|
||||
self.stubs.Set(cells_rpcapi.CellsAPI, 'get_cell_info_for_neighbors',
|
||||
fake_cells_api_get_all_cell_info)
|
||||
|
||||
@ -137,17 +131,22 @@ class CellsTest(test.TestCase):
|
||||
def test_cell_delete(self):
|
||||
call_info = {'delete_called': 0}
|
||||
|
||||
def fake_db_cell_delete(context, cell_name):
|
||||
def fake_cell_delete(inst, context, cell_name):
|
||||
self.assertEqual(cell_name, 'cell999')
|
||||
call_info['delete_called'] += 1
|
||||
|
||||
self.stubs.Set(db, 'cell_delete', fake_db_cell_delete)
|
||||
self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_delete', fake_cell_delete)
|
||||
|
||||
req = self._get_request("cells/cell999")
|
||||
self.controller.delete(req, 'cell999')
|
||||
self.assertEqual(call_info['delete_called'], 1)
|
||||
|
||||
def test_delete_bogus_cell_raises(self):
|
||||
def fake_cell_delete(inst, context, cell_name):
|
||||
return 0
|
||||
|
||||
self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_delete', fake_cell_delete)
|
||||
|
||||
req = self._get_request("cells/cell999")
|
||||
req.environ['nova.context'] = self.context
|
||||
self.assertRaises(exc.HTTPNotFound, self.controller.delete, req,
|
||||
|
@ -83,7 +83,7 @@ class FakeCellState(cells_state.CellState):
|
||||
message.process()
|
||||
|
||||
|
||||
class FakeCellStateManager(cells_state.CellStateManager):
|
||||
class FakeCellStateManager(cells_state.CellStateManagerDB):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(FakeCellStateManager, self).__init__(*args,
|
||||
cell_state_cls=FakeCellState, **kwargs)
|
||||
|
@ -617,3 +617,49 @@ class CellsManagerClassTestCase(test.TestCase):
|
||||
self.cells_manager.stop_instance(self.ctxt,
|
||||
instance='fake-instance',
|
||||
do_cast='meow')
|
||||
|
||||
def test_cell_create(self):
|
||||
values = 'values'
|
||||
response = 'created_cell'
|
||||
self.mox.StubOutWithMock(self.state_manager,
|
||||
'cell_create')
|
||||
self.state_manager.cell_create(self.ctxt, values).\
|
||||
AndReturn(response)
|
||||
self.mox.ReplayAll()
|
||||
self.assertEqual(response,
|
||||
self.cells_manager.cell_create(self.ctxt, values))
|
||||
|
||||
def test_cell_update(self):
|
||||
cell_name = 'cell_name'
|
||||
values = 'values'
|
||||
response = 'updated_cell'
|
||||
self.mox.StubOutWithMock(self.state_manager,
|
||||
'cell_update')
|
||||
self.state_manager.cell_update(self.ctxt, cell_name, values).\
|
||||
AndReturn(response)
|
||||
self.mox.ReplayAll()
|
||||
self.assertEqual(response,
|
||||
self.cells_manager.cell_update(self.ctxt, cell_name,
|
||||
values))
|
||||
|
||||
def test_cell_delete(self):
|
||||
cell_name = 'cell_name'
|
||||
response = 1
|
||||
self.mox.StubOutWithMock(self.state_manager,
|
||||
'cell_delete')
|
||||
self.state_manager.cell_delete(self.ctxt, cell_name).\
|
||||
AndReturn(response)
|
||||
self.mox.ReplayAll()
|
||||
self.assertEqual(response,
|
||||
self.cells_manager.cell_delete(self.ctxt, cell_name))
|
||||
|
||||
def test_cell_get(self):
|
||||
cell_name = 'cell_name'
|
||||
response = 'cell_info'
|
||||
self.mox.StubOutWithMock(self.state_manager,
|
||||
'cell_get')
|
||||
self.state_manager.cell_get(self.ctxt, cell_name).\
|
||||
AndReturn(response)
|
||||
self.mox.ReplayAll()
|
||||
self.assertEqual(response,
|
||||
self.cells_manager.cell_get(self.ctxt, cell_name))
|
||||
|
@ -494,3 +494,58 @@ class CellsAPITestCase(test.TestCase):
|
||||
self._check_result(call_info, 'stop_instance',
|
||||
expected_args, version='1.12')
|
||||
self.assertEqual(result, 'fake_response')
|
||||
|
||||
def test_cell_create(self):
|
||||
call_info = self._stub_rpc_method('call', 'fake_response')
|
||||
|
||||
result = self.cells_rpcapi.cell_create(self.fake_context, 'values')
|
||||
|
||||
expected_args = {'values': 'values'}
|
||||
self._check_result(call_info, 'cell_create',
|
||||
expected_args, version='1.13')
|
||||
self.assertEqual(result, 'fake_response')
|
||||
|
||||
def test_cell_update(self):
|
||||
call_info = self._stub_rpc_method('call', 'fake_response')
|
||||
|
||||
result = self.cells_rpcapi.cell_update(self.fake_context,
|
||||
'cell_name', 'values')
|
||||
|
||||
expected_args = {'cell_name': 'cell_name',
|
||||
'values': 'values'}
|
||||
self._check_result(call_info, 'cell_update',
|
||||
expected_args, version='1.13')
|
||||
self.assertEqual(result, 'fake_response')
|
||||
|
||||
def test_cell_delete(self):
|
||||
call_info = self._stub_rpc_method('call', 'fake_response')
|
||||
|
||||
result = self.cells_rpcapi.cell_delete(self.fake_context,
|
||||
'cell_name')
|
||||
|
||||
expected_args = {'cell_name': 'cell_name'}
|
||||
self._check_result(call_info, 'cell_delete',
|
||||
expected_args, version='1.13')
|
||||
self.assertEqual(result, 'fake_response')
|
||||
|
||||
def test_cell_delete(self):
|
||||
call_info = self._stub_rpc_method('call', 'fake_response')
|
||||
|
||||
result = self.cells_rpcapi.cell_delete(self.fake_context,
|
||||
'cell_name')
|
||||
|
||||
expected_args = {'cell_name': 'cell_name'}
|
||||
self._check_result(call_info, 'cell_delete',
|
||||
expected_args, version='1.13')
|
||||
self.assertEqual(result, 'fake_response')
|
||||
|
||||
def test_cell_get(self):
|
||||
call_info = self._stub_rpc_method('call', 'fake_response')
|
||||
|
||||
result = self.cells_rpcapi.cell_get(self.fake_context,
|
||||
'cell_name')
|
||||
|
||||
expected_args = {'cell_name': 'cell_name'}
|
||||
self._check_result(call_info, 'cell_get',
|
||||
expected_args, version='1.13')
|
||||
self.assertEqual(result, 'fake_response')
|
||||
|
@ -166,3 +166,43 @@ class TestCellsGetCapacity(TestCellsStateManager):
|
||||
self.assertRaises(exception.CellNotFound,
|
||||
self.state_manager.get_capacities,
|
||||
cell_name="invalid_cell_name")
|
||||
|
||||
|
||||
class FakeCellStateManager(object):
|
||||
def __init__(self):
|
||||
self.called = []
|
||||
|
||||
def _cell_data_sync(self, force=False):
|
||||
self.called.append(('_cell_data_sync', force))
|
||||
|
||||
|
||||
class TestSyncDecorators(test.TestCase):
|
||||
def test_sync_before(self):
|
||||
manager = FakeCellStateManager()
|
||||
|
||||
def test(inst, *args, **kwargs):
|
||||
self.assertEqual(inst, manager)
|
||||
self.assertEqual(args, (1, 2, 3))
|
||||
self.assertEqual(kwargs, dict(a=4, b=5, c=6))
|
||||
return 'result'
|
||||
wrapper = state.sync_before(test)
|
||||
|
||||
result = wrapper(manager, 1, 2, 3, a=4, b=5, c=6)
|
||||
|
||||
self.assertEqual(result, 'result')
|
||||
self.assertEqual(manager.called, [('_cell_data_sync', False)])
|
||||
|
||||
def test_sync_after(self):
|
||||
manager = FakeCellStateManager()
|
||||
|
||||
def test(inst, *args, **kwargs):
|
||||
self.assertEqual(inst, manager)
|
||||
self.assertEqual(args, (1, 2, 3))
|
||||
self.assertEqual(kwargs, dict(a=4, b=5, c=6))
|
||||
return 'result'
|
||||
wrapper = state.sync_after(test)
|
||||
|
||||
result = wrapper(manager, 1, 2, 3, a=4, b=5, c=6)
|
||||
|
||||
self.assertEqual(result, 'result')
|
||||
self.assertEqual(manager.called, [('_cell_data_sync', True)])
|
||||
|
@ -33,6 +33,7 @@ from nova.api.openstack.compute.contrib import coverage_ext
|
||||
from nova.api.openstack.compute.contrib import fping
|
||||
from nova.api.openstack.compute.extensions import ExtensionManager as ext_mgr
|
||||
# Import extensions to pull in osapi_compute_extension CONF option used below.
|
||||
from nova.cells import rpcapi as cells_rpcapi
|
||||
from nova.cells import state
|
||||
from nova.cloudpipe import pipelib
|
||||
from nova.compute import api as compute_api
|
||||
@ -2878,7 +2879,7 @@ class CellsSampleJsonTest(ApiSampleTestBase):
|
||||
def _fake_cell_get_all(context):
|
||||
return self.cells
|
||||
|
||||
def _fake_cell_get(context, cell_name):
|
||||
def _fake_cell_get(inst, context, cell_name):
|
||||
for cell in self.cells:
|
||||
if cell['name'] == cell_name:
|
||||
return cell
|
||||
@ -2895,7 +2896,7 @@ class CellsSampleJsonTest(ApiSampleTestBase):
|
||||
self.cells.append(cell)
|
||||
|
||||
self.stubs.Set(db, 'cell_get_all', _fake_cell_get_all)
|
||||
self.stubs.Set(db, 'cell_get', _fake_cell_get)
|
||||
self.stubs.Set(cells_rpcapi.CellsAPI, 'cell_get', _fake_cell_get)
|
||||
|
||||
def test_cells_empty_list(self):
|
||||
# Override this
|
||||
|
Loading…
x
Reference in New Issue
Block a user