¡@

Home 

OpenStack Study: plugin.py

OpenStack Index

**** CubicPower OpenStack Study ****

# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright (C) 2012 Midokura Japan K.K.

# Copyright (C) 2013 Midokura PTE LTD

# 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: Takaaki Suzuki, Midokura Japan KK

# @author: Tomoe Sugihara, Midokura Japan KK

# @author: Ryu Ishimoto, Midokura Japan KK

# @author: Rossella Sblendido, Midokura Japan KK

# @author: Duarte Nunes, Midokura Japan KK

from midonetclient import api

from oslo.config import cfg

from sqlalchemy.orm import exc as sa_exc

from neutron.api.v2 import attributes

from neutron.common import constants

from neutron.common import exceptions as n_exc

from neutron.common import rpc as n_rpc

from neutron.common import topics

from neutron.db import agents_db

from neutron.db import agentschedulers_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 l3_db

from neutron.db import models_v2

from neutron.db import portbindings_db

from neutron.db import securitygroups_db

from neutron.extensions import external_net as ext_net

from neutron.extensions import l3

from neutron.extensions import portbindings

from neutron.extensions import securitygroup as ext_sg

from neutron.openstack.common import excutils

from neutron.openstack.common import log as logging

from neutron.openstack.common import rpc

from neutron.plugins.midonet.common import config # noqa

from neutron.plugins.midonet.common import net_util

from neutron.plugins.midonet import midonet_lib

LOG = logging.getLogger(__name__)

EXTERNAL_GW_INFO = l3.EXTERNAL_GW_INFO

METADATA_DEFAULT_IP = "169.254.169.254/32"

OS_FLOATING_IP_RULE_KEY = 'OS_FLOATING_IP'

OS_SG_RULE_KEY = 'OS_SG_RULE_ID'

OS_TENANT_ROUTER_RULE_KEY = 'OS_TENANT_ROUTER_RULE'

PRE_ROUTING_CHAIN_NAME = "OS_PRE_ROUTING_%s"

PORT_INBOUND_CHAIN_NAME = "OS_PORT_%s_INBOUND"

PORT_OUTBOUND_CHAIN_NAME = "OS_PORT_%s_OUTBOUND"

POST_ROUTING_CHAIN_NAME = "OS_POST_ROUTING_%s"

SG_INGRESS_CHAIN_NAME = "OS_SG_%s_INGRESS"

SG_EGRESS_CHAIN_NAME = "OS_SG_%s_EGRESS"

SG_PORT_GROUP_NAME = "OS_PG_%s"

SNAT_RULE = 'SNAT'

**** CubicPower OpenStack Study ****

def _get_nat_ips(type, fip):

    """Get NAT IP address information.

    From the route type given, determine the source and target IP addresses

    from the provided floating IP DB object.

    """

    if type == 'pre-routing':

        return fip["floating_ip_address"], fip["fixed_ip_address"]

    elif type == 'post-routing':

        return fip["fixed_ip_address"], fip["floating_ip_address"]

    else:

        raise ValueError(_("Invalid nat_type %s") % type)

**** CubicPower OpenStack Study ****

def _nat_chain_names(router_id):

    """Get the chain names for NAT.

    These names are used to associate MidoNet chains to the NAT rules

    applied to the router.  For each of these, there are two NAT types,

    'dnat' and 'snat' that are returned as keys, and the corresponding

    chain names as their values.

    """

    pre_routing_name = PRE_ROUTING_CHAIN_NAME % router_id

    post_routing_name = POST_ROUTING_CHAIN_NAME % router_id

    return {'pre-routing': pre_routing_name, 'post-routing': post_routing_name}

**** CubicPower OpenStack Study ****

def _sg_chain_names(sg_id):

    """Get the chain names for security group.

    These names are used to associate a security group to MidoNet chains.

    There are two names for ingress and egress security group directions.

    """

    ingress = SG_INGRESS_CHAIN_NAME % sg_id

    egress = SG_EGRESS_CHAIN_NAME % sg_id

    return {'ingress': ingress, 'egress': egress}

**** CubicPower OpenStack Study ****

def _port_chain_names(port_id):

    """Get the chain names for a port.

    These are chains to hold security group chains.

    """

    inbound = PORT_INBOUND_CHAIN_NAME % port_id

    outbound = PORT_OUTBOUND_CHAIN_NAME % port_id

    return {'inbound': inbound, 'outbound': outbound}

**** CubicPower OpenStack Study ****

def _sg_port_group_name(sg_id):

    """Get the port group name for security group..

    This name is used to associate a security group to MidoNet  port groups.

    """

    return SG_PORT_GROUP_NAME % sg_id

**** CubicPower OpenStack Study ****

def _rule_direction(sg_direction):

    """Convert the SG direction to MidoNet direction

    MidoNet terms them 'inbound' and 'outbound' instead of 'ingress' and

    'egress'.  Also, the direction is reversed since MidoNet sees it

    from the network port's point of view, not the VM's.

    """

    if sg_direction == 'ingress':

        return 'outbound'

    elif sg_direction == 'egress':

        return 'inbound'

    else:

        raise ValueError(_("Unrecognized direction %s") % sg_direction)

**** CubicPower OpenStack Study ****

def _is_router_interface_port(port):

    """Check whether the given port is a router interface port."""

    device_owner = port['device_owner']

    return (device_owner in l3_db.DEVICE_OWNER_ROUTER_INTF)

**** CubicPower OpenStack Study ****

def _is_router_gw_port(port):

    """Check whether the given port is a router gateway port."""

    device_owner = port['device_owner']

    return (device_owner in l3_db.DEVICE_OWNER_ROUTER_GW)

**** CubicPower OpenStack Study ****

def _is_vif_port(port):

    """Check whether the given port is a standard VIF port."""

    device_owner = port['device_owner']

    return (not _is_dhcp_port(port) and

            device_owner not in (l3_db.DEVICE_OWNER_ROUTER_GW,

                                 l3_db.DEVICE_OWNER_ROUTER_INTF))

**** CubicPower OpenStack Study ****

def _is_dhcp_port(port):

    """Check whether the given port is a DHCP port."""

    device_owner = port['device_owner']

    return device_owner.startswith(constants.DEVICE_OWNER_DHCP)

