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:
Kevin L. Mitchell 2013-07-15 10:32:23 -05:00
parent fe1e15678e
commit 88ab9369e3
14 changed files with 401 additions and 83 deletions

26
etc/nova/cells.json Normal file
View 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,
}
}

View File

@ -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):

View File

@ -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):

View File

@ -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)

View File

@ -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')

View File

@ -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()

View File

@ -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")

View File

@ -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,

View File

@ -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,

View File

@ -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)

View File

@ -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))

View File

@ -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')

View File

@ -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)])

View File

@ -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