neutron/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/ofswitch.py

203 lines
8.0 KiB
Python

# Copyright (C) 2014,2015 VA Linux Systems Japan K.K.
# Copyright (C) 2014,2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
# 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 eventlet
import netaddr
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import timeutils
import ryu.app.ofctl.api as ofctl_api
import ryu.exception as ryu_exc
from neutron._i18n import _LE, _LW
LOG = logging.getLogger(__name__)
class OpenFlowSwitchMixin(object):
"""Mixin to provide common convenient routines for an openflow switch.
NOTE(yamamoto): super() points to ovs_lib.OVSBridge.
See ovs_bridge.py how this class is actually used.
"""
@staticmethod
def _cidr_to_ryu(ip):
n = netaddr.IPNetwork(ip)
if n.hostmask:
return (str(n.ip), str(n.netmask))
return str(n.ip)
def __init__(self, *args, **kwargs):
self._app = kwargs.pop('ryu_app')
super(OpenFlowSwitchMixin, self).__init__(*args, **kwargs)
def _get_dp_by_dpid(self, dpid_int):
"""Get Ryu datapath object for the switch."""
timeout_sec = cfg.CONF.OVS.of_connect_timeout
start_time = timeutils.now()
while True:
dp = ofctl_api.get_datapath(self._app, dpid_int)
if dp is not None:
break
# The switch has not established a connection to us.
# Wait for a little.
if timeutils.now() > start_time + timeout_sec:
m = _LE("Switch connection timeout")
LOG.error(m)
# NOTE(yamamoto): use RuntimeError for compat with ovs_lib
raise RuntimeError(m)
eventlet.sleep(1)
return dp
def _send_msg(self, msg, reply_cls=None, reply_multi=False):
timeout_sec = cfg.CONF.OVS.of_request_timeout
timeout = eventlet.timeout.Timeout(seconds=timeout_sec)
try:
result = ofctl_api.send_msg(self._app, msg, reply_cls, reply_multi)
except ryu_exc.RyuException as e:
m = _LE("ofctl request %(request)s error %(error)s") % {
"request": msg,
"error": e,
}
LOG.error(m)
# NOTE(yamamoto): use RuntimeError for compat with ovs_lib
raise RuntimeError(m)
except eventlet.timeout.Timeout as e:
with excutils.save_and_reraise_exception() as ctx:
if e is timeout:
ctx.reraise = False
m = _LE("ofctl request %(request)s timed out") % {
"request": msg,
}
LOG.error(m)
# NOTE(yamamoto): use RuntimeError for compat with ovs_lib
raise RuntimeError(m)
finally:
timeout.cancel()
LOG.debug("ofctl request %(request)s result %(result)s",
{"request": msg, "result": result})
return result
@staticmethod
def _match(_ofp, ofpp, match, **match_kwargs):
if match is not None:
return match
return ofpp.OFPMatch(**match_kwargs)
def delete_flows(self, table_id=None, strict=False, priority=0,
cookie=0, cookie_mask=0,
match=None, **match_kwargs):
(dp, ofp, ofpp) = self._get_dp()
if table_id is None:
table_id = ofp.OFPTT_ALL
match = self._match(ofp, ofpp, match, **match_kwargs)
if strict:
cmd = ofp.OFPFC_DELETE_STRICT
else:
cmd = ofp.OFPFC_DELETE
msg = ofpp.OFPFlowMod(dp,
command=cmd,
cookie=cookie,
cookie_mask=cookie_mask,
table_id=table_id,
match=match,
priority=priority,
out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY)
self._send_msg(msg)
def dump_flows(self, table_id=None):
(dp, ofp, ofpp) = self._get_dp()
if table_id is None:
table_id = ofp.OFPTT_ALL
msg = ofpp.OFPFlowStatsRequest(dp, table_id=table_id)
replies = self._send_msg(msg,
reply_cls=ofpp.OFPFlowStatsReply,
reply_multi=True)
flows = []
for rep in replies:
flows += rep.body
return flows
def cleanup_flows(self):
cookies = set([f.cookie for f in self.dump_flows()])
for c in cookies:
if c == self.agent_uuid_stamp:
continue
LOG.warn(_LW("Deleting flow with cookie 0x%(cookie)x") % {
'cookie': c})
self.delete_flows(cookie=c, cookie_mask=((1 << 64) - 1))
def install_goto_next(self, table_id):
self.install_goto(table_id=table_id, dest_table_id=table_id + 1)
def install_output(self, port, table_id=0, priority=0,
match=None, **match_kwargs):
(_dp, ofp, ofpp) = self._get_dp()
actions = [ofpp.OFPActionOutput(port, 0)]
instructions = [ofpp.OFPInstructionActions(
ofp.OFPIT_APPLY_ACTIONS, actions)]
self.install_instructions(table_id=table_id, priority=priority,
instructions=instructions,
match=match, **match_kwargs)
def install_normal(self, table_id=0, priority=0,
match=None, **match_kwargs):
(_dp, ofp, _ofpp) = self._get_dp()
self.install_output(port=ofp.OFPP_NORMAL,
table_id=table_id, priority=priority,
match=match, **match_kwargs)
def install_goto(self, dest_table_id, table_id=0, priority=0,
match=None, **match_kwargs):
(_dp, _ofp, ofpp) = self._get_dp()
instructions = [ofpp.OFPInstructionGotoTable(table_id=dest_table_id)]
self.install_instructions(table_id=table_id, priority=priority,
instructions=instructions,
match=match, **match_kwargs)
def install_drop(self, table_id=0, priority=0, match=None, **match_kwargs):
self.install_instructions(table_id=table_id, priority=priority,
instructions=[], match=match, **match_kwargs)
def install_instructions(self, instructions,
table_id=0, priority=0,
match=None, **match_kwargs):
(dp, ofp, ofpp) = self._get_dp()
match = self._match(ofp, ofpp, match, **match_kwargs)
msg = ofpp.OFPFlowMod(dp,
table_id=table_id,
cookie=self.agent_uuid_stamp,
match=match,
priority=priority,
instructions=instructions)
self._send_msg(msg)
def install_apply_actions(self, actions,
table_id=0, priority=0,
match=None, **match_kwargs):
(dp, ofp, ofpp) = self._get_dp()
instructions = [
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
]
self.install_instructions(table_id=table_id,
priority=priority,
match=match,
instructions=instructions,
**match_kwargs)