Browse Source

Add ML2 VLAN mechanism driver for Brocade MLX and ICX switches

This introduces support for ML2 VLAN operations on Brocade MLX
and ICX switches.  Also including L3 router plugin for MLX.
Partial-Bug: #1420045
Change-Id: I53e9af67001f4c372f687f79c823542ad965a8c3
Shiv Haris 4 years ago
parent
commit
cc97a515a1
28 changed files with 1801 additions and 4 deletions
  1. 0
    0
      networking_brocade/mlx/ml2/__init__.py
  2. 35
    0
      networking_brocade/mlx/ml2/commands.py
  3. 49
    0
      networking_brocade/mlx/ml2/connector_factory.py
  4. 223
    0
      networking_brocade/mlx/ml2/device_connector.py
  5. 0
    0
      networking_brocade/mlx/ml2/fi_ni/__init__.py
  6. 94
    0
      networking_brocade/mlx/ml2/fi_ni/brcd_config.py
  7. 87
    0
      networking_brocade/mlx/ml2/fi_ni/driver_factory.py
  8. 54
    0
      networking_brocade/mlx/ml2/fi_ni/fi_driver.py
  9. 137
    0
      networking_brocade/mlx/ml2/fi_ni/fi_ni_driver.py
  10. 223
    0
      networking_brocade/mlx/ml2/fi_ni/mechanism_brocade_fi_ni.py
  11. 29
    0
      networking_brocade/mlx/ml2/fi_ni/ni_driver.py
  12. 100
    0
      networking_brocade/mlx/ml2/ssh_connector.py
  13. 90
    0
      networking_brocade/mlx/ml2/telnet_connector.py
  14. 0
    0
      networking_brocade/mlx/services/__init__.py
  15. 0
    0
      networking_brocade/mlx/services/l3_router/__init__.py
  16. 0
    0
      networking_brocade/mlx/services/l3_router/brocade/__init__.py
  17. 205
    0
      networking_brocade/mlx/services/l3_router/brocade/l3_router_fi_ni_plugin.py
  18. 0
    0
      networking_brocade/mlx/tests/__init__.py
  19. 0
    0
      networking_brocade/mlx/tests/unit/__init__.py
  20. 0
    0
      networking_brocade/mlx/tests/unit/ml2/__init__.py
  21. 0
    0
      networking_brocade/mlx/tests/unit/ml2/drivers/__init__.py
  22. 0
    0
      networking_brocade/mlx/tests/unit/ml2/drivers/brocade/__init__.py
  23. 202
    0
      networking_brocade/mlx/tests/unit/ml2/drivers/brocade/test_brocade_fi_ni_mechanism_driver.py
  24. 0
    0
      networking_brocade/mlx/tests/unit/services/__init__.py
  25. 0
    0
      networking_brocade/mlx/tests/unit/services/l3_router/__init__.py
  26. 0
    0
      networking_brocade/mlx/tests/unit/services/l3_router/brocade/__init__.py
  27. 265
    0
      networking_brocade/mlx/tests/unit/services/l3_router/brocade/test_brocade_l3_router_fi_ni_plugin.py
  28. 8
    4
      networking_brocade/test_discover/test_discover.py

+ 0
- 0
networking_brocade/mlx/ml2/__init__.py View File


+ 35
- 0
networking_brocade/mlx/ml2/commands.py View File

@@ -0,0 +1,35 @@
1
+# Copyright 2015 Brocade Communications System, Inc.
2
+# All rights reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+
16
+"""
17
+Constants used by device_connector
18
+"""
19
+
20
+ENABLE_TERMINAL_CMD = "en\r"
21
+CONFIGURE_TERMINAL_CMD = "conf t\r"
22
+CONFIGURE_VLAN = "vlan {vlan_id}\r"
23
+CONFIGURE_ETHERNET = "tagged ethe {port_number}\r"
24
+DELETE_CONFIGURED_VLAN = "no vlan {vlan_id}\r"
25
+EXIT = "exit\r"
26
+SHOW_VERSION = "show version\r"
27
+CTRL_C = '\x03'
28
+
29
+"""
30
+Constants used by ni_driver
31
+"""
32
+CONFIGURE_ROUTER_INTERFACE = "router-interface ve {vlan_id}\r"
33
+CONFIGURE_INTERFACE = "interface ve {vlan_id}\r"
34
+CONFIGURE_GATEWAY_IP = "ip address {gateway_ip_addr}\r"
35
+DELETE_ROUTER_INTERFACE = "no router-interface ve {vlan_id}\r"

+ 49
- 0
networking_brocade/mlx/ml2/connector_factory.py View File

@@ -0,0 +1,49 @@
1
+# Copyright 2015 Brocade Communications System, Inc.
2
+# All rights reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#         http://www.apache.org/licenses/LICENSE-2.0
8
+#
9
+#    Unless required by applicable law or agreed to in writing, software
10
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+#    License for the specific language governing permissions and limitations
13
+#    under the License.
14
+
15
+"""
16
+    Connector class used to connect to device.
17
+    Decides which connector to use - TELNET or SSH, based on
18
+    the argument passed
19
+"""
20
+
21
+from oslo_utils import importutils
22
+
23
+
24
+class ConnectorFactory(object):
25
+    """
26
+    Connector class used to connect to device.
27
+    Decides which connector to use - TELNET or SSH, based on
28
+    the argument passed
29
+    """
30
+
31
+    connectors = {"SSH": "networking_brocade.mlx.ml2.ssh_connector."
32
+                  "SSHConnector",
33
+                  "TELNET": "networking_brocade.mlx.ml2."
34
+                            "telnet_connector.TelnetConnector"
35
+                  }
36
+
37
+    def get_connector(self, device_info):
38
+        """
39
+        Returns the connector based on the transport to be used
40
+
41
+        :param:device_info: Device details in the form of dictionary
42
+        :returns: Connector for the device based on the transport parameter.
43
+            Currently only SSH is supported and it is the default connector
44
+            that is returned.
45
+        """
46
+        transport = device_info.get('transport')
47
+        connector = self.connectors.get(transport)
48
+        connection = importutils.import_object(connector, device_info)
49
+        return connection

+ 223
- 0
networking_brocade/mlx/ml2/device_connector.py View File

