]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Thin MLNX ML2 mechanism driver and agent
authorMoshe Levi <moshele@mellanox.com>
Sun, 11 Jan 2015 13:25:59 +0000 (15:25 +0200)
committerMoshe Levi <moshele@mellanox.com>
Thu, 29 Jan 2015 18:47:09 +0000 (20:47 +0200)
This commit thins the in-tree MLNX ML2 MechanismDriver and Agent. A matching
change to the stackforge/networking-mlnx project has the backend logic
there.

Partial-Implements: blueprint core-vendor-decomposition
Closes-Bug: 1414902

Change-Id: I22e6ff37ea289f58ca5a7f49d65f634f72730402

13 files changed:
neutron/plugins/ml2/drivers/mlnx/mech_mlnx.py
neutron/plugins/ml2/drivers/mlnx/requirements.txt [new file with mode: 0644]
neutron/plugins/mlnx/agent/config.py [moved from neutron/plugins/mlnx/common/config.py with 97% similarity]
neutron/plugins/mlnx/agent/eswitch_neutron_agent.py
neutron/plugins/mlnx/agent/utils.py [deleted file]
neutron/plugins/mlnx/common/__init__.py [deleted file]
neutron/plugins/mlnx/common/comm_utils.py [deleted file]
neutron/plugins/mlnx/common/constants.py [deleted file]
neutron/plugins/mlnx/common/exceptions.py [deleted file]
neutron/tests/unit/ml2/drivers/test_mech_mlnx.py
neutron/tests/unit/mlnx/__init__.py [deleted file]
neutron/tests/unit/mlnx/test_mlnx_comm_utils.py [deleted file]
neutron/tests/unit/mlnx/test_mlnx_neutron_agent.py [deleted file]

index b1d0f1ba8f975e6339994a615a4a930b20bb534f..c754c5890adf22ec92734f72a383d99f1e914c73 100644 (file)
 # 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
@@ -45,7 +46,7 @@ class MlnxMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
         # 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)
@@ -59,18 +60,12 @@ class MlnxMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
 
     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
diff --git a/neutron/plugins/ml2/drivers/mlnx/requirements.txt b/neutron/plugins/ml2/drivers/mlnx/requirements.txt
new file mode 100644 (file)
index 0000000..0165a4c
--- /dev/null
@@ -0,0 +1,6 @@
+# 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
similarity index 97%
rename from neutron/plugins/mlnx/common/config.py
rename to neutron/plugins/mlnx/agent/config.py
index caed6db8d8b6b6488f98a440504ab738a8f507ef..1776ae376551b6365d396d099307efd90fa67bb4 100644 (file)
 # 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 = []
 
index 7c8e981f1e80961561921612a36732aea49af193..05b689db6fd170a082b8fbb667f965b3510fe19f 100644 (file)
 # 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. "
@@ -406,7 +43,8 @@ def main():
 
     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)
@@ -414,7 +52,7 @@ def main():
 
     # Start everything.
     LOG.info(_LI("Agent initialised successfully, now running... "))
-    agent.daemon_loop()
+    agent.run()
     sys.exit(0)
 
 
diff --git a/neutron/plugins/mlnx/agent/utils.py b/neutron/plugins/mlnx/agent/utils.py
deleted file mode 100644 (file)
index 5cf056e..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-# 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 ""
diff --git a/neutron/plugins/mlnx/common/__init__.py b/neutron/plugins/mlnx/common/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/neutron/plugins/mlnx/common/comm_utils.py b/neutron/plugins/mlnx/common/comm_utils.py
deleted file mode 100644 (file)
index d0cbb2b..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-# 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
diff --git a/neutron/plugins/mlnx/common/constants.py b/neutron/plugins/mlnx/common/constants.py
deleted file mode 100644 (file)
index 4ca82d7..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-# 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'
diff --git a/neutron/plugins/mlnx/common/exceptions.py b/neutron/plugins/mlnx/common/exceptions.py
deleted file mode 100644 (file)
index 457a100..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-# 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")
index 1192ab489ca264812d233d12dc2ebc032affddf5..6ccbe8658bbb671e562e3195a0d165039668408a 100644 (file)
 #    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
@@ -49,6 +60,8 @@ class MlnxMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
         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,
@@ -68,6 +81,7 @@ class MlnxMechanismFlatTestCase(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,
@@ -78,10 +92,15 @@ class MlnxMechanismVnicTypeTestCase(MlnxMechanismBaseTestCase,
         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)
 
diff --git a/neutron/tests/unit/mlnx/__init__.py b/neutron/tests/unit/mlnx/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/neutron/tests/unit/mlnx/test_mlnx_comm_utils.py b/neutron/tests/unit/mlnx/test_mlnx_comm_utils.py
deleted file mode 100644 (file)
index 49f2eac..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-# 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)
diff --git a/neutron/tests/unit/mlnx/test_mlnx_neutron_agent.py b/neutron/tests/unit/mlnx/test_mlnx_neutron_agent.py
deleted file mode 100644 (file)
index e149af2..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-# 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)