nat support for neutron
diff --git a/planetstack/neutron_extension/1:2013.2.2-0ubuntu1~cloud0/nat.py b/planetstack/neutron_extension/1:2013.2.2-0ubuntu1~cloud0/nat.py
new file mode 100644
index 0000000..04e39f1
--- /dev/null
+++ b/planetstack/neutron_extension/1:2013.2.2-0ubuntu1~cloud0/nat.py
@@ -0,0 +1,50 @@
+from neutron.api.v2 import attributes
+
+FORWARD_PORTS = 'nat:forward_ports'
+
+EXTENDED_ATTRIBUTES_2_0 = {
+ 'ports': {
+ FORWARD_PORTS: {'allow_post': True, 'allow_put': True,
+ 'default': attributes.ATTR_NOT_SPECIFIED,
+ 'is_visible': True},
+ }
+}
+
+
+class Nat(object):
+ """Extension class supporting OpenCloud NAT networking
+
+ This class is used by Quantum's extension framework to make
+ metadata about the OpenCloud Port extension available to
+ clients. No new resources are defined by this extension. Instead,
+ the existing Port resource's request and response messages are
+ extended with attributes in the OpenCloud namespace.
+ """
+
+ @classmethod
+ def get_name(cls):
+ return "OpenCloud NAT Networking Extension"
+
+ @classmethod
+ def get_alias(cls):
+ return "nat"
+
+ @classmethod
+ def get_description(cls):
+ return "Add TCP/UDP port forwarding through NAT to Quantum Port objects"
+
+ @classmethod
+ def get_namespace(cls):
+ # return "http://docs.openstack.org/ext/provider/api/v1.0"
+ # Nothing there right now
+ return "http://www.vicci.org/ext/opencloud/nat/api/v0.1"
+
+ @classmethod
+ def get_updated(cls):
+ return "2013-09-12T10:00:00-00:00"
+
+ def get_extended_resources(self, version):
+ if version == "2.0":
+ return EXTENDED_ATTRIBUTES_2_0
+ else:
+ return {}
diff --git a/planetstack/neutron_extension/1:2013.2.2-0ubuntu1~cloud0/ovs_db_v2.py b/planetstack/neutron_extension/1:2013.2.2-0ubuntu1~cloud0/ovs_db_v2.py
new file mode 100644
index 0000000..39cf315
--- /dev/null
+++ b/planetstack/neutron_extension/1:2013.2.2-0ubuntu1~cloud0/ovs_db_v2.py
@@ -0,0 +1,425 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright 2011 Nicira Networks, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+# @author: Aaron Rosen, Nicira Networks, Inc.
+# @author: Bob Kukura, Red Hat, Inc.
+
+from sqlalchemy import func
+from sqlalchemy.orm import exc
+
+from neutron.common import exceptions as q_exc
+import neutron.db.api as db
+from neutron.db import models_v2
+from neutron.db import securitygroups_db as sg_db
+from neutron.extensions import securitygroup as ext_sg
+from neutron import manager
+from neutron.openstack.common.db import exception as db_exc
+from neutron.openstack.common import log as logging
+from neutron.plugins.openvswitch.common import constants
+from neutron.plugins.openvswitch import ovs_models_v2
+
+LOG = logging.getLogger(__name__)
+
+
+def initialize():
+ db.configure_db()
+
+
+def get_network_binding(session, network_id):
+ session = session or db.get_session()
+ try:
+ binding = (session.query(ovs_models_v2.NetworkBinding).
+ filter_by(network_id=network_id).
+ one())
+ return binding
+ except exc.NoResultFound:
+ return
+
+
+def add_network_binding(session, network_id, network_type,
+ physical_network, segmentation_id):
+ with session.begin(subtransactions=True):
+ binding = ovs_models_v2.NetworkBinding(network_id, network_type,
+ physical_network,
+ segmentation_id)
+ session.add(binding)
+
+def get_port_forwarding(session, port_id):
+ session = session or db.get_session()
+ try:
+ forward = (session.query(ovs_models_v2.PortForwarding).
+ filter_by(port_id=port_id).one())
+ return forward['forward_ports']
+ except exc.NoResultFound:
+ return
+
+def clear_port_forwarding(session, port_id):
+ with session.begin(subtransactions=True):
+ try:
+ # Get rid of old port bindings
+ forward = (session.query(ovs_models_v2.PortForwarding).
+ filter_by(port_id=port_id).one())
+ if forward:
+ session.delete(forward)
+ except exc.NoResultFound:
+ pass
+
+def add_port_forwarding(session, port_id, forward_ports):
+ with session.begin(subtransactions=True):
+ forward = ovs_models_v2.PortForwarding(port_id, forward_ports)
+ session.add(forward)
+
+def sync_vlan_allocations(network_vlan_ranges):
+ """Synchronize vlan_allocations table with configured VLAN ranges."""
+
+ session = db.get_session()
+ with session.begin():
+ # get existing allocations for all physical networks
+ allocations = dict()
+ allocs = (session.query(ovs_models_v2.VlanAllocation).
+ all())
+ for alloc in allocs:
+ if alloc.physical_network not in allocations:
+ allocations[alloc.physical_network] = set()
+ allocations[alloc.physical_network].add(alloc)
+
+ # process vlan ranges for each configured physical network
+ for physical_network, vlan_ranges in network_vlan_ranges.iteritems():
+ # determine current configured allocatable vlans for this
+ # physical network
+ vlan_ids = set()
+ for vlan_range in vlan_ranges:
+ vlan_ids |= set(xrange(vlan_range[0], vlan_range[1] + 1))
+
+ # remove from table unallocated vlans not currently allocatable
+ if physical_network in allocations:
+ for alloc in allocations[physical_network]:
+ try:
+ # see if vlan is allocatable
+ vlan_ids.remove(alloc.vlan_id)
+ except KeyError:
+ # it's not allocatable, so check if its allocated
+ if not alloc.allocated:
+ # it's not, so remove it from table
+ LOG.debug(_("Removing vlan %(vlan_id)s on "
+ "physical network "
+ "%(physical_network)s from pool"),
+ {'vlan_id': alloc.vlan_id,
+ 'physical_network': physical_network})
+ session.delete(alloc)
+ del allocations[physical_network]
+
+ # add missing allocatable vlans to table
+ for vlan_id in sorted(vlan_ids):
+ alloc = ovs_models_v2.VlanAllocation(physical_network, vlan_id)
+ session.add(alloc)
+
+ # remove from table unallocated vlans for any unconfigured physical
+ # networks
+ for allocs in allocations.itervalues():
+ for alloc in allocs:
+ if not alloc.allocated:
+ LOG.debug(_("Removing vlan %(vlan_id)s on physical "
+ "network %(physical_network)s from pool"),
+ {'vlan_id': alloc.vlan_id,
+ 'physical_network': alloc.physical_network})
+ session.delete(alloc)
+
+
+def get_vlan_allocation(physical_network, vlan_id):
+ session = db.get_session()
+ try:
+ alloc = (session.query(ovs_models_v2.VlanAllocation).
+ filter_by(physical_network=physical_network,
+ vlan_id=vlan_id).
+ one())
+ return alloc
+ except exc.NoResultFound:
+ return
+
+
+def reserve_vlan(session):
+ with session.begin(subtransactions=True):
+ alloc = (session.query(ovs_models_v2.VlanAllocation).
+ filter_by(allocated=False).
+ with_lockmode('update').
+ first())
+ if alloc:
+ LOG.debug(_("Reserving vlan %(vlan_id)s on physical network "
+ "%(physical_network)s from pool"),
+ {'vlan_id': alloc.vlan_id,
+ 'physical_network': alloc.physical_network})
+ alloc.allocated = True
+ return (alloc.physical_network, alloc.vlan_id)
+ raise q_exc.NoNetworkAvailable()
+
+
+def reserve_specific_vlan(session, physical_network, vlan_id):
+ with session.begin(subtransactions=True):
+ try:
+ alloc = (session.query(ovs_models_v2.VlanAllocation).
+ filter_by(physical_network=physical_network,
+ vlan_id=vlan_id).
+ with_lockmode('update').
+ one())
+ if alloc.allocated:
+ if vlan_id == constants.FLAT_VLAN_ID:
+ raise q_exc.FlatNetworkInUse(
+ physical_network=physical_network)
+ else:
+ raise q_exc.VlanIdInUse(vlan_id=vlan_id,
+ physical_network=physical_network)
+ LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical "
+ "network %(physical_network)s from pool"),
+ {'vlan_id': vlan_id,
+ 'physical_network': physical_network})
+ alloc.allocated = True
+ except exc.NoResultFound:
+ LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical "
+ "network %(physical_network)s outside pool"),
+ {'vlan_id': vlan_id,
+ 'physical_network': physical_network})
+ alloc = ovs_models_v2.VlanAllocation(physical_network, vlan_id)
+ alloc.allocated = True
+ session.add(alloc)
+
+
+def release_vlan(session, physical_network, vlan_id, network_vlan_ranges):
+ with session.begin(subtransactions=True):
+ try:
+ alloc = (session.query(ovs_models_v2.VlanAllocation).
+ filter_by(physical_network=physical_network,
+ vlan_id=vlan_id).
+ with_lockmode('update').
+ one())
+ alloc.allocated = False
+ inside = False
+ for vlan_range in network_vlan_ranges.get(physical_network, []):
+ if vlan_id >= vlan_range[0] and vlan_id <= vlan_range[1]:
+ inside = True
+ break
+ if not inside:
+ session.delete(alloc)
+ LOG.debug(_("Releasing vlan %(vlan_id)s on physical network "
+ "%(physical_network)s outside pool"),
+ {'vlan_id': vlan_id,
+ 'physical_network': physical_network})
+ else:
+ LOG.debug(_("Releasing vlan %(vlan_id)s on physical network "
+ "%(physical_network)s to pool"),
+ {'vlan_id': vlan_id,
+ 'physical_network': physical_network})
+ except exc.NoResultFound:
+ LOG.warning(_("vlan_id %(vlan_id)s on physical network "
+ "%(physical_network)s not found"),
+ {'vlan_id': vlan_id,
+ 'physical_network': physical_network})
+
+
+def sync_tunnel_allocations(tunnel_id_ranges):
+ """Synchronize tunnel_allocations table with configured tunnel ranges."""
+
+ # determine current configured allocatable tunnels
+ tunnel_ids = set()
+ for tunnel_id_range in tunnel_id_ranges:
+ tun_min, tun_max = tunnel_id_range
+ if tun_max + 1 - tun_min > 1000000:
+ LOG.error(_("Skipping unreasonable tunnel ID range "
+ "%(tun_min)s:%(tun_max)s"),
+ {'tun_min': tun_min, 'tun_max': tun_max})
+ else:
+ tunnel_ids |= set(xrange(tun_min, tun_max + 1))
+
+ session = db.get_session()
+ with session.begin():
+ # remove from table unallocated tunnels not currently allocatable
+ allocs = (session.query(ovs_models_v2.TunnelAllocation).
+ all())
+ for alloc in allocs:
+ try:
+ # see if tunnel is allocatable
+ tunnel_ids.remove(alloc.tunnel_id)
+ except KeyError:
+ # it's not allocatable, so check if its allocated
+ if not alloc.allocated:
+ # it's not, so remove it from table
+ LOG.debug(_("Removing tunnel %s from pool"),
+ alloc.tunnel_id)
+ session.delete(alloc)
+
+ # add missing allocatable tunnels to table
+ for tunnel_id in sorted(tunnel_ids):
+ alloc = ovs_models_v2.TunnelAllocation(tunnel_id)
+ session.add(alloc)
+
+
+def get_tunnel_allocation(tunnel_id):
+ session = db.get_session()
+ try:
+ alloc = (session.query(ovs_models_v2.TunnelAllocation).
+ filter_by(tunnel_id=tunnel_id).
+ with_lockmode('update').
+ one())
+ return alloc
+ except exc.NoResultFound:
+ return
+
+
+def reserve_tunnel(session):
+ with session.begin(subtransactions=True):
+ alloc = (session.query(ovs_models_v2.TunnelAllocation).
+ filter_by(allocated=False).
+ with_lockmode('update').
+ first())
+ if alloc:
+ LOG.debug(_("Reserving tunnel %s from pool"), alloc.tunnel_id)
+ alloc.allocated = True
+ return alloc.tunnel_id
+ raise q_exc.NoNetworkAvailable()
+
+
+def reserve_specific_tunnel(session, tunnel_id):
+ with session.begin(subtransactions=True):
+ try:
+ alloc = (session.query(ovs_models_v2.TunnelAllocation).
+ filter_by(tunnel_id=tunnel_id).
+ with_lockmode('update').
+ one())
+ if alloc.allocated:
+ raise q_exc.TunnelIdInUse(tunnel_id=tunnel_id)
+ LOG.debug(_("Reserving specific tunnel %s from pool"), tunnel_id)
+ alloc.allocated = True
+ except exc.NoResultFound:
+ LOG.debug(_("Reserving specific tunnel %s outside pool"),
+ tunnel_id)
+ alloc = ovs_models_v2.TunnelAllocation(tunnel_id)
+ alloc.allocated = True
+ session.add(alloc)
+
+
+def release_tunnel(session, tunnel_id, tunnel_id_ranges):
+ with session.begin(subtransactions=True):
+ try:
+ alloc = (session.query(ovs_models_v2.TunnelAllocation).
+ filter_by(tunnel_id=tunnel_id).
+ with_lockmode('update').
+ one())
+ alloc.allocated = False
+ inside = False
+ for tunnel_id_range in tunnel_id_ranges:
+ if (tunnel_id >= tunnel_id_range[0]
+ and tunnel_id <= tunnel_id_range[1]):
+ inside = True
+ break
+ if not inside:
+ session.delete(alloc)
+ LOG.debug(_("Releasing tunnel %s outside pool"), tunnel_id)
+ else:
+ LOG.debug(_("Releasing tunnel %s to pool"), tunnel_id)
+ except exc.NoResultFound:
+ LOG.warning(_("tunnel_id %s not found"), tunnel_id)
+
+
+def get_port(port_id):
+ session = db.get_session()
+ try:
+ port = session.query(models_v2.Port).filter_by(id=port_id).one()
+ except exc.NoResultFound:
+ port = None
+ return port
+
+
+def get_port_from_device(port_id):
+ """Get port from database."""
+ LOG.debug(_("get_port_with_securitygroups() called:port_id=%s"), port_id)
+ session = db.get_session()
+ sg_binding_port = sg_db.SecurityGroupPortBinding.port_id
+
+ query = session.query(models_v2.Port,
+ sg_db.SecurityGroupPortBinding.security_group_id)
+ query = query.outerjoin(sg_db.SecurityGroupPortBinding,
+ models_v2.Port.id == sg_binding_port)
+ query = query.filter(models_v2.Port.id == port_id)
+ port_and_sgs = query.all()
+ if not port_and_sgs:
+ return None
+ port = port_and_sgs[0][0]
+ plugin = manager.NeutronManager.get_plugin()
+ port_dict = plugin._make_port_dict(port)
+ port_dict[ext_sg.SECURITYGROUPS] = [
+ sg_id for port_, sg_id in port_and_sgs if sg_id]
+ port_dict['security_group_rules'] = []
+ port_dict['security_group_source_groups'] = []
+ port_dict['fixed_ips'] = [ip['ip_address']
+ for ip in port['fixed_ips']]
+ return port_dict
+
+
+def set_port_status(port_id, status):
+ session = db.get_session()
+ try:
+ port = session.query(models_v2.Port).filter_by(id=port_id).one()
+ port['status'] = status
+ session.merge(port)
+ session.flush()
+ except exc.NoResultFound:
+ raise q_exc.PortNotFound(port_id=port_id)
+
+
+def get_tunnel_endpoints():
+ session = db.get_session()
+
+ tunnels = session.query(ovs_models_v2.TunnelEndpoint)
+ return [{'id': tunnel.id,
+ 'ip_address': tunnel.ip_address} for tunnel in tunnels]
+
+
+def _generate_tunnel_id(session):
+ max_tunnel_id = session.query(
+ func.max(ovs_models_v2.TunnelEndpoint.id)).scalar() or 0
+ return max_tunnel_id + 1
+
+
+def add_tunnel_endpoint(ip, max_retries=10):
+ """Return the endpoint of the given IP address or generate a new one."""
+
+ # NOTE(rpodolyaka): generation of a new tunnel endpoint must be put into a
+ # repeatedly executed transactional block to ensure it
+ # doesn't conflict with any other concurrently executed
+ # DB transactions in spite of the specified transactions
+ # isolation level value
+ for i in xrange(max_retries):
+ LOG.debug(_('Adding a tunnel endpoint for %s'), ip)
+ try:
+ session = db.get_session()
+ with session.begin(subtransactions=True):
+ tunnel = (session.query(ovs_models_v2.TunnelEndpoint).
+ filter_by(ip_address=ip).with_lockmode('update').
+ first())
+
+ if tunnel is None:
+ tunnel_id = _generate_tunnel_id(session)
+ tunnel = ovs_models_v2.TunnelEndpoint(ip, tunnel_id)
+ session.add(tunnel)
+
+ return tunnel
+ except db_exc.DBDuplicateEntry:
+ # a concurrent transaction has been commited, try again
+ LOG.debug(_('Adding a tunnel endpoint failed due to a concurrent'
+ 'transaction had been commited (%s attempts left)'),
+ max_retries - (i + 1))
+
+ raise q_exc.NeutronException(
+ message=_('Unable to generate a new tunnel id'))
diff --git a/planetstack/neutron_extension/1:2013.2.2-0ubuntu1~cloud0/ovs_models_v2.py b/planetstack/neutron_extension/1:2013.2.2-0ubuntu1~cloud0/ovs_models_v2.py
new file mode 100644
index 0000000..7e022f5
--- /dev/null
+++ b/planetstack/neutron_extension/1:2013.2.2-0ubuntu1~cloud0/ovs_models_v2.py
@@ -0,0 +1,118 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright 2011 Nicira Networks, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+# @author: Aaron Rosen, Nicira Networks, Inc.
+# @author: Bob Kukura, Red Hat, Inc.
+
+
+from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, PickleType
+from sqlalchemy.schema import UniqueConstraint
+
+from neutron.db.models_v2 import model_base
+
+
+class VlanAllocation(model_base.BASEV2):
+ """Represents allocation state of vlan_id on physical network."""
+ __tablename__ = 'ovs_vlan_allocations'
+
+ physical_network = Column(String(64), nullable=False, primary_key=True)
+ vlan_id = Column(Integer, nullable=False, primary_key=True,
+ autoincrement=False)
+ allocated = Column(Boolean, nullable=False)
+
+ def __init__(self, physical_network, vlan_id):
+ self.physical_network = physical_network
+ self.vlan_id = vlan_id
+ self.allocated = False
+
+ def __repr__(self):
+ return "<VlanAllocation(%s,%d,%s)>" % (self.physical_network,
+ self.vlan_id, self.allocated)
+
+
+class TunnelAllocation(model_base.BASEV2):
+ """Represents allocation state of tunnel_id."""
+ __tablename__ = 'ovs_tunnel_allocations'
+
+ tunnel_id = Column(Integer, nullable=False, primary_key=True,
+ autoincrement=False)
+ allocated = Column(Boolean, nullable=False)
+
+ def __init__(self, tunnel_id):
+ self.tunnel_id = tunnel_id
+ self.allocated = False
+
+ def __repr__(self):
+ return "<TunnelAllocation(%d,%s)>" % (self.tunnel_id, self.allocated)
+
+
+class NetworkBinding(model_base.BASEV2):
+ """Represents binding of virtual network to physical realization."""
+ __tablename__ = 'ovs_network_bindings'
+
+ network_id = Column(String(36),
+ ForeignKey('networks.id', ondelete="CASCADE"),
+ primary_key=True)
+ # 'gre', 'vlan', 'flat', 'local'
+ network_type = Column(String(32), nullable=False)
+ physical_network = Column(String(64))
+ segmentation_id = Column(Integer) # tunnel_id or vlan_id
+
+ def __init__(self, network_id, network_type, physical_network,
+ segmentation_id):
+ self.network_id = network_id
+ self.network_type = network_type
+ self.physical_network = physical_network
+ self.segmentation_id = segmentation_id
+
+ def __repr__(self):
+ return "<NetworkBinding(%s,%s,%s,%d)>" % (self.network_id,
+ self.network_type,
+ self.physical_network,
+ self.segmentation_id)
+
+class PortForwarding(model_base.BASEV2):
+ """Ports to be forwarded through NAT """
+ __tablename__ = 'ovs_port_forwarding'
+
+ port_id = Column(String(36),
+ ForeignKey('ports.id', ondelete="CASCADE"),
+ primary_key=True)
+ forward_ports = Column(PickleType)
+
+ def __init__(self, port_id, forward_ports):
+ self.port_id = port_id
+ self.forward_ports = forward_ports
+
+ def __repr__(self):
+ return "<PortForwarding(%s,%s)>" % (self.port_id, self.forward_ports)
+
+class TunnelEndpoint(model_base.BASEV2):
+ """Represents tunnel endpoint in RPC mode."""
+ __tablename__ = 'ovs_tunnel_endpoints'
+ __table_args__ = (
+ UniqueConstraint('id', name='uniq_ovs_tunnel_endpoints0id'),
+ )
+
+ ip_address = Column(String(64), primary_key=True)
+ id = Column(Integer, nullable=False)
+
+ def __init__(self, ip_address, id):
+ self.ip_address = ip_address
+ self.id = id
+
+ def __repr__(self):
+ return "<TunnelEndpoint(%s,%s)>" % (self.ip_address, self.id)
+
diff --git a/planetstack/neutron_extension/1:2013.2.2-0ubuntu1~cloud0/ovs_neutron_plugin.py b/planetstack/neutron_extension/1:2013.2.2-0ubuntu1~cloud0/ovs_neutron_plugin.py
new file mode 100644
index 0000000..cacf165
--- /dev/null
+++ b/planetstack/neutron_extension/1:2013.2.2-0ubuntu1~cloud0/ovs_neutron_plugin.py
@@ -0,0 +1,697 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright 2011 Nicira Networks, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+# @author: Somik Behera, Nicira Networks, Inc.
+# @author: Brad Hall, Nicira Networks, Inc.
+# @author: Dan Wendlandt, Nicira Networks, Inc.
+# @author: Dave Lapsley, Nicira Networks, Inc.
+# @author: Aaron Rosen, Nicira Networks, Inc.
+# @author: Bob Kukura, Red Hat, Inc.
+# @author: Seetharama Ayyadevara, Freescale Semiconductor, Inc.
+
+import sys
+
+from oslo.config import cfg
+
+from neutron.agent import securitygroups_rpc as sg_rpc
+from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
+from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
+from neutron.api.v2 import attributes
+from neutron.common import constants as q_const
+from neutron.common import exceptions as q_exc
+from neutron.common import rpc as q_rpc
+from neutron.common import topics
+from neutron.common import utils
+from neutron.db import agents_db
+from neutron.db import agentschedulers_db
+from neutron.db import allowedaddresspairs_db as addr_pair_db
+from neutron.db import db_base_plugin_v2
+from neutron.db import dhcp_rpc_base
+from neutron.db import external_net_db
+from neutron.db import extradhcpopt_db
+from neutron.db import extraroute_db
+from neutron.db import l3_agentschedulers_db
+from neutron.db import l3_gwmode_db
+from neutron.db import l3_rpc_base
+from neutron.db import portbindings_db
+from neutron.db import quota_db # noqa
+from neutron.db import securitygroups_rpc_base as sg_db_rpc
+from neutron.extensions import allowedaddresspairs as addr_pair
+from neutron.extensions import extra_dhcp_opt as edo_ext
+from neutron.extensions import portbindings
+from neutron.extensions import providernet as provider
+from neutron.extensions import nat
+from neutron import manager
+from neutron.openstack.common import importutils
+from neutron.openstack.common import log as logging
+from neutron.openstack.common import rpc
+from neutron.openstack.common.rpc import proxy
+from neutron.plugins.common import constants as svc_constants
+from neutron.plugins.common import utils as plugin_utils
+from neutron.plugins.openvswitch.common import config # noqa
+from neutron.plugins.openvswitch.common import constants
+from neutron.plugins.openvswitch import ovs_db_v2
+
+
+LOG = logging.getLogger(__name__)
+
+
+class OVSRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin,
+ l3_rpc_base.L3RpcCallbackMixin,
+ sg_db_rpc.SecurityGroupServerRpcCallbackMixin):
+
+ # history
+ # 1.0 Initial version
+ # 1.1 Support Security Group RPC
+
+ RPC_API_VERSION = '1.1'
+
+ def __init__(self, notifier, tunnel_type):
+ self.notifier = notifier
+ self.tunnel_type = tunnel_type
+
+ def create_rpc_dispatcher(self):
+ '''Get the rpc dispatcher for this manager.
+
+ If a manager would like to set an rpc API version, or support more than
+ one class as the target of rpc messages, override this method.
+ '''
+ return q_rpc.PluginRpcDispatcher([self,
+ agents_db.AgentExtRpcCallback()])
+
+ @classmethod
+ def get_port_from_device(cls, device):
+ port = ovs_db_v2.get_port_from_device(device)
+ if port:
+ port['device'] = device
+ return port
+
+ def get_device_details(self, rpc_context, **kwargs):
+ """Agent requests device details."""
+ agent_id = kwargs.get('agent_id')
+ device = kwargs.get('device')
+ LOG.debug(_("Device %(device)s details requested from %(agent_id)s"),
+ {'device': device, 'agent_id': agent_id})
+ port = ovs_db_v2.get_port(device)
+ if port:
+ binding = ovs_db_v2.get_network_binding(None, port['network_id'])
+ entry = {'device': device,
+ 'network_id': port['network_id'],
+ 'port_id': port['id'],
+ 'admin_state_up': port['admin_state_up'],
+ 'network_type': binding.network_type,
+ 'segmentation_id': binding.segmentation_id,
+ 'physical_network': binding.physical_network}
+ new_status = (q_const.PORT_STATUS_ACTIVE if port['admin_state_up']
+ else q_const.PORT_STATUS_DOWN)
+ if port['status'] != new_status:
+ ovs_db_v2.set_port_status(port['id'], new_status)
+ else:
+ entry = {'device': device}
+ LOG.debug(_("%s can not be found in database"), device)
+ return entry
+
+ def update_device_down(self, rpc_context, **kwargs):
+ """Device no longer exists on agent."""
+ agent_id = kwargs.get('agent_id')
+ device = kwargs.get('device')
+ host = kwargs.get('host')
+ port = ovs_db_v2.get_port(device)
+ LOG.debug(_("Device %(device)s no longer exists on %(agent_id)s"),
+ {'device': device, 'agent_id': agent_id})
+ if port:
+ entry = {'device': device,
+ 'exists': True}
+ plugin = manager.NeutronManager.get_plugin()
+ if (host and
+ not plugin.get_port_host(rpc_context, port['id']) == host):
+ LOG.debug(_("Device %(device)s not bound to the"
+ " agent host %(host)s"),
+ {'device': device, 'host': host})
+ elif port['status'] != q_const.PORT_STATUS_DOWN:
+ # Set port status to DOWN
+ ovs_db_v2.set_port_status(port['id'],
+ q_const.PORT_STATUS_DOWN)
+ else:
+ entry = {'device': device,
+ 'exists': False}
+ LOG.debug(_("%s can not be found in database"), device)
+ return entry
+
+ def update_device_up(self, rpc_context, **kwargs):
+ """Device is up on agent."""
+ agent_id = kwargs.get('agent_id')
+ device = kwargs.get('device')
+ host = kwargs.get('host')
+ port = ovs_db_v2.get_port(device)
+ LOG.debug(_("Device %(device)s up on %(agent_id)s"),
+ {'device': device, 'agent_id': agent_id})
+ plugin = manager.NeutronManager.get_plugin()
+ if port:
+ if (host and
+ not plugin.get_port_host(rpc_context, port['id']) == host):
+ LOG.debug(_("Device %(device)s not bound to the"
+ " agent host %(host)s"),
+ {'device': device, 'host': host})
+ return
+ elif port['status'] != q_const.PORT_STATUS_ACTIVE:
+ ovs_db_v2.set_port_status(port['id'],
+ q_const.PORT_STATUS_ACTIVE)
+ else:
+ LOG.debug(_("%s can not be found in database"), device)
+
+ def tunnel_sync(self, rpc_context, **kwargs):
+ """Update new tunnel.
+
+ Updates the datbase with the tunnel IP. All listening agents will also
+ be notified about the new tunnel IP.
+ """
+ tunnel_ip = kwargs.get('tunnel_ip')
+ # Update the database with the IP
+ tunnel = ovs_db_v2.add_tunnel_endpoint(tunnel_ip)
+ tunnels = ovs_db_v2.get_tunnel_endpoints()
+ entry = dict()
+ entry['tunnels'] = tunnels
+ # Notify all other listening agents
+ self.notifier.tunnel_update(rpc_context, tunnel.ip_address,
+ tunnel.id, self.tunnel_type)
+ # Return the list of tunnels IP's to the agent
+ return entry
+
+
+class AgentNotifierApi(proxy.RpcProxy,
+ sg_rpc.SecurityGroupAgentRpcApiMixin):
+ '''Agent side of the openvswitch rpc API.
+
+ API version history:
+ 1.0 - Initial version.
+
+ '''
+
+ BASE_RPC_API_VERSION = '1.0'
+
+ def __init__(self, topic):
+ super(AgentNotifierApi, self).__init__(
+ topic=topic, default_version=self.BASE_RPC_API_VERSION)
+ self.topic_network_delete = topics.get_topic_name(topic,
+ topics.NETWORK,
+ topics.DELETE)
+ self.topic_port_update = topics.get_topic_name(topic,
+ topics.PORT,
+ topics.UPDATE)
+ self.topic_tunnel_update = topics.get_topic_name(topic,
+ constants.TUNNEL,
+ topics.UPDATE)
+
+ def network_delete(self, context, network_id):
+ self.fanout_cast(context,
+ self.make_msg('network_delete',
+ network_id=network_id),
+ topic=self.topic_network_delete)
+
+ def port_update(self, context, port, network_type, segmentation_id,
+ physical_network):
+ self.fanout_cast(context,
+ self.make_msg('port_update',
+ port=port,
+ network_type=network_type,
+ segmentation_id=segmentation_id,
+ physical_network=physical_network),
+ topic=self.topic_port_update)
+
+ def tunnel_update(self, context, tunnel_ip, tunnel_id, tunnel_type):
+ self.fanout_cast(context,
+ self.make_msg('tunnel_update',
+ tunnel_ip=tunnel_ip,
+ tunnel_id=tunnel_id,
+ tunnel_type=tunnel_type),
+ topic=self.topic_tunnel_update)
+
+
+class OVSNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
+ external_net_db.External_net_db_mixin,
+ extraroute_db.ExtraRoute_db_mixin,
+ l3_gwmode_db.L3_NAT_db_mixin,
+ sg_db_rpc.SecurityGroupServerRpcMixin,
+ l3_agentschedulers_db.L3AgentSchedulerDbMixin,
+ agentschedulers_db.DhcpAgentSchedulerDbMixin,
+ portbindings_db.PortBindingMixin,
+ extradhcpopt_db.ExtraDhcpOptMixin,
+ addr_pair_db.AllowedAddressPairsMixin):
+
+ """Implement the Neutron abstractions using Open vSwitch.
+
+ Depending on whether tunneling is enabled, either a GRE, VXLAN tunnel or
+ a new VLAN is created for each network. An agent is relied upon to
+ perform the actual OVS configuration on each host.
+
+ The provider extension is also supported. As discussed in
+ https://bugs.launchpad.net/neutron/+bug/1023156, this class could
+ be simplified, and filtering on extended attributes could be
+ handled, by adding support for extended attributes to the
+ NeutronDbPluginV2 base class. When that occurs, this class should
+ be updated to take advantage of it.
+
+ The port binding extension enables an external application relay
+ information to and from the plugin.
+ """
+
+ # This attribute specifies whether the plugin supports or not
+ # bulk/pagination/sorting operations. Name mangling is used in
+ # order to ensure it is qualified by class
+ __native_bulk_support = True
+ __native_pagination_support = True
+ __native_sorting_support = True
+
+ _supported_extension_aliases = ["provider", "external-net", "router",
+ "ext-gw-mode", "binding", "quotas",
+ "security-group", "agent", "extraroute",
+ "l3_agent_scheduler",
+ "dhcp_agent_scheduler",
+ "extra_dhcp_opt",
+ "allowed-address-pairs",
+ "nat"]
+
+ @property
+ def supported_extension_aliases(self):
+ if not hasattr(self, '_aliases'):
+ aliases = self._supported_extension_aliases[:]
+ sg_rpc.disable_security_group_extension_if_noop_driver(aliases)
+ self._aliases = aliases
+ return self._aliases
+
+ def __init__(self, configfile=None):
+ self.base_binding_dict = {
+ portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS,
+ portbindings.CAPABILITIES: {
+ portbindings.CAP_PORT_FILTER:
+ 'security-group' in self.supported_extension_aliases}}
+ ovs_db_v2.initialize()
+ self._parse_network_vlan_ranges()
+ ovs_db_v2.sync_vlan_allocations(self.network_vlan_ranges)
+ self.tenant_network_type = cfg.CONF.OVS.tenant_network_type
+ if self.tenant_network_type not in [constants.TYPE_LOCAL,
+ constants.TYPE_VLAN,
+ constants.TYPE_GRE,
+ constants.TYPE_VXLAN,
+ constants.TYPE_NONE]:
+ LOG.error(_("Invalid tenant_network_type: %s. "
+ "Server terminated!"),
+ self.tenant_network_type)
+ sys.exit(1)
+ self.enable_tunneling = cfg.CONF.OVS.enable_tunneling
+ self.tunnel_type = None
+ if self.enable_tunneling:
+ self.tunnel_type = cfg.CONF.OVS.tunnel_type or constants.TYPE_GRE
+ elif cfg.CONF.OVS.tunnel_type:
+ self.tunnel_type = cfg.CONF.OVS.tunnel_type
+ self.enable_tunneling = True
+ self.tunnel_id_ranges = []
+ if self.enable_tunneling:
+ self._parse_tunnel_id_ranges()
+ ovs_db_v2.sync_tunnel_allocations(self.tunnel_id_ranges)
+ elif self.tenant_network_type in constants.TUNNEL_NETWORK_TYPES:
+ LOG.error(_("Tunneling disabled but tenant_network_type is '%s'. "
+ "Server terminated!"), self.tenant_network_type)
+ sys.exit(1)
+ self.setup_rpc()
+ self.network_scheduler = importutils.import_object(
+ cfg.CONF.network_scheduler_driver
+ )
+ self.router_scheduler = importutils.import_object(
+ cfg.CONF.router_scheduler_driver
+ )
+
+ def setup_rpc(self):
+ # RPC support
+ self.service_topics = {svc_constants.CORE: topics.PLUGIN,
+ svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN}
+ self.conn = rpc.create_connection(new=True)
+ self.notifier = AgentNotifierApi(topics.AGENT)
+ self.agent_notifiers[q_const.AGENT_TYPE_DHCP] = (
+ dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
+ )
+ self.agent_notifiers[q_const.AGENT_TYPE_L3] = (
+ l3_rpc_agent_api.L3AgentNotify
+ )
+ self.callbacks = OVSRpcCallbacks(self.notifier, self.tunnel_type)
+ self.dispatcher = self.callbacks.create_rpc_dispatcher()
+ for svc_topic in self.service_topics.values():
+ self.conn.create_consumer(svc_topic, self.dispatcher, fanout=False)
+ # Consume from all consumers in a thread
+ self.conn.consume_in_thread()
+
+ def _parse_network_vlan_ranges(self):
+ try:
+ self.network_vlan_ranges = plugin_utils.parse_network_vlan_ranges(
+ cfg.CONF.OVS.network_vlan_ranges)
+ except Exception as ex:
+ LOG.error(_("%s. Server terminated!"), ex)
+ sys.exit(1)
+ LOG.info(_("Network VLAN ranges: %s"), self.network_vlan_ranges)
+
+ def _parse_tunnel_id_ranges(self):
+ for entry in cfg.CONF.OVS.tunnel_id_ranges:
+ entry = entry.strip()
+ try:
+ tun_min, tun_max = entry.split(':')
+ self.tunnel_id_ranges.append((int(tun_min), int(tun_max)))
+ except ValueError as ex:
+ LOG.error(_("Invalid tunnel ID range: "
+ "'%(range)s' - %(e)s. Server terminated!"),
+ {'range': entry, 'e': ex})
+ sys.exit(1)
+ LOG.info(_("Tunnel ID ranges: %s"), self.tunnel_id_ranges)
+
+ def _extend_network_dict_provider(self, context, network):
+ binding = ovs_db_v2.get_network_binding(context.session,
+ network['id'])
+ network[provider.NETWORK_TYPE] = binding.network_type
+ if binding.network_type in constants.TUNNEL_NETWORK_TYPES:
+ network[provider.PHYSICAL_NETWORK] = None
+ network[provider.SEGMENTATION_ID] = binding.segmentation_id
+ elif binding.network_type == constants.TYPE_FLAT:
+ network[provider.PHYSICAL_NETWORK] = binding.physical_network
+ network[provider.SEGMENTATION_ID] = None
+ elif binding.network_type == constants.TYPE_VLAN:
+ network[provider.PHYSICAL_NETWORK] = binding.physical_network
+ network[provider.SEGMENTATION_ID] = binding.segmentation_id
+ elif binding.network_type == constants.TYPE_LOCAL:
+ network[provider.PHYSICAL_NETWORK] = None
+ network[provider.SEGMENTATION_ID] = None
+
+ def _process_provider_create(self, context, attrs):
+ network_type = attrs.get(provider.NETWORK_TYPE)
+ physical_network = attrs.get(provider.PHYSICAL_NETWORK)
+ segmentation_id = attrs.get(provider.SEGMENTATION_ID)
+
+ network_type_set = attributes.is_attr_set(network_type)
+ physical_network_set = attributes.is_attr_set(physical_network)
+ segmentation_id_set = attributes.is_attr_set(segmentation_id)
+
+ if not (network_type_set or physical_network_set or
+ segmentation_id_set):
+ return (None, None, None)
+
+ if not network_type_set:
+ msg = _("provider:network_type required")
+ raise q_exc.InvalidInput(error_message=msg)
+ elif network_type == constants.TYPE_FLAT:
+ if segmentation_id_set:
+ msg = _("provider:segmentation_id specified for flat network")
+ raise q_exc.InvalidInput(error_message=msg)
+ else:
+ segmentation_id = constants.FLAT_VLAN_ID
+ elif network_type == constants.TYPE_VLAN:
+ if not segmentation_id_set:
+ msg = _("provider:segmentation_id required")
+ raise q_exc.InvalidInput(error_message=msg)
+ if not utils.is_valid_vlan_tag(segmentation_id):
+ msg = (_("provider:segmentation_id out of range "
+ "(%(min_id)s through %(max_id)s)") %
+ {'min_id': q_const.MIN_VLAN_TAG,
+ 'max_id': q_const.MAX_VLAN_TAG})
+ raise q_exc.InvalidInput(error_message=msg)
+ elif network_type in constants.TUNNEL_NETWORK_TYPES:
+ if not self.enable_tunneling:
+ msg = _("%s networks are not enabled") % network_type
+ raise q_exc.InvalidInput(error_message=msg)
+ if physical_network_set:
+ msg = _("provider:physical_network specified for %s "
+ "network") % network_type
+ raise q_exc.InvalidInput(error_message=msg)
+ else:
+ physical_network = None
+ if not segmentation_id_set:
+ msg = _("provider:segmentation_id required")
+ raise q_exc.InvalidInput(error_message=msg)
+ elif network_type == constants.TYPE_LOCAL:
+ if physical_network_set:
+ msg = _("provider:physical_network specified for local "
+ "network")
+ raise q_exc.InvalidInput(error_message=msg)
+ else:
+ physical_network = None
+ if segmentation_id_set:
+ msg = _("provider:segmentation_id specified for local "
+ "network")
+ raise q_exc.InvalidInput(error_message=msg)
+ else:
+ segmentation_id = None
+ else:
+ msg = _("provider:network_type %s not supported") % network_type
+ raise q_exc.InvalidInput(error_message=msg)
+
+ if network_type in [constants.TYPE_VLAN, constants.TYPE_FLAT]:
+ if physical_network_set:
+ if physical_network not in self.network_vlan_ranges:
+ msg = _("Unknown provider:physical_network "
+ "%s") % physical_network
+ raise q_exc.InvalidInput(error_message=msg)
+ elif 'default' in self.network_vlan_ranges:
+ physical_network = 'default'
+ else:
+ msg = _("provider:physical_network required")
+ raise q_exc.InvalidInput(error_message=msg)
+
+ return (network_type, physical_network, segmentation_id)
+
+ def create_network(self, context, network):
+ (network_type, physical_network,
+ segmentation_id) = self._process_provider_create(context,
+ network['network'])
+
+ session = context.session
+ #set up default security groups
+ tenant_id = self._get_tenant_id_for_create(
+ context, network['network'])
+ self._ensure_default_security_group(context, tenant_id)
+
+ with session.begin(subtransactions=True):
+ if not network_type:
+ # tenant network
+ network_type = self.tenant_network_type
+ if network_type == constants.TYPE_NONE:
+ raise q_exc.TenantNetworksDisabled()
+ elif network_type == constants.TYPE_VLAN:
+ (physical_network,
+ segmentation_id) = ovs_db_v2.reserve_vlan(session)
+ elif network_type in constants.TUNNEL_NETWORK_TYPES:
+ segmentation_id = ovs_db_v2.reserve_tunnel(session)
+ # no reservation needed for TYPE_LOCAL
+ else:
+ # provider network
+ if network_type in [constants.TYPE_VLAN, constants.TYPE_FLAT]:
+ ovs_db_v2.reserve_specific_vlan(session, physical_network,
+ segmentation_id)
+ elif network_type in constants.TUNNEL_NETWORK_TYPES:
+ ovs_db_v2.reserve_specific_tunnel(session, segmentation_id)
+ # no reservation needed for TYPE_LOCAL
+ net = super(OVSNeutronPluginV2, self).create_network(context,
+ network)
+ ovs_db_v2.add_network_binding(session, net['id'], network_type,
+ physical_network, segmentation_id)
+
+ self._process_l3_create(context, net, network['network'])
+ self._extend_network_dict_provider(context, net)
+ # note - exception will rollback entire transaction
+ LOG.debug(_("Created network: %s"), net['id'])
+ return net
+
+ def update_network(self, context, id, network):
+ provider._raise_if_updates_provider_attributes(network['network'])
+
+ session = context.session
+ with session.begin(subtransactions=True):
+ net = super(OVSNeutronPluginV2, self).update_network(context, id,
+ network)
+ self._process_l3_update(context, net, network['network'])
+ self._extend_network_dict_provider(context, net)
+ return net
+
+ def delete_network(self, context, id):
+ session = context.session
+ with session.begin(subtransactions=True):
+ binding = ovs_db_v2.get_network_binding(session, id)
+ super(OVSNeutronPluginV2, self).delete_network(context, id)
+ if binding.network_type in constants.TUNNEL_NETWORK_TYPES:
+ ovs_db_v2.release_tunnel(session, binding.segmentation_id,
+ self.tunnel_id_ranges)
+ elif binding.network_type in [constants.TYPE_VLAN,
+ constants.TYPE_FLAT]:
+ ovs_db_v2.release_vlan(session, binding.physical_network,
+ binding.segmentation_id,
+ self.network_vlan_ranges)
+ # the network_binding record is deleted via cascade from
+ # the network record, so explicit removal is not necessary
+ self.notifier.network_delete(context, id)
+
+ def get_network(self, context, id, fields=None):
+ session = context.session
+ with session.begin(subtransactions=True):
+ net = super(OVSNeutronPluginV2, self).get_network(context,
+ id, None)
+ self._extend_network_dict_provider(context, net)
+ return self._fields(net, fields)
+
+ def get_networks(self, context, filters=None, fields=None,
+ sorts=None,
+ limit=None, marker=None, page_reverse=False):
+ session = context.session
+ with session.begin(subtransactions=True):
+ nets = super(OVSNeutronPluginV2,
+ self).get_networks(context, filters, None, sorts,
+ limit, marker, page_reverse)
+ for net in nets:
+ self._extend_network_dict_provider(context, net)
+
+ return [self._fields(net, fields) for net in nets]
+
+ def create_port(self, context, port):
+ # Set port status as 'DOWN'. This will be updated by agent
+ port['port']['status'] = q_const.PORT_STATUS_DOWN
+ port_data = port['port']
+ session = context.session
+ with session.begin(subtransactions=True):
+ self._ensure_default_security_group_on_port(context, port)
+ sgids = self._get_security_groups_on_port(context, port)
+ dhcp_opts = port['port'].get(edo_ext.EXTRADHCPOPTS, [])
+ port = super(OVSNeutronPluginV2, self).create_port(context, port)
+ self._process_portbindings_create_and_update(context,
+ port_data, port)
+ self._process_port_create_security_group(context, port, sgids)
+ self._process_port_create_extra_dhcp_opts(context, port,
+ dhcp_opts)
+ port[addr_pair.ADDRESS_PAIRS] = (
+ self._process_create_allowed_address_pairs(
+ context, port,
+ port_data.get(addr_pair.ADDRESS_PAIRS)))
+ self.notify_security_groups_member_updated(context, port)
+ return port
+
+ def _extend_port_dict_nat(self, context, port):
+ forward = ovs_db_v2.get_port_forwarding(context.session, port['id'])
+ if forward:
+ port[nat.FORWARD_PORTS] = forward
+ else:
+ port[nat.FORWARD_PORTS] = None
+
+ def _process_nat_update(self, context, attrs, id):
+ forward_ports = attrs.get(nat.FORWARD_PORTS)
+ forward_ports_set = attributes.is_attr_set(forward_ports)
+
+ if not forward_ports_set:
+ return None
+
+ # LOG.info("forward ports %s" % forward_ports)
+ valid_protocols = ["tcp", "udp"]
+ for entry in forward_ports:
+ if not isinstance(entry, dict):
+ msg = _("nat:forward_ports: must specify a list of dicts (ex: 'l4_protocol=tcp,l4_port=80')")
+ raise q_exc.InvalidInput(error_message=msg)
+ if not ("l4_protocol" in entry and "l4_port" in entry):
+ msg = _("nat:forward_ports: dict is missing l4_protocol and l4_port (ex: 'l4_protocol=tcp,l4_port=80')")
+ raise q_exc.InvalidInput(error_message=msg)
+ if entry['l4_protocol'] not in valid_protocols:
+ msg = _("nat:forward_ports: invalid protocol (only tcp and udp allowed)")
+ raise q_exc.InvalidInput(error_message=msg)
+ try:
+ l4_port = int(entry['l4_port'])
+ except:
+ msg = _("nat:forward_ports: l4_port must be an integer")
+ raise q_exc.InvalidInput(error_message=msg)
+
+ return forward_ports
+
+ def get_port(self, context, id, fields=None):
+ session = context.session
+ with session.begin(subtransactions=True):
+ port = super(OVSNeutronPluginV2, self).get_port(context, id, None)
+ self._extend_port_dict_nat(context, port)
+ return self._fields(port, fields)
+
+ def get_ports(self, context, filters=None, fields=None):
+ session = context.session
+ with session.begin(subtransactions=True):
+ ports = super(OVSNeutronPluginV2, self).get_ports(context, filters,
+ None)
+ for port in ports:
+ self._extend_port_dict_nat(context, port)
+
+ return [self._fields(port, fields) for port in ports]
+
+ def update_port(self, context, id, port):
+ forward_ports = self._process_nat_update(context, port['port'], id)
+
+ session = context.session
+ need_port_update_notify = False
+ changed_fixed_ips = 'fixed_ips' in port['port']
+ with session.begin(subtransactions=True):
+ original_port = super(OVSNeutronPluginV2, self).get_port(
+ context, id)
+ updated_port = super(OVSNeutronPluginV2, self).update_port(
+ context, id, port)
+ if addr_pair.ADDRESS_PAIRS in port['port']:
+ self._delete_allowed_address_pairs(context, id)
+ self._process_create_allowed_address_pairs(
+ context, updated_port,
+ port['port'][addr_pair.ADDRESS_PAIRS])
+ need_port_update_notify = True
+ elif changed_fixed_ips:
+ self._check_fixed_ips_and_address_pairs_no_overlap(
+ context, updated_port)
+
+ if forward_ports:
+ ovs_db_v2.clear_port_forwarding(session, updated_port['id'])
+ ovs_db_v2.add_port_forwarding(session, updated_port['id'], forward_ports)
+ self._extend_port_dict_nat(context, updated_port)
+
+ need_port_update_notify |= self.update_security_group_on_port(
+ context, id, port, original_port, updated_port)
+ self._process_portbindings_create_and_update(context,
+ port['port'],
+ updated_port)
+ need_port_update_notify |= self._update_extra_dhcp_opts_on_port(
+ context, id, port, updated_port)
+
+ need_port_update_notify |= self.is_security_group_member_updated(
+ context, original_port, updated_port)
+ if original_port['admin_state_up'] != updated_port['admin_state_up']:
+ need_port_update_notify = True
+
+ if need_port_update_notify:
+ binding = ovs_db_v2.get_network_binding(None,
+ updated_port['network_id'])
+ self.notifier.port_update(context, updated_port,
+ binding.network_type,
+ binding.segmentation_id,
+ binding.physical_network)
+ return updated_port
+
+ def delete_port(self, context, id, l3_port_check=True):
+
+ # if needed, check to see if this is a port owned by
+ # and l3-router. If so, we should prevent deletion.
+ if l3_port_check:
+ self.prevent_l3_port_deletion(context, id)
+
+ session = context.session
+ with session.begin(subtransactions=True):
+ self.disassociate_floatingips(context, id)
+ port = self.get_port(context, id)
+ self._delete_port_security_group_bindings(context, id)
+ super(OVSNeutronPluginV2, self).delete_port(context, id)
+
+ self.notify_security_groups_member_updated(context, port)