@@ -0,0 +1,223 @@
1
+# Copyright 2015 Brocade Communications System, Inc.
2
+# All rights reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+
16
+"""
17
+    Connector class used to connect to device.
18
+    Decides which connector to use - TELNET or SSH, based on
19
+    the argument passed
20
+"""
21
+import networking_brocade.mlx.ml2.commands as Commands
22
+from neutron.openstack.common import log as logging
23
+
24
+LOG = logging.getLogger(__name__)
25
+
26
+
27
+class DeviceConnector(object):
28
+
29
+    """
30
+    Connector class used to connect to device.
31
+    Decides which connector to use - TELNET or SSH, based on
32
+    the argument passed
33
+    """
34
+
35
+    def __init__(self, device_info):
36
+        self.host = device_info.get('address')
37
+        self.username = device_info.get('username')
38
+        self.password = device_info.get('password')
39
+        self.transport = device_info.get('transport')
40
+
41
+    def enter_configuration_mode(self):
42
+        """
43
+        Enter configuration mode. First it enters enable mode
44
+        and then to configuration mode. There should be no
45
+        Enable password.
46
+        """
47
+
48
+        self.write(Commands.ENABLE_TERMINAL_CMD)
49
+        self.write(Commands.CONFIGURE_TERMINAL_CMD)
50
+
51
+    def exit_configuration_mode(self):
52
+        """
53
+        Exit Configuration mode.
54
+        """
55
+        self.send_exit(2)
56
+
57
+    def exit_from_device(self):
58
+        """
59
+        Exit Configuration mode and device.
60
+        """
61
+        self.exit_configuration_mode()
62
+        self.send_exit(1)
63
+
64
+    def create_vlan(self, vlanid, ports):
65
+        """
66
+        Creates VLAN and tags the ports to the created VLAN
67
+        :param:vlanid: vlan id to be created
68
+        :param:ports: ports to be tagged to the created vlan
69
+        :returns: Response from the device as a list
70
+        """
71
+        LOG.debug("DeviceConnector:create_vlan:Creating vlan %(vlanid)s on "
72
+                  "device %(host)s", {'vlanid': vlanid, 'host': self.host})
73
+        self.enter_configuration_mode()
74
+        self.write(
75
+            Commands.CONFIGURE_VLAN.format(
76
+                vlan_id=vlanid))
77
+        LOG.debug(
78
+            "Created VLAN with id %(vlanid)s on device %(host)s", {
79
+                'vlanid': vlanid, 'host': self.host})
80
+        for port in ports:
81
+            self.tag_port(port)
82
+            LOG.debug("tagged port %(port)s", {'port': port})
83
+        self.send_exit(1)
84
+        self.exit_from_device()
85
+        return self.read_response()
86
+
87
+    def tag_port(self, port):
88
+        """Tag Ports to the vlan
89
+
90
+        :param:port: Port to be tagged to the vlan
91
+        """
92
+        LOG.debug("DeviceConnector:tag_port:Tagging port %(portid)s on device"
93
+                  " %(host)s", {'portid': port, 'host': self.host})
94
+        self.write(
95
+            Commands.CONFIGURE_ETHERNET.format(
96
+                port_number=port))
97
+
98
+    def delete_vlan(self, vlan_id):
99
+        """Deletes the VLAN from the device
100
+
101
+        :param:vlan_id: vlan id to be deleted
102
+        :returns: Response from the device as a list
103
+        """
104
+        LOG.debug("DeviceConnector:delete_vlan:Deleting VLAN with id "
105
+                  "%(vlan_id)s on device %(host)s", {'vlan_id': vlan_id,
106
+                                                     'host': self.host})
107
+        self.enter_configuration_mode()
108
+        self.write(
109
+            Commands.DELETE_CONFIGURED_VLAN.format(
110
+                vlan_id=vlan_id))
111
+        LOG.debug(
112
+            "Deleted VLAN with id %(vlan_id)s on device %(host)s", {
113
+                'vlan_id': vlan_id, 'host': self.host})
114
+        self.exit_from_device()
115
+        return self.read_response()
116
+
117
+    def get_version(self):
118
+        """Get Firmware Version
119
+
120
+        :returns: Response from the device as a string
121
+        """
122
+        LOG.debug("DeviceConnector:get_version:Executing show version for "
123
+                  "device %(host)s", {'host': self.host})
124
+        self.write(Commands.SHOW_VERSION)
125
+        self.write(Commands.CTRL_C)
126
+        self.send_exit(1)
127
+        return self.read_response(read_lines=False)
128
+
129
+    def create_l3_router(self, vlan_id, gateway_ip_cidr):
130
+        """Configures a Router Interface interface
131
+
132
+        :param:vlan_id: vlan id to which the created router interface will
133
+            be part of
134
+        :param:gateway_ip_cidr: Gateway IP address with network length.
135
+        :returns: Response from the device as a list
136
+        """
137
+
138
+        self.enter_configuration_mode()
139
+        LOG.debug(("DeviceConnector:create_l3_router:Configuring router "
140
+                   "interface with id %(vlanid)s on device %(host)s"),
141
+                  {'vlanid': vlan_id, 'host': self.host})
142
+        self._create_router_interface(vlan_id)
143
+        self._configure_ipaddress(vlan_id, gateway_ip_cidr)
144
+        LOG.debug(("DeviceConnector:create_l3_router:Configured router "
145
+                   "interface with id %(vlanid)s on device %(host)s"),
146
+                  {'vlanid': vlan_id, 'host': self.host})
147
+        self.exit_from_device()
148
+        return self.read_response()
149
+
150
+    def _create_router_interface(self, vlan_id):
151
+        """Create VLAN and Router Interface
152
+
153
+        :param:vlan_id: vlan id to which the created router interface will
154
+            be part of
155
+        """
156
+        LOG.debug("DeviceConnector:_create_router_interface:Creating l3 "
157
+                  "router interface %(vlanid)s on device "
158
+                  "%(host)s", {'vlanid': vlan_id,
159
+                               'host': self.host})
160
+        self.write(
161
+            Commands.CONFIGURE_VLAN.format(
162
+                vlan_id=vlan_id))
163
+        self.write(
164
+            Commands.CONFIGURE_ROUTER_INTERFACE.format(
165
+                vlan_id=vlan_id))
166
+        LOG.debug("DeviceConnector:_create_router_interface:Created l3 "
167
+                  "router interface %(vlanid)s on device "
168
+                  "%(host)s", {'vlanid': vlan_id,
169
+                               'host': self.host})
170
+        self.send_exit(1)
171
+
172
+    def _configure_ipaddress(self, vlan_id, gateway_ip_cidr):
173
+        """Assigns Gateway ip for the configured vlan
174
+
175
+        :param:vlan_id: vlan id to which the created router interface will
176
+            be part of
177
+        :param:gateway_ip_cidr: Gateway IP address with network length.
178
+        """
179
+        LOG.debug("DeviceConnector:_configure_ipaddress:Assigning IP address"
180
+                  " %(ipaddr)s to the router interface %(vlanid)s on device"
181
+                  " %(host)s", {'ipaddr': gateway_ip_cidr,
182
+                                'vlanid': vlan_id,
183
+                                'host': self.host})
184
+        self.write(
185
+            Commands.CONFIGURE_INTERFACE.format(
186
+                vlan_id=vlan_id))
187
+        self.write(
188
+            Commands.CONFIGURE_GATEWAY_IP.format(
189
+                gateway_ip_addr=gateway_ip_cidr))
190
+        LOG.debug("DeviceConnector:_configure_ipaddress:Assigned IP address"
191
+                  " %(ipaddr)s to the router interface %(vlanid)s on device"
192
+                  " %(host)s", {'ipaddr': gateway_ip_cidr,
193
+                                'vlanid': vlan_id,
194
+                                'host': self.host})
195
+        self.send_exit(1)
196
+
197
+    def delete_l3_router(self, vlan_id):
198
+        """
199
+        Deletes Router Interface with the vlan id
200
+
201
+        :param:vlan_id: vlan id to which the created router interface will
202
+            be part of
203
+        :returns: Response from the device as a list
204
+        """
205
+
206
+        self.enter_configuration_mode()
207
+
208
+        LOG.debug(("DeviceConnector:delete_l3_router:Deleting router interface"
209
+                   " %(vlan_id)s from vlan %(vlan_id)s on device %(host)s"),
210
+                  {'vlan_id': vlan_id, 'host': self.host})
211
+        self.write(
212
+            Commands.CONFIGURE_VLAN.format(vlan_id=vlan_id))
213
+        self.write(
214
+            Commands.DELETE_ROUTER_INTERFACE.format(
215
+                vlan_id=vlan_id))
216
+        self.send_exit(1)
217
+
218
+        LOG.debug(("DeviceConnector:delete_l3_router:Deleted router interface"
219
+                   " %(vlan_id)s from vlan %(vlan_id)s on device %(host)s"),
220
+                  {'vlan_id': vlan_id, 'host': self.host})
221
+
222
+        self.exit_from_device()
223
+        return self.read_response()

+ 0
- 0
networking_brocade/mlx/ml2/fi_ni/__init__.py View File


+ 94
- 0
networking_brocade/mlx/ml2/fi_ni/brcd_config.py View File

@@ -0,0 +1,94 @@
1
+# Copyright 2015 Brocade Communications Systems, Inc.
2
+# All rights reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+#
16
+
17
+"""
18
+Parses the brocade ethernet configuration template
19
+"""
20
+
21
+from oslo_config import cfg
22
+
23
+SWITCHES = [
24
+    cfg.StrOpt(
25
+        'switch_names',
26
+        default='',
27
+        help=('Switches connected to the compute nodes'))]
28
+
29
+ML2_BROCADE = [cfg.StrOpt('address', default='',
30
+                          help=('The address of the host to SSH to')),
31
+               cfg.StrOpt('username', default='admin',
32
+                          help=('The SSH username to use')),
33
+               cfg.StrOpt('password', default='password', secret=True,
34
+                          help=('The SSH password to use')),
35
+               cfg.StrOpt('physical_networks', default='',
36
+                          help=('Allowed physical networks')),
37
+               cfg.StrOpt('ports', default='',
38
+                          help=('Ports')),
39
+               cfg.StrOpt('transport', default='SSH',
40
+                          help=('Protocol used to communicate with Switch')),
41
+               cfg.StrOpt('ostype', default=None,
42
+                          help=('OS type of the device. NI or FI')),
43
+               ]
44
+
45
+
46
+class ML2BrocadeConfig(object):
47
+
48
+    """
49
+    Parse the configuration template for Brocade Ethernet ML2 plugin
50
+    """
51
+
52
+    def __init__(self):
53
+        self._brocade_switches = None
54
+        self._brocade_dict = {}
55
+        self._physnet_dict = {}
56
+
57
+    def create_brocade_dictionary(self, isL2=True):
58
+        """
59
+        Create the brocade dictionary.
60
+        Read data from the ml2_conf_brocade.ini.
61
+
62
+        :returns: Two dictionaries, one contains device details with device
63
+            name as key and the details as value. Second dictionary contains
64
+            physical_network as key and list of device names as value
65
+        :param:isL2: Boolean value based on the router type L2 or L3
66
+        """
67
+        if isL2:
68
+            cfg.CONF.register_opts(SWITCHES, 'ml2_brocade_fi_ni')
69
+            self._brocade_switches = (cfg.CONF.ml2_brocade_fi_ni.
70
+                                      switch_names)
71
+        else:
72
+            cfg.CONF.register_opts(SWITCHES, 'l3_brocade_fi_ni')
73
+            self._brocade_switches = (cfg.CONF.l3_brocade_fi_ni.
74
+                                      switch_names)
75
+        switches = self._brocade_switches.split(',')
76
+        for switch in switches:
77
+            switch_info = {}
78
+            switch = switch.strip()
79
+            cfg.CONF.register_opts(ML2_BROCADE, switch)
80
+            for key, value in cfg.CONF._get(switch).items():
81
+                value = value.strip()
82
+                switch_info.update({key: value})
83
+                if "physical_networks" in key:
84
+                    switch_names = self._physnet_dict.get(value)
85
+                    if switch_names is None:
86
+                        switch_names = []
87
+                        switch_names.append(switch)
88
+                    else:
89
+                        switch_names.append(switch)
90
+
91
+                    self._physnet_dict.update({value: switch_names})
92
+
93
+            self._brocade_dict.update({switch: switch_info})
94
+        return self._brocade_dict, self._physnet_dict

