ovs/vsctl: Fix API for parsing column/key/value set

Currently, VSCtlContext.parse_column_key_value() method fails to
parse "<column>:<key>=<value>" formatted str, which indicates
a "map" type column.
So some implementation to set "map" type column have avoided this
bug with "<column>=<key>=<value>" formatted str (not ":").

This patch fixes this problem and enable to parse the correct format
value.

Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
IWASE Yusuke 2016-10-20 16:48:22 +09:00 committed by FUJITA Tomonori
parent d7cfe6687b
commit df95d2c660
2 changed files with 115 additions and 102 deletions

View File

@ -299,7 +299,7 @@ class OVSBridge(object):
options += ',local_ip=%(local_ip)s' % locals()
args = ['Interface', name, 'type=%s' % tunnel_type,
'options=%s' % options]
'options:%s' % options]
if ofport:
args.append('ofport_request=%(ofport)s' % locals())

View File

@ -478,7 +478,8 @@ class VSCtlContext(object):
ovsrec_qos = qos[0]
ovsrec_qos.type = type
if max_rate is not None:
self.set_column(ovsrec_qos, 'other_config', 'max-rate', max_rate)
value_json = ['map', [['max-rate', max_rate]]]
self.set_column(ovsrec_qos, 'other_config', value_json)
self.add_qos_to_cache(vsctl_port, [ovsrec_qos])
return ovsrec_qos
@ -492,13 +493,13 @@ class VSCtlContext(object):
ovsrec_queue = self.txn.insert(
self.txn.idl.tables[vswitch_idl.OVSREC_TABLE_QUEUE])
if max_rate is not None:
self.set_column(ovsrec_queue, 'other_config',
'max-rate', max_rate)
value_json = ['map', [['max-rate', max_rate]]]
self.add_column(ovsrec_queue, 'other_config', value_json)
if min_rate is not None:
self.set_column(ovsrec_queue, 'other_config',
'min-rate', min_rate)
self.set_column(ovsrec_qos, 'queues', queue_id,
['uuid', str(ovsrec_queue.uuid)])
value_json = ['map', [['min-rate', min_rate]]]
self.add_column(ovsrec_queue, 'other_config', value_json)
value_json = ['map', [[queue_id, ['uuid', str(ovsrec_queue.uuid)]]]]
self.add_column(ovsrec_qos, 'queues', value_json)
self.add_queue_to_cache(vsctl_qos, ovsrec_queue)
return ovsrec_queue
@ -582,8 +583,8 @@ class VSCtlContext(object):
def add_port(self, br_name, port_name, may_exist, fake_iface,
iface_names, settings=None):
"""
:type settings: list of (column, key, value_json)
where column and key are str,
:type settings: list of (column, value_json)
where column is str,
value_json is json that is represented
by Datum.to_json()
"""
@ -630,10 +631,9 @@ class VSCtlContext(object):
if vsctl_bridge.parent:
tag = vsctl_bridge.vlan
ovsrec_port.tag = tag
for setting in settings:
for column, value in settings:
# TODO:XXX self.symtab:
column, key, value = setting
self.set_column(ovsrec_port, column, key, value)
self.set_column(ovsrec_port, column, value)
if vsctl_bridge.parent:
ovsrec_bridge = vsctl_bridge.parent.br_cfg
@ -734,26 +734,25 @@ class VSCtlContext(object):
@staticmethod
def parse_column_key_value(table_schema, setting_string):
"""
parse <column>[:<key>]=<value>
Parses 'setting_string' as str formatted in <column>[:<key>]=<value>
and returns str type 'column' and json formatted 'value'
"""
column_value = setting_string.split('=', 1)
if len(column_value) == 1:
column = column_value[0]
if ':' in setting_string:
# splits <column>:<key>=<value> into <column> and <key>=<value>
column, value = setting_string.split(':', 1)
elif '=' in setting_string:
# splits <column>=<value> into <column> and <value>
column, value = setting_string.split('=', 1)
else:
# stores <column> and <value>=None
column = setting_string
value = None
else:
column, value = column_value
if ':' in column:
column, key = column.split(':', 1)
else:
key = None
if value is not None:
LOG.debug("columns %s", list(table_schema.columns.keys()))
type_ = table_schema.columns[column].type
value = datum_from_string(type_, value)
LOG.debug("column %s value %s", column, value)
return column, key, value
return column, value
def get_column(self, ovsrec_row, column, key=None, if_exists=False):
value = getattr(ovsrec_row, column, None)
@ -769,25 +768,19 @@ class VSCtlContext(object):
return value
def _pre_mod_column(self, ovsrec_row, column, key, value_json):
def _pre_mod_column(self, ovsrec_row, column, value_json):
if column not in ovsrec_row._table.columns:
vsctl_fatal('%s does not contain a column whose name matches "%s"'
% (ovsrec_row._table.name, column))
column_schema = ovsrec_row._table.columns[column]
if key is not None:
value_json = ['map', [[key, value_json]]]
if column_schema.type.value.type == ovs.db.types.VoidType:
vsctl_fatal('cannot specify key to set for non-map column %s' %
column)
datum = ovs.db.data.Datum.from_json(
column_schema.type, value_json, self.symtab)
return datum.to_python(ovs.db.idl._uuid_to_row)
def set_column(self, ovsrec_row, column, key, value_json):
def set_column(self, ovsrec_row, column, value_json):
column_schema = ovsrec_row._table.columns[column]
datum = self._pre_mod_column(ovsrec_row, column, key, value_json)
datum = self._pre_mod_column(ovsrec_row, column, value_json)
if column_schema.type.is_map():
values = getattr(ovsrec_row, column, {})
@ -797,9 +790,9 @@ class VSCtlContext(object):
setattr(ovsrec_row, column, values)
def add_column(self, ovsrec_row, column, key, value_json):
def add_column(self, ovsrec_row, column, value_json):
column_schema = ovsrec_row._table.columns[column]
datum = self._pre_mod_column(ovsrec_row, column, key, value_json)
datum = self._pre_mod_column(ovsrec_row, column, value_json)
if column_schema.type.is_map():
values = getattr(ovsrec_row, column, {})
@ -812,9 +805,9 @@ class VSCtlContext(object):
setattr(ovsrec_row, column, values)
def remove_column(self, ovsrec_row, column, key, value_json):
def remove_column(self, ovsrec_row, column, value_json):
column_schema = ovsrec_row._table.columns[column]
datum = self._pre_mod_column(ovsrec_row, column, key, value_json)
datum = self._pre_mod_column(ovsrec_row, column, value_json)
if column_schema.type.is_map():
values = getattr(ovsrec_row, column, {})
@ -1433,7 +1426,8 @@ class VSCtl(object):
vsctl_table = self._get_table(table_name)
ovsrec_row = ctx.must_get_row(vsctl_table, br_name)
ctx.add_column(ovsrec_row, column, key, value)
value_json = ['map', [[key, value]]]
ctx.add_column(ovsrec_row, column, value_json)
ctx.invalidate_cache()
def _br_clear_external_id(self, ctx, br_name, key):
@ -1526,8 +1520,9 @@ class VSCtl(object):
columns = [
ctx.parse_column_key_value(
self.schema.tables[vswitch_idl.OVSREC_TABLE_PORT],
setting)[0] for setting in command.args[3:]]
self.schema.tables[vswitch_idl.OVSREC_TABLE_PORT], setting)[0]
for setting in command.args[3:]]
self._pre_add_port(ctx, columns)
def _cmd_add_port(self, ctx, command):
@ -1553,8 +1548,9 @@ class VSCtl(object):
iface_names = list(command.args[2])
settings = [
ctx.parse_column_key_value(
self.schema.tables[vswitch_idl.OVSREC_TABLE_PORT],
setting) for setting in command.args[3:]]
self.schema.tables[vswitch_idl.OVSREC_TABLE_PORT], setting)
for setting in command.args[3:]]
ctx.add_port(br_name, port_name, may_exist, fake_iface,
iface_names, settings)
@ -2048,28 +2044,36 @@ class VSCtl(object):
columns = [
ctx.parse_column_key_value(table_schema, column_key_value)[0]
for column_key_value in command.args[1:]]
self._pre_get_columns(ctx, table_name, columns)
def _check_value(self, ovsrec_row, column_key_value):
column, key, value_json = column_key_value
def _check_value(self, ovsrec_row, column_value):
"""
:type column_value: tuple of column and value_json
"""
column, value_json = column_value
column_schema = ovsrec_row._table.columns[column]
value = ovs.db.data.Datum.from_json(
column_schema.type, value_json).to_python(ovs.db.idl._uuid_to_row)
datum = getattr(ovsrec_row, column)
if key is None:
if datum == value:
return True
else:
if datum[key] != value:
return True
if column_schema.type.is_map():
for k, v in value.items():
if k in datum and datum[k] == v:
return True
elif datum == value:
return True
return False
def _find(self, ctx, table_name, column_key_values):
def _find(self, ctx, table_name, column_values):
"""
:type column_values: list of (column, value_json)
"""
result = []
for ovsrec_row in ctx.idl.tables[table_name].rows.values():
LOG.debug('ovsrec_row %s', ovsrec_row_to_string(ovsrec_row))
if all(self._check_value(ovsrec_row, column_key_value)
for column_key_value in column_key_values):
if all(self._check_value(ovsrec_row, column_value)
for column_value in column_values):
result.append(ovsrec_row)
return result
@ -2077,10 +2081,10 @@ class VSCtl(object):
def _cmd_find(self, ctx, command):
table_name = command.args[0]
table_schema = self.schema.tables[table_name]
column_key_values = [ctx.parse_column_key_value(table_schema,
column_key_value)
for column_key_value in command.args[1:]]
command.result = self._find(ctx, table_name, column_key_values)
column_values = [
ctx.parse_column_key_value(table_schema, column_key_value)
for column_key_value in command.args[1:]]
command.result = self._find(ctx, table_name, column_values)
def _pre_cmd_get(self, ctx, command):
table_name = command.args[0]
@ -2138,19 +2142,20 @@ class VSCtl(object):
def _pre_cmd_set(self, ctx, command):
table_name = command.args[0]
table_schema = self.schema.tables[table_name]
columns = [ctx.parse_column_key_value(table_schema,
column_key_value)[0]
for column_key_value in command.args[2:]]
columns = [
ctx.parse_column_key_value(table_schema, column_key_value)[0]
for column_key_value in command.args[2:]]
self._pre_mod_columns(ctx, table_name, columns)
def _set(self, ctx, table_name, record_id, column_key_values):
def _set(self, ctx, table_name, record_id, column_values):
"""
:type column_key_values: list of (column, key_string, value_json)
:type column_values: list of (column, value_json)
"""
vsctl_table = self._get_table(table_name)
ovsrec_row = ctx.must_get_row(vsctl_table, record_id)
for column, key, value in column_key_values:
ctx.set_column(ovsrec_row, column, key, value)
for column, value in column_values:
ctx.set_column(ovsrec_row, column, value)
ctx.invalidate_cache()
def _cmd_set(self, ctx, command):
@ -2159,30 +2164,26 @@ class VSCtl(object):
# column_key_value: <column>[:<key>]=<value>
table_schema = self.schema.tables[table_name]
column_key_values = [ctx.parse_column_key_value(table_schema,
column_key_value)
for column_key_value in command.args[2:]]
column_values = [
ctx.parse_column_key_value(table_schema, column_key_value)
for column_key_value in command.args[2:]]
self._set(ctx, table_name, record_id, column_key_values)
self._set(ctx, table_name, record_id, column_values)
def _pre_cmd_add(self, ctx, command):
table_name = command.args[0]
table_schema = self.schema.tables[table_name]
column = command.args[2]
columns = [
ctx.parse_column_key_value(
table_schema, '%s=%s' % (column, key_value))[0]
for key_value in command.args[3:]]
columns = [command.args[2]]
self._pre_mod_columns(ctx, table_name, columns)
def _add(self, ctx, table_name, record_id, column_key_values):
def _add(self, ctx, table_name, record_id, column_values):
"""
:type column_key_values: list of (column, key_string, value_json)
:type column_values: list of (column, value_json)
"""
vsctl_table = self._get_table(table_name)
ovsrec_row = ctx.must_get_row(vsctl_table, record_id)
for column, key, value in column_key_values:
ctx.add_column(ovsrec_row, column, key, value)
for column, value in column_values:
ctx.add_column(ovsrec_row, column, value)
ctx.invalidate_cache()
def _cmd_add(self, ctx, command):
@ -2190,32 +2191,36 @@ class VSCtl(object):
record_id = command.args[1]
column = command.args[2]
table_schema = self.schema.tables[table_name]
column_key_values = [
ctx.parse_column_key_value(
table_schema, '%s=%s' % (column, key_value))
for key_value in command.args[3:]]
column_key_value_strings = []
for value in command.args[3:]:
if '=' in value:
# construct <column>:<key>=value
column_key_value_strings.append('%s:%s' % (column, value))
else:
# construct <column>=value
column_key_value_strings.append('%s=%s' % (column, value))
self._add(ctx, table_name, record_id, column_key_values)
table_schema = self.schema.tables[table_name]
column_values = [
ctx.parse_column_key_value(table_schema, column_key_value_string)
for column_key_value_string in column_key_value_strings]
self._add(ctx, table_name, record_id, column_values)
def _pre_cmd_remove(self, ctx, command):
table_name = command.args[0]
table_schema = self.schema.tables[table_name]
column = command.args[2]
columns = [
ctx.parse_column_key_value(
table_schema, '%s=%s' % (column, key_value))[0]
for key_value in command.args[3:]]
columns = [command.args[2]]
self._pre_mod_columns(ctx, table_name, columns)
def _remove(self, ctx, table_name, record_id, column_key_values):
def _remove(self, ctx, table_name, record_id, column_values):
"""
:type column_key_values: list of (column, key_string, value_json)
:type column_values: list of (column, value_json)
"""
vsctl_table = self._get_table(table_name)
ovsrec_row = ctx.must_get_row(vsctl_table, record_id)
for column, key, value in column_key_values:
ctx.remove_column(ovsrec_row, column, key, value)
for column, value in column_values:
ctx.remove_column(ovsrec_row, column, value)
ctx.invalidate_cache()
def _cmd_remove(self, ctx, command):
@ -2223,13 +2228,21 @@ class VSCtl(object):
record_id = command.args[1]
column = command.args[2]
table_schema = self.schema.tables[table_name]
column_key_values = [
ctx.parse_column_key_value(
table_schema, '%s=%s' % (column, key_value))
for key_value in command.args[3:]]
column_key_value_strings = []
for value in command.args[3:]:
if '=' in value:
# construct <column>:<key>=value
column_key_value_strings.append('%s:%s' % (column, value))
else:
# construct <column>=value
column_key_value_strings.append('%s=%s' % (column, value))
self._remove(ctx, table_name, record_id, column_key_values)
table_schema = self.schema.tables[table_name]
column_values = [
ctx.parse_column_key_value(table_schema, column_key_value_string)
for column_key_value_string in column_key_value_strings]
self._remove(ctx, table_name, record_id, column_values)
def _pre_cmd_clear(self, ctx, command):
table_name = command.args[0]