**** CubicPower OpenStack Study ****

def _check_resource_exists(func, id, name, raise_exc=False):

    """Check whether the given resource exists in MidoNet data store."""

    try:

        func(id)

    except midonet_lib.MidonetResourceNotFound as exc:

        LOG.error(_("There is no %(name)s with ID %(id)s in MidoNet."),

                  {"name": name, "id": id})

        if raise_exc:

            raise MidonetPluginException(msg=exc)

**** CubicPower OpenStack Study ****

class MidoRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin):

RPC_API_VERSION = '1.1'

**** CubicPower OpenStack Study ****

    def create_rpc_dispatcher(self):

        """Get the rpc dispatcher for this manager.

        This a basic implementation that will call the plugin like get_ports

        and handle basic events

        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 n_rpc.PluginRpcDispatcher([self,

                                          agents_db.AgentExtRpcCallback()])

**** CubicPower OpenStack Study ****

class MidonetPluginException(n_exc.NeutronException):

message = _("%(msg)s")

**** CubicPower OpenStack Study ****

class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2, portbindings_db.PortBindingMixin, external_net_db.External_net_db_mixin, l3_db.L3_NAT_db_mixin, agentschedulers_db.DhcpAgentSchedulerDbMixin, securitygroups_db.SecurityGroupDbMixin):

supported_extension_aliases = ['external-net', 'router', 'security-group',

'agent', 'dhcp_agent_scheduler', 'binding']

__native_bulk_support = False

**** CubicPower OpenStack Study ****

    def __init__(self):

        super(MidonetPluginV2, self).__init__()

        # Read config values

        midonet_conf = cfg.CONF.MIDONET

        midonet_uri = midonet_conf.midonet_uri

        admin_user = midonet_conf.username

        admin_pass = midonet_conf.password

        admin_project_id = midonet_conf.project_id

        self.provider_router_id = midonet_conf.provider_router_id

        self.provider_router = None

        self.mido_api = api.MidonetApi(midonet_uri, admin_user,

                                       admin_pass,

                                       project_id=admin_project_id)

        self.client = midonet_lib.MidoClient(self.mido_api)

        # self.provider_router_id should have been set.

        if self.provider_router_id is None:

            msg = _('provider_router_id should be configured in the plugin '

                    'config file')

            LOG.exception(msg)

            raise MidonetPluginException(msg=msg)

        self.setup_rpc()

        self.base_binding_dict = {

            portbindings.VIF_TYPE: portbindings.VIF_TYPE_MIDONET,

            portbindings.VIF_DETAILS: {

                # TODO(rkukura): Replace with new VIF security details

                portbindings.CAP_PORT_FILTER:

                'security-group' in self.supported_extension_aliases}}

**** CubicPower OpenStack Study ****

    def _get_provider_router(self):

        if self.provider_router is None:

            self.provider_router = self.client.get_router(

                self.provider_router_id)

        return self.provider_router

**** CubicPower OpenStack Study ****

    def _dhcp_mappings(self, context, fixed_ips, mac):

        for fixed_ip in fixed_ips:

            subnet = self._get_subnet(context, fixed_ip["subnet_id"])

            if subnet["ip_version"] == 6:

                # TODO(ryu) handle IPv6

                continue

            if not subnet["enable_dhcp"]:

                # Skip if DHCP is disabled

                continue

            yield subnet['cidr'], fixed_ip["ip_address"], mac

**** CubicPower OpenStack Study ****

    def _metadata_subnets(self, context, fixed_ips):

        for fixed_ip in fixed_ips:

            subnet = self._get_subnet(context, fixed_ip["subnet_id"])

            if subnet["ip_version"] == 6:

                continue

            yield subnet['cidr'], fixed_ip["ip_address"]

**** CubicPower OpenStack Study ****

    def _initialize_port_chains(self, port, in_chain, out_chain, sg_ids):

        tenant_id = port["tenant_id"]

        position = 1

        # mac spoofing protection

        self._add_chain_rule(in_chain, action='drop',

                             dl_src=port["mac_address"], inv_dl_src=True,

                             position=position)

        # ip spoofing protection

        for fixed_ip in port["fixed_ips"]:

            position += 1

            self._add_chain_rule(in_chain, action="drop",

                                 src_addr=fixed_ip["ip_address"] + "/32",

                                 inv_nw_src=True, dl_type=0x0800,  # IPv4

                                 position=position)

        # conntrack

        position += 1

        self._add_chain_rule(in_chain, action='accept',

                             match_forward_flow=True,

                             position=position)

        # Reset the position to process egress

        position = 1

        # Add rule for SGs

        if sg_ids:

            for sg_id in sg_ids:

                chain_name = _sg_chain_names(sg_id)["ingress"]

                chain = self.client.get_chain_by_name(tenant_id, chain_name)

                self._add_chain_rule(out_chain, action='jump',

                                     jump_chain_id=chain.get_id(),

                                     jump_chain_name=chain_name,

                                     position=position)

                position += 1

        # add reverse flow matching at the end

        self._add_chain_rule(out_chain, action='accept',

                             match_return_flow=True,

                             position=position)

        position += 1

        # fall back DROP rule at the end except for ARP

        self._add_chain_rule(out_chain, action='drop',

                             dl_type=0x0806,  # ARP

                             inv_dl_type=True, position=position)

**** CubicPower OpenStack Study ****

    def _bind_port_to_sgs(self, context, port, sg_ids):

        self._process_port_create_security_group(context, port, sg_ids)

        if sg_ids is not None:

            for sg_id in sg_ids:

                pg_name = _sg_port_group_name(sg_id)

                self.client.add_port_to_port_group_by_name(

                    port["tenant_id"], pg_name, port["id"])

**** CubicPower OpenStack Study ****

    def _unbind_port_from_sgs(self, context, port_id):

        self._delete_port_security_group_bindings(context, port_id)

        self.client.remove_port_from_port_groups(port_id)

**** CubicPower OpenStack Study ****

    def _create_accept_chain_rule(self, context, sg_rule, chain=None):

        direction = sg_rule["direction"]

        tenant_id = sg_rule["tenant_id"]

        sg_id = sg_rule["security_group_id"]

        chain_name = _sg_chain_names(sg_id)[direction]

        if chain is None:

            chain = self.client.get_chain_by_name(tenant_id, chain_name)

        pg_id = None

        if sg_rule["remote_group_id"] is not None:

            pg_name = _sg_port_group_name(sg_id)

            pg = self.client.get_port_group_by_name(tenant_id, pg_name)

            pg_id = pg.get_id()

        props = {OS_SG_RULE_KEY: str(sg_rule["id"])}

        # Determine source or destination address by looking at direction

        src_pg_id = dst_pg_id = None

        src_addr = dst_addr = None

        src_port_to = dst_port_to = None

        src_port_from = dst_port_from = None

        if direction == "egress":

            dst_pg_id = pg_id

            dst_addr = sg_rule["remote_ip_prefix"]

            dst_port_from = sg_rule["port_range_min"]

            dst_port_to = sg_rule["port_range_max"]

        else:

            src_pg_id = pg_id

            src_addr = sg_rule["remote_ip_prefix"]

            src_port_from = sg_rule["port_range_min"]

            src_port_to = sg_rule["port_range_max"]

        return self._add_chain_rule(

            chain, action='accept', port_group_src=src_pg_id,

            port_group_dst=dst_pg_id,

            src_addr=src_addr, src_port_from=src_port_from,

            src_port_to=src_port_to,

            dst_addr=dst_addr, dst_port_from=dst_port_from,

            dst_port_to=dst_port_to,

            nw_proto=net_util.get_protocol_value(sg_rule["protocol"]),

            dl_type=net_util.get_ethertype_value(sg_rule["ethertype"]),

            properties=props)

**** CubicPower OpenStack Study ****

    def _remove_nat_rules(self, context, fip):

        router = self.client.get_router(fip["router_id"])

        self.client.remove_static_route(self._get_provider_router(),

                                        fip["floating_ip_address"])

        chain_names = _nat_chain_names(router.get_id())

        for _type, name in chain_names.iteritems():

            self.client.remove_rules_by_property(

                router.get_tenant_id(), name,

                OS_FLOATING_IP_RULE_KEY, fip["id"])

**** CubicPower OpenStack Study ****

    def setup_rpc(self):

        # RPC support

        self.topic = topics.PLUGIN

        self.conn = rpc.create_connection(new=True)

        self.callbacks = MidoRpcCallbacks()

        self.dispatcher = self.callbacks.create_rpc_dispatcher()

        self.conn.create_consumer(self.topic, self.dispatcher,

                                  fanout=False)

        # Consume from all consumers in a thread

        self.conn.consume_in_thread()

**** CubicPower OpenStack Study ****

    def create_subnet(self, context, subnet):

        """Create Neutron subnet.

        Creates a Neutron subnet and a DHCP entry in MidoNet bridge.

        """

        LOG.debug(_("MidonetPluginV2.create_subnet called: subnet=%r"), subnet)

        s = subnet["subnet"]

        net = super(MidonetPluginV2, self).get_network(

            context, subnet['subnet']['network_id'], fields=None)

        session = context.session

        with session.begin(subtransactions=True):

            sn_entry = super(MidonetPluginV2, self).create_subnet(context,

                                                                  subnet)

            bridge = self.client.get_bridge(sn_entry['network_id'])

            gateway_ip = s['gateway_ip']

            cidr = s['cidr']

            if s['enable_dhcp']:

                dns_nameservers = None

                host_routes = None

                if s['dns_nameservers'] is not attributes.ATTR_NOT_SPECIFIED:

                    dns_nameservers = s['dns_nameservers']

                if s['host_routes'] is not attributes.ATTR_NOT_SPECIFIED:

                    host_routes = s['host_routes']

                self.client.create_dhcp(bridge, gateway_ip, cidr,

                                        host_rts=host_routes,

                                        dns_servers=dns_nameservers)

            # For external network, link the bridge to the provider router.

            if net['router:external']:

                self._link_bridge_to_gw_router(

                    bridge, self._get_provider_router(), gateway_ip, cidr)

        LOG.debug(_("MidonetPluginV2.create_subnet exiting: sn_entry=%r"),

                  sn_entry)

        return sn_entry

**** CubicPower OpenStack Study ****

    def delete_subnet(self, context, id):

        """Delete Neutron subnet.

        Delete neutron network and its corresponding MidoNet bridge.

        """

        LOG.debug(_("MidonetPluginV2.delete_subnet called: id=%s"), id)

        subnet = super(MidonetPluginV2, self).get_subnet(context, id,

                                                         fields=None)

        net = super(MidonetPluginV2, self).get_network(context,

                                                       subnet['network_id'],

                                                       fields=None)

        session = context.session

        with session.begin(subtransactions=True):

            super(MidonetPluginV2, self).delete_subnet(context, id)

            bridge = self.client.get_bridge(subnet['network_id'])

            if subnet['enable_dhcp']:

                self.client.delete_dhcp(bridge, subnet['cidr'])

            # If the network is external, clean up routes, links, ports

            if net[ext_net.EXTERNAL]:

                self._unlink_bridge_from_gw_router(

                    bridge, self._get_provider_router())

            LOG.debug(_("MidonetPluginV2.delete_subnet exiting"))

**** CubicPower OpenStack Study ****

    def create_network(self, context, network):

        """Create Neutron network.

        Create a new Neutron network and its corresponding MidoNet bridge.

        """

        LOG.debug(_('MidonetPluginV2.create_network called: network=%r'),

                  network)

        net_data = network['network']

        tenant_id = self._get_tenant_id_for_create(context, net_data)

        net_data['tenant_id'] = tenant_id

        self._ensure_default_security_group(context, tenant_id)

        bridge = self.client.create_bridge(**net_data)

        net_data['id'] = bridge.get_id()

        session = context.session

        with session.begin(subtransactions=True):

            net = super(MidonetPluginV2, self).create_network(context, network)

            self._process_l3_create(context, net, net_data)

        LOG.debug(_("MidonetPluginV2.create_network exiting: net=%r"), net)

        return net

**** CubicPower OpenStack Study ****

    def update_network(self, context, id, network):

        """Update Neutron network.

        Update an existing Neutron network and its corresponding MidoNet

        bridge.

        """

        LOG.debug(_("MidonetPluginV2.update_network called: id=%(id)r, "

                    "network=%(network)r"), {'id': id, 'network': network})

        session = context.session

        with session.begin(subtransactions=True):

            net = super(MidonetPluginV2, self).update_network(

                context, id, network)

            self._process_l3_update(context, net, network['network'])

            self.client.update_bridge(id, **network['network'])

        LOG.debug(_("MidonetPluginV2.update_network exiting: net=%r"), net)

        return net

**** CubicPower OpenStack Study ****

    def get_network(self, context, id, fields=None):

        """Get Neutron network.

        Retrieves a Neutron network and its corresponding MidoNet bridge.

        """

        LOG.debug(_("MidonetPluginV2.get_network called: id=%(id)r, "

                    "fields=%(fields)r"), {'id': id, 'fields': fields})

        qnet = super(MidonetPluginV2, self).get_network(context, id, fields)

        self.client.get_bridge(id)

        LOG.debug(_("MidonetPluginV2.get_network exiting: qnet=%r"), qnet)

        return qnet

**** CubicPower OpenStack Study ****

    def delete_network(self, context, id):

        """Delete a network and its corresponding MidoNet bridge."""

        LOG.debug(_("MidonetPluginV2.delete_network called: id=%r"), id)

        self.client.delete_bridge(id)

        try:

            super(MidonetPluginV2, self).delete_network(context, id)

        except Exception:

            with excutils.save_and_reraise_exception():

                LOG.error(_('Failed to delete neutron db, while Midonet '

                            'bridge=%r had been deleted'), id)

**** CubicPower OpenStack Study ****

    def create_port(self, context, port):

        """Create a L2 port in Neutron/MidoNet."""

        LOG.debug(_("MidonetPluginV2.create_port called: port=%r"), port)

        port_data = port['port']

        # Create a bridge port in MidoNet and set the bridge port ID as the

        # port ID in Neutron.

        bridge = self.client.get_bridge(port_data["network_id"])

        tenant_id = bridge.get_tenant_id()

        asu = port_data.get("admin_state_up", True)

        bridge_port = self.client.add_bridge_port(bridge,

                                                  admin_state_up=asu)

        port_data["id"] = bridge_port.get_id()

        try:

            session = context.session

            with session.begin(subtransactions=True):

                # Create a Neutron port

                new_port = super(MidonetPluginV2, self).create_port(context,

                                                                    port)

                port_data.update(new_port)

                self._ensure_default_security_group_on_port(context,

                                                            port)

                if _is_vif_port(port_data):

                    # Bind security groups to the port

                    sg_ids = self._get_security_groups_on_port(context, port)

                    self._bind_port_to_sgs(context, new_port, sg_ids)

                    # Create port chains

                    port_chains = {}

                    for d, name in _port_chain_names(

                            new_port["id"]).iteritems():

                        port_chains[d] = self.client.create_chain(tenant_id,

                                                                  name)

                    self._initialize_port_chains(port_data,

                                                 port_chains['inbound'],

                                                 port_chains['outbound'],

                                                 sg_ids)

                    # Update the port with the chain

                    self.client.update_port_chains(

                        bridge_port, port_chains["inbound"].get_id(),

                        port_chains["outbound"].get_id())

                    # DHCP mapping is only for VIF ports

                    for cidr, ip, mac in self._dhcp_mappings(

                            context, port_data["fixed_ips"],

                            port_data["mac_address"]):

                        self.client.add_dhcp_host(bridge, cidr, ip, mac)

                elif _is_dhcp_port(port_data):

                    # For DHCP port, add a metadata route

                    for cidr, ip in self._metadata_subnets(

                            context, port_data["fixed_ips"]):

                        self.client.add_dhcp_route_option(bridge, cidr, ip,

                                                          METADATA_DEFAULT_IP)

            self._process_portbindings_create_and_update(context,

                                                         port_data, new_port)

        except Exception as ex:

            # Try removing the MidoNet port before raising an exception.

            with excutils.save_and_reraise_exception():

                LOG.error(_("Failed to create a port on network %(net_id)s: "

                            "%(err)s"),

                          {"net_id": port_data["network_id"], "err": ex})

                self.client.delete_port(bridge_port.get_id())

        LOG.debug(_("MidonetPluginV2.create_port exiting: port=%r"), new_port)

        return new_port

**** CubicPower OpenStack Study ****

    def get_port(self, context, id, fields=None):

        """Retrieve port."""

        LOG.debug(_("MidonetPluginV2.get_port called: id=%(id)s "

                    "fields=%(fields)r"), {'id': id, 'fields': fields})

        port = super(MidonetPluginV2, self).get_port(context, id, fields)

        "Check if the port exists in MidoNet DB"""

        try:

            self.client.get_port(id)

        except midonet_lib.MidonetResourceNotFound as exc:

            LOG.error(_("There is no port with ID %(id)s in MidoNet."),

                      {"id": id})

            port['status'] = constants.PORT_STATUS_ERROR

            raise exc

        LOG.debug(_("MidonetPluginV2.get_port exiting: port=%r"), port)

        return port