+ 87
- 0
networking_brocade/mlx/ml2/fi_ni/driver_factory.py View File

@@ -0,0 +1,87 @@
1
+# Copyright 2015 Brocade Communications Systems, Inc.
2
+# All rights reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+#
16
+
17
+"""
18
+Returns the driver based on the firmware version of the device
19
+"""
20
+from neutron.i18n import _LE
21
+from neutron.i18n import _LI
22
+from neutron.openstack.common import log as logging
23
+from oslo_utils import importutils
24
+
25
+LOG = logging.getLogger(__name__)
26
+CONNECTION_FACTORY = ("networking_brocade.mlx.ml2.connector_factory."
27
+                      "ConnectorFactory")
28
+FI_DRIVER = "networking_brocade.mlx.ml2.fi_ni.fi_driver.FastIronDriver"
29
+NI_DRIVER = "networking_brocade.mlx.ml2.fi_ni.ni_driver.NetIronDriver"
30
+NETIRON = "NetIron"
31
+FASTIRON = "ICX"
32
+FI = "FI"
33
+NI = "NI"
34
+
35
+
36
+class BrocadeDriverFactory(object):
37
+
38
+    """
39
+    Factory class that decides which driver to use based on the
40
+    device type. It uses FastIron driver for ICX devices and
41
+    NetIron driver for MLX devices
42
+    """
43
+
44
+    def get_driver(self, device):
45
+        """
46
+        Returns the driver based on the firmware.
47
+
48
+        :param:device: A dictionary which has the device details
49
+        :returns: Appropriate driver for the device based on the firmware
50
+            version, None otherwise
51
+        :raises: Exception
52
+        """
53
+        driver = None
54
+        address = device.get('address')
55
+        os_type = device.get('ostype')
56
+        if os_type == FI:
57
+            driver = importutils.import_object(FI_DRIVER, device)
58
+        elif os_type == NI:
59
+            driver = importutils.import_object(NI_DRIVER, device)
60
+        else:
61
+            connector = importutils.import_object(CONNECTION_FACTORY
62
+                                                  ).get_connector(device)
63
+            connector.connect()
64
+            version = connector.get_version()
65
+            connector.close_session()
66
+            if NETIRON in version:
67
+                LOG.info(
68
+                    _LI("OS Type of the device %(host)s is as NetIron"),
69
+                    {'host': address})
70
+                driver = importutils.import_object(NI_DRIVER, device)
71
+                device.update({'ostype': NI})
72
+            elif FASTIRON in version:
73
+                LOG.info(
74
+                    _LI("OS Type of the device %(host)s is as FastIron"),
75
+                    {'host': device.get('address')})
76
+                driver = importutils.import_object(FI_DRIVER, device)
77
+                device.update({'ostype': FI})
78
+            else:
79
+                LOG.exception(_LE("Brocade Driver Factory: failed to "
80
+                                  "identify device type for device="
81
+                                  "%(device)s"), {'device': address})
82
+
83
+                raise Exception("Unsupported firmware %(firmware)s for device "
84
+                                "%(host)s", {'firmware': version,
85
+                                             'host': address})
86
+
87
+        return driver

+ 54
- 0
networking_brocade/mlx/ml2/fi_ni/fi_driver.py View File

@@ -0,0 +1,54 @@
1
+# Copyright 2015 Brocade Communications System, Inc.
2
+# All rights reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+
16
+"""
17
+Driver to interact with fastiron devices. Currently has no specific
18
+implementations. Refer EthernetDriver
19
+"""
20
+
21
+from networking_brocade.mlx.ml2.fi_ni.fi_ni_driver import FiNiDriver
22
+
23
+
24
+class FastIronDriver(FiNiDriver):
25
+
26
+    """
27
+    Driver to interact with fastiron devices. Currently has no specific
28
+    implementations. Refer EthernetDriver
29
+    """
30
+
31
+    def __init__(self, device):
32
+        super(FastIronDriver, self).__init__(device)
33
+
34
+    def create_l3_router(self, vlan_id, gateway_ip_cidr):
35
+        """
36
+        Create router interface.
37
+
38
+        :param:vlan_id: vlan id to which the created router interface will
39
+            be part of
40
+        :param:gateway_ip_cidr: Gateway IP address with network length.
41
+        :raises: Exception
42
+        """
43
+        raise Exception("create_l3_router - This operation is currently not"
44
+                        " supported in this platform ")
45
+
46
+    def delete_l3_router(self, vlan_id):
47
+        """
48
+        Remove router interface.
49
+
50
+        :param:vlan_id: vlan id to which the router interface is part of
51
+        :raises: Exception
52
+        """
53
+        raise Exception("delete_l3_router - This operation is currently not"
54
+                        " supported in this platform ")

+ 137
- 0
networking_brocade/mlx/ml2/fi_ni/fi_ni_driver.py View File

@@ -0,0 +1,137 @@
1
+# Copyright 2015 Brocade Communications Systems, Inc.
2
+# All rights reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+
16
+"""
17
+Common driver for both FI and NI devices
18
+"""
19
+
20
+from neutron.openstack.common import log as logging
21
+from oslo_utils import importutils
22
+
23
+CONNECTION_FACTORY = ("networking_brocade.mlx.ml2.connector_factory."
24
+                      "ConnectorFactory")
25
+INVALID_INPUT = "Invalid input"
26
+RANGE_ERROR = "outside of allowed max"
27
+ERROR = "Error"
28
+DUPLICATE_IP = "Error: Duplicate ip address"
29
+
30
+LOG = logging.getLogger(__name__)
31
+
32
+
33
+class FiNiDriver(object):
34
+
35
+    """
36
+    Base driver which handles all common CLI Commands for both FI and NI
37
+    devices
38
+    """
39
+
40
+    def __init__(self, device):
41
+        self.device = device
42
+
43
+    def create_network(self, vlan_id, ports):
44
+        """
45
+        Invoked when user creates a VLAN network. Triggers creation of
46
+        VLAN on the devices listed in the configuration template
47
+
48
+        :param:vlan_id: vlan id to add
49
+        :param:ports: ports to be tagged to the above vlan id
50
+        :raises: Exception
51
+        """
52
+
53
+        connector = importutils.import_object(
54
+            CONNECTION_FACTORY).get_connector(self.device)
55
+        connector.connect()
56
+        response = connector.create_vlan(vlan_id, ports)
57
+        msg = self.get_error_msg(response)
58
+        if msg:
59
+            raise Exception(reason=msg)
60
+        connector.close_session()
61
+
62
+    def delete_network(self, vlan_id):
63
+        """
64
+        Invoked when user deletes a VLAN network. Triggers deletion of
65
+        VLAN on the devices listed in the configuration template
66
+
67
+        :param:vlan_id: vlan id to delete
68
+        """
69
+
70
+        connector = importutils.import_object(
71
+            CONNECTION_FACTORY).get_connector(self.device)
72
+        connector.connect()
73
+        connector.delete_vlan(vlan_id)
74
+        connector.close_session()
75
+
76
+    def add_router_interface(self, vlan_id, gateway_ip_cidr):
77
+        """
78
+        Invoked when user creates router interface. Triggers creation of
79
+        router interface based on the VLAN ID.
80
+
81
+        :param:vlan_id: vlan id to which the created router interface will
82
+            be part of
83
+        :param:gateway_ip_cidr: Gateway IP address with network length.
84
+        :raises: Exception
85
+        """
86
+        connector = importutils.import_object(
87
+            CONNECTION_FACTORY).get_connector(self.device)
88
+        connector.connect()
89
+        response = connector.create_l3_router(vlan_id, gateway_ip_cidr)
90
+        msg = self.get_error_msg(response)
91
+        if msg:
92
+            raise Exception(reason=msg)
93
+        connector.close_session()
94
+
95
+    def remove_router_interface(self, vlan_id):
96
+        """
97
+        Invoked when user deletes router interface. Triggers deletion of
98
+        router interface based on the VLAN ID.
99
+
100
+        :param:vlan_id: vlan id to which the router interface is part of
101
+        :raises: Exception
102
+        """
103
+        connector = importutils.import_object(
104
+            CONNECTION_FACTORY).get_connector(self.device)
105
+        connector.connect()
106
+        response = connector.delete_l3_router(vlan_id)
107
+        msg = self.get_error_msg(response)
108
+        if msg:
109
+            raise Exception(reason=msg)
110
+        connector.close_session()
111
+
112
+    def get_error_msg(self, response_list):
113
+        """
114
+        Checks for error in response
115
+
116
+        :param:response_list: List of response from device
117
+        :returns:msg - Message will contain error description in case of an
118
+            error, None otherwise.
119
+        """
120
+        msg = None
121
+        for response in response_list:
122
+            if INVALID_INPUT in response:
123
+                msg = _("Ethernet Driver : Create"
124
+                        "network failed: error= Invalid Input")
125
+                LOG.error(msg)
126
+                break
127
+            elif RANGE_ERROR in response_list:
128
+                msg = _("Configuring router interface failed: "
129
+                        "ve out of range error")
130
+                LOG.error(msg)
131
+                break
132
+            elif ERROR in response_list:
133
+                msg = _("Configuring router interface failed: "
134
+                        "vlan not associated to router interface")
135
+                LOG.error(msg)
136
+                break
137
+        return msg

