neutron ovn fip QoS

分析一下 neutron ovn (ovs) fip QoS 的实现方式

1. db l3_fip_qos.py

搜索 FloatingQoSDbMixin

2. 测试用例分析

neutron/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/test_qos.py

可以看到这个路径在 ovn/mech_driver/ovsdb/extensions/ 下,说明 ovn 确实支持该功能

meter 是支持 router gw ip 的 QoS 的

可以看到 ovn 启用了分布式公网 IP

2.1 子网绑定 qos policy

python 复制代码
    def _update_network(self, network_id, qos_policy_id):
        data = {'network': {'qos_policy_id': qos_policy_id}}
        return self._update('networks', network_id, data,
                            as_admin=True)['network']

    def test_update_network(self):
        """Test update network (internal ports).

        net1: [(1) from qos_policy0 to no QoS policy,
               (2) from qos_policy0 to qos_policy1]
        - port10: no QoS port policy
        - port11: qos_policy0
        - port12: qos_policy1
        """
        policies_ports = [
            (None, {self.ports[0].id}),
            (self.qos_policies[1].id, {self.ports[0].id})]

        self.ports[1].qos_policy_id = self.qos_policies[0].id
        self.ports[1].update()
        self.ports[2].qos_policy_id = self.qos_policies[1].id
        self.ports[2].update()
        for qos_policy_id, reference_ports in policies_ports:
            self.networks[0] = self._update_network(self.networks[0]['id'],
                                                    qos_policy_id)
            original_network = {'qos_policy_id': self.qos_policies[0],
                                pnet_api.NETWORK_TYPE: mock.ANY,
                                }
            reviewed_port_ids, _, _ = self.qos_driver.update_network(
                mock.ANY, self.networks[0], original_network)
            self.assertEqual(reference_ports, reviewed_port_ids)
            calls = [mock.call(mock.ANY, self.ports[0].id,
                               self.ports[0].network_id, self.tenant_type,
                               qos_policy_id, None)]
            self.mock_rules.assert_has_calls(calls)
            self.mock_rules.reset_mock()
            
## 外部 network 也支持绑定

    def test_update_external_network(self):
        """Test update external network (floating IPs and GW IPs).

        - fip0: qos_policy0
        - fip1: no QoS FIP policy (inherits from external network QoS)
        - router_fips: no QoS FIP policy (inherits from external network QoS)
        """
        network_policies = [(self.qos_policies[1].id,
                             {self.fips[1].id},
                             {self.router_fips.id}),
                            (None,
                             {self.fips[1].id},
                             {self.router_fips.id})]

        self.fips[0].qos_policy_id = self.qos_policies[0].id
        self.fips[0].update()
        for qos_policy_id, ref_fips, ref_routers in network_policies:
            self.fips_network = self._update_network(self.fips_network['id'],
                                                     qos_policy_id)
            original_network = {'qos_policy_id': self.qos_policies[0],
                                pnet_api.NETWORK_TYPE: mock.ANY,
                                }
            _, reviewed_fips_ids, reviewed_router_ids = (
                self.qos_driver.update_network(
                    mock.Mock(), self.fips_network, original_network))
            self.assertEqual(ref_fips, reviewed_fips_ids)
            self.assertEqual(ref_routers, reviewed_router_ids)
            
    def test_update_network_external_ports(self):
        """Test update network with external ports.

        - port10: no QoS port policy
        - port11: no QoS port policy but external
        - port12: qos_policy0
        """
        policies_ports = [(self.qos_policies[0].id, {self.ports[0].id})]
        self.ports[2].qos_policy_id = self.qos_policies[0].id
        self.ports[2].update()
        port_obj.PortBinding(self.ctx, port_id=self.ports[1].id, host='host',
                             profile={}, vif_type='',
                             vnic_type=portbindings_api.VNIC_DIRECT).create()
        with mock.patch.object(self.qos_driver._driver._nb_idl,
                               'get_lswitch_port') as mock_lsp:
            mock_lsp.side_effect = [
                mock.Mock(type=ovn_const.LSP_TYPE_LOCALNET),
                mock.Mock(type=ovn_const.LSP_TYPE_EXTERNAL)]
            for qos_policy_id, reference_ports in policies_ports:
                self.networks[0] = self._update_network(self.networks[0]['id'],
                                                        qos_policy_id)
                original_network = {'qos_policy_id': self.qos_policies[0]}
                reviewed_port_ids, _, _ = self.qos_driver.update_network(
                    mock.ANY, self.networks[0], original_network, reset=True)
                self.assertEqual(reference_ports, reviewed_port_ids)
                calls = [mock.call(
                    mock.ANY, self.ports[0].id, self.ports[0].network_id,
                    self.tenant_type, qos_policy_id, None)]
                self.mock_rules.assert_has_calls(calls)
                self.mock_rules.reset_mock()

