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

« back to all changes in this revision

Viewing changes to neutron/plugins/nec/nec_router.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 NEC Corporation.  All rights reserved.
2
 
#
3
 
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
4
 
#    not use this file except in compliance with the License. You may obtain
5
 
#    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, 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
 
from oslo.utils import excutils
16
 
from oslo.utils import importutils
17
 
 
18
 
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
19
 
from neutron.api.v2 import attributes as attr
20
 
from neutron.common import exceptions as n_exc
21
 
from neutron.db import db_base_plugin_v2
22
 
from neutron.db import extraroute_db
23
 
from neutron.db import l3_agentschedulers_db
24
 
from neutron.db import l3_db
25
 
from neutron.db import l3_gwmode_db
26
 
from neutron.db import models_v2
27
 
from neutron.extensions import l3
28
 
from neutron.i18n import _LE, _LI, _LW
29
 
from neutron.openstack.common import log as logging
30
 
from neutron.plugins.nec.common import config
31
 
from neutron.plugins.nec.common import constants as nconst
32
 
from neutron.plugins.nec.common import exceptions as nexc
33
 
from neutron.plugins.nec.db import router as rdb
34
 
from neutron.plugins.nec.extensions import router_provider as ext_provider
35
 
 
36
 
LOG = logging.getLogger(__name__)
37
 
 
38
 
PROVIDER_L3AGENT = nconst.ROUTER_PROVIDER_L3AGENT
39
 
PROVIDER_OPENFLOW = nconst.ROUTER_PROVIDER_OPENFLOW
40
 
 
41
 
ROUTER_DRIVER_PATH = 'neutron.plugins.nec.router_drivers.'
42
 
ROUTER_DRIVER_MAP = {
43
 
    PROVIDER_L3AGENT: ROUTER_DRIVER_PATH + 'RouterL3AgentDriver',
44
 
    PROVIDER_OPENFLOW: ROUTER_DRIVER_PATH + 'RouterOpenFlowDriver'
45
 
}
46
 
 
47
 
ROUTER_DRIVERS = {}
48
 
 
49
 
STATUS_ACTIVE = nconst.ROUTER_STATUS_ACTIVE
50
 
STATUS_ERROR = nconst.ROUTER_STATUS_ERROR
51
 
 
52
 
 
53
 
class RouterMixin(extraroute_db.ExtraRoute_db_mixin,
54
 
                  l3_gwmode_db.L3_NAT_db_mixin):
55
 
 
56
 
    def create_router(self, context, router):
57
 
        """Create a new router entry on DB, and create it on OFC."""
58
 
        LOG.debug("RouterMixin.create_router() called, "
59
 
                  "router=%s .", router)
60
 
        tenant_id = self._get_tenant_id_for_create(context, router['router'])
61
 
 
62
 
        provider = get_provider_with_default(
63
 
            router['router'].get(ext_provider.ROUTER_PROVIDER))
64
 
        driver = get_driver_by_provider(provider)
65
 
 
66
 
        with context.session.begin(subtransactions=True):
67
 
            new_router = super(RouterMixin, self).create_router(context,
68
 
                                                                router)
69
 
            new_router['gw_port'] = self._get_gw_port_detail(
70
 
                context, driver, new_router['gw_port_id'])
71
 
            rdb.add_router_provider_binding(context.session,
72
 
                                            provider, str(new_router['id']))
73
 
            self._extend_router_dict_provider(new_router, provider)
74
 
 
75
 
        # create router on the network controller
76
 
        try:
77
 
            return driver.create_router(context, tenant_id, new_router)
78
 
        except nexc.RouterOverLimit:
79
 
            with excutils.save_and_reraise_exception():
80
 
                super(RouterMixin, self).delete_router(context,
81
 
                                                       new_router['id'])
82
 
 
83
 
    def update_router(self, context, router_id, router):
84
 
        LOG.debug("RouterMixin.update_router() called, "
85
 
                  "id=%(id)s, router=%(router)s .",
86
 
                  {'id': router_id, 'router': router})
87
 
 
88
 
        with context.session.begin(subtransactions=True):
89
 
            old_rtr = super(RouterMixin, self).get_router(context, router_id)
90
 
            provider = old_rtr[ext_provider.ROUTER_PROVIDER]
91
 
            driver = get_driver_by_provider(provider)
92
 
            old_rtr['gw_port'] = self._get_gw_port_detail(
93
 
                context, driver, old_rtr['gw_port_id'])
94
 
            new_rtr = super(RouterMixin, self).update_router(
95
 
                context, router_id, router)