+ 223
- 0
networking_brocade/mlx/ml2/fi_ni/mechanism_brocade_fi_ni.py View File

@@ -0,0 +1,223 @@
1
+# Copyright 2015 Brocade Communications Systems, Inc.
2
+# All rights reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+
16
+
17
+"""Implementation of Brocade ML2 Mechanism driver for ICX and MLX."""
18
+
19
+from neutron.i18n import _LE
20
+from neutron.i18n import _LI
21
+from neutron.openstack.common import log as logging
22
+from neutron.plugins.ml2.common import exceptions as ml2_exc
23
+from neutron.plugins.ml2 import driver_api
24
+from oslo_utils import importutils
25
+
26
+MECHANISM_VERSION = 0.1
27
+BROCADE_CONFIG = ("networking_brocade.mlx.ml2.fi_ni.brcd_config."
28
+                  "ML2BrocadeConfig")
29
+DRIVER_FACTORY = ("networking_brocade.mlx.ml2.fi_ni."
30
+                  "driver_factory.BrocadeDriverFactory")
31
+LOG = logging.getLogger(__name__)
32
+
33
+
34
+class BrocadeFiNiMechanism(driver_api.MechanismDriver):
35
+
36
+    """
37
+    ML2 Mechanism driver for Brocade ICX and MLX devices. This is the upper
38
+    layer driver class that interfaces to lower layer (SSH/TELNET) below.
39
+
40
+    """
41
+
42
+    def __init__(self):
43
+        self._driver = None
44
+        self._physical_networks = None
45
+        self._switch = None
46
+        self._driver_map = {}
47
+        self._devices = {}
48
+        self.initialize()
49
+
50
+    def initialize(self):
51
+        """Initialize of variables needed by this class."""
52
+
53
+        self._devices, self._physical_networks = importutils.import_object(
54
+            BROCADE_CONFIG).create_brocade_dictionary()
55
+
56
+    def create_network_precommit(self, mech_context):
57
+        """No-op."""
58
+        pass
59
+
60
+    def create_network_postcommit(self, mech_context):
61
+        """Create VLAN on the switch.
62
+
63
+        :param:mech_context: Details about the network to be created
64
+        :raise: Exception
65
+        """
66
+
67
+        LOG.debug("create_network_postcommit: called")
68
+        network = mech_context.current
69
+        network_id = network['id']
70
+        tenant_id = network['tenant_id']
71
+        segments = mech_context.network_segments
72
+        network_type = segments[0]['network_type']
73
+        vlan_id = segments[0]['segmentation_id']
74
+        physical_network = segments[0]['physical_network']
75
+        try:
76
+            devices = self._physical_networks.get(physical_network)
77
+            for device in devices:
78
+                device_info = self._devices.get(device)
79
+                address = device_info.get('address')
80
+                driver = None
81
+                try:
82
+                    driver = self._get_driver(device)
83
+                except Exception as e:
84
+                    LOG.exception(_LE("BrocadeFiNiMechanism: create_network"
85
+                                      "_postcommit failed while configuring "
86
+                                      "device %(host)s exception=%(error)s"),
87
+                                  {'host': address,
88
+                                   'error': e.args})
89
+                    raise ml2_exc.MechanismDriverError()
90
+                # Proceed only if the driver is not None
91
+                if driver is not None:
92
+                    driver.create_network(
93
+                        vlan_id,
94
+                        device_info.get("ports").split(","))
95
+        except Exception as e:
96
+            LOG.exception(
97
+                _LE("Brocade FI/NI driver: create_network_postcommit failed"
98
+                    "Error = %(error)s"), {'error': e.args})
99
+            raise ml2_exc.MechanismDriverError()
100
+        LOG.info(_LI("BrocadeFiNiMechanism:created_network_postcommit: "
101
+                     "%(network_id)s of network type = %(network_type)s with "
102
+                     "vlan = %(vlan_id)s for tenant %(tenant_id)s"),
103
+                 {'network_id': network_id,
104
+                  'network_type': network_type,
105
+                  'vlan_id': vlan_id,
106
+                  'tenant_id': tenant_id})
107
+
108
+    def delete_network_precommit(self, mech_context):
109
+        """No-op."""
110
+        pass
111
+
112
+    def delete_network_postcommit(self, mech_context):
113
+        """Delete network which translates to removing vlan
114
+        from the switch.
115
+
116
+        :param:mech_context: Details about the network to be created
117
+        :raise: MechanismDriverError
118
+        """
119
+
120
+        LOG.debug("delete_network_postcommit: called")
121
+        network = mech_context.current
122
+        network_id = network['id']
123
+        vlan_id = network['provider:segmentation_id']
124
+        tenant_id = network['tenant_id']
125
+        segments = mech_context.network_segments
126
+        segment = segments[0]
127
+        physical_network = segment['physical_network']
128
+        try:
129
+            devices = self._physical_networks.get(physical_network)
130
+            for device in devices:
131
+                driver = self._get_driver(device)
132
+                driver.delete_network(vlan_id)
133
+        except Exception:
134
+            LOG.exception(
135
+                _LE("BrocadeFiNiMechanism: failed to delete network"))
136
+            raise ml2_exc.MechanismDriverError()
137
+
138
+        LOG.info(_LI("BrocadeFiNiMechanism: delete network (postcommit): "
139
+                     "%(network_id)s with vlan = %(vlan_id)s for tenant "
140
+                     "%(tenant_id)s"), {'network_id': network_id,
141
+                                        'vlan_id': vlan_id,
142
+                                        'tenant_id': tenant_id})
143
+
144
+    def update_network_precommit(self, mech_context):
145
+        """Noop now, it is left here for future."""
146
+        pass
147
+
148
+    def update_network_postcommit(self, mech_context):
149
+        """Noop now, it is left here for future."""
150
+        pass
151
+
152
+    def create_port_precommit(self, mech_context):
153
+        """Noop now, it is left here for future."""
154
+        pass
155
+
156
+    def create_port_postcommit(self, mech_context):
157
+        """Noop now, it is left here for future."""
158
+        pass
159
+
160
+    def delete_port_precommit(self, mech_context):
161
+        """Noop now, it is left here for future."""
162
+        pass
163
+
164
+    def delete_port_postcommit(self, mech_context):
165
+        """Noop now, it is left here for future."""
166
+        pass
167
+
168
+    def update_port_precommit(self, mech_context):
169
+        """Noop now, it is left here for future."""
170
+        LOG.debug("update_port_precommit(self: called")
171
+
172
+    def update_port_postcommit(self, mech_context):
173
+        """Noop now, it is left here for future."""
174
+        LOG.debug("update_port_postcommit: called")
175
+
176
+    def create_subnet_precommit(self, mech_context):
177
+        """Noop now, it is left here for future."""
178
+        LOG.debug("create_subnetwork_precommit: called")
179
+
180
+    def create_subnet_postcommit(self, mech_context):
181
+        """Noop now, it is left here for future."""
182
+        LOG.debug("create_subnetwork_postcommit: called")
183
+
184
+    def delete_subnet_precommit(self, mech_context):
185
+        """Noop now, it is left here for future."""
186
+        LOG.debug("delete_subnetwork_precommit: called")
187
+
188
+    def delete_subnet_postcommit(self, mech_context):
189
+        """Noop now, it is left here for future."""
190
+        LOG.debug("delete_subnetwork_postcommit: called")
191
+
192
+    def update_subnet_precommit(self, mech_context):
193
+        """Noop now, it is left here for future."""
194
+        LOG.debug("update_subnet_precommit(self: called")
195
+
196
+    def update_subnet_postcommit(self, mech_context):
197
+        """Noop now, it is left here for future."""
198
+        LOG.debug("update_subnet_postcommit: called")
199
+
200
+    def _get_driver(self, device):
201
+        """
202
+        Gets the driver based on the firmware version of the device
203
+
204
+        :param:device: A dictionary which contains details of all the devices
205
+            parsed from the configuration template
206
+        :raise: Exception
207
+        """
208
+        driver = self._driver_map.get(device)
209
+        if driver is None:
210
+            driver_factory = importutils.import_object(DRIVER_FACTORY)
211
+            device_info = self._devices.get(device)
212
+            address = device_info.get('address')
213
+            try:
214
+                driver = driver_factory.get_driver(device_info)
215
+            except Exception as e:
216
+                LOG.exception(_LE("BrocadeFiNiMechanism:_get_driver failed for"
217
+                                  "device %(host)s: Error = %(error)s"),
218
+                              {'host': address,
219
+                               'error': e.args})
220
+                raise Exception(_("BrocadeFiNiMechanism: "
221
+                                  "Failed to get driver"))
222
+            self._driver_map.update({device: driver})
223
+        return driver