2.2 router 绑定 qos

python 复制代码
    def _update_router_qos(self, context, router_id, qos_policy_id,
                           attach=True):
        # NOTE(ralonsoh): router QoS policy is not yet implemented in Router
        # OVO. Once we have this feature, this method can be removed.
        qos = policy_obj.QosPolicy.get_policy_obj(context, qos_policy_id)
        if attach:
            qos.attach_router(router_id)
        else:
            qos.detach_router(router_id)

2.3 测试资源创建

2.4 QoS 规则的类型

python 复制代码
    def test__qos_rules(self, mock_get_rules, mock_warning):
        rules = [
            rule_obj.QosBandwidthLimitRule(
                direction=constants.EGRESS_DIRECTION, **QOS_RULE_BW_1),
            rule_obj.QosBandwidthLimitRule(
                direction=constants.INGRESS_DIRECTION, **QOS_RULE_BW_2),
            rule_obj.QosDscpMarkingRule(**QOS_RULE_DSCP_1),
            rule_obj.QosMinimumBandwidthRule(
                direction=constants.EGRESS_DIRECTION, **QOS_RULE_MINBW_1),
            rule_obj.QosMinimumBandwidthRule(
                direction=constants.INGRESS_DIRECTION, **QOS_RULE_MINBW_2),
        ]
        mock_get_rules.return_value = rules
        expected = {
            constants.EGRESS_DIRECTION: {
                qos_constants.RULE_TYPE_BANDWIDTH_LIMIT: QOS_RULE_BW_1,
                qos_constants.RULE_TYPE_DSCP_MARKING: QOS_RULE_DSCP_1,
                qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH: QOS_RULE_MINBW_1},
            constants.INGRESS_DIRECTION: {
                qos_constants.RULE_TYPE_BANDWIDTH_LIMIT: QOS_RULE_BW_2}
        }
        self.assertEqual(expected, self.qos_driver._qos_rules(mock.ANY,
                                                              'policy_id1'))
        mock_warning.assert_called_once_with(
            'ML2/OVN QoS driver does not support minimum bandwidth rules '
            'enforcement with ingress direction')
            

qos rule 在普通 port 和 fip 两种场景,用两种类型区分

2.5 QoS policy 支持绑定的对象类型一览

网络

路由器

FIP

(隧道 port)

