193 lines
6.3 KiB
Python
193 lines
6.3 KiB
Python
# Copyright (c) 2014 OpenStack Foundation.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import re
|
|
|
|
import netaddr
|
|
from oslo_log import log as logging
|
|
import six
|
|
|
|
from neutron.agent.common import ovs_lib
|
|
from neutron.agent.linux import ip_lib
|
|
from neutron.agent.linux import ip_link_support
|
|
from neutron.agent.linux import utils as agent_utils
|
|
from neutron.common import utils
|
|
from neutron.i18n import _LE
|
|
from neutron.openstack.common import uuidutils
|
|
from neutron.plugins.common import constants as const
|
|
from neutron.plugins.openvswitch.common import constants as ovs_const
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
MINIMUM_DNSMASQ_VERSION = 2.67
|
|
|
|
|
|
def ovs_vxlan_supported(from_ip='192.0.2.1', to_ip='192.0.2.2'):
|
|
name = "vxlantest-" + utils.get_random_string(6)
|
|
with ovs_lib.OVSBridge(name) as br:
|
|
port = br.add_tunnel_port(from_ip, to_ip, const.TYPE_VXLAN)
|
|
return port != ovs_lib.INVALID_OFPORT
|
|
|
|
|
|
def iproute2_vxlan_supported():
|
|
ip = ip_lib.IPWrapper()
|
|
name = "vxlantest-" + utils.get_random_string(4)
|
|
port = ip.add_vxlan(name, 3000)
|
|
ip.del_veth(name)
|
|
return name == port.name
|
|
|
|
|
|
def patch_supported():
|
|
seed = utils.get_random_string(6)
|
|
name = "patchtest-" + seed
|
|
peer_name = "peertest0-" + seed
|
|
patch_name = "peertest1-" + seed
|
|
with ovs_lib.OVSBridge(name) as br:
|
|
port = br.add_patch_port(patch_name, peer_name)
|
|
return port != ovs_lib.INVALID_OFPORT
|
|
|
|
|
|
def nova_notify_supported():
|
|
try:
|
|
import neutron.notifiers.nova # noqa since unused
|
|
return True
|
|
except ImportError:
|
|
return False
|
|
|
|
|
|
def ofctl_arg_supported(cmd, **kwargs):
|
|
"""Verify if ovs-ofctl binary supports cmd with **kwargs.
|
|
|
|
:param cmd: ovs-ofctl command to use for test.
|
|
:param **kwargs: arguments to test with the command.
|
|
:returns: a boolean if the supplied arguments are supported.
|
|
"""
|
|
br_name = 'br-test-%s' % utils.get_random_string(6)
|
|
with ovs_lib.OVSBridge(br_name) as test_br:
|
|
full_args = ["ovs-ofctl", cmd, test_br.br_name,
|
|
ovs_lib._build_flow_expr_str(kwargs, cmd.split('-')[0])]
|
|
try:
|
|
agent_utils.execute(full_args, run_as_root=True)
|
|
except RuntimeError as e:
|
|
LOG.debug("Exception while checking supported feature via "
|
|
"command %s. Exception: %s", full_args, e)
|
|
return False
|
|
except Exception:
|
|
LOG.exception(_LE("Unexpected exception while checking supported"
|
|
" feature via command: %s"), full_args)
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
|
|
def arp_responder_supported():
|
|
mac = netaddr.EUI('dead:1234:beef', dialect=netaddr.mac_unix)
|
|
ip = netaddr.IPAddress('240.0.0.1')
|
|
actions = ovs_const.ARP_RESPONDER_ACTIONS % {'mac': mac, 'ip': ip}
|
|
|
|
return ofctl_arg_supported(cmd='add-flow',
|
|
table=21,
|
|
priority=1,
|
|
proto='arp',
|
|
dl_vlan=42,
|
|
nw_dst='%s' % ip,
|
|
actions=actions)
|
|
|
|
|
|
def arp_header_match_supported():
|
|
return ofctl_arg_supported(cmd='add-flow',
|
|
table=24,
|
|
priority=1,
|
|
proto='arp',
|
|
arp_op='0x2',
|
|
arp_spa='1.1.1.1',
|
|
actions="NORMAL")
|
|
|
|
|
|
def vf_management_supported():
|
|
try:
|
|
vf_section = ip_link_support.IpLinkSupport.get_vf_mgmt_section()
|
|
if not ip_link_support.IpLinkSupport.vf_mgmt_capability_supported(
|
|
vf_section,
|
|
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_STATE):
|
|
LOG.debug("ip link command does not support vf capability")
|
|
return False
|
|
except ip_link_support.UnsupportedIpLinkCommand:
|
|
LOG.exception(_LE("Unexpected exception while checking supported "
|
|
"ip link command"))
|
|
return False
|
|
return True
|
|
|
|
|
|
def netns_read_requires_helper():
|
|
ipw = ip_lib.IPWrapper()
|
|
nsname = "netnsreadtest-" + uuidutils.generate_uuid()
|
|
ipw.netns.add(nsname)
|
|
try:
|
|
# read without root_helper. if exists, not required.
|
|
ipw_nohelp = ip_lib.IPWrapper()
|
|
exists = ipw_nohelp.netns.exists(nsname)
|
|
finally:
|
|
ipw.netns.delete(nsname)
|
|
return not exists
|
|
|
|
|
|
def get_minimal_dnsmasq_version_supported():
|
|
return MINIMUM_DNSMASQ_VERSION
|
|
|
|
|
|
def dnsmasq_version_supported():
|
|
try:
|
|
cmd = ['dnsmasq', '--version']
|
|
env = {'LC_ALL': 'C'}
|
|
out = agent_utils.execute(cmd, addl_env=env)
|
|
m = re.search(r"version (\d+\.\d+)", out)
|
|
ver = float(m.group(1)) if m else 0
|
|
if ver < MINIMUM_DNSMASQ_VERSION:
|
|
return False
|
|
except (OSError, RuntimeError, IndexError, ValueError) as e:
|
|
LOG.debug("Exception while checking minimal dnsmasq version. "
|
|
"Exception: %s", e)
|
|
return False
|
|
return True
|
|
|
|
|
|
def ovsdb_native_supported():
|
|
# Running the test should ensure we are configured for OVSDB native
|
|
try:
|
|
ovs = ovs_lib.BaseOVS()
|
|
ovs.get_bridges()
|
|
return True
|
|
except ImportError as ex:
|
|
LOG.error(_LE("Failed to import required modules. Ensure that the "
|
|
"python-openvswitch package is installed. Error: %s"),
|
|
ex.message)
|
|
except Exception as ex:
|
|
LOG.exception(six.text_type(ex))
|
|
|
|
return False
|
|
|
|
|
|
def ebtables_supported():
|
|
try:
|
|
cmd = ['ebtables', '--version']
|
|
agent_utils.execute(cmd)
|
|
return True
|
|
except (OSError, RuntimeError, IndexError, ValueError) as e:
|
|
LOG.debug("Exception while checking for installed ebtables. "
|
|
"Exception: %s", e)
|
|
return False
|