+ 29
- 0
networking_brocade/mlx/ml2/fi_ni/ni_driver.py View File

@@ -0,0 +1,29 @@
1
+# Copyright 2015 Brocade Communications Systems, Inc.
2
+# All rights reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+
16
+"""
17
+Driver to interact with netiron devices. Currently has no specific
18
+implementations. Refer FiNiDriver
19
+"""
20
+
21
+from networking_brocade.mlx.ml2.fi_ni.fi_ni_driver import FiNiDriver
22
+
23
+
24
+class NetIronDriver(FiNiDriver):
25
+
26
+    """
27
+    Driver to interact with netiron devices. Currently has no specific
28
+    implementations. Refer FiNiDriver
29
+    """

+ 100
- 0
networking_brocade/mlx/ml2/ssh_connector.py View File

@@ -0,0 +1,100 @@
1
+# Copyright 2015 Brocade Communications System, Inc.
2
+# All rights reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+
16
+""" Implementation of SSH Connector"""
17
+
18
+import networking_brocade.mlx.ml2.commands as Commands
19
+from networking_brocade.mlx.ml2.device_connector import (
20
+    DeviceConnector as DevConn)
21
+from neutron.i18n import _LE
22
+from neutron.openstack.common import log as logging
23
+import paramiko
24
+LOG = logging.getLogger(__name__)
25
+WRITE = "wb"
26
+READ = "rb"
27
+
28
+
29
+class SSHConnector(DevConn):
30
+
31
+    """
32
+    Uses SSH to connect to device
33
+    """
34
+
35
+    def connect(self):
36
+        """
37
+        Connect to the device
38
+        """
39
+        try:
40
+            self.connector = paramiko.SSHClient()
41
+            self.connector.set_missing_host_key_policy(
42
+                paramiko.AutoAddPolicy())
43
+            self.connector.connect(
44
+                hostname=self.host,
45
+                username=self.username,
46
+                password=self.password)
47
+
48
+            channel = self.connector.invoke_shell()
49
+            self.stdin_stream = channel.makefile(WRITE)
50
+            self.stdout_stream = channel.makefile(READ)
51
+            self.stderr_stream = channel.makefile(READ)
52
+
53
+        except Exception as e:
54
+            LOG.exception(_LE("Connect failed to switch %(host)s with error"
55
+                              " %(error)s"),
56
+                          {'host': self.host, 'error': e.args})
57
+            raise Exception(_("Connection Failed"))
58
+
59
+    def write(self, command):
60
+        """
61
+        Write from input stream to device
62
+
63
+        :param:command: Command to be executed on the device
64
+        """
65
+        self.stdin_stream.write(command)
66
+        self.stdin_stream.flush()
67
+
68
+    def read_response(self, read_lines=True):
69
+        """Read the response from the output stream.
70
+
71
+        :param:read_lines: Boolean value which indicated to read multiple line
72
+            or single line. It is true by default.
73
+        :returns: Response from the device as list when read_lines is True or
74
+            string when read_lines is false.
75
+        """
76
+        response = None
77
+        if read_lines:
78
+            response = self.stdout_stream.readlines()
79
+        else:
80
+            response = self.stdout_stream.read()
81
+        return response
82
+
83
+    def send_exit(self, count):
84
+        """Send Exit command.
85
+
86
+        :param:count: Indicates number of times to execute exit command
87
+        """
88
+        index = 0
89
+        while index < count:
90
+            self.stdin_stream.write(Commands.EXIT)
91
+            self.stdin_stream.flush()
92
+            index += 1
93
+
94
+    def close_session(self):
95
+        """Close SSH session."""
96
+        if self.connector:
97
+            self.stdin_stream.close()
98
+            self.stdout_stream.close()
99
+            self.stderr_stream.close()
100
+            self.connector.close()

+ 90
- 0
networking_brocade/mlx/ml2/telnet_connector.py View File

@@ -0,0 +1,90 @@
1
+# Copyright 2015 Brocade Communications System, Inc.
2
+# All rights reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+
16
+""" Implementation of Telnet Connector """
17
+
18
+import telnetlib
19
+
20
+from networking_brocade.mlx.ml2.device_connector import (
21
+    DeviceConnector as DevConn)
22
+from neutron.i18n import _LE
23
+from neutron.openstack.common import log as logging
24
+
25
+LOG = logging.getLogger(__name__)
26
+
27
+TELNET_PORT = 23
28
+LOGIN_USER_TOKEN = "Name:"
29
+LOGIN_PASS_TOKEN = "Password:"
30
+PATTERN_EN_AUTH = '.*\\r\\n.*Name\\:$'
31
+SUPER_USER_AUTH = '^Password\\:$'
32
+TERMINAL_LENGTH = "terminal length 0"
33
+
34
+END_OF_LINE = "\r"
35
+TELNET_TERMINAL = ">"
36
+CONFIGURE_TERMINAL = "#"
37
+
38
+MIN_TIMEOUT = 2
39
+AVG_TIMEOUT = 4
40
+MAX_TIMEOUT = 8
41
+
42
+
43
+class TelnetConnector(DevConn):
44
+
45
+    """
46
+    Uses Telnet to connect to device
47
+    """
48
+
49
+    def connect(self):
50
+        """
51
+        Connect to device via Telnet
52
+        """
53
+        try:
54
+            self.connector = telnetlib.Telnet(host=self.host, port=TELNET_PORT)
55
+            self.connector.read_until(LOGIN_USER_TOKEN, MIN_TIMEOUT)
56
+            self.connector.write(self.username + END_OF_LINE)
57
+            self.connector.read_until(LOGIN_PASS_TOKEN, AVG_TIMEOUT)
58
+            self.connector.write(self.password + END_OF_LINE)
59
+            self.connector.read_until(TELNET_TERMINAL, MAX_TIMEOUT)
60
+        except Exception as e:
61
+            LOG.exception(_LE("Connect failed to switch %(host)s with error"
62
+                              " %(error)s"),
63
+                          {'host': self.host, 'error': e.args})
64
+            raise Exception(_("Connection Failed"))
65
+
66
+    def write(self, command):
67
+        """
68
+        Write from input stream to device
69
+
70
+        :param:command: Command to be executed on the device
71
+        """
72
+        self.connector.write(command)
73
+
74
+    def read_response(self, read_lines=True):
75
+        """Read the response from the output stream.
76
+
77
+        :param:read_lines: This is used only by the SSH connector.
78
+        :returns: Response from the device as list.
79
+        """
80
+        return self.connector.read_until(CONFIGURE_TERMINAL, MIN_TIMEOUT)
81
+
82
+    def close_session(self):
83
+        """Close TELNET session."""
84
+        if self.connector:
85
+            self.connector.close()
86
+            self.connector = None
87
+
88
+    def send_exit(self, count):
89
+        """No operation. Used by SSH connector only."""
90
+        pass

+ 0
- 0
networking_brocade/mlx/services/__init__.py View File


+ 0
- 0
networking_brocade/mlx/services/l3_router/__init__.py View File


+ 0
- 0
networking_brocade/mlx/services/l3_router/brocade/__init__.py View File


+ 205
- 0
networking_brocade/mlx/services/l3_router/brocade/l3_router_fi_ni_plugin.py View File