96
 
            new_rtr['gw_port'] = self._get_gw_port_detail(
97
 
                context, driver, new_rtr['gw_port_id'])
98
 
        driver.update_router(context, router_id, old_rtr, new_rtr)
99
 
        return new_rtr
100
 
 
101
 
    def delete_router(self, context, router_id):
102
 
        LOG.debug("RouterMixin.delete_router() called, id=%s.", router_id)
103
 
 
104
 
        router = super(RouterMixin, self).get_router(context, router_id)
105
 
        tenant_id = router['tenant_id']
106
 
        # Since l3_db.delete_router() has no interaction with the plugin layer,
107
 
        # we need to check if the router can be deleted first.
108
 
        self._check_router_in_use(context, router_id)
109
 
        driver = self._get_router_driver_by_id(context, router_id)
110
 
        # If gw_port exists, remove it.
111
 
        gw_port = self._get_gw_port(context, router_id)
112
 
        if gw_port:
113
 
            driver.delete_interface(context, router_id, gw_port)
114
 
        driver.delete_router(context, router_id, router)
115
 
 
116
 
        super(RouterMixin, self).delete_router(context, router_id)
117
 
 
118
 
        self._cleanup_ofc_tenant(context, tenant_id)
119
 
 
120
 
    def add_router_interface(self, context, router_id, interface_info):
121
 
        LOG.debug("RouterMixin.add_router_interface() called, "
122
 
                  "id=%(id)s, interface=%(interface)s.",
123
 
                  {'id': router_id, 'interface': interface_info})
124
 
        return super(RouterMixin, self).add_router_interface(
125
 
            context, router_id, interface_info)
126
 
 
127
 
    def remove_router_interface(self, context, router_id, interface_info):
128
 
        LOG.debug("RouterMixin.remove_router_interface() called, "
129
 
                  "id=%(id)s, interface=%(interface)s.",
130
 
                  {'id': router_id, 'interface': interface_info})
131
 
        return super(RouterMixin, self).remove_router_interface(
132
 
            context, router_id, interface_info)
133
 
 
134
 
    def create_router_port(self, context, port):
135
 
        # This method is called from plugin.create_port()
136
 
        router_id = port['device_id']
137
 
        driver = self._get_router_driver_by_id(context, router_id)
138
 
        port = driver.add_interface(context, router_id, port)
139
 
        return port
140
 
 
141
 
    def delete_router_port(self, context, port):
142
 
        # This method is called from plugin.delete_port()
143
 
        router_id = port['device_id']
144
 
        driver = self._get_router_driver_by_id(context, router_id)
145
 
        return driver.delete_interface(context, router_id, port)
146
 
 
147
 
    def _get_gw_port_detail(self, context, driver, gw_port_id):
148
 
        if not gw_port_id or not driver.need_gw_info:
149
 
            return
150
 
        ctx_elevated = context.elevated()
151
 
        gw_port = self._get_port(ctx_elevated, gw_port_id)
152
 
        # At this moment gw_port has been created, so it is guaranteed
153
 
        # that fixed_ip is assigned for the gw_port.
154
 
        ext_subnet_id = gw_port['fixed_ips'][0]['subnet_id']
155
 
        ext_subnet = self._get_subnet(ctx_elevated, ext_subnet_id)
156
 
        gw_info = {'network_id': gw_port['network_id'],
157
 
                   'ip_address': gw_port['fixed_ips'][0]['ip_address'],
158
 
                   'mac_address': gw_port['mac_address'],
159
 
                   'cidr': ext_subnet['cidr'],
160
 
                   'gateway_ip': ext_subnet['gateway_ip']}
161
 
        return gw_info
162
 
 
163
 
    def _get_gw_port(self, context, router_id):
164
 
        device_filter = {'device_id': [router_id],
165
 
                         'device_owner': [l3_db.DEVICE_OWNER_ROUTER_GW]}
166
 
        ports = self.get_ports(context.elevated(), filters=device_filter)
167
 
        if ports:
168
 
            return ports[0]
169
 
 
170
 
    def _check_router_in_use(self, context, router_id):
171
 
        with context.session.begin(subtransactions=True):
172
 
            # Ensure that the router is not used
173
 
            router_filter = {'router_id': [router_id]}
174
 
            fips = self.get_floatingips_count(context.elevated(),
175
 
                                              filters=router_filter)
176
 
            if fips:
177
 
                raise l3.RouterInUse(router_id=router_id)
