Add Create/Destroy API to OVS QoS BW Limiting

Add infrastructure needed for the implementations
(CLI and native) and add API to ovs_lib
Add functional tests for ovs_lib

blueprint ml2-ovs-qos-with-bwlimiting

Change-Id: Ided0740548987ca91f1549f251c7906e6449f91d
This commit is contained in:
Gal Sagie 2015-06-27 13:16:11 +03:00
parent b17f865f85
commit be1d242fa3
6 changed files with 153 additions and 2 deletions

View File

@ -455,6 +455,81 @@ class OVSBridge(BaseOVS):
txn.add(self.ovsdb.db_set('Controller',
controller_uuid, *attr))
def _create_qos_bw_limit_queue(self, port_name, max_bw_in_bits,
max_burst_in_bits):
external_ids = {'id': port_name}
queue_other_config = {'min-rate': max_bw_in_bits,
'max-rate': max_bw_in_bits,
'burst': max_burst_in_bits}
self.ovsdb.db_create(
'Queue', external_ids=external_ids,
other_config=queue_other_config).execute(check_error=True)
def _create_qos_bw_limit_profile(self, port_name, max_bw_in_bits):
external_ids = {'id': port_name}
queue = self.ovsdb.db_find(
'Queue',
('external_ids', '=', {'id': port_name}),
columns=['_uuid']).execute(
check_error=True)
queues = {}
queues[0] = queue[0]['_uuid']
qos_other_config = {'max-rate': max_bw_in_bits}
self.ovsdb.db_create('QoS', external_ids=external_ids,
other_config=qos_other_config,
type='linux-htb',
queues=queues).execute(check_error=True)
def create_qos_bw_limit_for_port(self, port_name, max_kbps,
max_burst_kbps):
# TODO(QoS) implement this with transactions,
# or roll back on failure
max_bw_in_bits = str(max_kbps * 1000)
max_burst_in_bits = str(max_burst_kbps * 1000)
self._create_qos_bw_limit_queue(port_name, max_bw_in_bits,
max_burst_in_bits)
self._create_qos_bw_limit_profile(port_name, max_bw_in_bits)
qos = self.ovsdb.db_find('QoS',
('external_ids', '=', {'id': port_name}),
columns=['_uuid']).execute(check_error=True)
qos_profile = qos[0]['_uuid']
self.set_db_attribute('Port', port_name, 'qos', qos_profile,
check_error=True)
def get_qos_bw_limit_for_port(self, port_name):
res = self.ovsdb.db_find(
'Queue',
('external_ids', '=', {'id': port_name}),
columns=['other_config']).execute(check_error=True)
if res is None or len(res) == 0:
return None, None
other_config = res[0]['other_config']
max_kbps = int(other_config['max-rate']) / 1000
max_burst_kbps = int(other_config['burst']) / 1000
return max_kbps, max_burst_kbps
def del_qos_bw_limit_for_port(self, port_name):
qos = self.ovsdb.db_find('QoS',
('external_ids', '=', {'id': port_name}),
columns=['_uuid']).execute(check_error=True)
qos_row = qos[0]['_uuid']
queue = self.ovsdb.db_find('Queue',
('external_ids', '=', {'id': port_name}),
columns=['_uuid']).execute(check_error=True)
queue_row = queue[0]['_uuid']
with self.ovsdb.transaction(check_error=True) as txn:
txn.add(self.ovsdb.db_set('Port', port_name, ('qos', [])))
txn.add(self.ovsdb.db_destroy('QoS', qos_row))
txn.add(self.ovsdb.db_destroy('Queue', queue_row))
def __enter__(self):
self.create()
return self

View File

@ -161,6 +161,29 @@ class API(object):
:returns: :class:`Command` with field value result
"""
@abc.abstractmethod
def db_create(self, table, **col_values):
"""Create a command to create new record
:param table: The OVS table containing the record to be created
:type table: string
:param col_values: The columns and their associated values
to be set after create
:type col_values: Dictionary of columns id's and values
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def db_destroy(self, table, record):
"""Create a command to destroy a record
:param table: The OVS table containing the record to be destroyed
:type table: string
:param record: The record id (name/uuid) to be destroyed
:type record: uuid/string
:returns: :class:`Command` with no result
"""
@abc.abstractmethod
def db_set(self, table, record, *col_values):
"""Create a command to set fields in a record

View File

@ -169,6 +169,12 @@ class OvsdbIdl(api.API):
def br_set_external_id(self, name, field, value):
return cmd.BrSetExternalIdCommand(self, name, field, value)
def db_create(self, table, **col_values):
return cmd.DbCreateCommand(self, table, **col_values)
def db_destroy(self, table, record):
return cmd.DbDestroyCommand(self, table, record)
def db_set(self, table, record, *col_values):
return cmd.DbSetCommand(self, table, record, *col_values)

View File

@ -184,6 +184,15 @@ class OvsdbVsctl(ovsdb.API):
return BaseCommand(self.context, 'br-get-external-id',
args=[name, field])
def db_create(self, table, **col_values):
args = [table]
args += _set_colval_args(*col_values.items())
return BaseCommand(self.context, 'create', args=args)
def db_destroy(self, table, record):
args = [table, record]
return BaseCommand(self.context, 'destroy', args=args)
def db_set(self, table, record, *col_values):
args = [table, record]
args += _set_colval_args(*col_values)
@ -256,8 +265,11 @@ def _set_colval_args(*col_values):
col, k, op, ovsdb.py_to_val(v)) for k, v in val.items()]
elif (isinstance(val, collections.Sequence)
and not isinstance(val, six.string_types)):
args.append(
"%s%s%s" % (col, op, ",".join(map(ovsdb.py_to_val, val))))
if len(val) == 0:
args.append("%s%s%s" % (col, op, "[]"))
else:
args.append(
"%s%s%s" % (col, op, ",".join(map(ovsdb.py_to_val, val))))
else:
args.append("%s%s%s" % (col, op, ovsdb.py_to_val(val)))
return args

View File

@ -148,6 +148,30 @@ class BrSetExternalIdCommand(BaseCommand):
br.external_ids = external_ids
class DbCreateCommand(BaseCommand):
def __init__(self, api, table, **columns):
super(DbCreateCommand, self).__init__(api)
self.table = table
self.columns = columns
def run_idl(self, txn):
row = txn.insert(self.api._tables[self.table])
for col, val in self.columns.items():
setattr(row, col, val)
self.result = row
class DbDestroyCommand(BaseCommand):
def __init__(self, api, table, record):
super(DbDestroyCommand, self).__init__(api)
self.table = table
self.record = record
def run_idl(self, txn):
record = idlutils.row_by_record(self.api.idl, self.table, self.record)
record.delete()
class DbSetCommand(BaseCommand):
def __init__(self, api, table, record, *col_values):
super(DbSetCommand, self).__init__(api)

View File

@ -261,6 +261,17 @@ class OVSBridgeTestCase(OVSBridgeTestBase):
controller,
'connection_mode'))
def test_qos_bw_limit(self):
port_name, _ = self.create_ovs_port()
self.br.create_qos_bw_limit_for_port(port_name, 700, 70)
max_rate, burst = self.br.get_qos_bw_limit_for_port(port_name)
self.assertEqual(700, max_rate)
self.assertEqual(70, burst)
self.br.del_qos_bw_limit_for_port(port_name)
max_rate, burst = self.br.get_qos_bw_limit_for_port(port_name)
self.assertIsNone(max_rate)
self.assertIsNone(burst)
class OVSLibTestCase(base.BaseOVSLinuxTestCase):