@@ -0,0 +1,205 @@
1
+# Copyright 2015 Brocade Communications System, Inc.
2
+# All rights reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+#
16
+
17
+
18
+"""Implementation of Brocade L3RouterPlugin for NI devices."""
19
+
20
+
21
+from neutron.i18n import _LE
22
+from neutron.i18n import _LI
23
+from neutron.openstack.common import log as logging
24
+from neutron.plugins.ml2.drivers.brocade.db import models as brocade_db
25
+from neutron.services.l3_router import l3_router_plugin as router
26
+from oslo_utils import importutils
27
+
28
+LOG = logging.getLogger(__name__)
29
+NI = "neutron.plugins.ml2.drivers.brocade.fi_ni.ni_driver.NetIronDriver"
30
+BROCADE_CONFIG = ("neutron.plugins.ml2.drivers.brocade.fi_ni.brcd_config."
31
+                  "ML2BrocadeConfig")
32
+DRIVER_FACTORY = ("neutron.plugins.ml2.drivers.brocade.fi_ni."
33
+                  "driver_factory.BrocadeDriverFactory")
34
+# Property to identify which device type to use
35
+# Currently supports only NI devices. If FI devices needs to be
36
+# supported add FI to the list as shown below:
37
+# ['NI', 'FI']
38
+ROUTER_DEVICE_TYPE = ['NI']
39
+
40
+
41
+class BrocadeFiNiL3RouterPlugin(router.L3RouterPlugin):
42
+
43
+    """
44
+    SVI Mechanism driver for Brocade ICX and MLX devices. This will take care
45
+    of Create/Delete router interface to introduce L3 support.
46
+    """
47
+
48
+    def __init__(self):
49
+        """Initialize Brocade Plugin
50
+
51
+        Specify switch address and db configuration.
52
+        """
53
+        self._driver_map = {}
54
+        self._router_devices_map = {}
55
+        super(BrocadeFiNiL3RouterPlugin, self).__init__()
56
+        self.brocade_init()
57
+
58
+    def brocade_init(self):
59
+        """Brocade specific initialization. Parses the configuration template
60
+        and stores all the device information in a dictionary. Then it filters
61
+        the router devices from it.
62
+        """
63
+        LOG.debug("BrocadeFiNiL3RouterPlugin::brocade_init()")
64
+
65
+        self._devices, self._physical_networks = importutils.import_object(
66
+            BROCADE_CONFIG).create_ml2_brocade_dictionary()
67
+        self._filter_router_devices(self._devices)
68
+
69
+    def add_router_interface(self, context, router_id, interface_info):
70
+        """Adds router interface to a vlan on NI device and assigns ip
71
+         address to configure l3 router interface.
72
+        """
73
+
74
+        self.add_remove_router_interface(context, router_id,
75
+                                         interface_info,
76
+                                         True)
77
+
78
+    def remove_router_interface(self, context, router_id, interface_info):
79
+        """Removes router interface on NI device
80
+        """
81
+        return self.add_remove_router_interface(context, router_id,
82
+                                                interface_info,
83
+                                                False)
84
+
85
+    def add_remove_router_interface(self, context, router_id, interface_info,
86
+                                    is_add):
87
+        """
88
+        Add/Remove router interface on NI device
89
+
90
+        :param:context: Contains the network details
91
+        :param:router_id: The router ID
92
+        :param:interface_info: Contains interface details
93
+        :param:is_add: True for add operation, False for remove operation.
94
+        :raises: Exception
95
+        """
96
+        operation = None
97
+        method = None
98
+        if is_add is True:
99
+            method = "add_router_interface"
100
+            operation = "Add"
101
+        else:
102
+            method = "remove_router_interface"
103
+            operation = "Remove"
104
+
105
+        with context.session.begin(subtransactions=True):
106
+            info = getattr(super(BrocadeFiNiL3RouterPlugin, self),
107
+                           method)(context, router_id, interface_info)
108
+            interface_info = info
109
+            subnet = self._core_plugin._get_subnet(context,
110
+                                                   interface_info["subnet_id"])
111
+            vlan_id, gateway_ip_cidr = self._get_network_info(context, subnet)
112
+            for device in self._router_devices_map:
113
+                device_info = self._router_devices_map.get(device)
114
+                driver = self._driver_map.get(device)
115
+                address = device_info.get('address')
116
+                LOG.info(_LI("BrocadeFiNiL3RouterPlugin:Before %(op)s l3 "
117
+                             "router to vlan %(vlan_id)s with ip %(gatewayip)s"
118
+                             "on device "
119
+                             "%(host)s"), {'op': operation,
120
+                                           'vlan_id': vlan_id,
121
+                                           'gatewayip': gateway_ip_cidr,
122
+                                           'host': address})
123
+                try:
124
+                    if is_add is True:
125
+                        getattr(driver, method)(vlan_id, gateway_ip_cidr)
126
+                    else:
127
+                        getattr(driver, method)(vlan_id)
128
+                except Exception as e:
129
+                    LOG.exception(_LE("BrocadeFiNiL3RouterPlugin: failed to"
130
+                                      " %(op)s l3 router interface for "
131
+                                      "device= %(device)s exception : "
132
+                                      "%(error)s"), {'op': operation,
133
+                                                     'device': address,
134
+                                                     'error': e.args})
135
+                    raise Exception(
136
+                        _("BrocadeFiNiL3RouterPlugin: %(op)s router "
137
+                          "interface failed"), {'op': operation})
138
+                LOG.info(_LI("BrocadeFiNiL3RouterPlugin:%(op)sed router "
139
+                             "interface in vlan = %(vlan_id)s with ip address"
140
+                             " %(gatewayip)s on device %(host)s "
141
+                             "successful"), {'op': operation,
142
+                                             'vlan_id': vlan_id,
143
+                                             'gatewayip': gateway_ip_cidr,
144
+                                             'host': address})
145
+        return True
146
+
147
+    def _filter_router_devices(self, devices):
148
+        """
149
+        Filters devices using the firmware types in the ROUTER_DEVICE_TYPE from
150
+        devices. It stores the filtered devices in another dictionary
151
+        _router_devices_map. This method also identifies the driver for the
152
+        filtered devices and stores them in the map _driver_map with device
153
+        name as key and the driver as value.
154
+
155
+        :param:devices: Contains device name as key and device information
156
+            in a map with key-value pairs as value.
157
+        :raises: Exception
158
+        """
159
+        driver_factory = importutils.import_object(DRIVER_FACTORY)
160
+        for device in devices:
161
+            device_info = devices.get(device)
162
+            address = device_info.get('address')
163
+            try:
164
+                driver = driver_factory.get_driver(device_info)
165
+                os_type = device_info.get('ostype')
166
+                if driver is not None and os_type in ROUTER_DEVICE_TYPE:
167
+                    self._router_devices_map.update({device: device_info})
168
+                    self._driver_map.update({device: driver})
169
+            except Exception as e:
170
+                LOG.exception(_LE("BrocadeFiNiL3RouterPlugin:"
171
+                                  "_filter_router_devices: Error while getting"
172
+                                  " driver : device - %(host)s: %(error)s"),
173
+                              {'host': address, 'error': e.args})
174
+                raise Exception(_("BrocadeFiNiL3RouterPlugin:"
175
+                                  "_filter_router_devices failed for "
176
+                                  "device %(host)s"), {'host': address})
177
+
178
+    def _get_network_info(self, context, subnet):
179
+        """
180
+        Gets the network info from the context and brocade db
181
+
182
+        :param:context: Contains the network details
183
+        :param:subnet: Contains the subnet details
184
+        :returns:vlan_id - VLAN ID of the network
185
+        :returns:gateway_ip_cidr - Gateway IP address with network length.
186
+            Example: 1.1.1.1/24
187
+        """
188
+        cidr = subnet["cidr"]
189
+        net_addr, net_len = self.net_addr(cidr)
190
+        gateway_ip = subnet["gateway_ip"]
191
+        network_id = subnet['network_id']
192
+        bnet = brocade_db.get_network(context, network_id)
193
+        vlan_id = bnet['vlan']
194
+        gateway_ip_cidr = gateway_ip + '/' + str(net_len)
195
+
196
+        return vlan_id, gateway_ip_cidr
197
+
198
+    @staticmethod
199
+    def net_addr(addr):
200
+        """Get network address prefix and length from a given address."""
201
+        if addr is None:
202
+            return None, None
203
+        nw_addr, nw_len = addr.split('/')
204
+        nw_len = int(nw_len)
205
+        return nw_addr, nw_len

+ 0
- 0
networking_brocade/mlx/tests/__init__.py View File


+ 0
- 0
networking_brocade/mlx/tests/unit/__init__.py View File


+ 0
- 0
networking_brocade/mlx/tests/unit/ml2/__init__.py View File


+ 0
- 0
networking_brocade/mlx/tests/unit/ml2/drivers/__init__.py View File


+ 0
- 0
networking_brocade/mlx/tests/unit/ml2/drivers/brocade/__init__.py View File


+ 202
- 0
networking_brocade/mlx/tests/unit/ml2/drivers/brocade/test_brocade_fi_ni_mechanism_driver.py View File