**** CubicPower OpenStack Study ****

    def get_ports(self, context, filters=None, fields=None):

        """List neutron ports and verify that they exist in MidoNet."""

        LOG.debug(_("MidonetPluginV2.get_ports called: filters=%(filters)s "

                    "fields=%(fields)r"),

                  {'filters': filters, 'fields': fields})

        ports = super(MidonetPluginV2, self).get_ports(context, filters,

                                                       fields)

        return ports

**** CubicPower OpenStack Study ****

    def delete_port(self, context, id, l3_port_check=True):

        """Delete a neutron port and corresponding MidoNet bridge port."""

        LOG.debug(_("MidonetPluginV2.delete_port called: id=%(id)s "

                    "l3_port_check=%(l3_port_check)r"),

                  {'id': id, 'l3_port_check': l3_port_check})

        # 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)

        self.disassociate_floatingips(context, id)

        port = self.get_port(context, id)

        device_id = port['device_id']

        # If this port is for router interface/gw, unlink and delete.

        if _is_router_interface_port(port):

            self._unlink_bridge_from_router(device_id, id)

        elif _is_router_gw_port(port):

            # Gateway removed

            # Remove all the SNAT rules that are tagged.

            router = self._get_router(context, device_id)

            tenant_id = router["tenant_id"]

            chain_names = _nat_chain_names(device_id)

            for _type, name in chain_names.iteritems():

                self.client.remove_rules_by_property(

                    tenant_id, name, OS_TENANT_ROUTER_RULE_KEY,

                    SNAT_RULE)

            # Remove the default routes and unlink

            self._remove_router_gateway(port['device_id'])

        self.client.delete_port(id, delete_chains=True)

        try:

            for cidr, ip, mac in self._dhcp_mappings(

                    context, port["fixed_ips"], port["mac_address"]):

                self.client.delete_dhcp_host(port["network_id"], cidr, ip,

                                             mac)

        except Exception:

            LOG.error(_("Failed to delete DHCP mapping for port %(id)s"),

                      {"id": id})

        super(MidonetPluginV2, self).delete_port(context, id)

