1
# Copyright 2014 VMware, Inc.
5
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6
# not use this file except in compliance with the License. You may obtain
7
# a copy of the License at
9
# http://www.apache.org/licenses/LICENSE-2.0
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
# License for the specific language governing permissions and limitations
18
from oslo.config import cfg
19
from oslo.db import exception as db_exc
20
from oslo.utils import excutils
22
from neutron.common import exceptions as n_exc
23
from neutron.i18n import _LE, _LW
24
from neutron.openstack.common import log as logging
25
from neutron.plugins.vmware.api_client import exception as api_exc
26
from neutron.plugins.vmware.common import exceptions as p_exc
27
from neutron.plugins.vmware.common import nsx_utils
28
from neutron.plugins.vmware.dbexts import lsn_db
29
from neutron.plugins.vmware.dhcp_meta import constants as const
30
from neutron.plugins.vmware.nsxlib import lsn as lsn_api
31
from neutron.plugins.vmware.nsxlib import switch as switch_api
33
LOG = logging.getLogger(__name__)
35
META_CONF = 'metadata-proxy'
40
cfg.BoolOpt('sync_on_missing_data', default=False,
41
help=_('Pull LSN information from NSX in case it is missing '
42
'from the local data store. This is useful to rebuild '
43
'the local store in case of server recovery.'))
47
def register_lsn_opts(config):
48
config.CONF.register_opts(lsn_opts, "NSX_LSN")
51
class LsnManager(object):
52
"""Manage LSN entities associated with networks."""
54
def __init__(self, plugin):
59
return self.plugin.cluster
61
def lsn_exists(self, context, network_id):
62
"""Return True if a Logical Service Node exists for the network."""
64
context, network_id, raise_on_err=False) is not None
66
def lsn_get(self, context, network_id, raise_on_err=True):
67
"""Retrieve the LSN id associated to the network."""
69
return lsn_api.lsn_for_network_get(self.cluster, network_id)
70
except (n_exc.NotFound, api_exc.NsxApiException):
72
LOG.error(_LE('Unable to find Logical Service Node for '
75
raise p_exc.LsnNotFound(entity='network',
78
LOG.warn(_LW('Unable to find Logical Service Node for '
79
'the requested network %s.'),
82
def lsn_create(self, context, network_id):
83
"""Create a LSN associated to the network."""
85
return lsn_api.lsn_for_network_create(self.cluster, network_id)
86
except api_exc.NsxApiException:
87
err_msg = _('Unable to create LSN for network %s') % network_id
88
raise p_exc.NsxPluginException(err_msg=err_msg)
90
def lsn_delete(self, context, lsn_id):
91
"""Delete a LSN given its id."""
93
lsn_api.lsn_delete(self.cluster, lsn_id)
94
except (n_exc.NotFound, api_exc.NsxApiException):
95
LOG.warn(_LW('Unable to delete Logical Service Node %s'), lsn_id)
97
def lsn_delete_by_network(self, context, network_id):
98
"""Delete a LSN associated to the network."""
99
lsn_id = self.lsn_get(context, network_id, raise_on_err=False)
101
self.lsn_delete(context, lsn_id)
103
def lsn_port_get(self, context, network_id, subnet_id, raise_on_err=True):
104
"""Retrieve LSN and LSN port for the network and the subnet."""
105
lsn_id = self.lsn_get(context, network_id, raise_on_err=raise_on_err)
108
lsn_port_id = lsn_api.lsn_port_by_subnet_get(
109
self.cluster, lsn_id, subnet_id)
110
except (n_exc.NotFound, api_exc.NsxApiException):
112
LOG.error(_LE('Unable to find Logical Service Node Port '
113
'for LSN %(lsn_id)s and subnet '
115
{'lsn_id': lsn_id, 'subnet_id': subnet_id})
116
raise p_exc.LsnPortNotFound(lsn_id=lsn_id,
120
LOG.warn(_LW('Unable to find Logical Service Node Port '
121
'for LSN %(lsn_id)s and subnet '
123
{'lsn_id': lsn_id, 'subnet_id': subnet_id})
124
return (lsn_id, None)
126
return (lsn_id, lsn_port_id)
130
def lsn_port_get_by_mac(self, context, network_id, mac, raise_on_err=True):
131
"""Retrieve LSN and LSN port given network and mac address."""
132
lsn_id = self.lsn_get(context, network_id, raise_on_err=raise_on_err)
135
lsn_port_id = lsn_api.lsn_port_by_mac_get(
136
self.cluster, lsn_id, mac)
137
except (n_exc.NotFound, api_exc.NsxApiException):
139
LOG.error(_LE('Unable to find Logical Service Node Port '
140
'for LSN %(lsn_id)s and mac address '
142
{'lsn_id': lsn_id, 'mac': mac})
143
raise p_exc.LsnPortNotFound(lsn_id=lsn_id,
147
LOG.warn(_LW('Unable to find Logical Service Node '
148
'Port for LSN %(lsn_id)s and mac address '
150
{'lsn_id': lsn_id, 'mac': mac})
151
return (lsn_id, None)
153
return (lsn_id, lsn_port_id)
157
def lsn_port_create(self, context, lsn_id, subnet_info):
158
"""Create and return LSN port for associated subnet."""
160
return lsn_api.lsn_port_create(self.cluster, lsn_id, subnet_info)
161
except n_exc.NotFound:
162
raise p_exc.LsnNotFound(entity='', entity_id=lsn_id)
163
except api_exc.NsxApiException:
164
err_msg = _('Unable to create port for LSN %s') % lsn_id
165
raise p_exc.NsxPluginException(err_msg=err_msg)
167
def lsn_port_delete(self, context, lsn_id, lsn_port_id):
168
"""Delete a LSN port from the Logical Service Node."""
170
lsn_api.lsn_port_delete(self.cluster, lsn_id, lsn_port_id)
171
except (n_exc.NotFound, api_exc.NsxApiException):
172
LOG.warn(_LW('Unable to delete LSN Port %s'), lsn_port_id)
174
def lsn_port_dispose(self, context, network_id, mac_address):
175
"""Delete a LSN port given the network and the mac address."""
176
lsn_id, lsn_port_id = self.lsn_port_get_by_mac(
177
context, network_id, mac_address, raise_on_err=False)
179
self.lsn_port_delete(context, lsn_id, lsn_port_id)
180
if mac_address == const.METADATA_MAC:
182
lswitch_port_id = switch_api.get_port_by_neutron_tag(
183
self.cluster, network_id,
184
const.METADATA_PORT_ID)['uuid']
185
switch_api.delete_port(
186
self.cluster, network_id, lswitch_port_id)
187
except (n_exc.PortNotFoundOnNetwork,
188
api_exc.NsxApiException):
189
LOG.warn(_LW("Metadata port not found while attempting "
190
"to delete it from network %s"), network_id)
192
LOG.warn(_LW("Unable to find Logical Services Node "
193
"Port with MAC %s"), mac_address)
195
def lsn_port_dhcp_setup(
196
self, context, network_id, port_id, port_data, subnet_config=None):
197
"""Connect network to LSN via specified port and port_data."""
200
switch_id = nsx_utils.get_nsx_switch_ids(
201
context.session, self.cluster, network_id)[0]
202
lswitch_port_id = switch_api.get_port_by_neutron_tag(
203
self.cluster, switch_id, port_id)['uuid']
204
lsn_id = self.lsn_get(context, network_id)
205
lsn_port_id = self.lsn_port_create(context, lsn_id, port_data)
206
except (n_exc.NotFound, p_exc.NsxPluginException):
207
raise p_exc.PortConfigurationError(
208
net_id=network_id, lsn_id=lsn_id, port_id=port_id)
211
lsn_api.lsn_port_plug_network(
212
self.cluster, lsn_id, lsn_port_id, lswitch_port_id)
213
except p_exc.LsnConfigurationConflict:
214
self.lsn_port_delete(context, lsn_id, lsn_port_id)
215
raise p_exc.PortConfigurationError(
216
net_id=network_id, lsn_id=lsn_id, port_id=port_id)
218
self.lsn_port_dhcp_configure(
219
context, lsn_id, lsn_port_id, subnet_config)
221
return (lsn_id, lsn_port_id)
223
def lsn_port_metadata_setup(self, context, lsn_id, subnet):
224
"""Connect subnet to specified LSN."""
226
"mac_address": const.METADATA_MAC,
227
"ip_address": subnet['cidr'],
228
"subnet_id": subnet['id']
230
network_id = subnet['network_id']
231
tenant_id = subnet['tenant_id']
232
lswitch_port_id = None
234
switch_id = nsx_utils.get_nsx_switch_ids(
235
context.session, self.cluster, network_id)[0]
236
lswitch_port_id = switch_api.create_lport(
237
self.cluster, switch_id, tenant_id,
238
const.METADATA_PORT_ID, const.METADATA_PORT_NAME,
239
const.METADATA_DEVICE_ID, True)['uuid']
240
lsn_port_id = self.lsn_port_create(context, lsn_id, data)
241
except (n_exc.NotFound, p_exc.NsxPluginException,
242
api_exc.NsxApiException):
243
raise p_exc.PortConfigurationError(
244
net_id=network_id, lsn_id=lsn_id, port_id=lswitch_port_id)
247
lsn_api.lsn_port_plug_network(
248
self.cluster, lsn_id, lsn_port_id, lswitch_port_id)
249
except p_exc.LsnConfigurationConflict:
250
self.lsn_port_delete(self.cluster, lsn_id, lsn_port_id)
251
switch_api.delete_port(
252
self.cluster, network_id, lswitch_port_id)
253
raise p_exc.PortConfigurationError(
254
net_id=network_id, lsn_id=lsn_id, port_id=lsn_port_id)
256
def lsn_port_dhcp_configure(self, context, lsn_id, lsn_port_id, subnet):
257
"""Enable/disable dhcp services with the given config options."""
258
is_enabled = subnet["enable_dhcp"]
260
"domain_name": cfg.CONF.NSX_DHCP.domain_name,
261
"default_lease_time": cfg.CONF.NSX_DHCP.default_lease_time,
263
dns_servers = cfg.CONF.NSX_DHCP.extra_domain_name_servers or []
264
dns_servers.extend(subnet["dns_nameservers"])
265
if subnet['gateway_ip']:
266
dhcp_options["routers"] = subnet["gateway_ip"]
268
dhcp_options["domain_name_servers"] = ",".join(dns_servers)
269
if subnet["host_routes"]:
270
dhcp_options["classless_static_routes"] = (
271
",".join(subnet["host_routes"])
274
lsn_api.lsn_port_dhcp_configure(
275
self.cluster, lsn_id, lsn_port_id, is_enabled, dhcp_options)
276
except (n_exc.NotFound, api_exc.NsxApiException):
277
err_msg = (_('Unable to configure dhcp for Logical Service '
278
'Node %(lsn_id)s and port %(lsn_port_id)s')
279
% {'lsn_id': lsn_id, 'lsn_port_id': lsn_port_id})
281
raise p_exc.NsxPluginException(err_msg=err_msg)
283
def lsn_metadata_configure(self, context, subnet_id, is_enabled):
284
"""Configure metadata service for the specified subnet."""
285
subnet = self.plugin.get_subnet(context, subnet_id)
286
network_id = subnet['network_id']
287
meta_conf = cfg.CONF.NSX_METADATA
289
'metadata_server_ip': meta_conf.metadata_server_address,
290
'metadata_server_port': meta_conf.metadata_server_port,
291
'metadata_proxy_shared_secret': meta_conf.metadata_shared_secret
294
lsn_id = self.lsn_get(context, network_id)
295
lsn_api.lsn_metadata_configure(
296
self.cluster, lsn_id, is_enabled, metadata_options)
297
except (p_exc.LsnNotFound, api_exc.NsxApiException):
298
err_msg = (_('Unable to configure metadata '
299
'for subnet %s') % subnet_id)
301
raise p_exc.NsxPluginException(err_msg=err_msg)
304
# test that the lsn port exists
305
self.lsn_port_get(context, network_id, subnet_id)
306
except p_exc.LsnPortNotFound:
307
# this might happen if subnet had dhcp off when created
308
# so create one, and wire it
309
self.lsn_port_metadata_setup(context, lsn_id, subnet)
311
self.lsn_port_dispose(context, network_id, const.METADATA_MAC)
313
def _lsn_port_host_conf(self, context, network_id, subnet_id, data, hdlr):
314
lsn_id, lsn_port_id = self.lsn_port_get(
315
context, network_id, subnet_id, raise_on_err=False)
317
if lsn_id and lsn_port_id:
318
hdlr(self.cluster, lsn_id, lsn_port_id, data)
319
except (n_exc.NotFound, api_exc.NsxApiException):
320
LOG.error(_LE('Error while configuring LSN '
321
'port %s'), lsn_port_id)
322
raise p_exc.PortConfigurationError(
323
net_id=network_id, lsn_id=lsn_id, port_id=lsn_port_id)
325
def lsn_port_dhcp_host_add(self, context, network_id, subnet_id, host):
326
"""Add dhcp host entry to LSN port configuration."""
327
self._lsn_port_host_conf(context, network_id, subnet_id, host,
328
lsn_api.lsn_port_dhcp_host_add)
330
def lsn_port_dhcp_host_remove(self, context, network_id, subnet_id, host):
331
"""Remove dhcp host entry from LSN port configuration."""
332
self._lsn_port_host_conf(context, network_id, subnet_id, host,
333
lsn_api.lsn_port_dhcp_host_remove)
335
def lsn_port_meta_host_add(self, context, network_id, subnet_id, host):
336
"""Add dhcp host entry to LSN port configuration."""
337
self._lsn_port_host_conf(context, network_id, subnet_id, host,
338
lsn_api.lsn_port_metadata_host_add)
340
def lsn_port_meta_host_remove(self, context, network_id, subnet_id, host):
341
"""Remove dhcp host entry from LSN port configuration."""
342
self._lsn_port_host_conf(context, network_id, subnet_id, host,
343
lsn_api.lsn_port_metadata_host_remove)
346
self, context, network_id, subnet_id, dhcp=None, meta=None):
347
"""Update the specified configuration for the LSN port."""
348
if not dhcp and not meta:
351
lsn_id, lsn_port_id = self.lsn_port_get(
352
context, network_id, subnet_id, raise_on_err=False)
353
if dhcp and lsn_id and lsn_port_id:
354
lsn_api.lsn_port_host_entries_update(
355
self.cluster, lsn_id, lsn_port_id, DHCP_CONF, dhcp)
356
if meta and lsn_id and lsn_port_id:
357
lsn_api.lsn_port_host_entries_update(
358
self.cluster, lsn_id, lsn_port_id, META_CONF, meta)
359
except api_exc.NsxApiException:
360
raise p_exc.PortConfigurationError(
361
net_id=network_id, lsn_id=lsn_id, port_id=lsn_port_id)
364
class PersistentLsnManager(LsnManager):
365
"""Add local persistent state to LSN Manager."""
367
def __init__(self, plugin):
368
super(PersistentLsnManager, self).__init__(plugin)
369
self.sync_on_missing = cfg.CONF.NSX_LSN.sync_on_missing_data
371
def lsn_get(self, context, network_id, raise_on_err=True):
373
obj = lsn_db.lsn_get_for_network(
374
context, network_id, raise_on_err=raise_on_err)
375
return obj.lsn_id if obj else None
376
except p_exc.LsnNotFound:
377
with excutils.save_and_reraise_exception() as ctxt:
379
if self.sync_on_missing:
380
lsn_id = super(PersistentLsnManager, self).lsn_get(
381
context, network_id, raise_on_err=raise_on_err)
382
self.lsn_save(context, network_id, lsn_id)
387
def lsn_save(self, context, network_id, lsn_id):
388
"""Save LSN-Network mapping to the DB."""
390
lsn_db.lsn_add(context, network_id, lsn_id)
391
except db_exc.DBError:
392
err_msg = _('Unable to save LSN for network %s') % network_id
393
LOG.exception(err_msg)
394
raise p_exc.NsxPluginException(err_msg=err_msg)
396
def lsn_create(self, context, network_id):
397
lsn_id = super(PersistentLsnManager,
398
self).lsn_create(context, network_id)
400
self.lsn_save(context, network_id, lsn_id)
401
except p_exc.NsxPluginException:
402
with excutils.save_and_reraise_exception():
403
super(PersistentLsnManager, self).lsn_delete(context, lsn_id)
406
def lsn_delete(self, context, lsn_id):
407
lsn_db.lsn_remove(context, lsn_id)
408
super(PersistentLsnManager, self).lsn_delete(context, lsn_id)
410
def lsn_port_get(self, context, network_id, subnet_id, raise_on_err=True):
412
obj = lsn_db.lsn_port_get_for_subnet(
413
context, subnet_id, raise_on_err=raise_on_err)
414
return (obj.lsn_id, obj.lsn_port_id) if obj else (None, None)
415
except p_exc.LsnPortNotFound:
416
with excutils.save_and_reraise_exception() as ctxt:
418
if self.sync_on_missing:
419
lsn_id, lsn_port_id = (
420
super(PersistentLsnManager, self).lsn_port_get(
421
context, network_id, subnet_id,
422
raise_on_err=raise_on_err))
423
mac_addr = lsn_api.lsn_port_info_get(
424
self.cluster, lsn_id, lsn_port_id)['mac_address']
426
context, lsn_port_id, subnet_id, mac_addr, lsn_id)
427
return (lsn_id, lsn_port_id)
431
def lsn_port_get_by_mac(self, context, network_id, mac, raise_on_err=True):
433
obj = lsn_db.lsn_port_get_for_mac(
434
context, mac, raise_on_err=raise_on_err)
435
return (obj.lsn_id, obj.lsn_port_id) if obj else (None, None)
436
except p_exc.LsnPortNotFound:
437
with excutils.save_and_reraise_exception() as ctxt:
439
if self.sync_on_missing:
440
lsn_id, lsn_port_id = (
441
super(PersistentLsnManager, self).lsn_port_get_by_mac(
442
context, network_id, mac,
443
raise_on_err=raise_on_err))
444
subnet_id = lsn_api.lsn_port_info_get(
445
self.cluster, lsn_id, lsn_port_id).get('subnet_id')
447
context, lsn_port_id, subnet_id, mac, lsn_id)
448
return (lsn_id, lsn_port_id)
452
def lsn_port_save(self, context, lsn_port_id, subnet_id, mac_addr, lsn_id):
453
"""Save LSN Port information to the DB."""
455
lsn_db.lsn_port_add_for_lsn(
456
context, lsn_port_id, subnet_id, mac_addr, lsn_id)
457
except db_exc.DBError:
458
err_msg = _('Unable to save LSN port for subnet %s') % subnet_id
459
LOG.exception(err_msg)
460
raise p_exc.NsxPluginException(err_msg=err_msg)
462
def lsn_port_create(self, context, lsn_id, subnet_info):
463
lsn_port_id = super(PersistentLsnManager,
464
self).lsn_port_create(context, lsn_id, subnet_info)
466
self.lsn_port_save(context, lsn_port_id, subnet_info['subnet_id'],
467
subnet_info['mac_address'], lsn_id)
468
except p_exc.NsxPluginException:
469
with excutils.save_and_reraise_exception():
470
super(PersistentLsnManager, self).lsn_port_delete(
471
context, lsn_id, lsn_port_id)
474
def lsn_port_delete(self, context, lsn_id, lsn_port_id):
475
lsn_db.lsn_port_remove(context, lsn_port_id)
476
super(PersistentLsnManager, self).lsn_port_delete(
477
context, lsn_id, lsn_port_id)