コード例 #1
0
ファイル: test_ovs.py プロジェクト: yan0s/charm-helpers
 def test_add_bridge_bond(self):
     self.patch_object(ovs.subprocess, 'check_call')
     self.patch_object(ovs, '_dict_to_vsctl_set')
     self._dict_to_vsctl_set.return_value = [['--', 'fakekey=fakevalue']]
     portdata = {
         'bond-mode': 'balance-tcp',
         'lacp': 'active',
         'other-config': {
             'lacp-time': 'fast',
         },
     }
     ifdatamap = {
         'eth0': {
             'type': 'dpdk',
             'mtu-request': '9000',
             'options': {
                 'dpdk-devargs': '0000:01:00.0',
             },
         },
         'eth1': {
             'type': 'dpdk',
             'mtu-request': '9000',
             'options': {
                 'dpdk-devargs': '0000:02:00.0',
             },
         },
     }
     ovs.add_bridge_bond('br-ex', 'bond42', ['eth0', 'eth1'], portdata,
                         ifdatamap)
     self._dict_to_vsctl_set.assert_has_calls([
         mock.call(portdata, 'port', 'bond42'),
         mock.call(ifdatamap['eth0'], 'Interface', 'eth0'),
         mock.call(ifdatamap['eth1'], 'Interface', 'eth1'),
     ],
                                              any_order=True)
     self.check_call.assert_called_once_with([
         'ovs-vsctl', '--', '--may-exist', 'add-bond', 'br-ex', 'bond42',
         'eth0', 'eth1', '--', 'fakekey=fakevalue', '--',
         'fakekey=fakevalue', '--', 'fakekey=fakevalue'
     ])
コード例 #2
0
ファイル: ovn_charm.py プロジェクト: thedac/charm-layer-ovn
    def configure_bridges(self):
        """Configure Open vSwitch bridges ports and interfaces."""
        if self.check_if_paused() != (None, None):
            ch_core.hookenv.log(
                'Unit is paused, defer Open vSwitch bridge '
                'port interface configuration tasks.',
                level=ch_core.hookenv.INFO)
            return
        bpi = os_context.BridgePortInterfaceMap(bridges_key=self.bridges_key)
        bond_config = os_context.BondConfig()
        ch_core.hookenv.log('BridgePortInterfaceMap: "{}"'.format(bpi.items()),
                            level=ch_core.hookenv.DEBUG)

        # build map of bridges to ovn networks with existing if-mapping on host
        # and at the same time build ovn-bridge-mappings string
        ovn_br_map_str = ''
        ovnbridges = collections.defaultdict(list)
        config_obm = self.config['ovn-bridge-mappings'] or ''
        for pair in sorted(config_obm.split()):
            network, bridge = pair.split(':', 1)
            if bridge in bpi:
                ovnbridges[bridge].append(network)
                if ovn_br_map_str:
                    ovn_br_map_str += ','
                ovn_br_map_str += '{}:{}'.format(network, bridge)

        bridges = ch_ovsdb.SimpleOVSDB('ovs-vsctl').bridge
        ports = ch_ovsdb.SimpleOVSDB('ovs-vsctl').port
        for bridge in bridges.find('external_ids:charm-ovn-chassis=managed'):
            # remove bridges and ports that are managed by us and no longer in
            # config
            if bridge['name'] not in bpi and bridge['name'] != 'br-int':
                ch_core.hookenv.log(
                    'removing bridge "{}" as it is no longer'
                    'present in configuration for this unit.'.format(
                        bridge['name']),
                    level=ch_core.hookenv.DEBUG)
                ch_ovs.del_bridge(bridge['name'])
            else:
                for port in ports.find(
                        'external_ids:charm-ovn-chassis={}'.format(
                            bridge['name'])):
                    if port['name'] not in bpi[bridge['name']]:
                        ch_core.hookenv.log(
                            'removing port "{}" from bridge '
                            '"{}" as it is no longer present '
                            'in configuration for this unit.'.format(
                                port['name'], bridge['name']),
                            level=ch_core.hookenv.DEBUG)
                        ch_ovs.del_bridge_port(bridge['name'], port['name'])
        brdata = {
            'external-ids': {
                'charm-ovn-chassis': 'managed'
            },
            'protocols': 'OpenFlow13,OpenFlow15',
        }
        if self.options.enable_dpdk:
            brdata.update({'datapath-type': 'netdev'})
        else:
            brdata.update({'datapath-type': 'system'})
        # we always update the integration bridge to make sure it has settings
        # apropriate for the current charm configuration
        ch_ovs.add_bridge(
            'br-int',
            brdata={
                **brdata,
                **{
                    # for the integration bridge we want the datapath to await
                    # controller action before adding any flows. This is to avoid
                    # switching packets between isolated logical networks before
                    # `ovn-controller` starts up.
                    'fail-mode': 'secure',
                    # Suppress in-band control flows for the integration bridge,
                    # refer to ovn-architecture(7) for more details.
                    'other-config': {
                        'disable-in-band': 'true'
                    },
                },
            })
        for br in bpi:
            if br not in ovnbridges:
                continue
            ch_ovs.add_bridge(
                br,
                brdata={
                    **brdata,
                    # for bridges used for external connectivity we want the
                    # datapath to act like an ordinary MAC-learning switch.
                    **{
                        'fail-mode': 'standalone'
                    },
                })
            for port in bpi[br]:
                ifdatamap = bpi.get_ifdatamap(br, port)
                ifdatamap = {
                    port: {
                        **ifdata,
                        **{
                            'external-ids': {
                                'charm-ovn-chassis': br
                            }
                        },
                    }
                    for port, ifdata in ifdatamap.items()
                }

                if len(ifdatamap) > 1:
                    ch_ovs.add_bridge_bond(br, port, list(ifdatamap.keys()),
                                           bond_config.get_ovs_portdata(port),
                                           ifdatamap)
                else:
                    ch_ovs.add_bridge_port(
                        br,
                        port,
                        ifdata=ifdatamap.get(port, {}),
                        linkup=not self.options.enable_dpdk,
                        promisc=None,
                        portdata={'external-ids': {
                            'charm-ovn-chassis': br
                        }})

        opvs = ch_ovsdb.SimpleOVSDB('ovs-vsctl').open_vswitch
        if ovn_br_map_str:
            opvs.set('.', 'external_ids:ovn-bridge-mappings', ovn_br_map_str)
            # NOTE(fnordahl): Workaround for LP: #1848757
            opvs.set('.', 'external_ids:ovn-cms-options',
                     'enable-chassis-as-gw')
        else:
            opvs.remove('.', 'external_ids', 'ovn-bridge-mappings')
            # NOTE(fnordahl): Workaround for LP: #1848757
            opvs.remove('.', 'external_ids', 'ovn-cms-options')
