# See the License for the specific language governing permissions and
# limitations under the License.
+from networking_mlnx.plugins.ml2.drivers.mlnx import constants
from oslo.config import cfg
-from neutron.common import constants
+from neutron.common import constants as n_const
from neutron.extensions import portbindings
from neutron.openstack.common import log
from neutron.plugins.common import constants as p_constants
# several MDs are capable to bing bind port on chosen host, the
# first listed MD will bind the port for VNIC_NORMAL.
super(MlnxMechanismDriver, self).__init__(
- constants.AGENT_TYPE_MLNX,
+ n_const.AGENT_TYPE_MLNX,
cfg.CONF.ESWITCH.vnic_type,
{portbindings.CAP_PORT_FILTER: False},
portbindings.VNIC_TYPES)
def try_to_bind_segment_for_agent(self, context, segment, agent):
if self.check_segment_for_agent(segment, agent):
- vif_type = self._get_vif_type(
- context.current[portbindings.VNIC_TYPE])
- if segment[api.NETWORK_TYPE] in ['flat', 'vlan']:
+ vif_type = constants.VNIC_TO_VIF_MAPPING.get(
+ context.current[portbindings.VNIC_TYPE], self.vif_type)
+ if (segment[api.NETWORK_TYPE] in
+ (p_constants.TYPE_FLAT, p_constants.TYPE_VLAN)):
self.vif_details['physical_network'] = segment[
'physical_network']
context.set_binding(segment[api.ID],
vif_type,
self.vif_details)
-
- def _get_vif_type(self, requested_vnic_type):
- if requested_vnic_type == portbindings.VNIC_MACVTAP:
- return portbindings.VIF_TYPE_MLNX_DIRECT
- elif requested_vnic_type == portbindings.VNIC_DIRECT:
- return portbindings.VIF_TYPE_MLNX_HOSTDEV
- return self.vif_type
--- /dev/null
+# The order of packages is significant, because pip processes them in the
+# order
+# of appearance. Changing the order has an impact on the overall integration
+# process, which may cause wedges in the gate later.
+
+networking_mlnx
# See the License for the specific language governing permissions and
# limitations under the License.
+from networking_mlnx.plugins.mlnx.agent import constants
from oslo.config import cfg
from neutron.agent.common import config
-from neutron.plugins.mlnx.common import constants
DEFAULT_INTERFACE_MAPPINGS = []
# limitations under the License.
-import socket
import sys
-import time
-
-import eventlet
-eventlet.monkey_patch()
+from networking_mlnx.plugins.mlnx.agent import mlnx_eswitch_neutron_agent
from oslo.config import cfg
-from oslo import messaging
-from neutron.agent import rpc as agent_rpc
-from neutron.agent import securitygroups_rpc as sg_rpc
+from neutron.i18n import _LE, _LI
from neutron.common import config as common_config
-from neutron.common import constants as q_constants
-from neutron.common import topics
-from neutron.common import utils as q_utils
-from neutron import context
-from neutron.i18n import _LE, _LI, _LW
+from neutron.common import utils
from neutron.openstack.common import log as logging
-from neutron.openstack.common import loopingcall
-from neutron.plugins.common import constants as p_const
-from neutron.plugins.mlnx.agent import utils
-from neutron.plugins.mlnx.common import config # noqa
-from neutron.plugins.mlnx.common import exceptions
+from neutron.plugins.mlnx.agent import config # noqa
LOG = logging.getLogger(__name__)
-class EswitchManager(object):
- def __init__(self, interface_mappings, endpoint, timeout):
- self.utils = utils.EswitchUtils(endpoint, timeout)
- self.interface_mappings = interface_mappings
- self.network_map = {}
- self.utils.define_fabric_mappings(interface_mappings)
-
- def get_port_id_by_mac(self, port_mac):
- for network_id, data in self.network_map.iteritems():
- for port in data['ports']:
- if port['port_mac'] == port_mac:
- return port['port_id']
- LOG.error(_LE("Agent cache inconsistency - port id "
- "is not stored for %s"), port_mac)
- raise exceptions.MlnxException(err_msg=("Agent cache inconsistency, "
- "check logs"))
-
- def get_vnics_mac(self):
- return set(self.utils.get_attached_vnics().keys())
-
- def vnic_port_exists(self, port_mac):
- return port_mac in self.utils.get_attached_vnics()
-
- def remove_network(self, network_id):
- if network_id in self.network_map:
- del self.network_map[network_id]
- else:
- LOG.debug("Network %s not defined on Agent.", network_id)
-
- def port_down(self, network_id, physical_network, port_mac):
- """Sets port to down.
-
- Check internal network map for port data.
- If port exists set port to Down
- """
- for network_id, data in self.network_map.iteritems():
- for port in data['ports']:
- if port['port_mac'] == port_mac:
- self.utils.port_down(physical_network, port_mac)
- return
- LOG.info(_LI('Network %s is not available on this agent'), network_id)
-
- def port_up(self, network_id, network_type,
- physical_network, seg_id, port_id, port_mac):
- """Sets port to up.
-
- Update internal network map with port data.
- - Check if vnic defined
- - configure eswitch vport
- - set port to Up
- """
- LOG.debug("Connecting port %s", port_id)
-
- if network_id not in self.network_map:
- self.provision_network(port_id, port_mac,
- network_id, network_type,
- physical_network, seg_id)
- net_map = self.network_map[network_id]
- net_map['ports'].append({'port_id': port_id, 'port_mac': port_mac})
-
- if network_type == p_const.TYPE_VLAN:
- LOG.info(_LI('Binding Segmentation ID %(seg_id)s '
- 'to eSwitch for vNIC mac_address %(mac)s'),
- {'seg_id': seg_id,
- 'mac': port_mac})
- self.utils.set_port_vlan_id(physical_network,
- seg_id,
- port_mac)
- self.utils.port_up(physical_network, port_mac)
- else:
- LOG.error(_LE('Unsupported network type %s'), network_type)
-
- def port_release(self, port_mac):
- """Clear port configuration from eSwitch."""
- for network_id, net_data in self.network_map.iteritems():
- for port in net_data['ports']:
- if port['port_mac'] == port_mac:
- self.utils.port_release(net_data['physical_network'],
- port['port_mac'])
- return
- LOG.info(_LI('Port_mac %s is not available on this agent'), port_mac)
-
- def provision_network(self, port_id, port_mac,
- network_id, network_type,
- physical_network, segmentation_id):
- LOG.info(_LI("Provisioning network %s"), network_id)
- if network_type == p_const.TYPE_VLAN:
- LOG.debug("Creating VLAN Network")
- else:
- LOG.error(_LE("Unknown network type %(network_type)s "
- "for network %(network_id)s"),
- {'network_type': network_type,
- 'network_id': network_id})
- return
- data = {
- 'physical_network': physical_network,
- 'network_type': network_type,
- 'ports': [],
- 'vlan_id': segmentation_id}
- self.network_map[network_id] = data
-
-
-class MlnxEswitchRpcCallbacks(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
-
- # Set RPC API version to 1.0 by default.
- # history
- # 1.1 Support Security Group RPC
- target = messaging.Target(version='1.1')
-
- def __init__(self, context, agent, sg_agent):
- super(MlnxEswitchRpcCallbacks, self).__init__()
- self.context = context
- self.agent = agent
- self.eswitch = agent.eswitch
- self.sg_agent = sg_agent
-
- def network_delete(self, context, **kwargs):
- LOG.debug("network_delete received")
- network_id = kwargs.get('network_id')
- if not network_id:
- LOG.warning(_LW("Invalid Network ID, cannot remove Network"))
- else:
- LOG.debug("Delete network %s", network_id)
- self.eswitch.remove_network(network_id)
-
- def port_update(self, context, **kwargs):
- port = kwargs.get('port')
- self.agent.add_port_update(port['mac_address'])
- LOG.debug("port_update message processed for port with mac %s",
- port['mac_address'])
-
-
-class MlnxEswitchNeutronAgent(object):
-
- def __init__(self, interface_mapping, root_helper):
- self._polling_interval = cfg.CONF.AGENT.polling_interval
- self._setup_eswitches(interface_mapping)
- configurations = {'interface_mappings': interface_mapping}
- self.agent_state = {
- 'binary': 'neutron-mlnx-agent',
- 'host': cfg.CONF.host,
- 'topic': q_constants.L2_AGENT_TOPIC,
- 'configurations': configurations,
- 'agent_type': q_constants.AGENT_TYPE_MLNX,
- 'start_flag': True}
- # Stores port update notifications for processing in main rpc loop
- self.updated_ports = set()
- self.context = context.get_admin_context_without_session()
- self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
- self.sg_plugin_rpc = sg_rpc.SecurityGroupServerRpcApi(topics.PLUGIN)
- self.sg_agent = sg_rpc.SecurityGroupAgentRpc(self.context,
- self.sg_plugin_rpc, root_helper)
- self._setup_rpc()
-
- def _setup_eswitches(self, interface_mapping):
- daemon = cfg.CONF.ESWITCH.daemon_endpoint
- timeout = cfg.CONF.ESWITCH.request_timeout
- self.eswitch = EswitchManager(interface_mapping, daemon, timeout)
-
- def _report_state(self):
- try:
- devices = len(self.eswitch.get_vnics_mac())
- self.agent_state.get('configurations')['devices'] = devices
- self.state_rpc.report_state(self.context,
- self.agent_state)
- self.agent_state.pop('start_flag', None)
- except Exception:
- LOG.exception(_LE("Failed reporting state!"))
-
- def _setup_rpc(self):
- self.agent_id = 'mlnx-agent.%s' % socket.gethostname()
- LOG.info(_LI("RPC agent_id: %s"), self.agent_id)
-
- self.topic = topics.AGENT
- self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN)
- # RPC network init
- # Handle updates from service
- self.endpoints = [MlnxEswitchRpcCallbacks(self.context, self,
- self.sg_agent)]
- # Define the listening consumers for the agent
- consumers = [[topics.PORT, topics.UPDATE],
- [topics.NETWORK, topics.DELETE],
- [topics.SECURITY_GROUP, topics.UPDATE]]
- self.connection = agent_rpc.create_consumers(self.endpoints,
- self.topic,
- consumers)
-
- report_interval = cfg.CONF.AGENT.report_interval
- if report_interval:
- heartbeat = loopingcall.FixedIntervalLoopingCall(
- self._report_state)
- heartbeat.start(interval=report_interval)
-
- def add_port_update(self, port):
- self.updated_ports.add(port)
-
- def scan_ports(self, previous, sync):
- cur_ports = self.eswitch.get_vnics_mac()
- port_info = {'current': cur_ports}
- updated_ports = self.updated_ports
- self.updated_ports = set()
- if sync:
- # Either it's the first iteration or previous iteration had
- # problems.
- port_info['added'] = cur_ports
- port_info['removed'] = ((previous['removed'] | previous['current'])
- - cur_ports)
- port_info['updated'] = ((previous['updated'] | updated_ports)
- & cur_ports)
- else:
- # Shouldn't process updates for not existing ports
- port_info['added'] = cur_ports - previous['current']
- port_info['removed'] = previous['current'] - cur_ports
- port_info['updated'] = updated_ports & cur_ports
- return port_info
-
- def process_network_ports(self, port_info):
- resync_a = False
- resync_b = False
- device_added_updated = port_info['added'] | port_info['updated']
-
- if device_added_updated:
- resync_a = self.treat_devices_added_or_updated(
- device_added_updated)
- if port_info['removed']:
- resync_b = self.treat_devices_removed(port_info['removed'])
- # If one of the above opertaions fails => resync with plugin
- return (resync_a | resync_b)
-
- def treat_vif_port(self, port_id, port_mac,
- network_id, network_type,
- physical_network, segmentation_id,
- admin_state_up):
- if self.eswitch.vnic_port_exists(port_mac):
- if admin_state_up:
- self.eswitch.port_up(network_id,
- network_type,
- physical_network,
- segmentation_id,
- port_id,
- port_mac)
- else:
- self.eswitch.port_down(network_id, physical_network, port_mac)
- else:
- LOG.debug("No port %s defined on agent.", port_id)
-
- def treat_devices_added_or_updated(self, devices):
- try:
- devs_details_list = self.plugin_rpc.get_devices_details_list(
- self.context,
- devices,
- self.agent_id)
- except Exception as e:
- LOG.debug("Unable to get device details for devices "
- "with MAC address %(devices)s: due to %(exc)s",
- {'devices': devices, 'exc': e})
- # resync is needed
- return True
-
- for dev_details in devs_details_list:
- device = dev_details['device']
- LOG.info(_LI("Adding or updating port with mac %s"), device)
-
- if 'port_id' in dev_details:
- LOG.info(_LI("Port %s updated"), device)
- LOG.debug("Device details %s", str(dev_details))
- self.treat_vif_port(dev_details['port_id'],
- dev_details['device'],
- dev_details['network_id'],
- dev_details['network_type'],
- dev_details['physical_network'],
- dev_details['segmentation_id'],
- dev_details['admin_state_up'])
- if dev_details.get('admin_state_up'):
- LOG.debug("Setting status for %s to UP", device)
- self.plugin_rpc.update_device_up(
- self.context, device, self.agent_id)
- else:
- LOG.debug("Setting status for %s to DOWN", device)
- self.plugin_rpc.update_device_down(
- self.context, device, self.agent_id)
- else:
- LOG.debug("Device with mac_address %s not defined "
- "on Neutron Plugin", device)
- return False
-
- def treat_devices_removed(self, devices):
- resync = False
- for device in devices:
- LOG.info(_LI("Removing device with mac_address %s"), device)
- try:
- port_id = self.eswitch.get_port_id_by_mac(device)
- dev_details = self.plugin_rpc.update_device_down(self.context,
- port_id,
- self.agent_id,
- cfg.CONF.host)
- except Exception as e:
- LOG.debug("Removing port failed for device %(device)s "
- "due to %(exc)s", {'device': device, 'exc': e})
- resync = True
- continue
- if dev_details['exists']:
- LOG.info(_LI("Port %s updated."), device)
- else:
- LOG.debug("Device %s not defined on plugin", device)
- self.eswitch.port_release(device)
- return resync
-
- def _port_info_has_changes(self, port_info):
- return (port_info['added'] or
- port_info['removed'] or
- port_info['updated'])
-
- def daemon_loop(self):
- LOG.info(_LI("eSwitch Agent Started!"))
- sync = True
- port_info = {'current': set(),
- 'added': set(),
- 'removed': set(),
- 'updated': set()}
- while True:
- start = time.time()
- try:
- port_info = self.scan_ports(previous=port_info, sync=sync)
- except exceptions.RequestTimeout:
- LOG.exception(_LE("Request timeout in agent event loop "
- "eSwitchD is not responding - exiting..."))
- raise SystemExit(1)
- if sync:
- LOG.info(_LI("Agent out of sync with plugin!"))
- sync = False
- if self._port_info_has_changes(port_info):
- LOG.debug("Starting to process devices in:%s", port_info)
- try:
- sync = self.process_network_ports(port_info)
- except Exception:
- LOG.exception(_LE("Error in agent event loop"))
- sync = True
- # sleep till end of polling interval
- elapsed = (time.time() - start)
- if (elapsed < self._polling_interval):
- time.sleep(self._polling_interval - elapsed)
- else:
- LOG.debug("Loop iteration exceeded interval "
- "(%(polling_interval)s vs. %(elapsed)s)",
- {'polling_interval': self._polling_interval,
- 'elapsed': elapsed})
-
-
def main():
common_config.init(sys.argv[1:])
common_config.setup_logging()
try:
- interface_mappings = q_utils.parse_mappings(
+ interface_mappings = utils.parse_mappings(
cfg.CONF.ESWITCH.physical_interface_mappings)
except ValueError as e:
LOG.error(_LE("Parsing physical_interface_mappings failed: %s. "
root_helper = cfg.CONF.AGENT.root_helper
try:
- agent = MlnxEswitchNeutronAgent(interface_mappings, root_helper)
+ agent = mlnx_eswitch_neutron_agent.MlnxEswitchNeutronAgent(
+ interface_mappings, root_helper)
except Exception as e:
LOG.error(_LE("Failed on Agent initialisation : %s. "
"Agent terminated!"), e)
# Start everything.
LOG.info(_LI("Agent initialised successfully, now running... "))
- agent.daemon_loop()
+ agent.run()
sys.exit(0)
+++ /dev/null
-# Copyright 2013 Mellanox Technologies, Ltd
-#
-# 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.
-
-from oslo.serialization import jsonutils
-from oslo.utils import importutils
-
-from neutron.i18n import _LE
-from neutron.openstack.common import log as logging
-from neutron.plugins.mlnx.common import comm_utils
-from neutron.plugins.mlnx.common import exceptions
-
-zmq = importutils.try_import('eventlet.green.zmq')
-
-LOG = logging.getLogger(__name__)
-
-
-class EswitchUtils(object):
- def __init__(self, daemon_endpoint, timeout):
- if not zmq:
- LOG.error(_LE("Failed to import eventlet.green.zmq. "
- "Won't connect to eSwitchD - exiting..."))
- raise SystemExit(1)
- self.__conn = None
- self.daemon = daemon_endpoint
- self.timeout = timeout
-
- @property
- def _conn(self):
- if self.__conn is None:
- context = zmq.Context()
- socket = context.socket(zmq.REQ)
- socket.setsockopt(zmq.LINGER, 0)
- socket.connect(self.daemon)
- self.__conn = socket
- self.poller = zmq.Poller()
- self.poller.register(self._conn, zmq.POLLIN)
- return self.__conn
-
- @comm_utils.RetryDecorator(exceptions.RequestTimeout)
- def send_msg(self, msg):
- self._conn.send(msg)
-
- socks = dict(self.poller.poll(self.timeout))
- if socks.get(self._conn) == zmq.POLLIN:
- recv_msg = self._conn.recv()
- response = self.parse_response_msg(recv_msg)
- return response
- else:
- self._conn.setsockopt(zmq.LINGER, 0)
- self._conn.close()
- self.poller.unregister(self._conn)
- self.__conn = None
- raise exceptions.RequestTimeout()
-
- def parse_response_msg(self, recv_msg):
- msg = jsonutils.loads(recv_msg)
- if msg['status'] == 'OK':
- if 'response' in msg:
- return msg.get('response')
- return
- elif msg['status'] == 'FAIL':
- msg_dict = dict(action=msg['action'], reason=msg['reason'])
- error_msg = _LE("Action %(action)s failed: %(reason)s") % msg_dict
- else:
- error_msg = _LE("Unknown operation status %s") % msg['status']
- LOG.error(error_msg)
- raise exceptions.OperationFailed(err_msg=error_msg)
-
- def get_attached_vnics(self):
- LOG.debug("get_attached_vnics")
- msg = jsonutils.dumps({'action': 'get_vnics', 'fabric': '*'})
- vnics = self.send_msg(msg)
- return vnics
-
- def set_port_vlan_id(self, physical_network,
- segmentation_id, port_mac):
- LOG.debug("Set Vlan %(segmentation_id)s on Port %(port_mac)s "
- "on Fabric %(physical_network)s",
- {'port_mac': port_mac,
- 'segmentation_id': segmentation_id,
- 'physical_network': physical_network})
- msg = jsonutils.dumps({'action': 'set_vlan',
- 'fabric': physical_network,
- 'port_mac': port_mac,
- 'vlan': segmentation_id})
- self.send_msg(msg)
-
- def define_fabric_mappings(self, interface_mapping):
- for fabric, phy_interface in interface_mapping.iteritems():
- LOG.debug("Define Fabric %(fabric)s on interface %(ifc)s",
- {'fabric': fabric,
- 'ifc': phy_interface})
- msg = jsonutils.dumps({'action': 'define_fabric_mapping',
- 'fabric': fabric,
- 'interface': phy_interface})
- self.send_msg(msg)
-
- def port_up(self, fabric, port_mac):
- LOG.debug("Port Up for %(port_mac)s on fabric %(fabric)s",
- {'port_mac': port_mac, 'fabric': fabric})
- msg = jsonutils.dumps({'action': 'port_up',
- 'fabric': fabric,
- 'ref_by': 'mac_address',
- 'mac': 'port_mac'})
- self.send_msg(msg)
-
- def port_down(self, fabric, port_mac):
- LOG.debug("Port Down for %(port_mac)s on fabric %(fabric)s",
- {'port_mac': port_mac, 'fabric': fabric})
- msg = jsonutils.dumps({'action': 'port_down',
- 'fabric': fabric,
- 'ref_by': 'mac_address',
- 'mac': port_mac})
- self.send_msg(msg)
-
- def port_release(self, fabric, port_mac):
- LOG.debug("Port Release for %(port_mac)s on fabric %(fabric)s",
- {'port_mac': port_mac, 'fabric': fabric})
- msg = jsonutils.dumps({'action': 'port_release',
- 'fabric': fabric,
- 'ref_by': 'mac_address',
- 'mac': port_mac})
- self.send_msg(msg)
-
- def get_eswitch_ports(self, fabric):
- # TODO(irena) - to implement for next phase
- return {}
-
- def get_eswitch_id(self, fabric):
- # TODO(irena) - to implement for next phase
- return ""
+++ /dev/null
-# Copyright 2013 Mellanox Technologies, Ltd
-#
-# 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 time
-
-from oslo.config import cfg
-
-from neutron.openstack.common import log as logging
-from neutron.plugins.mlnx.common import config # noqa
-
-LOG = logging.getLogger(__name__)
-
-
-class RetryDecorator(object):
- """Retry decorator reruns a method 'retries' times if an exception occurs.
-
- Decorator for retrying a method if exceptionToCheck exception occurs
- If method raises exception, retries 'retries' times with increasing
- back off period between calls with 'interval' multiplier
-
- :param exceptionToCheck: the exception to check
- :param interval: initial delay between retries in seconds
- :param retries: number of times to try before giving up
- :raises: exceptionToCheck
- """
-
- def __init__(self, exceptionToCheck,
- interval=cfg.CONF.ESWITCH.request_timeout / 1000,
- retries=cfg.CONF.ESWITCH.retries,
- backoff_rate=cfg.CONF.ESWITCH.backoff_rate):
- self.exc = exceptionToCheck
- self.interval = interval
- self.retries = retries
- self.backoff_rate = backoff_rate
-
- def __call__(self, original_func):
- def decorated(*args, **kwargs):
- sleep_interval = self.interval
- num_of_iter = self.retries
- while num_of_iter > 0:
- try:
- return original_func(*args, **kwargs)
- except self.exc:
- LOG.debug("Request timeout - call again after "
- "%s seconds", sleep_interval)
- time.sleep(sleep_interval)
- num_of_iter -= 1
- sleep_interval *= self.backoff_rate
-
- return original_func(*args, **kwargs)
- return decorated
+++ /dev/null
-# Copyright 2013 Mellanox Technologies, Ltd
-#
-# 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.
-
-LOCAL_VLAN_ID = -2
-FLAT_VLAN_ID = -1
-
-# Values for physical network_type
-TYPE_IB = 'ib'
-TYPE_ETH = 'eth'
-
-VIF_TYPE_DIRECT = 'mlnx_direct'
-VIF_TYPE_HOSTDEV = 'hostdev'
-
-VNIC_TYPE = 'vnic_type'
+++ /dev/null
-# Copyright 2013 Mellanox Technologies, Ltd
-#
-# 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.
-
-from neutron.common import exceptions as qexc
-
-
-class MlnxException(qexc.NeutronException):
- message = _("Mlnx Exception: %(err_msg)s")
-
-
-class RequestTimeout(qexc.NeutronException):
- message = _("Request Timeout: no response from eSwitchD")
-
-
-class OperationFailed(qexc.NeutronException):
- message = _("Operation Failed: %(err_msg)s")
# 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 sys
-
+import mock
from neutron.common import constants
from neutron.extensions import portbindings
from neutron.plugins.ml2 import driver_api as api
-from neutron.plugins.ml2.drivers.mlnx import mech_mlnx
from neutron.tests.unit.ml2 import _test_mech_agent as base
+m_const_mock = mock.Mock()
+
+with mock.patch.dict(sys.modules,
+ {'networking_mlnx': mock.Mock(),
+ 'networking_mlnx.plugins': mock.Mock(),
+ 'networking_mlnx.plugins.ml2': mock.Mock(),
+ 'networking_mlnx.plugins.ml2.drivers': mock.Mock(),
+ 'networking_mlnx.plugins.ml2.drivers.mlnx':
+ m_const_mock}):
+ from neutron.plugins.ml2.drivers.mlnx import mech_mlnx
+
class MlnxMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
VIF_TYPE = portbindings.VIF_TYPE_MLNX_DIRECT
super(MlnxMechanismBaseTestCase, self).setUp()
self.driver = mech_mlnx.MlnxMechanismDriver()
self.driver.initialize()
+ m_const_mock.constants.VNIC_TO_VIF_MAPPING.get.return_value = (
+ self.driver.vif_type)
class MlnxMechanismGenericTestCase(MlnxMechanismBaseTestCase,
class MlnxMechanismVnicTypeTestCase(MlnxMechanismBaseTestCase,
base.AgentMechanismVlanTestCase):
+
def _check_vif_type_for_vnic_type(self, vnic_type,
expected_vif_type):
context = base.FakePortContext(self.AGENT_TYPE,
self.assertEqual(expected_vif_type, context._bound_vif_type)
def test_vnic_type_direct(self):
+ m_const_mock.constants.VNIC_TO_VIF_MAPPING.get.return_value = (
+ portbindings.VIF_TYPE_MLNX_HOSTDEV)
self._check_vif_type_for_vnic_type(portbindings.VNIC_DIRECT,
portbindings.VIF_TYPE_MLNX_HOSTDEV)
def test_vnic_type_macvtap(self):
+ m_const_mock.constants.VNIC_TO_VIF_MAPPING.get.return_value = (
+ portbindings.VIF_TYPE_MLNX_DIRECT)
+
self._check_vif_type_for_vnic_type(portbindings.VNIC_MACVTAP,
portbindings.VIF_TYPE_MLNX_DIRECT)
+++ /dev/null
-# Copyright (c) 2013 OpenStack Foundation
-#
-# 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 mock
-from oslo.config import cfg
-
-from neutron.plugins.mlnx.common import comm_utils
-from neutron.plugins.mlnx.common import config # noqa
-from neutron.plugins.mlnx.common import exceptions
-from neutron.tests import base
-
-
-class WrongException(Exception):
- pass
-
-
-class TestRetryDecorator(base.BaseTestCase):
- def setUp(self):
- super(TestRetryDecorator, self).setUp()
- self.sleep_fn_p = mock.patch("time.sleep")
- self.sleep_fn = self.sleep_fn_p.start()
-
- def test_no_retry_required(self):
- self.counter = 0
-
- @comm_utils.RetryDecorator(exceptions.RequestTimeout, interval=2,
- retries=3, backoff_rate=2)
- def succeeds():
- self.counter += 1
- return 'success'
-
- ret = succeeds()
- self.assertFalse(self.sleep_fn.called)
- self.assertEqual(ret, 'success')
- self.assertEqual(self.counter, 1)
-
- def test_retry_zero_times(self):
- self.counter = 0
- interval = 2
- backoff_rate = 2
- retries = 0
-
- @comm_utils.RetryDecorator(exceptions.RequestTimeout, interval,
- retries, backoff_rate)
- def always_fails():
- self.counter += 1
- raise exceptions.RequestTimeout()
-
- self.assertRaises(exceptions.RequestTimeout, always_fails)
- self.assertEqual(self.counter, 1)
- self.assertFalse(self.sleep_fn.called)
-
- def test_retries_once(self):
- self.counter = 0
- interval = 2
- backoff_rate = 2
- retries = 3
-
- @comm_utils.RetryDecorator(exceptions.RequestTimeout, interval,
- retries, backoff_rate)
- def fails_once():
- self.counter += 1
- if self.counter < 2:
- raise exceptions.RequestTimeout()
- else:
- return 'success'
-
- ret = fails_once()
- self.assertEqual(ret, 'success')
- self.assertEqual(self.counter, 2)
- self.assertEqual(self.sleep_fn.call_count, 1)
- self.sleep_fn.assert_called_with(interval)
-
- def test_limit_is_reached(self):
- self.counter = 0
- retries = 3
- interval = 2
- backoff_rate = 4
-
- @comm_utils.RetryDecorator(exceptions.RequestTimeout, interval,
- retries, backoff_rate)
- def always_fails():
- self.counter += 1
- raise exceptions.RequestTimeout()
-
- self.assertRaises(exceptions.RequestTimeout, always_fails)
- self.assertEqual(self.counter, retries + 1)
- self.assertEqual(self.sleep_fn.call_count, retries)
-
- expected_sleep_fn_arg = []
- for i in range(retries):
- expected_sleep_fn_arg.append(interval)
- interval *= backoff_rate
-
- self.sleep_fn.assert_has_calls(map(mock.call, expected_sleep_fn_arg))
-
- def test_limit_is_reached_with_conf(self):
- self.counter = 0
-
- @comm_utils.RetryDecorator(exceptions.RequestTimeout)
- def always_fails():
- self.counter += 1
- raise exceptions.RequestTimeout()
-
- retry = cfg.CONF.ESWITCH.retries
- interval = cfg.CONF.ESWITCH.request_timeout / 1000
- delay_rate = cfg.CONF.ESWITCH.backoff_rate
-
- expected_sleep_fn_arg = []
- for i in range(retry):
- expected_sleep_fn_arg.append(interval)
- interval *= delay_rate
-
- self.assertRaises(exceptions.RequestTimeout, always_fails)
- self.assertEqual(self.counter, retry + 1)
- self.assertEqual(self.sleep_fn.call_count, retry)
- self.sleep_fn.assert_has_calls(map(mock.call, expected_sleep_fn_arg))
-
- def test_wrong_exception_no_retry(self):
-
- @comm_utils.RetryDecorator(exceptions.RequestTimeout)
- def raise_unexpected_error():
- raise WrongException("wrong exception")
-
- self.assertRaises(WrongException, raise_unexpected_error)
- self.assertFalse(self.sleep_fn.called)
+++ /dev/null
-# Copyright 2014 Mellanox Technologies, Ltd
-#
-# 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 contextlib
-
-import mock
-from oslo.config import cfg
-import testtools
-
-from neutron.plugins.mlnx.agent import eswitch_neutron_agent
-from neutron.plugins.mlnx.agent import utils
-from neutron.plugins.mlnx.common import exceptions
-from neutron.tests import base
-
-
-class TestEswichManager(base.BaseTestCase):
-
- def setUp(self):
- super(TestEswichManager, self).setUp()
-
- class MockEswitchUtils(object):
- def __init__(self, endpoint, timeout):
- pass
-
- mock.patch('neutron.plugins.mlnx.agent.utils.EswitchManager',
- new=MockEswitchUtils)
-
- with mock.patch.object(utils, 'zmq'):
- self.manager = eswitch_neutron_agent.EswitchManager({}, None, None)
-
- def test_get_not_exist_port_id(self):
- with testtools.ExpectedException(exceptions.MlnxException):
- self.manager.get_port_id_by_mac('no-such-mac')
-
-
-class TestMlnxEswitchRpcCallbacks(base.BaseTestCase):
-
- def setUp(self):
- super(TestMlnxEswitchRpcCallbacks, self).setUp()
- agent = mock.Mock()
- self.rpc_callbacks = eswitch_neutron_agent.MlnxEswitchRpcCallbacks(
- 'context',
- agent,
- agent
- )
-
- def test_port_update(self):
- port = {'mac_address': '10:20:30:40:50:60'}
- add_port_update = self.rpc_callbacks.agent.add_port_update
- self.rpc_callbacks.port_update('context', port=port)
- add_port_update.assert_called_once_with(port['mac_address'])
-
-
-class TestEswitchAgent(base.BaseTestCase):
-
- def setUp(self):
- super(TestEswitchAgent, self).setUp()
- cfg.CONF.set_default('firewall_driver',
- 'neutron.agent.firewall.NoopFirewallDriver',
- group='SECURITYGROUP')
-
- class MockFixedIntervalLoopingCall(object):
- def __init__(self, f):
- self.f = f
-
- def start(self, interval=0):
- self.f()
-
- mock.patch('neutron.openstack.common.loopingcall.'
- 'FixedIntervalLoopingCall',
- new=MockFixedIntervalLoopingCall)
-
- with mock.patch.object(utils, 'zmq'):
- self.agent = eswitch_neutron_agent.MlnxEswitchNeutronAgent({}, {})
- self.agent.plugin_rpc = mock.Mock()
- self.agent.context = mock.Mock()
- self.agent.agent_id = mock.Mock()
- self.agent.eswitch = mock.Mock()
- self.agent.eswitch.get_vnics_mac.return_value = []
-
- def test_treat_devices_added_returns_true_for_missing_device(self):
- attrs = {'get_devices_details_list.side_effect': Exception()}
- self.agent.plugin_rpc.configure_mock(**attrs)
- with contextlib.nested(
- mock.patch('neutron.plugins.mlnx.agent.eswitch_neutron_agent.'
- 'EswitchManager.get_vnics_mac',
- return_value=[])):
- self.assertTrue(self.agent.treat_devices_added_or_updated([{}]))
-
- def _mock_treat_devices_added_updated(self, details, func_name):
- """Mock treat devices added.
-
- :param details: the details to return for the device
- :param func_name: the function that should be called
- :returns: whether the named function was called
- """
- with contextlib.nested(
- mock.patch('neutron.plugins.mlnx.agent.eswitch_neutron_agent.'
- 'EswitchManager.get_vnics_mac',
- return_value=[]),
- mock.patch.object(self.agent.plugin_rpc,
- 'get_devices_details_list',
- return_value=[details]),
- mock.patch.object(self.agent.plugin_rpc, 'update_device_up'),
- mock.patch.object(self.agent, func_name)
- ) as (vnics_fn, get_dev_fn, upd_dev_up, func):
- self.assertFalse(self.agent.treat_devices_added_or_updated([{}]))
- return (func.called, upd_dev_up.called)
-
- def test_treat_devices_added_updates_known_port(self):
- details = mock.MagicMock()
- details.__contains__.side_effect = lambda x: True
- func, dev_up = self._mock_treat_devices_added_updated(details,
- 'treat_vif_port')
- self.assertTrue(func)
- self.assertTrue(dev_up)
-
- def test_treat_devices_added_updates_known_port_admin_down(self):
- details = {'port_id': '1234567890',
- 'device': '01:02:03:04:05:06',
- 'network_id': '123456789',
- 'network_type': 'vlan',
- 'physical_network': 'default',
- 'segmentation_id': 2,
- 'admin_state_up': False}
- func, dev_up = self._mock_treat_devices_added_updated(details,
- 'treat_vif_port')
- self.assertTrue(func)
- self.assertFalse(dev_up)
-
- def test_treat_devices_removed_returns_true_for_missing_device(self):
- with mock.patch.object(self.agent.plugin_rpc, 'update_device_down',
- side_effect=Exception()):
- self.assertTrue(self.agent.treat_devices_removed([{}]))
-
- def test_treat_devices_removed_releases_port(self):
- details = dict(exists=False)
- with mock.patch.object(self.agent.plugin_rpc, 'update_device_down',
- return_value=details):
- with mock.patch.object(self.agent.eswitch,
- 'port_release') as port_release:
- self.assertFalse(self.agent.treat_devices_removed([{}]))
- self.assertTrue(port_release.called)
-
- def _test_process_network_ports(self, port_info):
- with contextlib.nested(
- mock.patch.object(self.agent, 'treat_devices_added_or_updated',
- return_value=False),
- mock.patch.object(self.agent, 'treat_devices_removed',
- return_value=False)
- ) as (device_added_updated, device_removed):
- self.assertFalse(self.agent.process_network_ports(port_info))
- device_added_updated.assert_called_once_with(
- port_info['added'] | port_info['updated'])
- device_removed.assert_called_once_with(port_info['removed'])
-
- def test_process_network_ports(self):
- self._test_process_network_ports(
- {'current': set(['10:20:30:40:50:60']),
- 'updated': set(),
- 'added': set(['11:21:31:41:51:61']),
- 'removed': set(['13:23:33:43:53:63'])})
-
- def test_process_network_ports_with_updated_ports(self):
- self._test_process_network_ports(
- {'current': set(['10:20:30:40:50:60']),
- 'updated': set(['12:22:32:42:52:62']),
- 'added': set(['11:21:31:41:51:61']),
- 'removed': set(['13:23:33:43:53:63'])})
-
- def test_add_port_update(self):
- mac_addr = '10:20:30:40:50:60'
- self.agent.add_port_update(mac_addr)
- self.assertEqual(set([mac_addr]), self.agent.updated_ports)
-
- def _mock_scan_ports(self, vif_port_set, previous,
- updated_ports, sync=False):
- self.agent.updated_ports = updated_ports
- with mock.patch.object(self.agent.eswitch, 'get_vnics_mac',
- return_value=vif_port_set):
- return self.agent.scan_ports(previous, sync)
-
- def test_scan_ports_return_current_for_unchanged_ports(self):
- vif_port_set = set([1, 2])
- previous = dict(current=set([1, 2]), added=set(),
- removed=set(), updated=set())
- expected = dict(current=vif_port_set, added=set(),
- removed=set(), updated=set())
- actual = self._mock_scan_ports(vif_port_set,
- previous, set())
- self.assertEqual(expected, actual)
-
- def test_scan_ports_return_port_changes(self):
- vif_port_set = set([1, 3])
- previous = dict(current=set([1, 2]), added=set(),
- removed=set(), updated=set())
- expected = dict(current=vif_port_set, added=set([3]),
- removed=set([2]), updated=set())
- actual = self._mock_scan_ports(vif_port_set,
- previous, set())
- self.assertEqual(expected, actual)
-
- def test_scan_ports_with_updated_ports(self):
- vif_port_set = set([1, 3, 4])
- previous = dict(current=set([1, 2, 4]), added=set(),
- removed=set(), updated=set())
- expected = dict(current=vif_port_set, added=set([3]),
- removed=set([2]), updated=set([4]))
- actual = self._mock_scan_ports(vif_port_set,
- previous, set([4]))
- self.assertEqual(expected, actual)
-
- def test_scan_ports_with_unknown_updated_ports(self):
- vif_port_set = set([1, 3, 4])
- previous = dict(current=set([1, 2, 4]), added=set(),
- removed=set(), updated=set())
- expected = dict(current=vif_port_set, added=set([3]),
- removed=set([2]), updated=set([4]))
- actual = self._mock_scan_ports(vif_port_set,
- previous,
- updated_ports=set([4, 5]))
- self.assertEqual(expected, actual)