components/openstack/neutron/files/agent/evs_l3_agent.py
author Drew Fisher <drew.fisher@oracle.com>
Mon, 17 Mar 2014 09:51:44 -0600
changeset 1760 353323c7bdc1
child 1944 56ac2df1785b
permissions -rw-r--r--
PSARC/2013/350 OpenStack for Solaris (Umbrella) PSARC/2014/007 OpenStack client API components for Grizzly PSARC/2014/054 OpenStack Cinder (OpenStack Block Storage Service) PSARC/2014/055 OpenStack Glance (OpenStack Image Service) PSARC/2014/058 OpenStack Horizon (OpenStack Dashboard) PSARC/2014/048 OpenStack Keystone (OpenStack Identity Service) PSARC/2014/059 OpenStack Neutron (OpenStack Networking Service) PSARC/2014/049 OpenStack Nova (OpenStack Compute Service) 18290089 integrate cinderclient 18290097 integrate glanceclient 18290102 integrate keystoneclient 18290109 integrate neutronclient 18290113 integrate novaclient 18290119 integrate swiftclient 18290125 integrate quantumclient 18307582 Request to integrate Cinder into userland 18307595 Request to integrate Glance into userland 18307626 Request to integrate Horizon into userland 18307641 Request to integrate Keystone into userland 18307650 Request to integrate Neutron into userland 18307659 Request to integrate Nova into userland 18321909 a few Python packages deliver both po and mo files

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

# Copyright 2012 Nicira Networks, Inc.  All rights reserved.
#
# Copyright (c) 2014, Oracle and/or its affiliates. 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: Dan Wendlandt, Nicira, Inc
# @author: Girish Moodalbail, Oracle, Inc.
#

"""
Based off generic l3_agent (quantum/agent/l3_agent) code
"""

from oslo.config import cfg

from quantum.agent import l3_agent
from quantum.agent.solaris import interface
from quantum.agent.solaris import ipfilters_manager
from quantum.agent.solaris import net_lib
from quantum.common import constants as l3_constants
from quantum.openstack.common import log as logging


LOG = logging.getLogger(__name__)
INTERNAL_DEV_PREFIX = 'l3i'
EXTERNAL_DEV_PREFIX = 'l3e'


class RouterInfo(object):

    def __init__(self, router_id, root_helper, use_namespaces, router):
        self.router_id = router_id
        self.ex_gw_port = None
        self.internal_ports = []
        self.floating_ips = []
        self.router = router
        self.ipfilters_manager = ipfilters_manager.IpfiltersManager()
        self.routes = []


