]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add Create/Destroy API to OVS QoS BW Limiting
authorGal Sagie <gal.sagie@huawei.com>
Sat, 27 Jun 2015 10:16:11 +0000 (13:16 +0300)
committerGal Sagie <gal.sagie@huawei.com>
Wed, 1 Jul 2015 10:04:54 +0000 (10:04 +0000)
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

neutron/agent/common/ovs_lib.py
neutron/agent/ovsdb/api.py
neutron/agent/ovsdb/impl_idl.py
neutron/agent/ovsdb/impl_vsctl.py
neutron/agent/ovsdb/native/commands.py
neutron/tests/functional/agent/test_ovs_lib.py

index 81340c59888759e4fddee41ce9a11e6b09255f15..26df498468867fe4a5b31851122e3108823fb6f8 100644 (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
index e696f8e85d60da4df14529a33bac0bae2e6d2d25..b6fa02ce0f93864803e243b1cefb61b8d722b5d1 100644 (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
index 5b15472874d666442a9837afc845e3c771389bc4..aa2df233bba621b32d0f380aa0ba08c43fe3f4da 100644 (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)
 
index 15f52529b52daadf59f137599a9a68f071d55e05..6c1f84e113f3dc8e24b31b3053e45316f0cde44e 100644 (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
index b8bb1b117e2212aa6a159b63311d74b8b6a9449d..0d5fa589d5f584c018a462d19b65796a8fbf1372 100644 (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)
index f430481899baed52c352acbc300b57ced38e3b6b..be71f2ef8a21d24f937a94fdcb952748696c6be0 100644 (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):