53ad85ac35
As the notation of ovs_port is not relevant any more, rename it throughout the code to switch_port which is the new class. Change-Id: I1eb1ff4d10265c7a64f38d158ddb6c7186eba06e Partial-Bug: #1781376
179 lines
6.7 KiB
Python
179 lines
6.7 KiB
Python
# Copyright (c) 2017 OpenStack Foundation.
|
|
# 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.
|
|
from oslo_log import log
|
|
from ryu.ofproto import ether
|
|
|
|
from dragonflow._i18n import _
|
|
from dragonflow import conf as cfg
|
|
from dragonflow.controller.apps import snat_mixin
|
|
from dragonflow.controller.common import constants as const
|
|
from dragonflow.controller import df_base_app
|
|
from dragonflow.db.models import constants as model_const
|
|
from dragonflow.db.models import l2
|
|
from dragonflow.db.models import switch
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
class ChassisSNATApp(df_base_app.DFlowApp, snat_mixin.SNATApp_mixin):
|
|
"""Implements single global IP allocation strategy for all hosted VMs
|
|
|
|
Methods provide strategy specific operations
|
|
Application has extra parameters
|
|
- external_host_ip - should be defined in provider range
|
|
-external_host_mac - optional
|
|
"""
|
|
def __init__(self, *args, **kwargs):
|
|
super(ChassisSNATApp, self).__init__(*args, **kwargs)
|
|
LOG.info("Loading SNAT application ... ")
|
|
self.external_network_bridge = (
|
|
cfg.CONF.df_snat_app.external_network_bridge)
|
|
self.external_bridge_mac = self.vswitch_api.get_port_mac_in_use(
|
|
self.external_network_bridge) or const.EMPTY_MAC
|
|
self.chassis = None
|
|
|
|
# new application configuration
|
|
self.external_host_ip = cfg.CONF.df.external_host_ip
|
|
|
|
# create mac address based on given 'external_host_ip'
|
|
if self.external_host_ip is not None:
|
|
split_ip = self.external_host_ip.split('.')
|
|
ip2mac = '{:02x}:{:02x}:{:02x}:{:02x}'.format(*map(int, split_ip))
|
|
self.external_host_mac = const.CHASSIS_MAC_PREFIX + ip2mac
|
|
else:
|
|
raise Exception(_('Please set external_host_ip conf. parameter '
|
|
'to enable SNAT application'))
|
|
|
|
def switch_features_handler(self, ev):
|
|
self._setup_patch_ports()
|
|
self.external_bridge_mac = self.vswitch_api.get_port_mac_in_use(
|
|
self.external_network_bridge) or const.EMPTY_MAC
|
|
|
|
# install static strategy flows
|
|
if self.external_host_ip is None:
|
|
raise Exception(_('Please set external_host_ip conf. parameter '
|
|
'to enable SNAT application'))
|
|
else:
|
|
self.install_strategy_based_flows()
|
|
|
|
def _setup_patch_ports(self):
|
|
integration_bridge = cfg.CONF.df.integration_bridge
|
|
ex_peer_patch_port = 'patch-snat-{0}'.format(
|
|
self.external_network_bridge)
|
|
int_peer_patch_port = 'patch-snat-int'
|
|
|
|
mapping = self.vswitch_api.create_patch_pair(
|
|
integration_bridge,
|
|
self.external_network_bridge,
|
|
ex_peer_patch_port,
|
|
int_peer_patch_port)
|
|
self.external_ofport = self.vswitch_api.get_port_ofport(
|
|
mapping[0])
|
|
|
|
@df_base_app.register_event(switch.SwitchPort, model_const.EVENT_CREATED)
|
|
@df_base_app.register_event(switch.SwitchPort, model_const.EVENT_UPDATED)
|
|
def switch_port_updated(self, switch_port, orig_switch_port=None):
|
|
if switch_port.name != self.external_network_bridge:
|
|
return
|
|
|
|
LOG.debug("Ex. Bridge port update is called ... ")
|
|
mac = switch_port.mac_in_use
|
|
if mac in (None, const.EMPTY_MAC, self.external_bridge_mac):
|
|
return
|
|
|
|
self.external_bridge_mac = mac
|
|
|
|
if self.chassis is None:
|
|
return
|
|
|
|
parser = self.parser
|
|
match = parser.OFPMatch(eth_type=ether.ETH_TYPE_IP)
|
|
self._install_snat_egress_after_conntrack(
|
|
match,
|
|
self.external_host_mac)
|
|
|
|
@df_base_app.register_event(l2.LogicalPort, l2.EVENT_UNBIND_LOCAL)
|
|
def _remove_local_port(self, lport):
|
|
"""override remove_local_port method to remove installed flows
|
|
|
|
:param lport: local logical port which is being removed
|
|
"""
|
|
LOG.info("SNAT application: remove local port %(lport)s",
|
|
{'lport': lport})
|
|
if self.external_host_mac is not None:
|
|
# remove VM specific flows
|
|
if self.is_data_port(lport):
|
|
self.remove_lport_based_flows(lport)
|
|
else:
|
|
LOG.info('SNAT application: not a compute port, skipped')
|
|
|
|
@df_base_app.register_event(l2.LogicalPort, l2.EVENT_BIND_LOCAL)
|
|
def _add_local_port(self, lport):
|
|
"""override add_local_port method to install sNAT related flows
|
|
|
|
:param lport: local logical port which is being added
|
|
"""
|
|
LOG.info("SNAT application: add local port %(lport)s",
|
|
{'lport': lport})
|
|
|
|
if self.external_host_mac is not None:
|
|
# install flows only when compute port is added
|
|
if self.is_data_port(lport):
|
|
self.chassis = lport.binding.chassis
|
|
|
|
self.install_lport_based_flows(lport)
|
|
else:
|
|
LOG.info('SNAT application: not a compute port, skipped')
|
|
|
|
def install_strategy_based_flows(self):
|
|
|
|
self._install_ingress_goto_rules()
|
|
self._install_egress_goto_rules()
|
|
|
|
self._install_snat_ingress_conntrack()
|
|
|
|
parser = self.parser
|
|
match = parser.OFPMatch(eth_type=ether.ETH_TYPE_IP)
|
|
self._install_snat_egress_conntrack(
|
|
match,
|
|
self.external_host_ip)
|
|
self._install_snat_egress_after_conntrack(
|
|
match,
|
|
self.external_host_mac)
|
|
|
|
self._install_arp_responder(
|
|
self.external_host_ip,
|
|
self.external_host_mac)
|
|
|
|
def install_lport_based_flows(self, lport):
|
|
# instance specific flows
|
|
self._install_snat_ingress_after_conntrack(
|
|
lport.unique_key,
|
|
lport.mac,
|
|
self.external_host_mac)
|
|
|
|
def remove_lport_based_flows(self, lport):
|
|
parser = self.parser
|
|
ofproto = self.ofproto
|
|
unique_key = lport.unique_key
|
|
match = parser.OFPMatch(eth_type=ether.ETH_TYPE_IP,
|
|
ct_mark=int(unique_key))
|
|
|
|
self.mod_flow(
|
|
command=ofproto.OFPFC_DELETE_STRICT,
|
|
table_id=const.INGRESS_SNAT_TABLE,
|
|
priority=const.PRIORITY_LOW,
|
|
match=match)
|