コード例 #3
0
def configure_ovs():
    """Configure the OVS plugin.

    This function uses the config.yaml parameters ext-port, data-port and
    bridge-mappings to configure the bridges and ports on the ovs on the
    unit.

    Note that the ext-port is deprecated and data-port/bridge-mappings are
    preferred.

    Thus, if data-port is set, then ext-port is ignored (and if set, then
    it is removed from the set of bridges unless it is defined in
    bridge-mappings/data-port).  A warning is issued, if both data-port and
    ext-port are set.
    """
    status_set('maintenance', 'Configuring ovs')
    if not service_running('openvswitch-switch'):
        full_restart()

    # all bridges use the same datapath_type
    brdata = {
        'datapath-type': determine_datapath_type(),
    }
    brdata.update(generate_external_ids())

    add_bridge(INT_BRIDGE, brdata=brdata)
    add_bridge(EXT_BRIDGE, brdata=brdata)

    # If data-port is defined in the config, we can ignore ext-port value
    # and log an error to the unit's log
    if config('data-port') and config('ext-port'):
        log(
            "Both ext-port and data-port are set. ext-port is deprecated"
            " and is not used when data-port is set.",
            level=ERROR)

    ext_port_ctx = None
    if use_dvr():
        ext_port_ctx = ExternalPortContext()()
    # Set ext-port only if data-port isn't defined.
    if not config('data-port') and ext_port_ctx and ext_port_ctx['ext_port']:
        add_bridge_port(EXT_BRIDGE,
                        ext_port_ctx['ext_port'],
                        ifdata=generate_external_ids(EXT_BRIDGE),
                        portdata=generate_external_ids(EXT_BRIDGE))

    modern_ovs = ovs_has_late_dpdk_init()

    bridgemaps = None
    portmaps = None
    if not use_dpdk():
        # NOTE(jamespage):
        # Its possible to support both hardware offloaded 'direct' ports
        # and default 'openvswitch' ports on the same hypervisor, so
        # configure bridge mappings in addition to any hardware offload
        # enablement.
        portmaps = DataPortContext()()
        bridgemaps = parse_bridge_mappings(config('bridge-mappings'))
        for br in bridgemaps.values():
            add_bridge(br, brdata=brdata)
            if not portmaps:
                continue

            for port, _br in portmaps.items():
                if _br == br:
                    if not is_linuxbridge_interface(port):
                        add_bridge_port(br,
                                        port,
                                        promisc=True,
                                        ifdata=generate_external_ids(br),
                                        portdata=generate_external_ids(br))
                    else:
                        log('{} is a Linux bridge: using Linux bridges in the '
                            'data-port config is deprecated for removal after '
                            '21.10 release of OpenStack charms.'.format(port),
                            level=WARNING)
                        add_ovsbridge_linuxbridge(
                            br,
                            port,
                            ifdata=generate_external_ids(br),
                            portdata=generate_external_ids(br))

    # NOTE(jamespage):
    # hw-offload and dpdk are mutually exclusive so log and error
    # and skip any subsequent DPDK configuration
    if use_dpdk() and use_hw_offload():
        log(
            'DPDK and Hardware offload are mutually exclusive, '
            'please disable enable-dpdk or enable-hardware-offload',
            level=ERROR)
    elif use_dpdk():
        log('Configuring bridges with DPDK', level=DEBUG)

        # TODO(sahid): We should also take into account the
        # "physical-network-mtus" in case different MTUs are
        # configured based on physical networks.
        global_mtu = (
            neutron_ovs_context.NeutronAPIContext()()['global_physnet_mtu'])

        dpdk_context = OVSDPDKDeviceContext()
        devices = dpdk_context.devices()

        portmaps = parse_data_port_mappings(config('data-port'))
        bridgemaps = parse_bridge_mappings(config('bridge-mappings'))

        bridge_port_interface_map = BridgePortInterfaceMap()
        bond_config = BondConfig()

        for br, port_iface_map in bridge_port_interface_map.items():
            log('Adding DPDK bridge: {}:{}'.format(br, brdata), level=DEBUG)
            add_bridge(br, brdata=brdata)
            if modern_ovs:
                for port in port_iface_map.keys():
                    ifdatamap = bridge_port_interface_map.get_ifdatamap(
                        br, port)
                    # set external-ids for all interfaces
                    for iface in ifdatamap:
                        ifdatamap[iface].update(generate_external_ids(br))
                    # NOTE: DPDK bonds are referenced by name and can be found
                    #       in the data-port config, regular DPDK ports are
                    #       referenced by MAC addresses and their names should
                    #       never be found in data-port
                    if port in portmaps.keys():
                        portdata = bond_config.get_ovs_portdata(port)
                        portdata.update(generate_external_ids(br))
                        log('Adding DPDK bond: {}({}) to bridge: {}'.format(
                            port, list(ifdatamap.keys()), br),
                            level=DEBUG)
                        add_bridge_bond(br,
                                        port,
                                        list(ifdatamap.keys()),
                                        portdata=portdata,
                                        ifdatamap=ifdatamap)
                    else:
                        log('Adding DPDK port: {} to bridge: {}'.format(
                            port, br),
                            level=DEBUG)
                        ifdata = ifdatamap[port]
                        add_bridge_port(br,
                                        port,
                                        ifdata=ifdata,
                                        portdata=generate_external_ids(br),
                                        linkup=False,
                                        promisc=None)
        if not modern_ovs:
            # port enumeration in legacy OVS-DPDK must follow alphabetic order
            # of the PCI addresses
            dev_idx = 0
            for pci, mac in sorted(devices.items()):
                # if mac.entity is a bridge, then the port can be added
                # directly, otherwise it is a bond (supported only in
                # modern_ovs) or misconfiguration
                if mac.entity in bridgemaps.values():
                    ifdata = {'type': 'dpdk', 'mtu-request': global_mtu}
                    ifdata.update(generate_external_ids(mac.entity))
                    ifname = 'dpdk{}'.format(dev_idx)
                    log('Adding DPDK port {}:{} to bridge {}'.format(
                        ifname, ifdata, mac.entity),
                        level=DEBUG)
                    add_bridge_port(mac.entity,
                                    ifname,
                                    ifdata=ifdata,
                                    portdata=generate_external_ids(mac.entity),
                                    linkup=False,
                                    promisc=None)
                else:
                    log('DPDK device {} skipped, {} is not a bridge'.format(
                        pci, mac.entity),
                        level=WARNING)
                dev_idx += 1

    target = config('ipfix-target')
    bridges = [INT_BRIDGE, EXT_BRIDGE]
    if bridgemaps:
        bridges.extend(bridgemaps.values())
    elif portmaps:
        bridges.extend([bridge_mac.entity for bridge_mac in portmaps.values()])

    if target:
        for bridge in bridges:
            disable_ipfix(bridge)
            enable_ipfix(bridge, target)
    else:
        # NOTE: removing ipfix setting from a bridge is idempotent and
        #       will pass regardless of the existence of the setting
        for bridge in bridges:
            disable_ipfix(bridge)

    # Ensure this runs so that mtu is applied to data-port interfaces if
    # provided.
    # NOTE(ajkavanagh) for pause/resume we don't gate this as it's not a
    # running service, but rather running a few commands.
    if not init_is_systemd():
        service_restart('os-charm-phy-nic-mtu')