**** CubicPower OpenStack Study ****

    def update_port(self, context, id, port):

        """Handle port update, including security groups and fixed IPs."""

        with context.session.begin(subtransactions=True):

            # Get the port and save the fixed IPs

            old_port = self._get_port(context, id)

            net_id = old_port["network_id"]

            mac = old_port["mac_address"]

            old_ips = old_port["fixed_ips"]

            # update the port DB

            p = super(MidonetPluginV2, self).update_port(context, id, port)

            if "admin_state_up" in port["port"]:

                asu = port["port"]["admin_state_up"]

                mido_port = self.client.update_port(id, admin_state_up=asu)

                # If we're changing the admin_state_up flag and the port is

                # associated with a router, then we also need to update the

                # peer port.

                if _is_router_interface_port(p):

                    self.client.update_port(mido_port.get_peer_id(),

                                            admin_state_up=asu)

            new_ips = p["fixed_ips"]

            if new_ips:

                bridge = self.client.get_bridge(net_id)

                # If it's a DHCP port, add a route to reach the MD server

                if _is_dhcp_port(p):

                    for cidr, ip in self._metadata_subnets(

                        context, new_ips):

                        self.client.add_dhcp_route_option(

                            bridge, cidr, ip, METADATA_DEFAULT_IP)

                else:

                # IPs have changed.  Re-map the DHCP entries

                    for cidr, ip, mac in self._dhcp_mappings(

                            context, old_ips, mac):

                        self.client.remove_dhcp_host(

                            bridge, cidr, ip, mac)

                    for cidr, ip, mac in self._dhcp_mappings(

                        context, new_ips, mac):

                        self.client.add_dhcp_host(

                            bridge, cidr, ip, mac)

            if (self._check_update_deletes_security_groups(port) or

                    self._check_update_has_security_groups(port)):

                self._unbind_port_from_sgs(context, p["id"])

                sg_ids = self._get_security_groups_on_port(context, port)

                self._bind_port_to_sgs(context, p, sg_ids)

            self._process_portbindings_create_and_update(context,

                                                         port['port'],

                                                         p)

        return p