python 复制代码
   def test_update_policy(self):
        """Test update QoS policy, networks and ports bound are updated.

        QoS policy updated: qos_policy0
        net1: no QoS policy
        - port10: no port QoS policy
        - port11: qos_policy0  --> handled during "update_port" and updated
        - port12: qos_policy1
        net2: qos_policy0
        - port20: no port QoS policy  --> handled during "update_network"
                                          and updated
        - port21: qos_policy0  --> handled during "update_network", not updated
                                   handled during "update_port" and updated
        - port22: qos_policy1  --> handled during "update_network", not updated
        fip1: qos_policy0
        fip2: qos_policy1
        router1: qos_policy0
        router2: qos_policy1
        """
        self.ports[1].qos_policy_id = self.qos_policies[0].id
        self.ports[1].update()
        self.ports[2].qos_policy_id = self.qos_policies[1].id
        self.ports[2].update()
        self.ports[4].qos_policy_id = self.qos_policies[0].id
        self.ports[4].update()
        self.ports[5].qos_policy_id = self.qos_policies[1].id
        self.ports[5].update()
        self.networks[1] = self._update_network(
            self.networks[1]['id'], self.qos_policies[0].id)
        self.fips[0].qos_policy_id = self.qos_policies[0].id
        self.fips[0].update()
        self.fips[1].qos_policy_id = self.qos_policies[1].id
        self.fips[1].update()
        self._update_router_qos(self.ctx, self.routers[0].id,
                                self.qos_policies[0].id)
        self._update_router_qos(self.ctx, self.routers[1].id,
                                self.qos_policies[1].id)
        mock_qos_rules = mock.Mock()
        with mock.patch.object(self.qos_driver, '_qos_rules',
                               return_value=mock_qos_rules), \
                mock.patch.object(self.qos_driver, 'update_floatingip') as \
                mock_update_fip, \
                mock.patch.object(self.qos_driver, 'update_router') as \
                mock_update_router:
            self.qos_driver.update_policy(self.ctx, self.qos_policies[0])
        # Ports updated from "update_port": self.ports[1], self.ports[4]
        updated_ports = [self.ports[1], self.ports[4]]
        calls = [mock.call(self.txn, port.id, port.network_id,
                           self.tenant_type, self.qos_policies[0].id,
                           mock_qos_rules, lsp=None)
                 for port in updated_ports]
        # Port updated from "update_network": self.ports[3]
        calls.append(mock.call(self.txn, self.ports[3].id,
                               self.ports[3].network_id, self.tenant_type,
                               self.qos_policies[0].id, mock_qos_rules))

        # We can't ensure the call order because we are not enforcing any order
        # when retrieving the port and the network list.
        self.mock_rules.assert_has_calls(calls, any_order=True)
        with db_api.CONTEXT_READER.using(self.ctx):
            fip = self.qos_driver._plugin_l3.get_floatingip(self.ctx,
                                                            self.fips[0].id)
        mock_update_fip.assert_called_once_with(self.txn, fip)

        with db_api.CONTEXT_READER.using(self.ctx):
            router = self.qos_driver._plugin_l3.get_router(self.ctx,
                                                           self.routers[0].id)
        mock_update_router.assert_called_once_with(self.txn, router)

2.6 fip 绑定 QoS: qos_driver.update_floatingip