class EVSL3NATAgent(l3_agent.L3NATAgent):

    RouterInfo = RouterInfo

    OPTS = [
        cfg.StrOpt('external_network_bridge', default='',
                   help=_("Name of bridge used for external network "
                          "traffic.")),
        cfg.StrOpt('interface_driver',
                   help=_("The driver used to manage the virtual "
                          "interface.")),
        cfg.BoolOpt('use_namespaces', default=False,
                    help=_("Allow overlapping IP.")),
        cfg.StrOpt('router_id',
                   help=_("If namespaces is disabled, the l3 agent can only"
                          " configure a router that has the matching router "
                          "ID.")),
        cfg.BoolOpt('handle_internal_only_routers', default=True,
                    help=_("Agent should implement routers with no gateway")),
        cfg.StrOpt('gateway_external_network_id', default='',
                   help=_("UUID of external network for routers implemented "
                          "by the agents.")),
        cfg.StrOpt('external_network_datalink', default='net0',
                   help=_("Name of the datalink that connects to "
                          "an external network.")),
        cfg.BoolOpt('allow_forwarding_between_networks', default=False,
                    help=_("Allow forwarding of packets between tenant's "
                           "networks")),
    ]

    def __init__(self, host, conf=None):
        cfg.CONF.register_opts(self.OPTS)
        cfg.CONF.register_opts(interface.OPTS)
        if not cfg.CONF.router_id:
            raise SystemExit(_("router_id option needs to be set"))

        super(EVSL3NATAgent, self).__init__(host=host, conf=conf)

    def _router_added(self, router_id, router):
        ri = RouterInfo(router_id, self.root_helper,
                        self.conf.use_namespaces, router)
        self.router_info[router_id] = ri

    def _router_removed(self, router_id):
        ri = self.router_info[router_id]
        ri.router['gw_port'] = None
        ri.router[l3_constants.INTERFACE_KEY] = []
        ri.router[l3_constants.FLOATINGIP_KEY] = []
        self.process_router(ri)
        del self.router_info[router_id]

    def get_internal_device_name(self, port_id):
        # Because of the way how dnsmasq works on Solaris, the length
        # of datalink name cannot exceed 16 (includes terminating nul
        # character). So, the linkname can only have 15 characters and
        # the last two characters are set aside for '_0'. So, we only
        # have 13 characters left.
        dname = (INTERNAL_DEV_PREFIX + port_id)[:13]
        dname += '_0'
        return dname.replace('-', '_')

    def get_external_device_name(self, port_id):
        dname = (EXTERNAL_DEV_PREFIX + port_id)[:13]
        dname += '_0'
        return dname.replace('-', '_')

    def external_gateway_added(self, ri, ex_gw_port, internal_cidrs):
        pass

    def external_gateway_removed(self, ri, ex_gw_port, internal_cidrs):
        pass

    def _get_ippool_name(self, mac_address):
        # generate a unique-name for ippool(1m) from that last 3
        # bytes of mac-address
        mac_suffix = mac_address.split(':')[3:]
        return int("".join(mac_suffix), 16)

    def internal_network_added(self, ri, ex_gw_port, port):

        internal_dlname = self.get_internal_device_name(port['id'])
        if not net_lib.Datalink.datalink_exists(internal_dlname):
            self.driver.plug(port['tenant_id'], port['network_id'], port['id'],
                             internal_dlname)
        self.driver.init_l3(internal_dlname, [port['ip_cidr']])

        # add ippool(1m) for the new internal port
        new_ippool_name = self._get_ippool_name(port['mac_address'])
        ri.ipfilters_manager.add_ippool(new_ippool_name, None)

        # walk through the other internal ports and retrieve their
        # cidrs and at the same time add the new internal port's
        # cidr to them
        subnet_cidr = port['subnet']['cidr']
        other_subnet_cidrs = []
        for oip in ri.internal_ports:
            if oip['mac_address'] != port['mac_address']:
                if (self.conf.allow_forwarding_between_networks and
                    oip['tenant_id'] == port['tenant_id']):
                    continue
                other_subnet_cidrs.append(oip['subnet']['cidr'])
                ippool_name = self._get_ippool_name(oip['mac_address'])
                ri.ipfilters_manager.add_ippool(ippool_name, [subnet_cidr])
        # update the new port's pool with other port's cidrs
        ri.ipfilters_manager.add_ippool(new_ippool_name, other_subnet_cidrs)

        # now setup the IPF rule
        rules = ['block in quick on %s from %s to pool/%d' %
                 (internal_dlname, subnet_cidr, new_ippool_name)]
        ri.ipfilters_manager.add_ipf_rules(rules)

    def internal_network_removed(self, ri, ex_gw_port, port):
        internal_dlname = self.get_internal_device_name(port['id'])
        if net_lib.Datalink.datalink_exists(internal_dlname):
            self.driver.fini_l3(internal_dlname)
            self.driver.unplug(internal_dlname)

        # remove all the IP filter rules that we added during addition.
        ippool_name = self._get_ippool_name(port['mac_address'])
        rules = ['block in quick on %s from %s to pool/%d' %
                 (internal_dlname, port['subnet']['cidr'], ippool_name)]
        ri.ipfilters_manager.remove_ipf_rules(rules)
        # remove the ippool
        ri.ipfilters_manager.remove_ippool(ippool_name, None)
        for internal_port in ri.internal_ports:
            if (self.conf.allow_forwarding_between_networks and
                internal_port['tenant_id'] == port['tenant_id']):
                continue
            ippool_name = \
                self._get_ippool_name(internal_port['mac_address'])
            ri.ipfilters_manager.remove_ippool(ippool_name,
                                               internal_port['subnet']['cidr'])

    def floating_ip_added(self, ri, ex_gw_port, floating_ip, fixed_ip):
        floating_ipcidr = str(floating_ip) + '/32'
        fixed_ipcidr = str(fixed_ip) + '/32'
        #ifname = self.get_external_device_name(ex_gw_port['id'])
        ifname = self.conf.external_network_datalink
        ipif = net_lib.IPInterface(ifname)
        ipif.create_address(floating_ipcidr)

        nat_rules = ['bimap %s %s -> %s' %
                     (ifname, fixed_ipcidr, floating_ipcidr)]
        ri.ipfilters_manager.add_nat_rules(nat_rules)

    def floating_ip_removed(self, ri, ex_gw_port, floating_ip, fixed_ip):
        floating_ipcidr = str(floating_ip) + '/32'
        fixed_ipcidr = str(fixed_ip) + '/32'
        ifname = self.conf.external_network_datalink
        ipif = net_lib.IPInterface(ifname)
        ipif.delete_address(floating_ipcidr)

        nat_rules = ['bimap %s %s -> %s' %
                     (ifname, fixed_ipcidr, floating_ipcidr)]
        ri.ipfilters_manager.remove_nat_rules(nat_rules)

    def routes_updated(self, ri):
        pass