@@ -0,0 +1,202 @@
1
+# Copyright 2015 Brocade Communications Systems, Inc.
2
+#
3
+# Licensed under the Apache License, Version 2.0 (the "License");
4
+# you may not use this file except in compliance with the License.
5
+# You may obtain a copy of the License at
6
+#
7
+#    http://www.apache.org/licenses/LICENSE-2.0
8
+#
9
+# Unless required by applicable law or agreed to in writing, software
10
+# distributed under the License is distributed on an "AS IS" BASIS,
11
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12
+# implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+import mock
17
+from networking_brocade.mlx.ml2.fi_ni import (
18
+    mechanism_brocade_fi_ni as brocadefinimechanism)
19
+from neutron import context
20
+from neutron.openstack.common import log as logging
21
+from neutron.tests import base
22
+from oslo_utils import importutils
23
+
24
+LOG = logging.getLogger(__name__)
25
+MECHANISM_NAME = ('networking_brocade.mlx.ml2.fi_ni.'
26
+                  'mechanism_brocade_fi_ni.BrocadeFiNiMechanism')
27
+FAKE_PHYSICAL_NETWORK = 'physnet1'
28
+
29
+config_map = {}
30
+
31
+devices = {
32
+    'icx': {
33
+        'address': '1.1.1.1',
34
+        'username': 'admin',
35
+        'password': 'pass',
36
+        'physical_networks': 'physnet1',
37
+        'ports': '1/1/1,1/1/2',
38
+        'os_type': 'FI'
39
+    },
40
+    'mlx': {
41
+        'address': '2.2.2.2',
42
+        'username': 'admin',
43
+        'password': 'pass',
44
+        'physical_networks': 'physnet1',
45
+        'ports': '2/1,2/2',
46
+        'os_type': 'NI'
47
+    },
48
+}
49
+
50
+physical_networks = {
51
+    'physnet1': ['icx', 'mlx']
52
+}
53
+
54
+actual_config_map = {
55
+    '1.1.1.1': {
56
+        200: ['1/1/1', '1/1/2']
57
+    },
58
+    '2.2.2.2': {
59
+        200: ['2/1', '2/2']
60
+    }
61
+}
62
+
63
+empty_config_map = {
64
+}
65
+
66
+
67
+class TestBrocadeFiNiMechDriver(base.BaseTestCase):
68
+
69
+    """
70
+    Test Brocade FI/NI mechanism driver.
71
+    """
72
+
73
+    def setUp(self):
74
+        _mechanism_name = MECHANISM_NAME
75
+
76
+        def mocked_initialize(self):
77
+            self._devices = devices
78
+            self._physical_networks = physical_networks
79
+
80
+        with mock.patch.object(brocadefinimechanism.BrocadeFiNiMechanism,
81
+                        'initialize', new=mocked_initialize):
82
+            super(TestBrocadeFiNiMechDriver, self).setUp()
83
+            self.mechanism_driver = importutils.import_object(_mechanism_name)
84
+
85
+    @mock.patch.object(brocadefinimechanism.BrocadeFiNiMechanism,
86
+                       '_get_driver')
87
+    def test_create_network_postcommit(self, mock_driver):
88
+        """
89
+        Test create network with correct input values
90
+        """
91
+        mock_driver.side_effect = self.side_effect
92
+        ctx = self._get_network_context('physnet1', 'vlan')
93
+        self.mechanism_driver.create_network_postcommit(ctx)
94
+        global config_map
95
+        self.assertDictSupersetOf(config_map, actual_config_map)
96
+
97
+    @mock.patch.object(brocadefinimechanism.BrocadeFiNiMechanism,
98
+                       '_get_driver')
99
+    def test_delete_network_postcommit(self, mock_driver):
100
+        """
101
+        Test delete network
102
+        """
103
+        global config_map
104
+        config_map = actual_config_map
105
+        mock_driver.side_effect = self.side_effect
106
+        ctx = self._get_network_context('physnet1', 'vlan')
107
+        self.mechanism_driver.delete_network_postcommit(ctx)
108
+        self.assertDictSupersetOf(config_map, empty_config_map)
109
+
110
+    def _get_network_context(self, physnet, network_type):
111
+        """
112
+        Create mock network context
113
+        """
114
+        network = {
115
+            'id': 1,
116
+            'name': 'private',
117
+            'tenant_id': 1,
118
+            'vlan': 200,
119
+            'network_type': network_type,
120
+            'provider:segmentation_id': 200
121
+        }
122
+
123
+        network_segments = [{
124
+            'id': 1,
125
+            'segmentation_id': 200,
126
+            'network_type': network_type,
127
+            'physical_network': physnet
128
+        }]
129
+
130
+        _plugin_context = context.get_admin_context()
131
+        return FakeNetworkContext(network, network_segments, _plugin_context)
132
+
133
+    def side_effect(self, dev_name):
134
+        """
135
+        Mock _get_driver method and return FakeDriver
136
+        """
137
+        device = devices.get(dev_name)
138
+        return FakeDriver(device)
139
+
140
+    def side_effect_error(self, dev_name):
141
+        """
142
+        Mock _get_driver method and return FakeDriver
143
+        """
144
+        device = devices.get(dev_name)
145
+        return FakeDriver(device, error=True)
146
+
147
+
148
+class FakeNetworkContext(object):
149
+
150
+    """To generate network context for testing purposes only."""
151
+
152
+    def __init__(self, network, segments=None, original_network=None):
153
+        self._network = network
154
+        self._original_network = original_network
155
+        self._segments = segments
156
+
157
+    @property
158
+    def current(self):
159
+        return self._network
160
+
161
+    @property
162
+    def _plugin_context(self):
163
+        return self._original_network
164
+
165
+    @property
166
+    def network_segments(self):
167
+        return self._segments
168
+
169
+
170
+class FakeDriver(object):
171
+    """
172
+    Fake driver which will implement create and delete
173
+    network. Create network will update the global dictionary with
174
+    the address of the device along with vlan and ports to be tagged.
175
+    Example : {'10.10.23.1':{'200':['1/1/1', '1/1/2']}}
176
+
177
+    Delete network will delete the corresponding entry from the dictionary.
178
+    """
179
+
180
+    def __init__(self, device, error=False):
181
+        self.error = error
182
+        self.device = device
183
+        self.address = device.get('address')
184
+
185
+    def create_network(self, vlan_id, ports):
186
+        if self.error:
187
+            raise Exception("Invalid Input - Create network failed")
188
+        vlan_map = {}
189
+        vlan_map.update({vlan_id: ports})
190
+        global config_map
191
+        config_map.update({self.address: vlan_map})
192
+
193
+    def delete_network(self, vlan_id):
194
+        global config_map
195
+        vlan_map = config_map.pop(self.address, None)
196
+        if vlan_map is None:
197
+            raise Exception("No Address")
198
+        else:
199
+            vlan_map.pop(vlan_id, None)
200
+
201
+    def _get_config_map(self):
202
+        return self.config_map

+ 0
- 0
networking_brocade/mlx/tests/unit/services/__init__.py View File


+ 0
- 0
networking_brocade/mlx/tests/unit/services/l3_router/__init__.py View File


+ 0
- 0
networking_brocade/mlx/tests/unit/services/l3_router/brocade/__init__.py View File


+ 265
- 0
networking_brocade/mlx/tests/unit/services/l3_router/brocade/test_brocade_l3_router_fi_ni_plugin.py View File

