Make py file line endings unix style
The line endings were CRLF (dos/windows style). The standard is to use UNIX style endings. No other change was made to the file. This is a result of following command in dragonflow folder. find ./ -name '*.py' -type f -exec dos2unix {} \; Change-Id: I732b1647eb2f9b8ef43f651993ec606b4998f383
This commit is contained in:
parent
e7241733b6
commit
117af79040
|
@ -1,34 +1,34 @@
|
|||
# Copyright (c) 2016 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_config import cfg
|
||||
|
||||
from dragonflow._i18n import _
|
||||
|
||||
df_active_port_detection_opts = [
|
||||
cfg.IntOpt('detection_interval_time',
|
||||
default=30,
|
||||
help=_("Interval time of sending detection arp packets."))
|
||||
]
|
||||
|
||||
|
||||
def register_opts():
|
||||
cfg.CONF.register_opts(df_active_port_detection_opts,
|
||||
group='df_active_port_detection')
|
||||
|
||||
|
||||
def list_opts():
|
||||
return {'df_active_port_detection': df_active_port_detection_opts}
|
||||
# Copyright (c) 2016 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_config import cfg
|
||||
|
||||
from dragonflow._i18n import _
|
||||
|
||||
df_active_port_detection_opts = [
|
||||
cfg.IntOpt('detection_interval_time',
|
||||
default=30,
|
||||
help=_("Interval time of sending detection arp packets."))
|
||||
]
|
||||
|
||||
|
||||
def register_opts():
|
||||
cfg.CONF.register_opts(df_active_port_detection_opts,
|
||||
group='df_active_port_detection')
|
||||
|
||||
|
||||
def list_opts():
|
||||
return {'df_active_port_detection': df_active_port_detection_opts}
|
||||
|
|
|
@ -1,319 +1,319 @@
|
|||
# Copyright (c) 2016 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.
|
||||
|
||||
import collections
|
||||
import netaddr
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_service import loopingcall
|
||||
from ryu.lib.packet import arp
|
||||
from ryu.lib.packet import packet
|
||||
from ryu.ofproto import ether
|
||||
|
||||
from dragonflow._i18n import _LE, _LI, _LW
|
||||
from dragonflow import conf as cfg
|
||||
from dragonflow.controller.common import constants as controller_const
|
||||
from dragonflow.controller.common import utils
|
||||
from dragonflow.controller import df_base_app
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class ActivePortDetectionApp(df_base_app.DFlowApp):
|
||||
|
||||
"""A application for detecting the active port for allowed address pairs
|
||||
|
||||
When this application is loaded, gratuitous ARP / ARP reply packets using
|
||||
the address of a VM port's allowed address pairs will be monitored. If
|
||||
any of those ARP packets arrives, a active port will be created with the
|
||||
sending VM port. The active port can be used by other applications (like
|
||||
L2App) to let packets to the address only be sent to the recorded VM port.
|
||||
When this application is not loaded, packets to a address in a allowed
|
||||
address pair will be broadcast to all VM ports with this allowed address
|
||||
pair.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ActivePortDetectionApp, self).__init__(*args, **kwargs)
|
||||
self.dection_interval_time = \
|
||||
cfg.CONF.df_active_port_detection.detection_interval_time
|
||||
self.allowed_address_pairs_refs_list = collections.defaultdict(set)
|
||||
self.api.register_table_handler(controller_const.ARP_TABLE,
|
||||
self.packet_in_handler)
|
||||
|
||||
def packet_in_handler(self, event):
|
||||
msg = event.msg
|
||||
pkt = packet.Packet(msg.data)
|
||||
arp_pkt = pkt.get_protocol(arp.arp)
|
||||
if arp_pkt is None:
|
||||
LOG.error(_LE("No support for non ARP protocol"))
|
||||
return
|
||||
|
||||
if (arp_pkt.opcode == arp.ARP_REQUEST and
|
||||
arp_pkt.src_ip == arp_pkt.dst_ip) or \
|
||||
arp_pkt.opcode == arp.ARP_REPLY:
|
||||
match = msg.match
|
||||
in_port = match.get('in_port', None)
|
||||
if in_port:
|
||||
self._update_active_port_in_db(
|
||||
arp_pkt.src_ip, arp_pkt.src_mac, in_port)
|
||||
|
||||
def _get_ips_in_allowed_address_pairs(self, lport):
|
||||
ips = set()
|
||||
allowed_address_pairs = lport.get_allowed_address_pairs()
|
||||
for pair in allowed_address_pairs:
|
||||
ip = pair["ip_address"]
|
||||
if netaddr.IPNetwork(ip).version == 4:
|
||||
ips.add(ip)
|
||||
else:
|
||||
# IPv6 addresses are not supported yet
|
||||
LOG.info(_LI("Don't support IPv6 addresses for now. IPv6"
|
||||
" address %s will be ignored."), ip)
|
||||
|
||||
return ips
|
||||
|
||||
def _get_match_arp_reply(self, in_port, network_id, ip):
|
||||
parser = self.get_datapath().ofproto_parser
|
||||
match = parser.OFPMatch()
|
||||
match.set_in_port(in_port)
|
||||
match.set_dl_type(ether.ETH_TYPE_ARP)
|
||||
match.set_arp_spa(utils.ipv4_text_to_int(str(ip)))
|
||||
match.set_arp_tpa(0)
|
||||
match.set_arp_opcode(arp.ARP_REPLY)
|
||||
match.set_metadata(network_id)
|
||||
return match
|
||||
|
||||
def _get_match_gratuitous_arp(self, in_port, network_id, ip):
|
||||
target_ip = utils.ipv4_text_to_int(str(ip))
|
||||
parser = self.get_datapath().ofproto_parser
|
||||
match = parser.OFPMatch()
|
||||
match.set_in_port(in_port)
|
||||
match.set_dl_type(ether.ETH_TYPE_ARP)
|
||||
match.set_arp_spa(target_ip)
|
||||
match.set_arp_tpa(target_ip)
|
||||
match.set_arp_opcode(arp.ARP_REQUEST)
|
||||
match.set_metadata(network_id)
|
||||
return match
|
||||
|
||||
def _get_instructions_packet_in(self):
|
||||
parser = self.get_datapath().ofproto_parser
|
||||
ofproto = self.get_datapath().ofproto
|
||||
|
||||
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
|
||||
ofproto.OFPCML_NO_BUFFER)]
|
||||
action_inst = parser.OFPInstructionActions(
|
||||
ofproto.OFPIT_APPLY_ACTIONS, actions)
|
||||
goto_inst = parser.OFPInstructionGotoTable(
|
||||
controller_const.L2_LOOKUP_TABLE)
|
||||
inst = [action_inst, goto_inst]
|
||||
return inst
|
||||
|
||||
def _install_flows_for_target_ip(self, ip, lport):
|
||||
instructions = self._get_instructions_packet_in()
|
||||
|
||||
arp_reply_match = self._get_match_arp_reply(
|
||||
lport.get_external_value('ofport'),
|
||||
lport.get_external_value('local_network_id'),
|
||||
ip)
|
||||
self.mod_flow(
|
||||
self.get_datapath(),
|
||||
inst=instructions,
|
||||
table_id=controller_const.ARP_TABLE,
|
||||
priority=controller_const.PRIORITY_MEDIUM,
|
||||
match=arp_reply_match)
|
||||
|
||||
gratuitous_arp_match = self._get_match_gratuitous_arp(
|
||||
lport.get_external_value('ofport'),
|
||||
lport.get_external_value('local_network_id'),
|
||||
ip)
|
||||
self.mod_flow(
|
||||
self.get_datapath(),
|
||||
inst=instructions,
|
||||
table_id=controller_const.ARP_TABLE,
|
||||
priority=controller_const.PRIORITY_MEDIUM,
|
||||
match=gratuitous_arp_match)
|
||||
|
||||
def _uninstall_flows_for_target_ip(self, ip, lport):
|
||||
ofproto = self.get_datapath().ofproto
|
||||
|
||||
arp_reply_match = self._get_match_arp_reply(
|
||||
lport.get_external_value('ofport'),
|
||||
lport.get_external_value('local_network_id'),
|
||||
ip)
|
||||
self.mod_flow(
|
||||
datapath=self.get_datapath(),
|
||||
table_id=controller_const.ARP_TABLE,
|
||||
match=arp_reply_match,
|
||||
command=ofproto.OFPFC_DELETE)
|
||||
|
||||
gratuitous_arp_match = self._get_match_gratuitous_arp(
|
||||
lport.get_external_value('ofport'),
|
||||
lport.get_external_value('local_network_id'),
|
||||
ip)
|
||||
self.mod_flow(
|
||||
datapath=self.get_datapath(),
|
||||
table_id=controller_const.ARP_TABLE,
|
||||
match=gratuitous_arp_match,
|
||||
command=ofproto.OFPFC_DELETE)
|
||||
|
||||
def _if_old_active_port_need_update(self, old_port, ip, mac, found_lport):
|
||||
if (old_port.get_network_id() == found_lport.get_lswitch_id() and
|
||||
old_port.get_ip() == ip and
|
||||
old_port.get_detected_mac() == mac and
|
||||
old_port.get_topic() == found_lport.get_topic() and
|
||||
old_port.get_detected_lport_id() == found_lport.get_id()):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _update_active_port_in_db(self, ip, mac, ofport):
|
||||
lports = self.db_store.get_ports()
|
||||
found_lport = None
|
||||
for lport in lports:
|
||||
if ofport == lport.get_external_value('ofport'):
|
||||
found_lport = lport
|
||||
break
|
||||
if found_lport is None:
|
||||
LOG.info(_LI("There is no logical port matched this "
|
||||
"ofport(%s)."), ofport)
|
||||
return
|
||||
|
||||
network_id = found_lport.get_lswitch_id()
|
||||
topic = found_lport.get_topic()
|
||||
found_lport_id = found_lport.get_id()
|
||||
key = network_id + ip
|
||||
old_active_port = self.db_store.get_active_port(key)
|
||||
if (not old_active_port or self._if_old_active_port_need_update(
|
||||
old_active_port, ip, mac, found_lport)):
|
||||
LOG.info(_LI("Detected new active node. ip=%(ip)s, "
|
||||
"mac=%(mac)s, lport_id=%(lport_id)s"),
|
||||
{'ip': ip, 'mac': mac, 'lport_id': found_lport_id})
|
||||
if old_active_port:
|
||||
self.nb_api.update_active_port(
|
||||
id=key,
|
||||
topic=topic,
|
||||
detected_mac=mac,
|
||||
detected_lport_id=found_lport_id)
|
||||
else:
|
||||
self.nb_api.create_active_port(
|
||||
id=key,
|
||||
topic=topic,
|
||||
network_id=network_id,
|
||||
ip=ip,
|
||||
detected_mac=mac,
|
||||
detected_lport_id=found_lport_id)
|
||||
|
||||
def _remove_active_port_from_db_by_lport(self, network_id, ip, lport):
|
||||
key = network_id + ip
|
||||
old_active_port = self.db_store.get_active_port(key)
|
||||
if (old_active_port and
|
||||
old_active_port.get_detected_lport_id() == lport.get_id()):
|
||||
self.nb_api.delete_active_port(key, lport.get_topic())
|
||||
|
||||
def _add_target_ip(self, ip, lport):
|
||||
# install flows which send the arp reply or gratuitous arp to
|
||||
# controller
|
||||
self._install_flows_for_target_ip(ip, lport)
|
||||
# send arp request
|
||||
self._send_arp_request(ip, lport)
|
||||
|
||||
network_id = lport.get_lswitch_id()
|
||||
key = (network_id, ip)
|
||||
lport_id = lport.get_id()
|
||||
self.allowed_address_pairs_refs_list[key].add(lport_id)
|
||||
|
||||
def _remove_target_ip(self, ip, lport):
|
||||
network_id = lport.get_lswitch_id()
|
||||
key = (network_id, ip)
|
||||
lport_id = lport.get_id()
|
||||
ip_refs_list = self.allowed_address_pairs_refs_list[key]
|
||||
if lport_id in ip_refs_list:
|
||||
ip_refs_list.remove(lport_id)
|
||||
if len(ip_refs_list) == 0:
|
||||
del self.allowed_address_pairs_refs_list[key]
|
||||
|
||||
# uninstall flows which send the arp reply or gratuitous arp to
|
||||
# controller
|
||||
self._uninstall_flows_for_target_ip(ip, lport)
|
||||
|
||||
# Try to remove the active node detected from this lport and used
|
||||
# this ip from dragonflow DB
|
||||
self._remove_active_port_from_db_by_lport(lport.get_lswitch_id(), ip,
|
||||
lport)
|
||||
|
||||
def _get_detect_items(self):
|
||||
items = []
|
||||
|
||||
for target_ip, refs in self.allowed_address_pairs_refs_list.items():
|
||||
for lport_id in refs:
|
||||
lport = self.db_store.get_port(lport_id)
|
||||
if lport is not None:
|
||||
items.append((target_ip, lport))
|
||||
|
||||
return items
|
||||
|
||||
def _send_arp_request_callback(self):
|
||||
items = self._get_detect_items()
|
||||
for item in items:
|
||||
network_id, ip = item[0]
|
||||
lport = item[1]
|
||||
# send arp request
|
||||
self._send_arp_request(ip, lport)
|
||||
|
||||
def _send_arp_request(self, ip, lport):
|
||||
mac = self.vswitch_api.get_local_port_mac_in_use(lport.get_id())
|
||||
if mac is not None:
|
||||
self.send_arp_request(mac,
|
||||
'0.0.0.0',
|
||||
ip,
|
||||
lport.get_external_value('ofport'))
|
||||
else:
|
||||
LOG.warning(_LW("Couldn't find a valid mac to detect active "
|
||||
"port in lport %s."), lport.get_id())
|
||||
|
||||
def _periodic_send_arp_request(self):
|
||||
"""Spawn a thread to periodically to detect active node among
|
||||
ports with allowed-address-pairs.
|
||||
"""
|
||||
periodic = loopingcall.FixedIntervalLoopingCall(
|
||||
self._send_arp_request_callback)
|
||||
periodic.start(interval=self.dection_interval_time)
|
||||
|
||||
def switch_features_handler(self, ev):
|
||||
self._periodic_send_arp_request()
|
||||
|
||||
def add_local_port(self, lport):
|
||||
ips = self._get_ips_in_allowed_address_pairs(lport)
|
||||
for target_ip in ips:
|
||||
self._add_target_ip(target_ip, lport)
|
||||
|
||||
def update_local_port(self, lport, original_lport):
|
||||
ips_set = self._get_ips_in_allowed_address_pairs(lport)
|
||||
original_ips_set = self._get_ips_in_allowed_address_pairs(
|
||||
original_lport)
|
||||
|
||||
for target_ip in ips_set - original_ips_set:
|
||||
self._add_target_ip(target_ip, lport)
|
||||
|
||||
for target_ip in original_ips_set - ips_set:
|
||||
self._remove_target_ip(target_ip, original_lport)
|
||||
|
||||
def remove_local_port(self, lport):
|
||||
ips = self._get_ips_in_allowed_address_pairs(lport)
|
||||
if not ips:
|
||||
return
|
||||
|
||||
for target_ip in ips:
|
||||
self._remove_target_ip(target_ip, lport)
|
||||
# Copyright (c) 2016 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.
|
||||
|
||||
import collections
|
||||
import netaddr
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_service import loopingcall
|
||||
from ryu.lib.packet import arp
|
||||
from ryu.lib.packet import packet
|
||||
from ryu.ofproto import ether
|
||||
|
||||
from dragonflow._i18n import _LE, _LI, _LW
|
||||
from dragonflow import conf as cfg
|
||||
from dragonflow.controller.common import constants as controller_const
|
||||
from dragonflow.controller.common import utils
|
||||
from dragonflow.controller import df_base_app
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class ActivePortDetectionApp(df_base_app.DFlowApp):
|
||||
|
||||
"""A application for detecting the active port for allowed address pairs
|
||||
|
||||
When this application is loaded, gratuitous ARP / ARP reply packets using
|
||||
the address of a VM port's allowed address pairs will be monitored. If
|
||||
any of those ARP packets arrives, a active port will be created with the
|
||||
sending VM port. The active port can be used by other applications (like
|
||||
L2App) to let packets to the address only be sent to the recorded VM port.
|
||||
When this application is not loaded, packets to a address in a allowed
|
||||
address pair will be broadcast to all VM ports with this allowed address
|
||||
pair.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ActivePortDetectionApp, self).__init__(*args, **kwargs)
|
||||
self.dection_interval_time = \
|
||||
cfg.CONF.df_active_port_detection.detection_interval_time
|
||||
self.allowed_address_pairs_refs_list = collections.defaultdict(set)
|
||||
self.api.register_table_handler(controller_const.ARP_TABLE,
|
||||
self.packet_in_handler)
|
||||
|
||||
def packet_in_handler(self, event):
|
||||
msg = event.msg
|
||||
pkt = packet.Packet(msg.data)
|
||||
arp_pkt = pkt.get_protocol(arp.arp)
|
||||
if arp_pkt is None:
|
||||
LOG.error(_LE("No support for non ARP protocol"))
|
||||
return
|
||||
|
||||
if (arp_pkt.opcode == arp.ARP_REQUEST and
|
||||
arp_pkt.src_ip == arp_pkt.dst_ip) or \
|
||||
arp_pkt.opcode == arp.ARP_REPLY:
|
||||
match = msg.match
|
||||
in_port = match.get('in_port', None)
|
||||
if in_port:
|
||||
self._update_active_port_in_db(
|
||||
arp_pkt.src_ip, arp_pkt.src_mac, in_port)
|
||||
|
||||
def _get_ips_in_allowed_address_pairs(self, lport):
|
||||
ips = set()
|
||||
allowed_address_pairs = lport.get_allowed_address_pairs()
|
||||
for pair in allowed_address_pairs:
|
||||
ip = pair["ip_address"]
|
||||
if netaddr.IPNetwork(ip).version == 4:
|
||||
ips.add(ip)
|
||||
else:
|
||||
# IPv6 addresses are not supported yet
|
||||
LOG.info(_LI("Don't support IPv6 addresses for now. IPv6"
|
||||
" address %s will be ignored."), ip)
|
||||
|
||||
return ips
|
||||
|
||||
def _get_match_arp_reply(self, in_port, network_id, ip):
|
||||
parser = self.get_datapath().ofproto_parser
|
||||
match = parser.OFPMatch()
|
||||
match.set_in_port(in_port)
|
||||
match.set_dl_type(ether.ETH_TYPE_ARP)
|
||||
match.set_arp_spa(utils.ipv4_text_to_int(str(ip)))
|
||||
match.set_arp_tpa(0)
|
||||
match.set_arp_opcode(arp.ARP_REPLY)
|
||||
match.set_metadata(network_id)
|
||||
return match
|
||||
|
||||
def _get_match_gratuitous_arp(self, in_port, network_id, ip):
|
||||
target_ip = utils.ipv4_text_to_int(str(ip))
|
||||
parser = self.get_datapath().ofproto_parser
|
||||
match = parser.OFPMatch()
|
||||
match.set_in_port(in_port)
|
||||
match.set_dl_type(ether.ETH_TYPE_ARP)
|
||||
match.set_arp_spa(target_ip)
|
||||
match.set_arp_tpa(target_ip)
|
||||
match.set_arp_opcode(arp.ARP_REQUEST)
|
||||
match.set_metadata(network_id)
|
||||
return match
|
||||
|
||||
def _get_instructions_packet_in(self):
|
||||
parser = self.get_datapath().ofproto_parser
|
||||
ofproto = self.get_datapath().ofproto
|
||||
|
||||
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
|
||||
ofproto.OFPCML_NO_BUFFER)]
|
||||
action_inst = parser.OFPInstructionActions(
|
||||
ofproto.OFPIT_APPLY_ACTIONS, actions)
|
||||
goto_inst = parser.OFPInstructionGotoTable(
|
||||
controller_const.L2_LOOKUP_TABLE)
|
||||
inst = [action_inst, goto_inst]
|
||||
return inst
|
||||
|
||||
def _install_flows_for_target_ip(self, ip, lport):
|
||||
instructions = self._get_instructions_packet_in()
|
||||
|
||||
arp_reply_match = self._get_match_arp_reply(
|
||||
lport.get_external_value('ofport'),
|
||||
lport.get_external_value('local_network_id'),
|
||||
ip)
|
||||
self.mod_flow(
|
||||
self.get_datapath(),
|
||||
inst=instructions,
|
||||
table_id=controller_const.ARP_TABLE,
|
||||
priority=controller_const.PRIORITY_MEDIUM,
|
||||
match=arp_reply_match)
|
||||
|
||||
gratuitous_arp_match = self._get_match_gratuitous_arp(
|
||||
lport.get_external_value('ofport'),
|
||||
lport.get_external_value('local_network_id'),
|
||||
ip)
|
||||
self.mod_flow(
|
||||
self.get_datapath(),
|
||||
inst=instructions,
|
||||
table_id=controller_const.ARP_TABLE,
|
||||
priority=controller_const.PRIORITY_MEDIUM,
|
||||
match=gratuitous_arp_match)
|
||||
|
||||
def _uninstall_flows_for_target_ip(self, ip, lport):
|
||||
ofproto = self.get_datapath().ofproto
|
||||
|
||||
arp_reply_match = self._get_match_arp_reply(
|
||||
lport.get_external_value('ofport'),
|
||||
lport.get_external_value('local_network_id'),
|
||||
ip)
|
||||
self.mod_flow(
|
||||
datapath=self.get_datapath(),
|
||||
table_id=controller_const.ARP_TABLE,
|
||||
match=arp_reply_match,
|
||||
command=ofproto.OFPFC_DELETE)
|
||||
|
||||
gratuitous_arp_match = self._get_match_gratuitous_arp(
|
||||
lport.get_external_value('ofport'),
|
||||
lport.get_external_value('local_network_id'),
|
||||
ip)
|
||||
self.mod_flow(
|
||||
datapath=self.get_datapath(),
|
||||
table_id=controller_const.ARP_TABLE,
|
||||
match=gratuitous_arp_match,
|
||||
command=ofproto.OFPFC_DELETE)
|
||||
|
||||
def _if_old_active_port_need_update(self, old_port, ip, mac, found_lport):
|
||||
if (old_port.get_network_id() == found_lport.get_lswitch_id() and
|
||||
old_port.get_ip() == ip and
|
||||
old_port.get_detected_mac() == mac and
|
||||
old_port.get_topic() == found_lport.get_topic() and
|
||||
old_port.get_detected_lport_id() == found_lport.get_id()):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _update_active_port_in_db(self, ip, mac, ofport):
|
||||
lports = self.db_store.get_ports()
|
||||
found_lport = None
|
||||
for lport in lports:
|
||||
if ofport == lport.get_external_value('ofport'):
|
||||
found_lport = lport
|
||||
break
|
||||
if found_lport is None:
|
||||
LOG.info(_LI("There is no logical port matched this "
|
||||
"ofport(%s)."), ofport)
|
||||
return
|
||||
|
||||
network_id = found_lport.get_lswitch_id()
|
||||
topic = found_lport.get_topic()
|
||||
found_lport_id = found_lport.get_id()
|
||||
key = network_id + ip
|
||||
old_active_port = self.db_store.get_active_port(key)
|
||||
if (not old_active_port or self._if_old_active_port_need_update(
|
||||
old_active_port, ip, mac, found_lport)):
|
||||
LOG.info(_LI("Detected new active node. ip=%(ip)s, "
|
||||
"mac=%(mac)s, lport_id=%(lport_id)s"),
|
||||
{'ip': ip, 'mac': mac, 'lport_id': found_lport_id})
|
||||
if old_active_port:
|
||||
self.nb_api.update_active_port(
|
||||
id=key,
|
||||
topic=topic,
|
||||
detected_mac=mac,
|
||||
detected_lport_id=found_lport_id)
|
||||
else:
|
||||
self.nb_api.create_active_port(
|
||||
id=key,
|
||||
topic=topic,
|
||||
network_id=network_id,
|
||||
ip=ip,
|
||||
detected_mac=mac,
|
||||
detected_lport_id=found_lport_id)
|
||||
|
||||
def _remove_active_port_from_db_by_lport(self, network_id, ip, lport):
|
||||
key = network_id + ip
|
||||
old_active_port = self.db_store.get_active_port(key)
|
||||
if (old_active_port and
|
||||
old_active_port.get_detected_lport_id() == lport.get_id()):
|
||||
self.nb_api.delete_active_port(key, lport.get_topic())
|
||||
|
||||
def _add_target_ip(self, ip, lport):
|
||||
# install flows which send the arp reply or gratuitous arp to
|
||||
# controller
|
||||
self._install_flows_for_target_ip(ip, lport)
|
||||
# send arp request
|
||||
self._send_arp_request(ip, lport)
|
||||
|
||||
network_id = lport.get_lswitch_id()
|
||||
key = (network_id, ip)
|
||||
lport_id = lport.get_id()
|
||||
self.allowed_address_pairs_refs_list[key].add(lport_id)
|
||||
|
||||
def _remove_target_ip(self, ip, lport):
|
||||
network_id = lport.get_lswitch_id()
|
||||
key = (network_id, ip)
|
||||
lport_id = lport.get_id()
|
||||
ip_refs_list = self.allowed_address_pairs_refs_list[key]
|
||||
if lport_id in ip_refs_list:
|
||||
ip_refs_list.remove(lport_id)
|
||||
if len(ip_refs_list) == 0:
|
||||
del self.allowed_address_pairs_refs_list[key]
|
||||
|
||||
# uninstall flows which send the arp reply or gratuitous arp to
|
||||
# controller
|
||||
self._uninstall_flows_for_target_ip(ip, lport)
|
||||
|
||||
# Try to remove the active node detected from this lport and used
|
||||
# this ip from dragonflow DB
|
||||
self._remove_active_port_from_db_by_lport(lport.get_lswitch_id(), ip,
|
||||
lport)
|
||||
|
||||
def _get_detect_items(self):
|
||||
items = []
|
||||
|
||||
for target_ip, refs in self.allowed_address_pairs_refs_list.items():
|
||||
for lport_id in refs:
|
||||
lport = self.db_store.get_port(lport_id)
|
||||
if lport is not None:
|
||||
items.append((target_ip, lport))
|
||||
|
||||
return items
|
||||
|
||||
def _send_arp_request_callback(self):
|
||||
items = self._get_detect_items()
|
||||
for item in items:
|
||||
network_id, ip = item[0]
|
||||
lport = item[1]
|
||||
# send arp request
|
||||
self._send_arp_request(ip, lport)
|
||||
|
||||
def _send_arp_request(self, ip, lport):
|
||||
mac = self.vswitch_api.get_local_port_mac_in_use(lport.get_id())
|
||||
if mac is not None:
|
||||
self.send_arp_request(mac,
|
||||
'0.0.0.0',
|
||||
ip,
|
||||
lport.get_external_value('ofport'))
|
||||
else:
|
||||
LOG.warning(_LW("Couldn't find a valid mac to detect active "
|
||||
"port in lport %s."), lport.get_id())
|
||||
|
||||
def _periodic_send_arp_request(self):
|
||||
"""Spawn a thread to periodically to detect active node among
|
||||
ports with allowed-address-pairs.
|
||||
"""
|
||||
periodic = loopingcall.FixedIntervalLoopingCall(
|
||||
self._send_arp_request_callback)
|
||||
periodic.start(interval=self.dection_interval_time)
|
||||
|
||||
def switch_features_handler(self, ev):
|
||||
self._periodic_send_arp_request()
|
||||
|
||||
def add_local_port(self, lport):
|
||||
ips = self._get_ips_in_allowed_address_pairs(lport)
|
||||
for target_ip in ips:
|
||||
self._add_target_ip(target_ip, lport)
|
||||
|
||||
def update_local_port(self, lport, original_lport):
|
||||
ips_set = self._get_ips_in_allowed_address_pairs(lport)
|
||||
original_ips_set = self._get_ips_in_allowed_address_pairs(
|
||||
original_lport)
|
||||
|
||||
for target_ip in ips_set - original_ips_set:
|
||||
self._add_target_ip(target_ip, lport)
|
||||
|
||||
for target_ip in original_ips_set - ips_set:
|
||||
self._remove_target_ip(target_ip, original_lport)
|
||||
|
||||
def remove_local_port(self, lport):
|
||||
ips = self._get_ips_in_allowed_address_pairs(lport)
|
||||
if not ips:
|
||||
return
|
||||
|
||||
for target_ip in ips:
|
||||
self._remove_target_ip(target_ip, lport)
|
||||
|
|
|
@ -1,160 +1,160 @@
|
|||
# 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.
|
||||
|
||||
import time
|
||||
|
||||
from dragonflow.controller.common import constants as const
|
||||
from dragonflow.tests.common import constants as test_const
|
||||
from dragonflow.tests.common import utils
|
||||
from dragonflow.tests.fullstack import test_base
|
||||
from dragonflow.tests.fullstack import test_objects as objects
|
||||
from ryu.lib.packet import arp
|
||||
|
||||
|
||||
class TestOVSFlowsForActivePortDectionApp(test_base.DFTestBase):
|
||||
|
||||
def _get_sending_arp_to_controller_flows(self, ofport, arp_op):
|
||||
ovs_flows_parser = utils.OvsFlowsParser()
|
||||
flows = ovs_flows_parser.dump(self.integration_bridge)
|
||||
expected_in_port = "in_port=" + ofport
|
||||
expected_arp_op = "arp_op=" + arp_op
|
||||
expected_actions = "CONTROLLER:65535," + "goto_table:" + \
|
||||
str(const.L2_LOOKUP_TABLE)
|
||||
flows = [flow for flow in flows
|
||||
if ((expected_in_port in flow['match']) and
|
||||
(expected_arp_op in flow['match']) and
|
||||
('arp' in flow['match']) and
|
||||
(flow['table'] == str(const.ARP_TABLE)) and
|
||||
(flow['actions'] == expected_actions))]
|
||||
return flows
|
||||
|
||||
def _get_sending_arp_reply_to_controller_flows(self, ofport):
|
||||
return self._get_sending_arp_to_controller_flows(ofport,
|
||||
str(arp.ARP_REPLY))
|
||||
|
||||
def _get_sending_gratuitous_arp_to_controller_flows(self, ofport):
|
||||
return self._get_sending_arp_to_controller_flows(ofport,
|
||||
str(arp.ARP_REQUEST))
|
||||
|
||||
def _check_sending_arp_reply_to_controller_flows(self, ofport, ip=None):
|
||||
flows = self._get_sending_arp_reply_to_controller_flows(ofport)
|
||||
expected_arp_tpa = '0.0.0.0'
|
||||
for flow in flows:
|
||||
if ip is not None:
|
||||
if expected_arp_tpa not in flow['match']:
|
||||
continue
|
||||
expected_arp_spa = 'arp_spa=' + ip
|
||||
if expected_arp_spa not in flow['match']:
|
||||
continue
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _check_sending_gratuitous_arp_to_controller_flows(self, ofport,
|
||||
ip=None):
|
||||
flows = self._get_sending_gratuitous_arp_to_controller_flows(ofport)
|
||||
for flow in flows:
|
||||
if ip is not None:
|
||||
expected_arp_spa = 'arp_spa=' + ip
|
||||
expected_arp_tpa = 'arp_tpa=' + ip
|
||||
if (expected_arp_spa not in flow['match']) or \
|
||||
(expected_arp_tpa not in flow['match']):
|
||||
continue
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def test_sending_arp_to_controller_flows(self):
|
||||
"""
|
||||
Add a VM with allowed address pairs configuration. Verify related
|
||||
flows is there.
|
||||
"""
|
||||
network = self.store(objects.NetworkTestObj(self.neutron, self.nb_api))
|
||||
network_id = network.create(network={'name': 'aap_test'})
|
||||
subnet_obj = self.store(objects.SubnetTestObj(
|
||||
self.neutron,
|
||||
self.nb_api,
|
||||
network_id,
|
||||
))
|
||||
|
||||
subnet = {'network_id': network_id,
|
||||
'cidr': '192.168.97.0/24',
|
||||
'gateway_ip': '192.168.97.1',
|
||||
'ip_version': 4,
|
||||
'name': 'aap_test',
|
||||
'enable_dhcp': True}
|
||||
subnet_obj.create(subnet)
|
||||
|
||||
vm = self.store(objects.VMTestObj(self, self.neutron))
|
||||
vm_id = vm.create(network=network)
|
||||
|
||||
ovsdb = utils.OvsDBParser()
|
||||
vm_port_id = ovsdb.get_port_id_by_vm_id(vm_id)
|
||||
self.assertIsNotNone(vm_port_id)
|
||||
|
||||
vm_port = objects.PortTestObj(self.neutron, self.nb_api, network_id,
|
||||
vm_port_id)
|
||||
|
||||
of_port = ovsdb.get_ofport(vm_port_id)
|
||||
self.assertIsNotNone(of_port)
|
||||
result = self._check_sending_arp_reply_to_controller_flows(of_port)
|
||||
self.assertFalse(result)
|
||||
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
||||
of_port)
|
||||
self.assertFalse(result)
|
||||
|
||||
ip_address1 = '192.168.97.100'
|
||||
mac_address1 = '1A:22:33:44:55:66'
|
||||
allowed_address_pairs1 = [{'ip_address': ip_address1,
|
||||
'mac_address': mac_address1}]
|
||||
vm_port.update({'allowed_address_pairs': allowed_address_pairs1})
|
||||
|
||||
time.sleep(test_const.DEFAULT_CMD_TIMEOUT)
|
||||
|
||||
result = self._check_sending_arp_reply_to_controller_flows(
|
||||
of_port, ip_address1)
|
||||
self.assertTrue(result)
|
||||
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
||||
of_port, ip_address1)
|
||||
self.assertTrue(result)
|
||||
|
||||
ip_address2 = '192.168.97.101'
|
||||
allowed_address_pairs2 = [{'ip_address': ip_address2}]
|
||||
vm_port.update({'allowed_address_pairs': allowed_address_pairs2})
|
||||
|
||||
time.sleep(test_const.DEFAULT_CMD_TIMEOUT)
|
||||
|
||||
result = self._check_sending_arp_reply_to_controller_flows(
|
||||
of_port, ip_address1)
|
||||
self.assertFalse(result)
|
||||
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
||||
of_port, ip_address1)
|
||||
self.assertFalse(result)
|
||||
result = self._check_sending_arp_reply_to_controller_flows(
|
||||
of_port, ip_address2)
|
||||
self.assertTrue(result)
|
||||
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
||||
of_port, ip_address2)
|
||||
self.assertTrue(result)
|
||||
|
||||
vm.close()
|
||||
|
||||
time.sleep(test_const.DEFAULT_CMD_TIMEOUT)
|
||||
|
||||
result = self._check_sending_arp_reply_to_controller_flows(
|
||||
of_port, ip_address2)
|
||||
self.assertFalse(result)
|
||||
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
||||
of_port, ip_address2)
|
||||
self.assertFalse(result)
|
||||
|
||||
network.close()
|
||||
# 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.
|
||||
|
||||
import time
|
||||
|
||||
from dragonflow.controller.common import constants as const
|
||||
from dragonflow.tests.common import constants as test_const
|
||||
from dragonflow.tests.common import utils
|
||||
from dragonflow.tests.fullstack import test_base
|
||||
from dragonflow.tests.fullstack import test_objects as objects
|
||||
from ryu.lib.packet import arp
|
||||
|
||||
|
||||
class TestOVSFlowsForActivePortDectionApp(test_base.DFTestBase):
|
||||
|
||||
def _get_sending_arp_to_controller_flows(self, ofport, arp_op):
|
||||
ovs_flows_parser = utils.OvsFlowsParser()
|
||||
flows = ovs_flows_parser.dump(self.integration_bridge)
|
||||
expected_in_port = "in_port=" + ofport
|
||||
expected_arp_op = "arp_op=" + arp_op
|
||||
expected_actions = "CONTROLLER:65535," + "goto_table:" + \
|
||||
str(const.L2_LOOKUP_TABLE)
|
||||
flows = [flow for flow in flows
|
||||
if ((expected_in_port in flow['match']) and
|
||||
(expected_arp_op in flow['match']) and
|
||||
('arp' in flow['match']) and
|
||||
(flow['table'] == str(const.ARP_TABLE)) and
|
||||
(flow['actions'] == expected_actions))]
|
||||
return flows
|
||||
|
||||
def _get_sending_arp_reply_to_controller_flows(self, ofport):
|
||||
return self._get_sending_arp_to_controller_flows(ofport,
|
||||
str(arp.ARP_REPLY))
|
||||
|
||||
def _get_sending_gratuitous_arp_to_controller_flows(self, ofport):
|
||||
return self._get_sending_arp_to_controller_flows(ofport,
|
||||
str(arp.ARP_REQUEST))
|
||||
|
||||
def _check_sending_arp_reply_to_controller_flows(self, ofport, ip=None):
|
||||
flows = self._get_sending_arp_reply_to_controller_flows(ofport)
|
||||
expected_arp_tpa = '0.0.0.0'
|
||||
for flow in flows:
|
||||
if ip is not None:
|
||||
if expected_arp_tpa not in flow['match']:
|
||||
continue
|
||||
expected_arp_spa = 'arp_spa=' + ip
|
||||
if expected_arp_spa not in flow['match']:
|
||||
continue
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _check_sending_gratuitous_arp_to_controller_flows(self, ofport,
|
||||
ip=None):
|
||||
flows = self._get_sending_gratuitous_arp_to_controller_flows(ofport)
|
||||
for flow in flows:
|
||||
if ip is not None:
|
||||
expected_arp_spa = 'arp_spa=' + ip
|
||||
expected_arp_tpa = 'arp_tpa=' + ip
|
||||
if (expected_arp_spa not in flow['match']) or \
|
||||
(expected_arp_tpa not in flow['match']):
|
||||
continue
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def test_sending_arp_to_controller_flows(self):
|
||||
"""
|
||||
Add a VM with allowed address pairs configuration. Verify related
|
||||
flows is there.
|
||||
"""
|
||||
network = self.store(objects.NetworkTestObj(self.neutron, self.nb_api))
|
||||
network_id = network.create(network={'name': 'aap_test'})
|
||||
subnet_obj = self.store(objects.SubnetTestObj(
|
||||
self.neutron,
|
||||
self.nb_api,
|
||||
network_id,
|
||||
))
|
||||
|
||||
subnet = {'network_id': network_id,
|
||||
'cidr': '192.168.97.0/24',
|
||||
'gateway_ip': '192.168.97.1',
|
||||
'ip_version': 4,
|
||||
'name': 'aap_test',
|
||||
'enable_dhcp': True}
|
||||
subnet_obj.create(subnet)
|
||||
|
||||
vm = self.store(objects.VMTestObj(self, self.neutron))
|
||||
vm_id = vm.create(network=network)
|
||||
|
||||
ovsdb = utils.OvsDBParser()
|
||||
vm_port_id = ovsdb.get_port_id_by_vm_id(vm_id)
|
||||
self.assertIsNotNone(vm_port_id)
|
||||
|
||||
vm_port = objects.PortTestObj(self.neutron, self.nb_api, network_id,
|
||||
vm_port_id)
|
||||
|
||||
of_port = ovsdb.get_ofport(vm_port_id)
|
||||
self.assertIsNotNone(of_port)
|
||||
result = self._check_sending_arp_reply_to_controller_flows(of_port)
|
||||
self.assertFalse(result)
|
||||
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
||||
of_port)
|
||||
self.assertFalse(result)
|
||||
|
||||
ip_address1 = '192.168.97.100'
|
||||
mac_address1 = '1A:22:33:44:55:66'
|
||||
allowed_address_pairs1 = [{'ip_address': ip_address1,
|
||||
'mac_address': mac_address1}]
|
||||
vm_port.update({'allowed_address_pairs': allowed_address_pairs1})
|
||||
|
||||
time.sleep(test_const.DEFAULT_CMD_TIMEOUT)
|
||||
|
||||
result = self._check_sending_arp_reply_to_controller_flows(
|
||||
of_port, ip_address1)
|
||||
self.assertTrue(result)
|
||||
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
||||
of_port, ip_address1)
|
||||
self.assertTrue(result)
|
||||
|
||||
ip_address2 = '192.168.97.101'
|
||||
allowed_address_pairs2 = [{'ip_address': ip_address2}]
|
||||
vm_port.update({'allowed_address_pairs': allowed_address_pairs2})
|
||||
|
||||
time.sleep(test_const.DEFAULT_CMD_TIMEOUT)
|
||||
|
||||
result = self._check_sending_arp_reply_to_controller_flows(
|
||||
of_port, ip_address1)
|
||||
self.assertFalse(result)
|
||||
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
||||
of_port, ip_address1)
|
||||
self.assertFalse(result)
|
||||
result = self._check_sending_arp_reply_to_controller_flows(
|
||||
of_port, ip_address2)
|
||||
self.assertTrue(result)
|
||||
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
||||
of_port, ip_address2)
|
||||
self.assertTrue(result)
|
||||
|
||||
vm.close()
|
||||
|
||||
time.sleep(test_const.DEFAULT_CMD_TIMEOUT)
|
||||
|
||||
result = self._check_sending_arp_reply_to_controller_flows(
|
||||
of_port, ip_address2)
|
||||
self.assertFalse(result)
|
||||
result = self._check_sending_gratuitous_arp_to_controller_flows(
|
||||
of_port, ip_address2)
|
||||
self.assertFalse(result)
|
||||
|
||||
network.close()
|
||||
|
|
Loading…
Reference in New Issue