python 复制代码
    def test_update_floatingip(self):
        # NOTE(ralonsoh): this rule will always apply:
        # - If the FIP is being deleted, "qos_del_ext_ids" is called;
        #   "qos_add" and "qos_del" won't.
        # - If the FIP is added or updated, "qos_del_ext_ids" won't be called
        #   and "qos_add" or "qos_del" will, depending on the rule directions.
        nb_idl = self.qos_driver._driver._nb_idl
        fip = self.fips[0]
        original_fip = self.fips[1]
        txn = mock.Mock()

        # Update FIP, no QoS policy nor port/router
        self.qos_driver.update_floatingip(txn, fip)
        nb_idl.qos_del_ext_ids.assert_called_once()
        nb_idl.qos_add.assert_not_called()
        nb_idl.qos_del.assert_not_called()
        nb_idl.reset_mock()

        # Attach a port and a router, not QoS policy
        fip.router_id = self.router_fips.id
        fip.fixed_port_id = self.fips_ports[0].id
        fip.update()
        self.qos_driver.update_floatingip(txn, fip)
        nb_idl.qos_del_ext_ids.assert_called_once()
        nb_idl.qos_add.assert_not_called()
        nb_idl.qos_del.assert_not_called()
        nb_idl.reset_mock()

        # Add a QoS policy
        fip.qos_policy_id = self.qos_policies[0].id
        fip.update()
        self.qos_driver.update_floatingip(txn, fip)
        nb_idl.qos_del_ext_ids.assert_not_called()
        # QoS DSCP rule has only egress direction, ingress one is deleted.
        # Check "OVNClientQosExtension.update_floatingip" and how the OVN QoS
        # rules are added (if there is a rule in this direction) or deleted.
        nb_idl.qos_add.assert_called_once()
        nb_idl.qos_del.assert_called_once()
        nb_idl.reset_mock()

        # Remove QoS
        fip.qos_policy_id = None
        fip.update()
        original_fip.qos_policy_id = self.qos_policies[0].id
        original_fip.update()
        self.qos_driver.update_floatingip(txn, fip)
        nb_idl.qos_del_ext_ids.assert_called_once()
        nb_idl.qos_add.assert_not_called()
        nb_idl.qos_del.assert_not_called()
        nb_idl.reset_mock()

        # Add network QoS policy
        fip.qos_network_policy_id = self.qos_policies[0].id
        fip.update()
        self.qos_driver.update_floatingip(txn, fip)
        nb_idl.qos_del_ext_ids.assert_not_called()
        nb_idl.qos_add.assert_called_once()
        nb_idl.qos_del.assert_called_once()
        nb_idl.reset_mock()

        # Add again another QoS policy
        fip.qos_policy_id = self.qos_policies[1].id
        fip.update()
        original_fip.qos_policy_id = None
        original_fip.update()
        self.qos_driver.update_floatingip(txn, fip)
        nb_idl.qos_del_ext_ids.assert_not_called()
        nb_idl.qos_add.assert_called_once()
        nb_idl.qos_del.assert_called_once()
        nb_idl.reset_mock()

        # Detach the port and the router
        fip.router_id = None
        fip.fixed_port_id = None
        fip.update()
        original_fip.router_id = self.router_fips.id
        original_fip.fixed_port_id = self.fips_ports[0].id
        original_fip.qos_policy_id = self.qos_policies[1].id
        original_fip.update()
        self.qos_driver.update_floatingip(txn, fip)
        nb_idl.qos_del_ext_ids.assert_called_once()
        nb_idl.qos_add.assert_not_called()
        nb_idl.qos_del.assert_not_called()
        nb_idl.reset_mock()

        # Force reset (delete any QoS)
        fip_dict = {'floating_network_id': fip.floating_network_id,
                    'id': fip.id}
        self.qos_driver.update_floatingip(txn, fip_dict)
        nb_idl.qos_del_ext_ids.assert_called_once()
        nb_idl.qos_add.assert_not_called()
        nb_idl.qos_del.assert_not_called()

2.7 router 以及 network 绑定 qos

self.qos_driver.update_router self._update_network

python 复制代码
    def test_update_router(self):
        nb_idl = self.qos_driver._driver._nb_idl
        txn = mock.Mock()

        # Update router, no QoS policy set.
        router = self._get_router(self.routers[0].id)
        self.qos_driver.update_router(txn, router)
        nb_idl.qos_add.assert_not_called()
        self.assertEqual(2, nb_idl.qos_del.call_count)
        nb_idl.reset_mock()

        # Add QoS policy.
        self._update_router_qos(self.ctx, router['id'],
                                self.qos_policies[0].id)
        router = self._get_router(self.routers[0].id)
        self.qos_driver.update_router(txn, router)
        nb_idl.qos_add.assert_called_once()
        nb_idl.qos_del.assert_called_once()
        nb_idl.reset_mock()

        # Remove QoS
        self._update_router_qos(self.ctx, router['id'],
                                self.qos_policies[0].id, attach=False)
        router = self._get_router(self.routers[0].id)
        self.qos_driver.update_router(txn, router)
        nb_idl.qos_add.assert_not_called()
        self.assertEqual(2, nb_idl.qos_del.call_count)
        nb_idl.reset_mock()

        # Add network QoS policy
        ext_net = self.router_networks[0]
        self.networks[1] = self._update_network(ext_net['id'],
                                                self.qos_policies[1].id)
        router = self._get_router(self.routers[0].id)
        self.qos_driver.update_router(txn, router)
        nb_idl.qos_add.assert_called_once()
        nb_idl.qos_del.assert_called_once()
        nb_idl.reset_mock()