178
 
 
179
 
            device_filter = {'device_id': [router_id],
180
 
                             'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF]}
181
 
            ports = self.get_ports_count(context.elevated(),
182
 
                                         filters=device_filter)
183
 
            if ports:
184
 
                raise l3.RouterInUse(router_id=router_id)
185
 
 
186
 
    def _get_router_for_floatingip(self, context, internal_port,
187
 
                                   internal_subnet_id,
188
 
                                   external_network_id):
189
 
        """Get a router for a requested floating IP.
190
 
 
191
 
        OpenFlow vrouter does not support NAT, so we need to exclude them
192
 
        from candidate routers for floating IP association.
193
 
        This method is called in l3_db.get_assoc_data().
194
 
        """
195
 
        subnet_db = self._get_subnet(context, internal_subnet_id)
196
 
        if not subnet_db['gateway_ip']:
197
 
            msg = (_('Cannot add floating IP to port on subnet %s '
198
 
                     'which has no gateway_ip') % internal_subnet_id)
199
 
            raise n_exc.BadRequest(resource='floatingip', msg=msg)
200
 
 
201
 
        # find router interface ports on this network
202
 
        router_intf_qry = context.session.query(models_v2.Port)
203
 
        router_intf_ports = router_intf_qry.filter_by(
204
 
            network_id=internal_port['network_id'],
205
 
            device_owner=l3_db.DEVICE_OWNER_ROUTER_INTF)
206
 
 
207
 
        for intf_p in router_intf_ports:
208
 
            if intf_p['fixed_ips'][0]['subnet_id'] == internal_subnet_id:
209
 
                router_id = intf_p['device_id']
210
 
                router_gw_qry = context.session.query(models_v2.Port)
211
 
                has_gw_port = router_gw_qry.filter_by(
212
 
                    network_id=external_network_id,
213
 
                    device_id=router_id,
214
 
                    device_owner=l3_db.DEVICE_OWNER_ROUTER_GW).count()
215
 
                driver = self._get_router_driver_by_id(context, router_id)
216
 
                if (has_gw_port and driver.floating_ip_support()):
217
 
                    return router_id
218
 
 
219
 
        raise l3.ExternalGatewayForFloatingIPNotFound(
220
 
            subnet_id=internal_subnet_id,
221
 
            external_network_id=external_network_id,
222
 
            port_id=internal_port['id'])
223
 
 
224
 
    def _get_sync_routers(self, context, router_ids=None, active=None):
225
 
        """Query routers and their gw ports for l3 agent.
226
 
 
227
 
        The difference from the superclass in l3_db is that this method
228
 
        only lists routers hosted on l3-agents.
229
 
        """
230
 
        router_list = super(RouterMixin, self)._get_sync_routers(
231
 
            context, router_ids, active)
232
 
        if router_list:
233
 
            _router_ids = [r['id'] for r in router_list]
234
 
            agent_routers = rdb.get_routers_by_provider(
235
 
                context.session, 'l3-agent',
236
 
                router_ids=_router_ids)
237
 
            router_list = [r for r in router_list
238
 
                           if r['id'] in agent_routers]
239
 
        return router_list
240
 
 
241
 
    def _get_router_driver_by_id(self, context, router_id):
242
 
        provider = self._get_provider_by_router_id(context, router_id)
243
 
        return get_driver_by_provider(provider)
244
 
 
245
 
    def _get_provider_by_router_id(self, context, router_id):
246
 
        return rdb.get_provider_by_router(context.session, router_id)
247
 
 
248
 
    def _extend_router_dict_provider(self, router_res, provider):
249
 
        router_res[ext_provider.ROUTER_PROVIDER] = provider
250
 
 
251
 
    def extend_router_dict_provider(self, router_res, router_db):
252
 
        # NOTE: router_db.provider is None just after creating a router,
253
 
        # so we need to skip setting router_provider here.
254
 
        if not router_db.provider:
255
 
            return
256
 
        self._extend_router_dict_provider(router_res,
257
 
                                          router_db.provider['provider'])
258
 
 
259
 
    db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
260
 
        l3.ROUTERS, [extend_router_dict_provider])
261
 
 
262
 
 
263
 
class L3AgentSchedulerDbMixin(l3_agentschedulers_db.L3AgentSchedulerDbMixin):
264
 
 
265
 
    def auto_schedule_routers(self, context, host, router_ids):
266
 
        router_ids = rdb.get_routers_by_provider(
267
 
            context.session, nconst.ROUTER_PROVIDER_L3AGENT, router_ids)
268
 
        # If no l3-agent hosted router, there is no need to schedule.
269
 
        if not router_ids:
270
 
            return
271
 
        return super(L3AgentSchedulerDbMixin, self).auto_schedule_routers(
272
 
            context, host, router_ids)
273
 
 
274
 
    def schedule_router(self, context, router, candidates=None):