**** CubicPower OpenStack Study ****

    def create_router(self, context, router):

        """Handle router creation.

        When a new Neutron router is created, its corresponding MidoNet router

        is also created.  In MidoNet, this router is initialized with chains

        for inbound and outbound traffic, which will be used to hold other

        chains that include various rules, such as NAT.

        :param router: Router information provided to create a new router.

        """

        # NOTE(dcahill): Similar to the NSX plugin, we completely override

        # this method in order to be able to use the MidoNet ID as Neutron ID

        # TODO(dcahill): Propose upstream patch for allowing

        # 3rd parties to specify IDs as we do with l2 plugin

        LOG.debug(_("MidonetPluginV2.create_router called: router=%(router)s"),

                  {"router": router})

        r = router['router']

        tenant_id = self._get_tenant_id_for_create(context, r)

        r['tenant_id'] = tenant_id

        mido_router = self.client.create_router(**r)

        mido_router_id = mido_router.get_id()

        try:

            has_gw_info = False

            if EXTERNAL_GW_INFO in r:

                has_gw_info = True

                gw_info = r.pop(EXTERNAL_GW_INFO)

            with context.session.begin(subtransactions=True):

                # pre-generate id so it will be available when

                # configuring external gw port

                router_db = l3_db.Router(id=mido_router_id,

                                         tenant_id=tenant_id,

                                         name=r['name'],

                                         admin_state_up=r['admin_state_up'],

                                         status="ACTIVE")

                context.session.add(router_db)

                if has_gw_info:

                    self._update_router_gw_info(context, router_db['id'],

                                                gw_info)

            router_data = self._make_router_dict(router_db,

                                                 process_extensions=False)

        except Exception:

            # Try removing the midonet router

            with excutils.save_and_reraise_exception():

                self.client.delete_router(mido_router_id)

        # Create router chains

        chain_names = _nat_chain_names(mido_router_id)

        try:

            self.client.add_router_chains(mido_router,

                                          chain_names["pre-routing"],

                                          chain_names["post-routing"])

        except Exception:

            # Set the router status to Error

            with context.session.begin(subtransactions=True):

                r = self._get_router(context, router_data["id"])

                router_data['status'] = constants.NET_STATUS_ERROR

                r['status'] = router_data['status']

                context.session.add(r)

        LOG.debug(_("MidonetPluginV2.create_router exiting: "

                    "router_data=%(router_data)s."),

                  {"router_data": router_data})

        return router_data

