blob: b7771f804b4d5fc579c6bd1b4c7d0575a58bdbf8 [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)
Scott Baker3c3ca112014-08-14 17:24:00 -0700610
611 l4_port = entry['l4_port']
612 if ":" in l4_port:
613 try:
614 (first, last) = l4_port.split(":")
615 first = int(first)
616 last = int(last)
617 except:
618 msg = _("nat:forward_ports: l4_port range must be integer:integer")
619 raise q_exc.InvalidInput(error_message=msg)
620 else:
621 try:
622 l4_port = int(l4_port)
623 except:
624 msg = _("nat:forward_ports: l4_port must be an integer")
625 raise q_exc.InvalidInput(error_message=msg)
Scott Baker352d4732014-08-14 16:10:59 -0700626
627 return forward_ports
628
629 def get_port(self, context, id, fields=None):
630 session = context.session
631 with session.begin(subtransactions=True):
632 port = super(OVSNeutronPluginV2, self).get_port(context, id, None)
633 self._extend_port_dict_nat(context, port)
634 return self._fields(port, fields)
635
636 def get_ports(self, context, filters=None, fields=None):
637 session = context.session
638 with session.begin(subtransactions=True):
639 ports = super(OVSNeutronPluginV2, self).get_ports(context, filters,
640 None)
641 for port in ports:
642 self._extend_port_dict_nat(context, port)
643
644 return [self._fields(port, fields) for port in ports]
645
646 def update_port(self, context, id, port):
647 forward_ports = self._process_nat_update(context, port['port'], id)
648
649 session = context.session
650 need_port_update_notify = False
651 changed_fixed_ips = 'fixed_ips' in port['port']
652 with session.begin(subtransactions=True):
653 original_port = super(OVSNeutronPluginV2, self).get_port(
654 context, id)
655 updated_port = super(OVSNeutronPluginV2, self).update_port(
656 context, id, port)
657 if addr_pair.ADDRESS_PAIRS in port['port']:
658 self._delete_allowed_address_pairs(context, id)
659 self._process_create_allowed_address_pairs(
660 context, updated_port,
661 port['port'][addr_pair.ADDRESS_PAIRS])
662 need_port_update_notify = True
663 elif changed_fixed_ips:
664 self._check_fixed_ips_and_address_pairs_no_overlap(
665 context, updated_port)
666
667 if forward_ports:
668 ovs_db_v2.clear_port_forwarding(session, updated_port['id'])
669 ovs_db_v2.add_port_forwarding(session, updated_port['id'], forward_ports)
670 self._extend_port_dict_nat(context, updated_port)
671
672 need_port_update_notify |= self.update_security_group_on_port(
673 context, id, port, original_port, updated_port)
674 self._process_portbindings_create_and_update(context,
675 port['port'],
676 updated_port)
677 need_port_update_notify |= self._update_extra_dhcp_opts_on_port(
678 context, id, port, updated_port)
679
680 need_port_update_notify |= self.is_security_group_member_updated(
681 context, original_port, updated_port)
682 if original_port['admin_state_up'] != updated_port['admin_state_up']:
683 need_port_update_notify = True
684
685 if need_port_update_notify:
686 binding = ovs_db_v2.get_network_binding(None,
687 updated_port['network_id'])
688 self.notifier.port_update(context, updated_port,
689 binding.network_type,
690 binding.segmentation_id,
691 binding.physical_network)
692 return updated_port
693
694 def delete_port(self, context, id, l3_port_check=True):
695
696 # if needed, check to see if this is a port owned by
697 # and l3-router. If so, we should prevent deletion.
698 if l3_port_check:
699 self.prevent_l3_port_deletion(context, id)
700
701 session = context.session
702 with session.begin(subtransactions=True):
703 self.disassociate_floatingips(context, id)
704 port = self.get_port(context, id)
705 self._delete_port_security_group_bindings(context, id)
706 super(OVSNeutronPluginV2, self).delete_port(context, id)
707
708 self.notify_security_groups_member_updated(context, port)