275
 
        if (self._get_provider_by_router_id(context, router) ==
276
 
            nconst.ROUTER_PROVIDER_L3AGENT):
277
 
            return super(L3AgentSchedulerDbMixin, self).schedule_router(
278
 
                context, router, candidates=candidates)
279
 
 
280
 
    def add_router_to_l3_agent(self, context, id, router_id):
281
 
        provider = self._get_provider_by_router_id(context, router_id)
282
 
        if provider != nconst.ROUTER_PROVIDER_L3AGENT:
283
 
            raise nexc.RouterProviderMismatch(
284
 
                router_id=router_id, provider=provider,
285
 
                expected_provider=nconst.ROUTER_PROVIDER_L3AGENT)
286
 
        return super(L3AgentSchedulerDbMixin, self).add_router_to_l3_agent(
287
 
            context, id, router_id)
288
 
 
289
 
 
290
 
class L3AgentNotifyAPI(l3_rpc_agent_api.L3AgentNotifyAPI):
291
 
 
292
 
    def _notification(self, context, method, router_ids, operation,
293
 
                      shuffle_agents):
294
 
        """Notify all the agents that are hosting the routers.
295
 
 
296
 
        _notification() is called in L3 db plugin for all routers regardless
297
 
        the routers are hosted on l3 agents or not. When the routers are
298
 
        not hosted on l3 agents, there is no need to notify.
299
 
        This method filters routers not hosted by l3 agents.
300
 
        """
301
 
        router_ids = rdb.get_routers_by_provider(
302
 
            context.session, nconst.ROUTER_PROVIDER_L3AGENT, router_ids)
303
 
        super(L3AgentNotifyAPI, self)._notification(
304
 
            context, method, router_ids, operation, shuffle_agents)
305
 
 
306
 
 
307
 
def load_driver(plugin, ofc_manager):
308
 
 
309
 
    if (PROVIDER_OPENFLOW in ROUTER_DRIVER_MAP and
310
 
        not ofc_manager.driver.router_supported):
311
 
        LOG.warning(
312
 
            _LW('OFC does not support router with provider=%(provider)s, '
313
 
              'so removed it from supported provider '
314
 
              '(new router driver map=%(driver_map)s)'),
315
 
            {'provider': PROVIDER_OPENFLOW,
316
 
             'driver_map': ROUTER_DRIVER_MAP})
317
 
        del ROUTER_DRIVER_MAP[PROVIDER_OPENFLOW]
318
 
 
319
 
    if config.PROVIDER.default_router_provider not in ROUTER_DRIVER_MAP:
320
 
        LOG.error(_LE('default_router_provider %(default)s is supported! '
321
 
                    'Please specify one of %(supported)s'),
322
 
                  {'default': config.PROVIDER.default_router_provider,
323
 
                   'supported': ROUTER_DRIVER_MAP.keys()})
324
 
        raise SystemExit(1)
325
 
 
326
 
    enabled_providers = (set(config.PROVIDER.router_providers +
327
 
                             [config.PROVIDER.default_router_provider]) &
328
 
                         set(ROUTER_DRIVER_MAP.keys()))
329
 
 
330
 
    for driver in enabled_providers:
331
 
        driver_klass = importutils.import_class(ROUTER_DRIVER_MAP[driver])
332
 
        ROUTER_DRIVERS[driver] = driver_klass(plugin, ofc_manager)
333
 
 
334
 
    LOG.info(_LI('Enabled router drivers: %s'), ROUTER_DRIVERS.keys())
335
 
 
336
 
    if not ROUTER_DRIVERS:
337
 
        LOG.error(_LE('No router provider is enabled. neutron-server '
338
 
                      'terminated! (supported=%(supported)s, '
339
 
                      'configured=%(config)s)'),
340
 
                  {'supported': ROUTER_DRIVER_MAP.keys(),
341
 
                   'config': config.PROVIDER.router_providers})
342
 
        raise SystemExit(1)
343
 
 
344
 
 
345
 
def get_provider_with_default(provider):
346
 
    if not attr.is_attr_set(provider):
347
 
        provider = config.PROVIDER.default_router_provider
348
 
    elif provider not in ROUTER_DRIVERS:
349
 
        raise nexc.ProviderNotFound(provider=provider)
350
 
    return provider
351
 
 
352
 
 
353
 
def get_driver_by_provider(provider):
354
 
    if provider is None:
355
 
        provider = config.PROVIDER.default_router_provider
356
 
    elif provider not in ROUTER_DRIVERS:
357
 
        raise nexc.ProviderNotFound(provider=provider)
358
 
    return ROUTER_DRIVERS[provider]