~ubuntu-branches/ubuntu/vivid/neutron/vivid-updates

« back to all changes in this revision

Viewing changes to neutron/plugins/ml2/drivers/cisco/nexus/nexus_network_driver.py

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2015-03-30 11:17:19 UTC
  • mfrom: (1.1.21)
  • Revision ID: package-import@ubuntu.com-20150330111719-h0gx7233p4jkkgfh
Tags: 1:2015.1~b3-0ubuntu1
* New upstream milestone release:
  - d/control: Align version requirements with upstream.
  - d/control: Add new dependency on oslo-log.
  - d/p/*: Rebase.
  - d/control,d/neutron-plugin-hyperv*: Dropped, decomposed into
    separate project upstream.
  - d/control,d/neutron-plugin-openflow*: Dropped, decomposed into
    separate project upstream.
  - d/neutron-common.install: Add neutron-rootwrap-daemon and 
    neutron-keepalived-state-change binaries.
  - d/rules: Ignore neutron-hyperv-agent when installing; only for Windows.
  - d/neutron-plugin-cisco.install: Drop neutron-cisco-cfg-agent as
    decomposed into separate project upstream.
  - d/neutron-plugin-vmware.install: Drop neutron-check-nsx-config and
    neutron-nsx-manage as decomposed into separate project upstream.
  - d/control: Add dependency on python-neutron-fwaas to neutron-l3-agent.
* d/pydist-overrides: Add overrides for oslo packages.
* d/control: Fixup type in package description (LP: #1263539).
* d/p/fixup-driver-test-execution.patch: Cherry pick fix from upstream VCS
  to support unit test exection in out-of-tree vendor drivers.
* d/neutron-common.postinst: Allow general access to /etc/neutron but limit
  access to root/neutron to /etc/neutron/neutron.conf to support execution
  of unit tests in decomposed vendor drivers.
* d/control: Add dependency on python-neutron-fwaas to neutron-l3-agent
  package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2013 OpenStack Foundation
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
 
Implements a Nexus-OS NETCONF over SSHv2 API Client
18
 
"""
19
 
 
20
 
from oslo.utils import excutils
21
 
from oslo.utils import importutils
22
 
 
23
 
from neutron.openstack.common import log as logging
24
 
from neutron.plugins.ml2.drivers.cisco.nexus import config as conf
25
 
from neutron.plugins.ml2.drivers.cisco.nexus import constants as const
26
 
from neutron.plugins.ml2.drivers.cisco.nexus import exceptions as cexc
27
 
from neutron.plugins.ml2.drivers.cisco.nexus import nexus_snippets as snipp
28
 
 
29
 
LOG = logging.getLogger(__name__)
30
 
 
31
 
 
32
 
class CiscoNexusDriver(object):
33
 
    """Nexus Driver Main Class."""
34
 
    def __init__(self):
35
 
        self.ncclient = None
36
 
        self.nexus_switches = conf.ML2MechCiscoConfig.nexus_dict
37
 
        self.connections = {}
38
 
 
39
 
    def _import_ncclient(self):
40
 
        """Import the NETCONF client (ncclient) module.
41
 
 
42
 
        The ncclient module is not installed as part of the normal Neutron
43
 
        distributions. It is imported dynamically in this module so that
44
 
        the import can be mocked, allowing unit testing without requiring
45
 
        the installation of ncclient.
46
 
 
47
 
        """
48
 
        return importutils.import_module('ncclient.manager')
49
 
 
50
 
    def _edit_config(self, nexus_host, target='running', config='',
51
 
                     allowed_exc_strs=None):
52
 
        """Modify switch config for a target config type.
53
 
 
54
 
        :param nexus_host: IP address of switch to configure
55
 
        :param target: Target config type
56
 
        :param config: Configuration string in XML format
57
 
        :param allowed_exc_strs: Exceptions which have any of these strings
58
 
                                 as a subset of their exception message
59
 
                                 (str(exception)) can be ignored
60
 
 
61
 
        :returns None: if config was edited successfully
62
 
                 Exception object: if _edit_config() encountered an exception
63
 
                                   containing one of allowed_exc_strs
64
 
 
65
 
        :raises: NexusConfigFailed: if _edit_config() encountered an exception
66
 
                                    not containing one of allowed_exc_strs
67
 
 
68
 
        """
69
 
        if not allowed_exc_strs:
70
 
            allowed_exc_strs = []
71
 
        mgr = self.nxos_connect(nexus_host)
72
 
        try:
73
 
            LOG.debug("NexusDriver config: %s", config)
74
 
            mgr.edit_config(target=target, config=config)
75
 
        except Exception as e:
76
 
            for exc_str in allowed_exc_strs:
77
 
                if exc_str in str(e):
78
 
                    return e
79
 
                # Raise a Neutron exception. Include a description of
80
 
                # the original ncclient exception.
81
 
            raise cexc.NexusConfigFailed(config=config, exc=e)
82
 
 
83
 
    def nxos_connect(self, nexus_host):
84
 
        """Make SSH connection to the Nexus Switch."""
85
 
        if getattr(self.connections.get(nexus_host), 'connected', None):
86
 
            return self.connections[nexus_host]
87
 
 
88
 
        if not self.ncclient:
89
 
            self.ncclient = self._import_ncclient()
90
 
        nexus_ssh_port = int(self.nexus_switches[nexus_host, 'ssh_port'])
91
 
        nexus_user = self.nexus_switches[nexus_host, const.USERNAME]
92
 
        nexus_password = self.nexus_switches[nexus_host, const.PASSWORD]
93
 
        try:
94
 
            try:
95
 
                # With new ncclient version, we can pass device_params...
96
 
                man = self.ncclient.connect(host=nexus_host,
97
 
                                            port=nexus_ssh_port,
98
 
                                            username=nexus_user,
99
 
                                            password=nexus_password,
100
 
                                            device_params={"name": "nexus"})
101
 
            except TypeError:
102
 
                # ... but if that causes an error, we appear to have the old
103
 
                # ncclient installed, which doesn't understand this parameter.
104
 
                man = self.ncclient.connect(host=nexus_host,
105
 
                                            port=nexus_ssh_port,
106
 
                                            username=nexus_user,
107
 
                                            password=nexus_password)
108
 
        except Exception as e:
109
 
            # Raise a Neutron exception. Include a description of
110
 
            # the original ncclient exception.
111
 
            raise cexc.NexusConnectFailed(nexus_host=nexus_host, exc=e)
112
 
 
113
 
        self.connections[nexus_host] = man
114
 
        return self.connections[nexus_host]
115
 
 
116
 
    def create_xml_snippet(self, customized_config):
117
 
        """Create XML snippet.
118
 
 
119
 
        Creates the Proper XML structure for the Nexus Switch Configuration.
120
 
        """
121
 
        conf_xml_snippet = snipp.EXEC_CONF_SNIPPET % (customized_config)
122
 
        return conf_xml_snippet
123
 
 
124
 
    def create_vlan(self, nexus_host, vlanid, vlanname):
125
 
        """Create a VLAN on Nexus Switch given the VLAN ID and Name."""
126
 
        confstr = self.create_xml_snippet(
127
 
            snipp.CMD_VLAN_CONF_SNIPPET % (vlanid, vlanname))
128
 
        self._edit_config(nexus_host, target='running', config=confstr)
129
 
 
130
 
        # Enable VLAN active and no-shutdown states. Some versions of
131
 
        # Nexus switch do not allow state changes for the extended VLAN
132
 
        # range (1006-4094), but these errors can be ignored (default
133
 
        # values are appropriate).
134
 
        for snippet in [snipp.CMD_VLAN_ACTIVE_SNIPPET,
135
 
                        snipp.CMD_VLAN_NO_SHUTDOWN_SNIPPET]:
136
 
            try:
137
 
                confstr = self.create_xml_snippet(snippet % vlanid)
138
 
                self._edit_config(
139
 
                    nexus_host,
140
 
                    target='running',
141
 
                    config=confstr,
142
 
                    allowed_exc_strs=["Can't modify state for extended",
143
 
                                      "Command is only allowed on VLAN"])
144
 
            except cexc.NexusConfigFailed:
145
 
                with excutils.save_and_reraise_exception():
146
 
                    self.delete_vlan(nexus_host, vlanid)
147
 
 
148
 
    def delete_vlan(self, nexus_host, vlanid):
149
 
        """Delete a VLAN on Nexus Switch given the VLAN ID."""
150
 
        confstr = snipp.CMD_NO_VLAN_CONF_SNIPPET % vlanid
151
 
        confstr = self.create_xml_snippet(confstr)
152
 
        self._edit_config(nexus_host, target='running', config=confstr)
153
 
 
154
 
    def build_intf_confstr(self, snippet, intf_type, interface, vlanid):
155
 
        """Build the VLAN config string xml snippet to be used."""
156
 
        confstr = snippet % (intf_type, interface, vlanid, intf_type)
157
 
        confstr = self.create_xml_snippet(confstr)
158
 
        LOG.debug("NexusDriver: %s", confstr)
159
 
        return confstr
160
 
 
161
 
    def enable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
162
 
                                 interface):
163
 
        """Enable a VLAN on a trunk interface."""
164
 
        # Configure a new VLAN into the interface using 'ADD' keyword
165
 
        confstr = self.build_intf_confstr(
166
 
            snippet=snipp.CMD_INT_VLAN_ADD_SNIPPET,
167
 
            intf_type=intf_type,
168
 
            interface=interface,
169
 
            vlanid=vlanid
170
 
        )
171
 
        exc_str = ["switchport trunk allowed vlan list is empty"]
172
 
        ret_exc = self._edit_config(nexus_host, target='running',
173
 
                                    config=confstr,
174
 
                                    allowed_exc_strs=exc_str)
175
 
        if ret_exc:
176
 
            # If no switchports have been configured on the switch
177
 
            # before the new 'ADD', configure the VLAN into the
178
 
            # interface without the keyword so as to create a vlan list
179
 
            confstr = self.build_intf_confstr(
180
 
                snippet=snipp.CMD_INT_VLAN_SNIPPET,
181
 
                intf_type=intf_type,
182
 
                interface=interface,
183
 
                vlanid=vlanid
184
 
            )
185
 
            self._edit_config(nexus_host, target='running',
186
 
                              config=confstr)
187
 
 
188
 
    def disable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
189
 
                                  interface):
190
 
        """Disable a VLAN on a trunk interface."""
191
 
        confstr = (snipp.CMD_NO_VLAN_INT_SNIPPET %
192
 
                   (intf_type, interface, vlanid, intf_type))
193
 
        confstr = self.create_xml_snippet(confstr)
194
 
        self._edit_config(nexus_host, target='running', config=confstr)
195
 
 
196
 
    def create_and_trunk_vlan(self, nexus_host, vlan_id, vlan_name,
197
 
                              intf_type, nexus_port):
198
 
        """Create VLAN and trunk it on the specified ports."""
199
 
        self.create_vlan(nexus_host, vlan_id, vlan_name)
200
 
        LOG.debug("NexusDriver created VLAN: %s", vlan_id)
201
 
        if nexus_port:
202
 
            self.enable_vlan_on_trunk_int(nexus_host, vlan_id, intf_type,
203
 
                                          nexus_port)