2. router l3 qos 插件

python 复制代码
class L3RouterPlugin(service_base.ServicePluginBase,
                     extraroute_db.ExtraRoute_db_mixin,
                     l3_hamode_db.L3_HA_NAT_db_mixin,
                     l3_gateway_ip_qos.L3_gw_ip_qos_db_mixin,
                     l3_dvr_ha_scheduler_db.L3_DVR_HA_scheduler_db_mixin,
                     dns_db.DNSDbMixin,
                     l3_fip_qos.FloatingQoSDbMixin,
                     l3_fip_port_details.Fip_port_details_db_mixin,
                     l3_fip_pools_db.FloatingIPPoolsMixin):
                     
                     
    def supported_extension_aliases(self):
        if not hasattr(self, '_aliases'):
            aliases = self._supported_extension_aliases[:]
            disable_dvr_extension_by_config(aliases)
            disable_l3_qos_extension_by_plugins('qos-fip', aliases)
            disable_l3_qos_extension_by_plugins('qos-gateway-ip', aliases)
            self._aliases = aliases
        return self._aliases
        

总结: 3. neutron (CMS) 操作 ovn nb 的位置

neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/extensions/qos.py

bash 复制代码
    def _apply_ovn_rule_qos(self, txn, rules, ovn_rule_qos):
       """Add or remove the OVN QoS rules (for max-bw and DSCP rules only).

       :param txn: the ovsdbapp transaction object.
       :param rules: Neutron QoS rules (per direction).
       :param ovn_rule_qos: dictionary with the Neutron QoS rules with the
                            parameters needed to call ``qos_add`` or
                            ``qos_del`` commands.
       """
       if rules and not (ovn_rule_qos.get('rate') is None and
                         ovn_rule_qos.get('dscp') is None):
           # NOTE(ralonsoh): with "may_exist=True", the "qos_add" will
           # create the QoS OVN rule or update the existing one.
           # NOTE(ralonsoh): if the Neutron QoS rules don't have at least
           # a max-bw rule or a DSCP rule, skip this command.
           txn.add(self.nb_idl.qos_add(**ovn_rule_qos, may_exist=True))
       else:
           # Delete, if exists, the QoS rule in this direction.
           txn.add(self.nb_idl.qos_del(**ovn_rule_qos, if_exists=True))
相关推荐
gongzairen8 分钟前
Ngrok 内网穿透实现Django+Vue部署
后端·python·django
冒泡的肥皂19 分钟前
JAVA-WEB系统问题排查闲扯
java·spring boot·后端
yuhaiqiang20 分钟前
聊聊我的开源经历——先做个垃圾出来
后端
追逐时光者1 小时前
6种流行的 API 架构风格,你知道几种?
后端
小麦果汁吨吨吨1 小时前
Flask快速入门
后端·python·flask
kinlon.liu1 小时前
SpringBoot整合Redis限流
spring boot·redis·后端
小p3 小时前
迈向全栈:服务器上的软件安装
前端·后端
Bohemian3 小时前
浅谈Golang逃逸分析
后端·面试·go
用户1529436849593 小时前
谷歌云代理商:如何配置谷歌云服务器的端口转发?
后端
努力的搬砖人.4 小时前
Spring Boot集成MinIO的详细步骤
java·spring boot·后端