**** CubicPower OpenStack Study ****

    def _set_router_gateway(self, id, gw_router, gw_ip):

        """Set router uplink gateway

        :param ID: ID of the router

        :param gw_router: gateway router to link to

        :param gw_ip: gateway IP address

        """

        LOG.debug(_("MidonetPluginV2.set_router_gateway called: id=%(id)s, "

                    "gw_router=%(gw_router)s, gw_ip=%(gw_ip)s"),

                  {'id': id, 'gw_router': gw_router, 'gw_ip': gw_ip}),

        router = self.client.get_router(id)

        # Create a port in the gw router

        gw_port = self.client.add_router_port(gw_router,

                                              port_address='169.254.255.1',

                                              network_address='169.254.255.0',

                                              network_length=30)

        # Create a port in the router

        port = self.client.add_router_port(router,

                                           port_address='169.254.255.2',

                                           network_address='169.254.255.0',

                                           network_length=30)

        # Link them

        self.client.link(gw_port, port.get_id())

        # Add a route for gw_ip to bring it down to the router

        self.client.add_router_route(gw_router, type='Normal',

                                     src_network_addr='0.0.0.0',

                                     src_network_length=0,

                                     dst_network_addr=gw_ip,

                                     dst_network_length=32,

                                     next_hop_port=gw_port.get_id(),

                                     weight=100)

        # Add default route to uplink in the router

        self.client.add_router_route(router, type='Normal',

                                     src_network_addr='0.0.0.0',

                                     src_network_length=0,

                                     dst_network_addr='0.0.0.0',

                                     dst_network_length=0,

                                     next_hop_port=port.get_id(),

                                     weight=100)

**** CubicPower OpenStack Study ****

    def _remove_router_gateway(self, id):

        """Clear router gateway

        :param ID: ID of the router

        """

        LOG.debug(_("MidonetPluginV2.remove_router_gateway called: "

                    "id=%(id)s"), {'id': id})

        router = self.client.get_router(id)

        # delete the port that is connected to the gateway router

        for p in router.get_ports():

            if p.get_port_address() == '169.254.255.2':

                peer_port_id = p.get_peer_id()

                if peer_port_id is not None:

                    self.client.unlink(p)

                    self.client.delete_port(peer_port_id)

        # delete default route

        for r in router.get_routes():

            if (r.get_dst_network_addr() == '0.0.0.0' and

                    r.get_dst_network_length() == 0):

                self.client.delete_route(r.get_id())

**** CubicPower OpenStack Study ****

    def update_router(self, context, id, router):

        """Handle router updates."""

        LOG.debug(_("MidonetPluginV2.update_router called: id=%(id)s "

                    "router=%(router)r"), {"id": id, "router": router})

        router_data = router["router"]

        # Check if the update included changes to the gateway.

        gw_updated = l3_db.EXTERNAL_GW_INFO in router_data

        with context.session.begin(subtransactions=True):

            # Update the Neutron DB

            r = super(MidonetPluginV2, self).update_router(context, id,

                                                           router)

            tenant_id = r["tenant_id"]

            if gw_updated:

                if (l3_db.EXTERNAL_GW_INFO in r and

                        r[l3_db.EXTERNAL_GW_INFO] is not None):

                    # Gateway created

                    gw_port_neutron = self._get_port(

                        context.elevated(), r["gw_port_id"])

                    gw_ip = gw_port_neutron['fixed_ips'][0]['ip_address']

                    # First link routers and set up the routes

                    self._set_router_gateway(r["id"],

                                             self._get_provider_router(),

                                             gw_ip)

                    gw_port_midonet = self.client.get_link_port(

                        self._get_provider_router(), r["id"])

                    # Get the NAT chains and add dynamic SNAT rules.

                    chain_names = _nat_chain_names(r["id"])

                    props = {OS_TENANT_ROUTER_RULE_KEY: SNAT_RULE}

                    self.client.add_dynamic_snat(tenant_id,

                                                 chain_names['pre-routing'],

                                                 chain_names['post-routing'],

                                                 gw_ip,

                                                 gw_port_midonet.get_id(),

                                                 **props)

            self.client.update_router(id, **router_data)

        LOG.debug(_("MidonetPluginV2.update_router exiting: router=%r"), r)

        return r

**** CubicPower OpenStack Study ****

    def delete_router(self, context, id):

        """Handler for router deletion.

        Deleting a router on Neutron simply means deleting its corresponding

        router in MidoNet.

        :param id: router ID to remove

        """

        LOG.debug(_("MidonetPluginV2.delete_router called: id=%s"), id)

        self.client.delete_router_chains(id)

        self.client.delete_router(id)

        super(MidonetPluginV2, self).delete_router(context, id)

**** CubicPower OpenStack Study ****

    def _link_bridge_to_gw_router(self, bridge, gw_router, gw_ip, cidr):

        """Link a bridge to the gateway router

        :param bridge:  bridge

        :param gw_router: gateway router to link to

        :param gw_ip: IP address of gateway

        :param cidr: network CIDR

        """

        net_addr, net_len = net_util.net_addr(cidr)

        # create a port on the gateway router

        gw_port = self.client.add_router_port(gw_router, port_address=gw_ip,

                                              network_address=net_addr,

                                              network_length=net_len)

        # create a bridge port, then link it to the router.

        port = self.client.add_bridge_port(bridge)

        self.client.link(gw_port, port.get_id())

        # add a route for the subnet in the gateway router

        self.client.add_router_route(gw_router, type='Normal',

                                     src_network_addr='0.0.0.0',

                                     src_network_length=0,

                                     dst_network_addr=net_addr,

                                     dst_network_length=net_len,

                                     next_hop_port=gw_port.get_id(),

                                     weight=100)

**** CubicPower OpenStack Study ****

    def _unlink_bridge_from_gw_router(self, bridge, gw_router):

        """Unlink a bridge from the gateway router

        :param bridge: bridge to unlink

        :param gw_router: gateway router to unlink from

        """

        # Delete routes and unlink the router and the bridge.

        routes = self.client.get_router_routes(gw_router.get_id())

        bridge_ports_to_delete = [

            p for p in gw_router.get_peer_ports()

            if p.get_device_id() == bridge.get_id()]

        for p in bridge.get_peer_ports():

            if p.get_device_id() == gw_router.get_id():

                # delete the routes going to the bridge

                for r in routes:

                    if r.get_next_hop_port() == p.get_id():

                        self.client.delete_route(r.get_id())

                self.client.unlink(p)

                self.client.delete_port(p.get_id())

        # delete bridge port

        for port in bridge_ports_to_delete:

            self.client.delete_port(port.get_id())