@@ -0,0 +1,265 @@
1
+# Copyright 2015 Brocade Communications Systems, Inc
2
+#
3
+# Licensed under the Apache License, Version 2.0 (the "License");
4
+# you may not use this file except in compliance with the License.
5
+# You may obtain a copy of the License at
6
+#
7
+#    http://www.apache.org/licenses/LICENSE-2.0
8
+#
9
+# Unless required by applicable law or agreed to in writing, software
10
+# distributed under the License is distributed on an "AS IS" BASIS,
11
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12
+# implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+
17
+import mock
18
+from networking_brocade.mlx.services.l3_router.brocade import (
19
+    l3_router_fi_ni_plugin as brocadel3routerfiniplugin)
20
+from neutron import context
21
+from neutron.db import l3_db
22
+from neutron.i18n import _LE
23
+from neutron.services.l3_router import l3_router_plugin as router
24
+from neutron.tests import base
25
+from oslo_utils import importutils
26
+
27
+
28
+MECHANISM_NAME = ('networking_brocade.mlx.services.l3_router.brocade.'
29
+                  'l3_router_fi_ni_plugin.BrocadeFiNiL3RouterPlugin')
30
+VE = 've '
31
+config_map = {}
32
+device_map = {
33
+    'mlx': {
34
+        'address': '2.2.2.2',
35
+        'username': 'admin',
36
+        'password': 'pass',
37
+        'physical_networks': 'physnet1',
38
+        'ports': '2/1,2/2',
39
+        'os_type': 'NI'
40
+    }
41
+}
42
+add_config_map = {
43
+    '2.2.2.2': {
44
+        '402': {'ve 402': '12.0.0.1/24'
45
+                }
46
+    }
47
+}
48
+
49
+delete_config_map = {
50
+    '2.2.2.2': {
51
+        '402': {}
52
+    }
53
+}
54
+
55
+vlan_id = '402'
56
+gateway_ip_cidr = '12.0.0.1/24'
57
+ROUTER = 'router1'
58
+SUBNET = 'subnet1'
59
+PORT = 'port1'
60
+
61
+INVALID_INPUT = "Invalid input"
62
+RANGE_ERROR = "outside of allowed max"
63
+
64
+
65
+class TestBrocadeL3RouterFiNiPlugin(base.BaseTestCase,
66
+                                    router.L3RouterPlugin):
67
+
68
+    """
69
+    Test Brocade L3 Router FI/NI plugin.
70
+    """
71
+
72
+    def setUp(self):
73
+        _mechanism_name = MECHANISM_NAME
74
+
75
+        def mocked_initialize(self):
76
+            self._router_devices_map = device_map
77
+            self._driver_map = {'mlx': FakeDriver(device_map.get('mlx'))}
78
+
79
+        with mock.patch.object(brocadel3routerfiniplugin
80
+                               .BrocadeFiNiL3RouterPlugin,
81
+                               'brocade_init', new=mocked_initialize):
82
+            super(TestBrocadeL3RouterFiNiPlugin, self).setUp()
83
+            self.interface_info = {'subnet_id': SUBNET,
84
+                                   'port_id': PORT}
85
+            self.driver = importutils.import_object(_mechanism_name)
86
+
87
+    @mock.patch.object(
88
+        brocadel3routerfiniplugin.BrocadeFiNiL3RouterPlugin,
89
+        '_get_network_info')
90
+    @mock.patch.object(l3_db.L3_NAT_dbonly_mixin, '_core_plugin')
91
+    @mock.patch.object(router.L3RouterPlugin,
92
+                       'add_router_interface')
93
+    def test_add_router_interface(
94
+            self, mock_super, mock_core_plugin, mock_network_info):
95
+
96
+        mech_ctx = _get_network_context('physnet1', 'vlan')
97
+        ctx = mech_ctx._plugin_context
98
+        ctx.session.begin = mock.MagicMock()
99
+        mock_super.returnValue = self.interface_info
100
+        mock_core_plugin.side_effect = mock.MagicMock()
101
+        mock_network_info.side_effect = side_effect_network_info
102
+        self.driver.add_router_interface(ctx, ROUTER,
103
+                                         self.interface_info)
104
+        global config_map
105
+        self.assertDictSupersetOf(config_map, add_config_map)
106
+
107
+    @mock.patch.object(
108
+        brocadel3routerfiniplugin.BrocadeFiNiL3RouterPlugin,
109
+        '_get_network_info')
110
+    @mock.patch.object(l3_db.L3_NAT_dbonly_mixin, '_core_plugin')
111
+    @mock.patch.object(router.L3RouterPlugin, 'remove_router_interface')
112
+    def test_remove_router_interface(
113
+            self, mock_super, mock_core_plugin, mock_network_info):
114
+        mech_ctx = _get_network_context('physnet1', 'vlan')
115
+        ctx = mech_ctx._plugin_context
116
+        ctx.session.begin = mock.MagicMock()
117
+        mock_super.returnValue = self.interface_info
118
+        mock_core_plugin.side_effect = mock.MagicMock()
119
+        mock_network_info.side_effect = side_effect_network_info
120
+        self.driver.remove_router_interface(ctx, ROUTER,
121
+                                            self.interface_info)
122
+        global config_map
123
+        self.assertDictSupersetOf(config_map, delete_config_map)
124
+
125
+
126
+class TestBrocadeL3RouterFiNiPluginException(base.BaseTestCase,
127
+                                             router.L3RouterPlugin):
128
+
129
+    """
130
+    Test Brocade L3 Router FI/NI plugin for negative scenario
131
+    """
132
+
133
+    def setUp(self):
134
+        _mechanism_name = MECHANISM_NAME
135
+
136
+        def mocked_initialize(self):
137
+            self._router_devices_map = device_map
138
+            self._driver_map = {
139
+                'mlx': FakeDriver(
140
+                    device_map.get('mlx'),
141
+                    error=True)}
142
+
143
+        with mock.patch.object(brocadel3routerfiniplugin
144
+                               .BrocadeFiNiL3RouterPlugin,
145
+                               'brocade_init', new=mocked_initialize):
146
+            super(TestBrocadeL3RouterFiNiPluginException, self).setUp()
147
+            self.interface_info = {'subnet_id': SUBNET,
148
+                                   'port_id': PORT}
149
+            self.driver = importutils.import_object(_mechanism_name)
150
+
151
+    @mock.patch.object(
152
+        brocadel3routerfiniplugin.BrocadeFiNiL3RouterPlugin,
153
+        '_get_network_info')
154
+    @mock.patch.object(l3_db.L3_NAT_dbonly_mixin, '_core_plugin')
155
+    @mock.patch.object(router.L3RouterPlugin, 'add_router_interface')
156
+    def test_add_router_interface_exception(
157
+            self, mock_super, mock_core_plugin, mock_network_info):
158
+
159
+        mech_ctx = _get_network_context('physnet1', 'vlan')
160
+        ctx = mech_ctx._plugin_context
161
+        ctx.session.begin = mock.MagicMock()
162
+        mock_super.returnValue = self.interface_info
163
+        mock_core_plugin.side_effect = mock.MagicMock()
164
+        mock_network_info.side_effect = side_effect_network_info
165
+        self.assertRaisesRegexp(
166
+            Exception, (_LE("BrocadeFiNiL3RouterPlugin")),
167
+            self.driver.add_router_interface,
168
+            ctx, ROUTER, self.interface_info)
169
+
170
+
171
+def _get_network_context(physnet, network_type):
172
+    """
173
+    Create mock network context
174
+    """
175
+    network = {
176
+        'id': 1,
177
+        'name': 'private',
178
+        'tenant_id': 1,
179
+        'vlan': 200,
180
+        'network_type': network_type,
181
+        'provider:segmentation_id': 200
182
+    }
183
+
184
+    network_segments = [{
185
+        'id': 1,
186
+        'segmentation_id': 200,
187
+        'network_type': network_type,
188
+        'physical_network': physnet
189
+    }]
190
+
191
+    _plugin_context = context.get_admin_context()
192
+    return FakeNetworkContext(network, network_segments, _plugin_context)
193
+
194
+
195
+def side_effect_network_info(vlan, ip):
196
+    """
197
+    Mock _get_driver method and return FakeDriver
198
+    """
199
+
200
+    return vlan_id, gateway_ip_cidr
201
+
202
+
203
+class FakeNetworkContext(object):
204
+
205
+    """To generate network context for testing purposes only."""
206
+
207
+    def __init__(self, network, segments=None, original_network=None):
208
+        self._network = network
209
+        self._original_network = original_network
210
+        self._segments = segments
211
+
212
+    @property
213
+    def current(self):
214
+        return self._network
215
+
216
+    @property
217
+    def _plugin_context(self):
218
+        return self._original_network
219
+
220
+    @property
221
+    def network_segments(self):
222
+        return self._segments
223
+
224
+
225
+class FakeDriver(object):
226
+
227
+    """
228
+    Fake driver which will implement create and delete
229
+    network. Create network will update the global dictionary with
230
+    the address of the device along with vlan and ports to be tagged.
231
+    Example : {'10.10.23.1':{'200':['1/1/1', '1/1/2']}}
232
+
233
+    Delete network will delete the corresponding entry from the dictionary.
234
+    """
235
+
236
+    def __init__(self, device, error=None):
237
+        self.error = error
238
+        self.device = device
239
+        self.address = device.get('address')
240
+
241
+    def add_router_interface(self, vlan_id, gateway_ip_cidr):
242
+        if self.error is INVALID_INPUT:
243
+            raise Exception("Ethernet Driver : Create"
244
+                            "network failed: error= Invalid Input")
245
+        elif self.error is RANGE_ERROR:
246
+            raise Exception("Configuring router interface failed: "
247
+                            "ve out of range error")
248
+        elif self.error:
249
+            raise Exception("Add Router Interface failed")
250
+        vlan_map = {}
251
+        interface_map = {}
252
+        interface_map.update({VE + vlan_id: gateway_ip_cidr})
253
+        vlan_map.update({vlan_id: interface_map})
254
+        global config_map
255
+        config_map.update({self.address: vlan_map})
256
+
257
+    def remove_router_interface(self, vlan_id):
258
+        vlan_map = delete_config_map.pop(self.address, None)
259
+        interface_map = vlan_map.pop(vlan_id, None)
260
+        if vlan_map is None:
261
+            raise Exception("No Address")
262
+        elif interface_map is None:
263
+            raise Exception("No Router Interface")
264
+        else:
265
+            interface_map.pop(VE + vlan_id, None)

+ 8
- 4
networking_brocade/test_discover/test_discover.py View File

@@ -1,4 +1,4 @@
1
-# Copyright 2013 IBM Corp.
1
+# Copyright 2015 Brocade Communications Systems, Inc.
2 2
 #
3 3
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
4 4
 #    not use this file except in compliance with the License. You may obtain
@@ -20,14 +20,18 @@ if sys.version_info >= (2, 7):
20 20
 else:
21 21
     import unittest2 as unittest
22 22
 
23
+MLX_TEST_BASE_PATH = './networking_brocade/mlx/tests'
24
+
23 25
 
24 26
 def load_tests(loader, tests, pattern):
25 27
     suite = unittest.TestSuite()
26 28
     base_path = os.path.split(os.path.dirname(os.path.abspath(__file__)))[0]
27 29
     base_path = os.path.split(base_path)[0]
28
-    for test_dir in ['./networking_brocade/tests',
29
-                     './networking_brocade/vdx/tests/unit/ml2/drivers/brocade'
30
-                     ]:
30
+    test_dirs = {'./networking_brocade/tests',
31
+                 './networking_brocade/vdx/tests/unit/ml2/drivers/brocade',
32
+                 MLX_TEST_BASE_PATH + '/unit/ml2/drivers/brocade',
33
+                 MLX_TEST_BASE_PATH + '/unit/services/l3_router/brocade'}
34
+    for test_dir in test_dirs:
31 35
         if not pattern:
32 36
             suite.addTests(loader.discover(test_dir, top_level_dir=base_path))
33 37
         else:

Loading…
Cancel
Save