blob: cacf165e93ca8d0dec27bd2fb8bba69e7629115d [file] [log] [blame]
Scott Baker352d4732014-08-14 16:10:59 -07001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2# Copyright 2011 Nicira Networks, Inc.
3# All Rights Reserved.
4#
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
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
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
15# under the License.
16# @author: Somik Behera, Nicira Networks, Inc.
17# @author: Brad Hall, Nicira Networks, Inc.
18# @author: Dan Wendlandt, Nicira Networks, Inc.
19# @author: Dave Lapsley, Nicira Networks, Inc.
20# @author: Aaron Rosen, Nicira Networks, Inc.
21# @author: Bob Kukura, Red Hat, Inc.
22# @author: Seetharama Ayyadevara, Freescale Semiconductor, Inc.
23
24import sys
25
26from oslo.config import cfg
27
28from neutron.agent import securitygroups_rpc as sg_rpc
29from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
30from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
31from neutron.api.v2 import attributes
32from neutron.common import constants as q_const
33from neutron.common import exceptions as q_exc
34from neutron.common import rpc as q_rpc
35from neutron.common import topics
36from neutron.common import utils
37from neutron.db import agents_db
38from neutron.db import agentschedulers_db
39from neutron.db import allowedaddresspairs_db as addr_pair_db
40from neutron.db import db_base_plugin_v2
41from neutron.db import dhcp_rpc_base
42from neutron.db import external_net_db
43from neutron.db import extradhcpopt_db
44from neutron.db import extraroute_db
45from neutron.db import l3_agentschedulers_db
46from neutron.db import l3_gwmode_db
47from neutron.db import l3_rpc_base
48from neutron.db import portbindings_db
49from neutron.db import quota_db # noqa
50from neutron.db import securitygroups_rpc_base as sg_db_rpc
51from neutron.extensions import allowedaddresspairs as addr_pair
52from neutron.extensions import extra_dhcp_opt as edo_ext
53from neutron.extensions import portbindings
54from neutron.extensions import providernet as provider
55from neutron.extensions import nat
56from neutron import manager
57from neutron.openstack.common import importutils
58from neutron.openstack.common import log as logging
59from neutron.openstack.common import rpc
60from neutron.openstack.common.rpc import proxy
61from neutron.plugins.common import constants as svc_constants
62from neutron.plugins.common import utils as plugin_utils
63from neutron.plugins.openvswitch.common import config # noqa
64from neutron.plugins.openvswitch.common import constants
65from neutron.plugins.openvswitch import ovs_db_v2
66
67
68LOG = logging.getLogger(__name__)
69
70
71class OVSRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin,
72 l3_rpc_base.L3RpcCallbackMixin,
73 sg_db_rpc.SecurityGroupServerRpcCallbackMixin):
74
75 # history
76 # 1.0 Initial version
77 # 1.1 Support Security Group RPC
78
79 RPC_API_VERSION = '1.1'
80
81 def __init__(self, notifier, tunnel_type):
82 self.notifier = notifier
83 self.tunnel_type = tunnel_type
84
85 def create_rpc_dispatcher(self):
86 '''Get the rpc dispatcher for this manager.
87
88 If a manager would like to set an rpc API version, or support more than
89 one class as the target of rpc messages, override this method.
90 '''
91 return q_rpc.PluginRpcDispatcher([self,
92 agents_db.AgentExtRpcCallback()])
93
94 @classmethod
95 def get_port_from_device(cls, device):
96 port = ovs_db_v2.get_port_from_device(device)
97 if port:
98 port['device'] = device
99 return port
100
101 def get_device_details(self, rpc_context, **kwargs):
102 """Agent requests device details."""
103 agent_id = kwargs.get('agent_id')
104 device = kwargs.get('device')
105 LOG.debug(_("Device %(device)s details requested from %(agent_id)s"),
106 {'device': device, 'agent_id': agent_id})
107 port = ovs_db_v2.get_port(device)
108 if port:
109 binding = ovs_db_v2.get_network_binding(None, port['network_id'])
110 entry = {'device': device,
111 'network_id': port['network_id'],
112 'port_id': port['id'],
113 'admin_state_up': port['admin_state_up'],
114 'network_type': binding.network_type,
115 'segmentation_id': binding.segmentation_id,
116 'physical_network': binding.physical_network}
117 new_status = (q_const.PORT_STATUS_ACTIVE if port['admin_state_up']
118 else q_const.PORT_STATUS_DOWN)
119 if port['status'] != new_status:
120 ovs_db_v2.set_port_status(port['id'], new_status)
121 else:
122 entry = {'device': device}
123 LOG.debug(_("%s can not be found in database"), device)
124 return entry
125
126 def update_device_down(self, rpc_context, **kwargs):
127 """Device no longer exists on agent."""
128 agent_id = kwargs.get('agent_id')
129 device = kwargs.get('device')
130 host = kwargs.get('host')
131 port = ovs_db_v2.get_port(device)
132 LOG.debug(_("Device %(device)s no longer exists on %(agent_id)s"),
133 {'device': device, 'agent_id': agent_id})
134 if port:
135 entry = {'device': device,
136 'exists': True}
137 plugin = manager.NeutronManager.get_plugin()
138 if (host and
139 not plugin.get_port_host(rpc_context, port['id']) == host):
140 LOG.debug(_("Device %(device)s not bound to the"
141 " agent host %(host)s"),
142 {'device': device, 'host': host})
143 elif port['status'] != q_const.PORT_STATUS_DOWN:
144 # Set port status to DOWN
145 ovs_db_v2.set_port_status(port['id'],
146 q_const.PORT_STATUS_DOWN)
147 else:
148 entry = {'device': device,
149 'exists': False}
150 LOG.debug(_("%s can not be found in database"), device)
151 return entry
152
153 def update_device_up(self, rpc_context, **kwargs):
154 """Device is up on agent."""
155 agent_id = kwargs.get('agent_id')
156 device = kwargs.get('device')
157 host = kwargs.get('host')
158 port = ovs_db_v2.get_port(device)
159 LOG.debug(_("Device %(device)s up on %(agent_id)s"),
160 {'device': device, 'agent_id': agent_id})
161 plugin = manager.NeutronManager.get_plugin()
162 if port:
163 if (host and
164 not plugin.get_port_host(rpc_context, port['id']) == host):
165 LOG.debug(_("Device %(device)s not bound to the"
166 " agent host %(host)s"),
167 {'device': device, 'host': host})
168 return
169 elif port['status'] != q_const.PORT_STATUS_ACTIVE:
170 ovs_db_v2.set_port_status(port['id'],
171 q_const.PORT_STATUS_ACTIVE)
172 else:
173 LOG.debug(_("%s can not be found in database"), device)
174
175 def tunnel_sync(self, rpc_context, **kwargs):
176 """Update new tunnel.
177
178 Updates the datbase with the tunnel IP. All listening agents will also
179 be notified about the new tunnel IP.
180 """
181 tunnel_ip = kwargs.get('tunnel_ip')
182 # Update the database with the IP
183 tunnel = ovs_db_v2.add_tunnel_endpoint(tunnel_ip)
184 tunnels = ovs_db_v2.get_tunnel_endpoints()
185 entry = dict()
186 entry['tunnels'] = tunnels
187 # Notify all other listening agents
188 self.notifier.tunnel_update(rpc_context, tunnel.ip_address,
189 tunnel.id, self.tunnel_type)
190 # Return the list of tunnels IP's to the agent
191 return entry
192
193
194class AgentNotifierApi(proxy.RpcProxy,
195 sg_rpc.SecurityGroupAgentRpcApiMixin):
196 '''Agent side of the openvswitch rpc API.
197
198 API version history:
199 1.0 - Initial version.
200
201 '''
202
203 BASE_RPC_API_VERSION = '1.0'
204
205 def __init__(self, topic):
206 super(AgentNotifierApi, self).__init__(
207 topic=topic, default_version=self.BASE_RPC_API_VERSION)
208 self.topic_network_delete = topics.get_topic_name(topic,
209 topics.NETWORK,
210 topics.DELETE)
211 self.topic_port_update = topics.get_topic_name(topic,
212 topics.PORT,
213 topics.UPDATE)
214 self.topic_tunnel_update = topics.get_topic_name(topic,
215 constants.TUNNEL,
216 topics.UPDATE)
217
218 def network_delete(self, context, network_id):
219 self.fanout_cast(context,
220 self.make_msg('network_delete',
221 network_id=network_id),
222 topic=self.topic_network_delete)
223
224 def port_update(self, context, port, network_type, segmentation_id,
225 physical_network):
226 self.fanout_cast(context,
227 self.make_msg('port_update',
228 port=port,
229 network_type=network_type,
230 segmentation_id=segmentation_id,
231 physical_network=physical_network),
232 topic=self.topic_port_update)
233
234 def tunnel_update(self, context, tunnel_ip, tunnel_id, tunnel_type):
235 self.fanout_cast(context,
236 self.make_msg('tunnel_update',
237 tunnel_ip=tunnel_ip,
238 tunnel_id=tunnel_id,
239 tunnel_type=tunnel_type),
240 topic=self.topic_tunnel_update)
241
242
243class OVSNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
244 external_net_db.External_net_db_mixin,
245 extraroute_db.ExtraRoute_db_mixin,
246 l3_gwmode_db.L3_NAT_db_mixin,
247 sg_db_rpc.SecurityGroupServerRpcMixin,
248 l3_agentschedulers_db.L3AgentSchedulerDbMixin,
249 agentschedulers_db.DhcpAgentSchedulerDbMixin,
250 portbindings_db.PortBindingMixin,
251 extradhcpopt_db.ExtraDhcpOptMixin,
252 addr_pair_db.AllowedAddressPairsMixin):
253
254 """Implement the Neutron abstractions using Open vSwitch.
255
256 Depending on whether tunneling is enabled, either a GRE, VXLAN tunnel or
257 a new VLAN is created for each network. An agent is relied upon to
258 perform the actual OVS configuration on each host.
259
260 The provider extension is also supported. As discussed in
261 https://bugs.launchpad.net/neutron/+bug/1023156, this class could
262 be simplified, and filtering on extended attributes could be
263 handled, by adding support for extended attributes to the
264 NeutronDbPluginV2 base class. When that occurs, this class should
265 be updated to take advantage of it.
266
267 The port binding extension enables an external application relay
268 information to and from the plugin.
269 """
270
271 # This attribute specifies whether the plugin supports or not
272 # bulk/pagination/sorting operations. Name mangling is used in
273 # order to ensure it is qualified by class
274 __native_bulk_support = True
275 __native_pagination_support = True
276 __native_sorting_support = True
277
278 _supported_extension_aliases = ["provider", "external-net", "router",
279 "ext-gw-mode", "binding", "quotas",
280 "security-group", "agent", "extraroute",
281 "l3_agent_scheduler",
282 "dhcp_agent_scheduler",
283 "extra_dhcp_opt",
284 "allowed-address-pairs",
285 "nat"]
286
287 @property
288 def supported_extension_aliases(self):
289 if not hasattr(self, '_aliases'):
290 aliases = self._supported_extension_aliases[:]
291 sg_rpc.disable_security_group_extension_if_noop_driver(aliases)
292 self._aliases = aliases
293 return self._aliases
294
295 def __init__(self, configfile=None):
296 self.base_binding_dict = {
297 portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS,
298 portbindings.CAPABILITIES: {
299 portbindings.CAP_PORT_FILTER:
300 'security-group' in self.supported_extension_aliases}}
301 ovs_db_v2.initialize()
302 self._parse_network_vlan_ranges()
303 ovs_db_v2.sync_vlan_allocations(self.network_vlan_ranges)
304 self.tenant_network_type = cfg.CONF.OVS.tenant_network_type
305 if self.tenant_network_type not in [constants.TYPE_LOCAL,
306 constants.TYPE_VLAN,
307 constants.TYPE_GRE,
308 constants.TYPE_VXLAN,
309 constants.TYPE_NONE]:
310 LOG.error(_("Invalid tenant_network_type: %s. "
311 "Server terminated!"),
312 self.tenant_network_type)
313 sys.exit(1)
314 self.enable_tunneling = cfg.CONF.OVS.enable_tunneling
315 self.tunnel_type = None
316 if self.enable_tunneling:
317 self.tunnel_type = cfg.CONF.OVS.tunnel_type or constants.TYPE_GRE
318 elif cfg.CONF.OVS.tunnel_type:
319 self.tunnel_type = cfg.CONF.OVS.tunnel_type
320 self.enable_tunneling = True
321 self.tunnel_id_ranges = []
322 if self.enable_tunneling:
323 self._parse_tunnel_id_ranges()
324 ovs_db_v2.sync_tunnel_allocations(self.tunnel_id_ranges)
325 elif self.tenant_network_type in constants.TUNNEL_NETWORK_TYPES:
326 LOG.error(_("Tunneling disabled but tenant_network_type is '%s'. "
327 "Server terminated!"), self.tenant_network_type)
328 sys.exit(1)
329 self.setup_rpc()
330 self.network_scheduler = importutils.import_object(
331 cfg.CONF.network_scheduler_driver
332 )
333 self.router_scheduler = importutils.import_object(
334 cfg.CONF.router_scheduler_driver
335 )
336
337 def setup_rpc(self):
338 # RPC support
339 self.service_topics = {svc_constants.CORE: topics.PLUGIN,
340 svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN}
341 self.conn = rpc.create_connection(new=True)
342 self.notifier = AgentNotifierApi(topics.AGENT)
343 self.agent_notifiers[q_const.AGENT_TYPE_DHCP] = (
344 dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
345 )
346 self.agent_notifiers[q_const.AGENT_TYPE_L3] = (
347 l3_rpc_agent_api.L3AgentNotify
348 )
349 self.callbacks = OVSRpcCallbacks(self.notifier, self.tunnel_type)
350 self.dispatcher = self.callbacks.create_rpc_dispatcher()
351 for svc_topic in self.service_topics.values():
352 self.conn.create_consumer(svc_topic, self.dispatcher, fanout=False)
353 # Consume from all consumers in a thread
354 self.conn.consume_in_thread()
355
356 def _parse_network_vlan_ranges(self):
357 try:
358 self.network_vlan_ranges = plugin_utils.parse_network_vlan_ranges(
359 cfg.CONF.OVS.network_vlan_ranges)
360 except Exception as ex:
361 LOG.error(_("%s. Server terminated!"), ex)
362 sys.exit(1)
363 LOG.info(_("Network VLAN ranges: %s"), self.network_vlan_ranges)
364
365 def _parse_tunnel_id_ranges(self):
366 for entry in cfg.CONF.OVS.tunnel_id_ranges:
367 entry = entry.strip()
368 try:
369 tun_min, tun_max = entry.split(':')
370 self.tunnel_id_ranges.append((int(tun_min), int(tun_max)))
371 except ValueError as ex:
372 LOG.error(_("Invalid tunnel ID range: "
373 "'%(range)s' - %(e)s. Server terminated!"),
374 {'range': entry, 'e': ex})
375 sys.exit(1)
376 LOG.info(_("Tunnel ID ranges: %s"), self.tunnel_id_ranges)
377
378 def _extend_network_dict_provider(self, context, network):
379 binding = ovs_db_v2.get_network_binding(context.session,
380 network['id'])
381 network[provider.NETWORK_TYPE] = binding.network_type
382 if binding.network_type in constants.TUNNEL_NETWORK_TYPES:
383 network[provider.PHYSICAL_NETWORK] = None
384 network[provider.SEGMENTATION_ID] = binding.segmentation_id
385 elif binding.network_type == constants.TYPE_FLAT:
386 network[provider.PHYSICAL_NETWORK] = binding.physical_network
387 network[provider.SEGMENTATION_ID] = None
388 elif binding.network_type == constants.TYPE_VLAN:
389 network[provider.PHYSICAL_NETWORK] = binding.physical_network
390 network[provider.SEGMENTATION_ID] = binding.segmentation_id
391 elif binding.network_type == constants.TYPE_LOCAL:
392 network[provider.PHYSICAL_NETWORK] = None
393 network[provider.SEGMENTATION_ID] = None
394
395 def _process_provider_create(self, context, attrs):
396 network_type = attrs.get(provider.NETWORK_TYPE)
397 physical_network = attrs.get(provider.PHYSICAL_NETWORK)
398 segmentation_id = attrs.get(provider.SEGMENTATION_ID)
399
400 network_type_set = attributes.is_attr_set(network_type)
401 physical_network_set = attributes.is_attr_set(physical_network)
402 segmentation_id_set = attributes.is_attr_set(segmentation_id)
403
404 if not (network_type_set or physical_network_set or
405 segmentation_id_set):
406 return (None, None, None)
407
408 if not network_type_set:
409 msg = _("provider:network_type required")
410 raise q_exc.InvalidInput(error_message=msg)
411 elif network_type == constants.TYPE_FLAT:
412 if segmentation_id_set:
413 msg = _("provider:segmentation_id specified for flat network")
414 raise q_exc.InvalidInput(error_message=msg)
415 else:
416 segmentation_id = constants.FLAT_VLAN_ID
417 elif network_type == constants.TYPE_VLAN:
418 if not segmentation_id_set:
419 msg = _("provider:segmentation_id required")
420 raise q_exc.InvalidInput(error_message=msg)
421 if not utils.is_valid_vlan_tag(segmentation_id):
422 msg = (_("provider:segmentation_id out of range "
423 "(%(min_id)s through %(max_id)s)") %
424 {'min_id': q_const.MIN_VLAN_TAG,
425 'max_id': q_const.MAX_VLAN_TAG})
426 raise q_exc.InvalidInput(error_message=msg)
427 elif network_type in constants.TUNNEL_NETWORK_TYPES:
428 if not self.enable_tunneling:
429 msg = _("%s networks are not enabled") % network_type
430 raise q_exc.InvalidInput(error_message=msg)
431 if physical_network_set:
432 msg = _("provider:physical_network specified for %s "
433 "network") % network_type
434 raise q_exc.InvalidInput(error_message=msg)
435 else:
436 physical_network = None
437 if not segmentation_id_set:
438 msg = _("provider:segmentation_id required")
439 raise q_exc.InvalidInput(error_message=msg)
440 elif network_type == constants.TYPE_LOCAL:
441 if physical_network_set:
442 msg = _("provider:physical_network specified for local "
443 "network")
444 raise q_exc.InvalidInput(error_message=msg)
445 else:
446 physical_network = None
447 if segmentation_id_set:
448 msg = _("provider:segmentation_id specified for local "
449 "network")
450 raise q_exc.InvalidInput(error_message=msg)
451 else:
452 segmentation_id = None
453 else:
454 msg = _("provider:network_type %s not supported") % network_type
455 raise q_exc.InvalidInput(error_message=msg)
456
457 if network_type in [constants.TYPE_VLAN, constants.TYPE_FLAT]:
458 if physical_network_set:
459 if physical_network not in self.network_vlan_ranges:
460 msg = _("Unknown provider:physical_network "
461 "%s") % physical_network
462 raise q_exc.InvalidInput(error_message=msg)
463 elif 'default' in self.network_vlan_ranges:
464 physical_network = 'default'
465 else:
466 msg = _("provider:physical_network required")
467 raise q_exc.InvalidInput(error_message=msg)
468
469 return (network_type, physical_network, segmentation_id)
470
471 def create_network(self, context, network):
472 (network_type, physical_network,
473 segmentation_id) = self._process_provider_create(context,
474 network['network'])
475
476 session = context.session
477 #set up default security groups
478 tenant_id = self._get_tenant_id_for_create(
479 context, network['network'])
480 self._ensure_default_security_group(context, tenant_id)
481
482 with session.begin(subtransactions=True):
483 if not network_type:
484 # tenant network
485 network_type = self.tenant_network_type
486 if network_type == constants.TYPE_NONE:
487 raise q_exc.TenantNetworksDisabled()
488 elif network_type == constants.TYPE_VLAN:
489 (physical_network,
490 segmentation_id) = ovs_db_v2.reserve_vlan(session)
491 elif network_type in constants.TUNNEL_NETWORK_TYPES:
492 segmentation_id = ovs_db_v2.reserve_tunnel(session)
493 # no reservation needed for TYPE_LOCAL
494 else:
495 # provider network
496 if network_type in [constants.TYPE_VLAN, constants.TYPE_FLAT]:
497 ovs_db_v2.reserve_specific_vlan(session, physical_network,
498 segmentation_id)
499 elif network_type in constants.TUNNEL_NETWORK_TYPES:
500 ovs_db_v2.reserve_specific_tunnel(session, segmentation_id)
501 # no reservation needed for TYPE_LOCAL
502 net = super(OVSNeutronPluginV2, self).create_network(context,
503 network)
504 ovs_db_v2.add_network_binding(session, net['id'], network_type,
505 physical_network, segmentation_id)
506
507 self._process_l3_create(context, net, network['network'])
508 self._extend_network_dict_provider(context, net)
509 # note - exception will rollback entire transaction
510 LOG.debug(_("Created network: %s"), net['id'])
511 return net
512
513 def update_network(self, context, id, network):
514 provider._raise_if_updates_provider_attributes(network['network'])
515
516 session = context.session
517 with session.begin(subtransactions=True):
518 net = super(OVSNeutronPluginV2, self).update_network(context, id,
519 network)
520 self._process_l3_update(context, net, network['network'])
521 self._extend_network_dict_provider(context, net)
522 return net
523
524 def delete_network(self, context, id):
525 session = context.session
526 with session.begin(subtransactions=True):
527 binding = ovs_db_v2.get_network_binding(session, id)
528 super(OVSNeutronPluginV2, self).delete_network(context, id)
529 if binding.network_type in constants.TUNNEL_NETWORK_TYPES:
530 ovs_db_v2.release_tunnel(session, binding.segmentation_id,
531 self.tunnel_id_ranges)
532 elif binding.network_type in [constants.TYPE_VLAN,
533 constants.TYPE_FLAT]:
534 ovs_db_v2.release_vlan(session, binding.physical_network,
535 binding.segmentation_id,
536 self.network_vlan_ranges)
537 # the network_binding record is deleted via cascade from
538 # the network record, so explicit removal is not necessary
539 self.notifier.network_delete(context, id)
540
541 def get_network(self, context, id, fields=None):
542 session = context.session
543 with session.begin(subtransactions=True):
544 net = super(OVSNeutronPluginV2, self).get_network(context,
545 id, None)
546 self._extend_network_dict_provider(context, net)
547 return self._fields(net, fields)
548
549 def get_networks(self, context, filters=None, fields=None,
550 sorts=None,
551 limit=None, marker=None, page_reverse=False):
552 session = context.session
553 with session.begin(subtransactions=True):
554 nets = super(OVSNeutronPluginV2,
555 self).get_networks(context, filters, None, sorts,
556 limit, marker, page_reverse)
557 for net in nets:
558 self._extend_network_dict_provider(context, net)
559
560 return [self._fields(net, fields) for net in nets]
561
562 def create_port(self, context, port):
563 # Set port status as 'DOWN'. This will be updated by agent
564 port['port']['status'] = q_const.PORT_STATUS_DOWN
565 port_data = port['port']
566 session = context.session
567 with session.begin(subtransactions=True):
568 self._ensure_default_security_group_on_port(context, port)
569 sgids = self._get_security_groups_on_port(context, port)
570 dhcp_opts = port['port'].get(edo_ext.EXTRADHCPOPTS, [])
571 port = super(OVSNeutronPluginV2, self).create_port(context, port)
572 self._process_portbindings_create_and_update(context,
573 port_data, port)
574 self._process_port_create_security_group(context, port, sgids)
575 self._process_port_create_extra_dhcp_opts(context, port,
576 dhcp_opts)
577 port[addr_pair.ADDRESS_PAIRS] = (
578 self._process_create_allowed_address_pairs(
579 context, port,
580 port_data.get(addr_pair.ADDRESS_PAIRS)))
581 self.notify_security_groups_member_updated(context, port)
582 return port
583
584 def _extend_port_dict_nat(self, context, port):
585 forward = ovs_db_v2.get_port_forwarding(context.session, port['id'])
586 if forward:
587 port[nat.FORWARD_PORTS] = forward
588 else:
589 port[nat.FORWARD_PORTS] = None
590
591 def _process_nat_update(self, context, attrs, id):
592 forward_ports = attrs.get(nat.FORWARD_PORTS)
593 forward_ports_set = attributes.is_attr_set(forward_ports)
594
595 if not forward_ports_set:
596 return None
597
598 # LOG.info("forward ports %s" % forward_ports)
599 valid_protocols = ["tcp", "udp"]
600 for entry in forward_ports:
601 if not isinstance(entry, dict):
602 msg = _("nat:forward_ports: must specify a list of dicts (ex: 'l4_protocol=tcp,l4_port=80')")
603 raise q_exc.InvalidInput(error_message=msg)
604 if not ("l4_protocol" in entry and "l4_port" in entry):
605 msg = _("nat:forward_ports: dict is missing l4_protocol and l4_port (ex: 'l4_protocol=tcp,l4_port=80')")
606 raise q_exc.InvalidInput(error_message=msg)
607 if entry['l4_protocol'] not in valid_protocols:
608 msg = _("nat:forward_ports: invalid protocol (only tcp and udp allowed)")
609 raise q_exc.InvalidInput(error_message=msg)
610 try:
611 l4_port = int(entry['l4_port'])
612 except:
613 msg = _("nat:forward_ports: l4_port must be an integer")
614 raise q_exc.InvalidInput(error_message=msg)
615
616 return forward_ports
617
618 def get_port(self, context, id, fields=None):
619 session = context.session
620 with session.begin(subtransactions=True):
621 port = super(OVSNeutronPluginV2, self).get_port(context, id, None)
622 self._extend_port_dict_nat(context, port)
623 return self._fields(port, fields)
624
625 def get_ports(self, context, filters=None, fields=None):
626 session = context.session
627 with session.begin(subtransactions=True):
628 ports = super(OVSNeutronPluginV2, self).get_ports(context, filters,
629 None)
630 for port in ports:
631 self._extend_port_dict_nat(context, port)
632
633 return [self._fields(port, fields) for port in ports]
634
635 def update_port(self, context, id, port):
636 forward_ports = self._process_nat_update(context, port['port'], id)
637
638 session = context.session
639 need_port_update_notify = False
640 changed_fixed_ips = 'fixed_ips' in port['port']
641 with session.begin(subtransactions=True):
642 original_port = super(OVSNeutronPluginV2, self).get_port(
643 context, id)
644 updated_port = super(OVSNeutronPluginV2, self).update_port(
645 context, id, port)
646 if addr_pair.ADDRESS_PAIRS in port['port']:
647 self._delete_allowed_address_pairs(context, id)
648 self._process_create_allowed_address_pairs(
649 context, updated_port,
650 port['port'][addr_pair.ADDRESS_PAIRS])
651 need_port_update_notify = True
652 elif changed_fixed_ips:
653 self._check_fixed_ips_and_address_pairs_no_overlap(
654 context, updated_port)
655
656 if forward_ports:
657 ovs_db_v2.clear_port_forwarding(session, updated_port['id'])
658 ovs_db_v2.add_port_forwarding(session, updated_port['id'], forward_ports)
659 self._extend_port_dict_nat(context, updated_port)
660
661 need_port_update_notify |= self.update_security_group_on_port(
662 context, id, port, original_port, updated_port)
663 self._process_portbindings_create_and_update(context,
664 port['port'],
665 updated_port)
666 need_port_update_notify |= self._update_extra_dhcp_opts_on_port(
667 context, id, port, updated_port)
668
669 need_port_update_notify |= self.is_security_group_member_updated(
670 context, original_port, updated_port)
671 if original_port['admin_state_up'] != updated_port['admin_state_up']:
672 need_port_update_notify = True
673
674 if need_port_update_notify:
675 binding = ovs_db_v2.get_network_binding(None,
676 updated_port['network_id'])
677 self.notifier.port_update(context, updated_port,
678 binding.network_type,
679 binding.segmentation_id,
680 binding.physical_network)
681 return updated_port
682
683 def delete_port(self, context, id, l3_port_check=True):
684
685 # if needed, check to see if this is a port owned by
686 # and l3-router. If so, we should prevent deletion.
687 if l3_port_check:
688 self.prevent_l3_port_deletion(context, id)
689
690 session = context.session
691 with session.begin(subtransactions=True):
692 self.disassociate_floatingips(context, id)
693 port = self.get_port(context, id)
694 self._delete_port_security_group_bindings(context, id)
695 super(OVSNeutronPluginV2, self).delete_port(context, id)
696
697 self.notify_security_groups_member_updated(context, port)