**** CubicPower OpenStack Study ****

    def _link_bridge_to_router(self, router, bridge_port, net_addr, net_len,

                               gw_ip, metadata_gw_ip):

        router_port = self.client.add_router_port(

            router, network_length=net_len, network_address=net_addr,

            port_address=gw_ip, admin_state_up=bridge_port['admin_state_up'])

        self.client.link(router_port, bridge_port['id'])

        self.client.add_router_route(router, type='Normal',

                                     src_network_addr='0.0.0.0',

                                     src_network_length=0,

                                     dst_network_addr=net_addr,

                                     dst_network_length=net_len,

                                     next_hop_port=router_port.get_id(),

                                     weight=100)

        if metadata_gw_ip:

            # Add a route for the metadata server.

            # Not all VM images supports DHCP option 121.  Add a route for the

            # Metadata server in the router to forward the packet to the bridge

            # that will send them to the Metadata Proxy.

            md_net_addr, md_net_len = net_util.net_addr(METADATA_DEFAULT_IP)

            self.client.add_router_route(

                router, type='Normal', src_network_addr=net_addr,

                src_network_length=net_len,

                dst_network_addr=md_net_addr,

                dst_network_length=md_net_len,

                next_hop_port=router_port.get_id(),

                next_hop_gateway=metadata_gw_ip)

**** CubicPower OpenStack Study ****

    def _unlink_bridge_from_router(self, router_id, bridge_port_id):

        """Unlink a bridge from a router."""

        # Remove the routes to the port and unlink the port

        bridge_port = self.client.get_port(bridge_port_id)

        routes = self.client.get_router_routes(router_id)

        self.client.delete_port_routes(routes, bridge_port.get_peer_id())

        self.client.unlink(bridge_port)

**** CubicPower OpenStack Study ****

    def add_router_interface(self, context, router_id, interface_info):

        """Handle router linking with network."""

        LOG.debug(_("MidonetPluginV2.add_router_interface called: "

                    "router_id=%(router_id)s "

                    "interface_info=%(interface_info)r"),

                  {'router_id': router_id, 'interface_info': interface_info})

        with context.session.begin(subtransactions=True):

            info = super(MidonetPluginV2, self).add_router_interface(

                context, router_id, interface_info)

        try:

            subnet = self._get_subnet(context, info["subnet_id"])

            cidr = subnet["cidr"]

            net_addr, net_len = net_util.net_addr(cidr)

            router = self.client.get_router(router_id)

            # Get the metadata GW IP

            metadata_gw_ip = None

            rport_qry = context.session.query(models_v2.Port)

            dhcp_ports = rport_qry.filter_by(

                network_id=subnet["network_id"],

                device_owner=constants.DEVICE_OWNER_DHCP).all()

            if dhcp_ports and dhcp_ports[0].fixed_ips:

                metadata_gw_ip = dhcp_ports[0].fixed_ips[0].ip_address

            else:

                LOG.warn(_("DHCP agent is not working correctly. No port "

                           "to reach the Metadata server on this network"))

            # Link the router and the bridge

            port = super(MidonetPluginV2, self).get_port(context,

                                                         info["port_id"])

            self._link_bridge_to_router(router, port, net_addr,

                                        net_len, subnet["gateway_ip"],

                                        metadata_gw_ip)

        except Exception:

            LOG.error(_("Failed to create MidoNet resources to add router "

                        "interface. info=%(info)s, router_id=%(router_id)s"),

                      {"info": info, "router_id": router_id})

            with excutils.save_and_reraise_exception():

                with context.session.begin(subtransactions=True):

                    self.remove_router_interface(context, router_id, info)

        LOG.debug(_("MidonetPluginV2.add_router_interface exiting: "

                    "info=%r"), info)

        return info

**** CubicPower OpenStack Study ****

    def _assoc_fip(self, fip):

        router = self.client.get_router(fip["router_id"])

        link_port = self.client.get_link_port(

            self._get_provider_router(), router.get_id())

        self.client.add_router_route(

            self._get_provider_router(),

            src_network_addr='0.0.0.0',

            src_network_length=0,

            dst_network_addr=fip["floating_ip_address"],

            dst_network_length=32,

            next_hop_port=link_port.get_peer_id())

        props = {OS_FLOATING_IP_RULE_KEY: fip['id']}

        tenant_id = router.get_tenant_id()

        chain_names = _nat_chain_names(router.get_id())

        for chain_type, name in chain_names.items():

            src_ip, target_ip = _get_nat_ips(chain_type, fip)

            if chain_type == 'pre-routing':

                nat_type = 'dnat'

            else:

                nat_type = 'snat'

            self.client.add_static_nat(tenant_id, name, src_ip,

                                       target_ip,

                                       link_port.get_id(),

                                       nat_type, **props)

**** CubicPower OpenStack Study ****

    def create_floatingip(self, context, floatingip):

        session = context.session

        with session.begin(subtransactions=True):

            fip = super(MidonetPluginV2, self).create_floatingip(

                context, floatingip)

            if fip['port_id']:

                self._assoc_fip(fip)

        return fip

**** CubicPower OpenStack Study ****

    def update_floatingip(self, context, id, floatingip):

        """Handle floating IP association and disassociation."""

        LOG.debug(_("MidonetPluginV2.update_floatingip called: id=%(id)s "

                    "floatingip=%(floatingip)s "),

                  {'id': id, 'floatingip': floatingip})

        session = context.session

        with session.begin(subtransactions=True):

            if floatingip['floatingip']['port_id']:

                fip = super(MidonetPluginV2, self).update_floatingip(

                    context, id, floatingip)

                self._assoc_fip(fip)

            # disassociate floating IP

            elif floatingip['floatingip']['port_id'] is None:

                fip = super(MidonetPluginV2, self).get_floatingip(context, id)

                self._remove_nat_rules(context, fip)

                super(MidonetPluginV2, self).update_floatingip(context, id,

                                                               floatingip)

        LOG.debug(_("MidonetPluginV2.update_floating_ip exiting: fip=%s"), fip)

        return fip

**** CubicPower OpenStack Study ****

    def disassociate_floatingips(self, context, port_id):

        """Disassociate floating IPs (if any) from this port."""

        try:

            fip_qry = context.session.query(l3_db.FloatingIP)

            fip_db = fip_qry.filter_by(fixed_port_id=port_id).one()

            self._remove_nat_rules(context, fip_db)

        except sa_exc.NoResultFound:

            pass

        super(MidonetPluginV2, self).disassociate_floatingips(context, port_id)

