Merge "Handle uuid references within an ovsdb transaction"
This commit is contained in:
commit
605cdce696
|
@ -381,4 +381,5 @@ def py_to_val(pyval):
|
|||
elif pyval == '':
|
||||
return '""'
|
||||
else:
|
||||
return pyval
|
||||
# NOTE(twilson) If a Command object, return its record_id as a value
|
||||
return getattr(pyval, "record_id", pyval)
|
||||
|
|
|
@ -81,7 +81,8 @@ class Transaction(api.Transaction):
|
|||
pass
|
||||
|
||||
def post_commit(self, txn):
|
||||
pass
|
||||
for command in self.commands:
|
||||
command.post_commit(txn)
|
||||
|
||||
def do_commit(self):
|
||||
self.start_time = time.time()
|
||||
|
@ -144,6 +145,7 @@ class NeutronOVSDBTransaction(Transaction):
|
|||
txn.expected_ifaces = set()
|
||||
|
||||
def post_commit(self, txn):
|
||||
super(NeutronOVSDBTransaction, self).post_commit(txn)
|
||||
# ovs-vsctl only logs these failures and does not return nonzero
|
||||
try:
|
||||
self.do_post_commit(txn)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import collections
|
||||
import itertools
|
||||
import uuid
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
|
@ -149,6 +150,22 @@ class DbGetCommand(DbCommand):
|
|||
self._result = list(self._result[0].values())[0]
|
||||
|
||||
|
||||
class DbCreateCommand(BaseCommand):
|
||||
def __init__(self, context, opts=None, args=None):
|
||||
super(DbCreateCommand, self).__init__(context, "create", opts, args)
|
||||
# NOTE(twilson) pre-commit result used for intra-transaction reference
|
||||
self.record_id = "@%s" % uuid.uuid4()
|
||||
self.opts.append("--id=%s" % self.record_id)
|
||||
|
||||
@property
|
||||
def result(self):
|
||||
return self._result
|
||||
|
||||
@result.setter
|
||||
def result(self, val):
|
||||
self._result = uuid.UUID(val) if val else val
|
||||
|
||||
|
||||
class BrExistsCommand(DbCommand):
|
||||
@DbCommand.result.setter
|
||||
def result(self, val):
|
||||
|
@ -194,7 +211,7 @@ class OvsdbVsctl(ovsdb.API):
|
|||
def db_create(self, table, **col_values):
|
||||
args = [table]
|
||||
args += _set_colval_args(*col_values.items())
|
||||
return BaseCommand(self.context, 'create', args=args)
|
||||
return DbCreateCommand(self.context, args=args)
|
||||
|
||||
def db_destroy(self, table, record):
|
||||
args = [table, record]
|
||||
|
|
|
@ -41,6 +41,9 @@ class BaseCommand(api.Command):
|
|||
if not check_error:
|
||||
ctx.reraise = False
|
||||
|
||||
def post_commit(self, txn):
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
command_info = self.__dict__
|
||||
return "%s(%s)" % (
|
||||
|
@ -164,9 +167,14 @@ class DbCreateCommand(BaseCommand):
|
|||
def run_idl(self, txn):
|
||||
row = txn.insert(self.api._tables[self.table])
|
||||
for col, val in self.columns.items():
|
||||
setattr(row, col, val)
|
||||
setattr(row, col, idlutils.db_replace_record(val))
|
||||
# This is a temporary row to be used within the transaction
|
||||
self.result = row
|
||||
|
||||
def post_commit(self, txn):
|
||||
# Replace the temporary row with the post-commit UUID to match vsctl
|
||||
self.result = txn.get_insert_uuid(self.result.uuid)
|
||||
|
||||
|
||||
class DbDestroyCommand(BaseCommand):
|
||||
def __init__(self, api, table, record):
|
||||
|
@ -194,7 +202,7 @@ class DbSetCommand(BaseCommand):
|
|||
# this soon.
|
||||
if isinstance(val, collections.OrderedDict):
|
||||
val = dict(val)
|
||||
setattr(record, col, val)
|
||||
setattr(record, col, idlutils.db_replace_record(val))
|
||||
|
||||
|
||||
class DbClearCommand(BaseCommand):
|
||||
|
|
|
@ -22,8 +22,10 @@ from ovs.db import idl
|
|||
from ovs import jsonrpc
|
||||
from ovs import poller
|
||||
from ovs import stream
|
||||
import six
|
||||
|
||||
from neutron._i18n import _
|
||||
from neutron.agent.ovsdb import api
|
||||
|
||||
|
||||
RowLookup = collections.namedtuple('RowLookup',
|
||||
|
@ -233,3 +235,28 @@ def get_index_column(table):
|
|||
idx = table.indexes[0]
|
||||
if len(idx) == 1:
|
||||
return idx[0].name
|
||||
|
||||
|
||||
def db_replace_record(obj):
|
||||
"""Replace any api.Command objects with their results
|
||||
|
||||
This method should leave obj untouched unless the object contains an
|
||||
api.Command object.
|
||||
"""
|
||||
if isinstance(obj, collections.Mapping):
|
||||
for k, v in six.iteritems(obj):
|
||||
if isinstance(v, api.Command):
|
||||
obj[k] = v.result
|
||||
elif (isinstance(obj, collections.Sequence)
|
||||
and not isinstance(obj, six.string_types)):
|
||||
for i, v in enumerate(obj):
|
||||
if isinstance(v, api.Command):
|
||||
try:
|
||||
obj[i] = v.result
|
||||
except TypeError:
|
||||
# NOTE(twilson) If someone passes a tuple, then just return
|
||||
# a tuple with the Commands replaced with their results
|
||||
return type(obj)(getattr(v, "result", v) for v in obj)
|
||||
elif isinstance(obj, api.Command):
|
||||
obj = obj.result
|
||||
return obj
|
||||
|
|
|
@ -334,6 +334,24 @@ class OVSBridgeTestCase(OVSBridgeTestBase):
|
|||
self.assertIsNone(max_rate)
|
||||
self.assertIsNone(burst)
|
||||
|
||||
def test_db_create_references(self):
|
||||
with self.ovs.ovsdb.transaction(check_error=True) as txn:
|
||||
queue = txn.add(self.ovs.ovsdb.db_create("Queue",
|
||||
other_config={'a': '1'}))
|
||||
qos = txn.add(self.ovs.ovsdb.db_create("QoS", queues={0: queue}))
|
||||
txn.add(self.ovs.ovsdb.db_set("Port", self.br.br_name,
|
||||
('qos', qos)))
|
||||
|
||||
def cleanup():
|
||||
with self.ovs.ovsdb.transaction() as t:
|
||||
t.add(self.ovs.ovsdb.db_destroy("QoS", qos.result))
|
||||
t.add(self.ovs.ovsdb.db_destroy("Queue", queue.result))
|
||||
t.add(self.ovs.ovsdb.db_clear("Port", self.br.br_name, 'qos'))
|
||||
|
||||
self.addCleanup(cleanup)
|
||||
val = self.ovs.ovsdb.db_get("Port", self.br.br_name, 'qos').execute()
|
||||
self.assertEqual(qos.result, val)
|
||||
|
||||
|
||||
class OVSLibTestCase(base.BaseOVSLinuxTestCase):
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import mock
|
||||
|
||||
from neutron.agent.ovsdb import api
|
||||
from neutron.agent.ovsdb.native import idlutils
|
||||
from neutron.tests import base
|
||||
|
||||
|
@ -47,6 +48,14 @@ class MockRow(object):
|
|||
return super(MockRow, self).__getattr__(attr)
|
||||
|
||||
|
||||
class MockCommand(api.Command):
|
||||
def __init__(self, result):
|
||||
self.result = result
|
||||
|
||||
def execute(self, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
class TestIdlUtils(base.BaseTestCase):
|
||||
def test_condition_match(self):
|
||||
"""
|
||||
|
@ -98,3 +107,38 @@ class TestIdlUtils(base.BaseTestCase):
|
|||
row, ("comments", "=", ["c", "b"])))
|
||||
self.assertTrue(idlutils.condition_match(
|
||||
row, ("comments", "!=", ["d"])))
|
||||
|
||||
def test_db_replace_record_dict(self):
|
||||
obj = {'a': 1, 'b': 2}
|
||||
self.assertIs(obj, idlutils.db_replace_record(obj))
|
||||
|
||||
def test_db_replace_record_dict_cmd(self):
|
||||
obj = {'a': 1, 'b': MockCommand(2)}
|
||||
res = {'a': 1, 'b': 2}
|
||||
self.assertEqual(res, idlutils.db_replace_record(obj))
|
||||
|
||||
def test_db_replace_record_list(self):
|
||||
obj = [1, 2, 3]
|
||||
self.assertIs(obj, idlutils.db_replace_record(obj))
|
||||
|
||||
def test_db_replace_record_list_cmd(self):
|
||||
obj = [1, MockCommand(2), 3]
|
||||
res = [1, 2, 3]
|
||||
self.assertEqual(res, idlutils.db_replace_record(obj))
|
||||
|
||||
def test_db_replace_record_tuple(self):
|
||||
obj = (1, 2, 3)
|
||||
self.assertIs(obj, idlutils.db_replace_record(obj))
|
||||
|
||||
def test_db_replace_record_tuple_cmd(self):
|
||||
obj = (1, MockCommand(2), 3)
|
||||
res = (1, 2, 3)
|
||||
self.assertEqual(res, idlutils.db_replace_record(obj))
|
||||
|
||||
def test_db_replace_record(self):
|
||||
obj = "test"
|
||||
self.assertIs(obj, idlutils.db_replace_record(obj))
|
||||
|
||||
def test_db_replace_record_cmd(self):
|
||||
obj = MockCommand("test")
|
||||
self.assertEqual("test", idlutils.db_replace_record(obj))
|
||||
|
|
Loading…
Reference in New Issue