**** CubicPower OpenStack Study ****

    def create_security_group(self, context, security_group, default_sg=False):

        """Create security group.

        Create a new security group, including the default security group.

        In MidoNet, this means creating a pair of chains, inbound and outbound,

        as well as a new port group.

        """

        LOG.debug(_("MidonetPluginV2.create_security_group called: "

                    "security_group=%(security_group)s "

                    "default_sg=%(default_sg)s "),

                  {'security_group': security_group, 'default_sg': default_sg})

        sg = security_group.get('security_group')

        tenant_id = self._get_tenant_id_for_create(context, sg)

        if not default_sg:

            self._ensure_default_security_group(context, tenant_id)

        # Create the Neutron sg first

        sg = super(MidonetPluginV2, self).create_security_group(

            context, security_group, default_sg)

        try:

            # Process the MidoNet side

            self.client.create_port_group(tenant_id,

                                          _sg_port_group_name(sg["id"]))

            chain_names = _sg_chain_names(sg["id"])

            chains = {}

            for direction, chain_name in chain_names.iteritems():

                c = self.client.create_chain(tenant_id, chain_name)

                chains[direction] = c

            # Create all the rules for this SG.  Only accept rules are created

            for r in sg['security_group_rules']:

                self._create_accept_chain_rule(context, r,

                                               chain=chains[r['direction']])

        except Exception:

            LOG.error(_("Failed to create MidoNet resources for sg %(sg)r"),

                      {"sg": sg})

            with excutils.save_and_reraise_exception():

                with context.session.begin(subtransactions=True):

                    sg = self._get_security_group(context, sg["id"])

                    context.session.delete(sg)

        LOG.debug(_("MidonetPluginV2.create_security_group exiting: sg=%r"),

                  sg)

        return sg

**** CubicPower OpenStack Study ****

    def delete_security_group(self, context, id):

        """Delete chains for Neutron security group."""

        LOG.debug(_("MidonetPluginV2.delete_security_group called: id=%s"), id)

        with context.session.begin(subtransactions=True):

            sg = super(MidonetPluginV2, self).get_security_group(context, id)

            if not sg:

                raise ext_sg.SecurityGroupNotFound(id=id)

            if sg["name"] == 'default' and not context.is_admin:

                raise ext_sg.SecurityGroupCannotRemoveDefault()

            sg_id = sg['id']

            filters = {'security_group_id': [sg_id]}

            if super(MidonetPluginV2, self)._get_port_security_group_bindings(

                    context, filters):

                raise ext_sg.SecurityGroupInUse(id=sg_id)

            # Delete MidoNet Chains and portgroup for the SG

            tenant_id = sg['tenant_id']

            self.client.delete_chains_by_names(

                tenant_id, _sg_chain_names(sg["id"]).values())

            self.client.delete_port_group_by_name(

                tenant_id, _sg_port_group_name(sg["id"]))

            super(MidonetPluginV2, self).delete_security_group(context, id)

**** CubicPower OpenStack Study ****

    def create_security_group_rule(self, context, security_group_rule):

        """Create a security group rule

        Create a security group rule in the Neutron DB and corresponding

        MidoNet resources in its data store.

        """

        LOG.debug(_("MidonetPluginV2.create_security_group_rule called: "

                    "security_group_rule=%(security_group_rule)r"),

                  {'security_group_rule': security_group_rule})

        with context.session.begin(subtransactions=True):

            rule = super(MidonetPluginV2, self).create_security_group_rule(

                context, security_group_rule)

            self._create_accept_chain_rule(context, rule)

            LOG.debug(_("MidonetPluginV2.create_security_group_rule exiting: "

                        "rule=%r"), rule)

            return rule

**** CubicPower OpenStack Study ****

    def delete_security_group_rule(self, context, sg_rule_id):

        """Delete a security group rule

        Delete a security group rule from the Neutron DB and corresponding

        MidoNet resources from its data store.

        """

        LOG.debug(_("MidonetPluginV2.delete_security_group_rule called: "

                    "sg_rule_id=%s"), sg_rule_id)

        with context.session.begin(subtransactions=True):

            rule = super(MidonetPluginV2, self).get_security_group_rule(

                context, sg_rule_id)

            if not rule:

                raise ext_sg.SecurityGroupRuleNotFound(id=sg_rule_id)

            sg = self._get_security_group(context,

                                          rule["security_group_id"])

            chain_name = _sg_chain_names(sg["id"])[rule["direction"]]

            self.client.remove_rules_by_property(rule["tenant_id"], chain_name,

                                                 OS_SG_RULE_KEY,

                                                 str(rule["id"]))

            super(MidonetPluginV2, self).delete_security_group_rule(

                context, sg_rule_id)

**** CubicPower OpenStack Study ****

    def _add_chain_rule(self, chain, action, **kwargs):

        nw_proto = kwargs.get("nw_proto")

        src_addr = kwargs.pop("src_addr", None)

        dst_addr = kwargs.pop("dst_addr", None)

        src_port_from = kwargs.pop("src_port_from", None)

        src_port_to = kwargs.pop("src_port_to", None)

        dst_port_from = kwargs.pop("dst_port_from", None)

        dst_port_to = kwargs.pop("dst_port_to", None)

        # Convert to the keys and values that midonet client understands

        if src_addr:

            kwargs["nw_src_addr"], kwargs["nw_src_length"] = net_util.net_addr(

                src_addr)

        if dst_addr:

            kwargs["nw_dst_addr"], kwargs["nw_dst_length"] = net_util.net_addr(

                dst_addr)

        kwargs["tp_src"] = {"start": src_port_from, "end": src_port_to}

        kwargs["tp_dst"] = {"start": dst_port_from, "end": dst_port_to}

        if nw_proto == 1:  # ICMP

            # Overwrite port fields regardless of the direction

            kwargs["tp_src"] = {"start": src_port_from, "end": src_port_from}

            kwargs["tp_dst"] = {"start": dst_port_to, "end": dst_port_to}

        return self.client.add_chain_rule(chain, action=action, **kwargs)