217 lines
6.2 KiB
Python
217 lines
6.2 KiB
Python
# 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 ovs.db import idl
|
|
from ovsdbapp.backend.ovs_idl import connection
|
|
from ovsdbapp.backend.ovs_idl import idlutils
|
|
from ovsdbapp.schema.open_vswitch import impl_idl
|
|
|
|
from dragonflow.common import constants
|
|
from dragonflow.db.models import switch
|
|
from dragonflow.ovsdb import commands
|
|
|
|
ovsdb_monitor_table_filter_default = {
|
|
'Interface': [
|
|
'ofport',
|
|
'name',
|
|
'admin_state',
|
|
'type',
|
|
'external_ids',
|
|
'options',
|
|
'mac_in_use',
|
|
'ingress_policing_burst',
|
|
'ingress_policing_rate',
|
|
],
|
|
'Bridge': [
|
|
'ports',
|
|
'name',
|
|
'controller',
|
|
'fail_mode',
|
|
'datapath_type',
|
|
],
|
|
'Port': [
|
|
'name',
|
|
'external_ids',
|
|
'interfaces',
|
|
'qos',
|
|
],
|
|
'QoS': [
|
|
'queues',
|
|
'external_ids',
|
|
'type',
|
|
],
|
|
'Queue': [
|
|
'dscp',
|
|
'external_ids',
|
|
'other_config',
|
|
],
|
|
'Controller': [
|
|
'target',
|
|
],
|
|
'Open_vSwitch': [
|
|
'bridges',
|
|
'cur_cfg',
|
|
'next_cfg'
|
|
]
|
|
}
|
|
|
|
_HANDLED_INTERFACE_TYPES = (
|
|
constants.SWITCH_COMPUTE_INTERFACE,
|
|
constants.SWITCH_TUNNEL_INTERFACE,
|
|
constants.SWITCH_BRIDGE_INTERFACE,
|
|
)
|
|
|
|
|
|
def _is_ovsport_update_valid(action, switch_port):
|
|
if switch_port.name == cfg.CONF.df_metadata.metadata_interface:
|
|
return True
|
|
|
|
if switch_port.type not in _HANDLED_INTERFACE_TYPES:
|
|
return False
|
|
|
|
if switch_port.name.startswith('qg'):
|
|
return False
|
|
|
|
if (switch_port.type == constants.SWITCH_COMPUTE_INTERFACE and
|
|
switch_port.lport is None):
|
|
return False
|
|
|
|
if action == 'set':
|
|
# No need for 'updated' event if the port_num is being deleted
|
|
port_num = switch_port.port_num
|
|
if (port_num is None) or (port_num < 0):
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def _get_interface_type(row):
|
|
interface_type = row.type
|
|
interface_name = row.name
|
|
|
|
if interface_type == "internal" and "br" in interface_name:
|
|
return constants.SWITCH_BRIDGE_INTERFACE
|
|
|
|
if interface_type == "patch":
|
|
return constants.SWITCH_PATCH_INTERFACE
|
|
|
|
if 'iface-id' in row.external_ids:
|
|
return constants.SWITCH_COMPUTE_INTERFACE
|
|
|
|
options = row.options
|
|
if 'remote_ip' in options:
|
|
return constants.SWITCH_TUNNEL_INTERFACE
|
|
|
|
return constants.SWITCH_UNKNOWN_INTERFACE
|
|
|
|
|
|
def _port_from_idl_row(row):
|
|
res = switch.SwitchPort(
|
|
id=str(row.uuid),
|
|
name=row.name,
|
|
type=_get_interface_type(row),
|
|
)
|
|
if row.ofport:
|
|
res.port_num = int(row.ofport[0])
|
|
|
|
if row.mac_in_use:
|
|
res.mac_in_use = row.mac_in_use[0]
|
|
|
|
if row.admin_state:
|
|
res.admin_state = row.admin_state[0]
|
|
|
|
if res.type == constants.SWITCH_PATCH_INTERFACE:
|
|
res.peer = row.options['peer']
|
|
|
|
if res.type == constants.SWITCH_TUNNEL_INTERFACE:
|
|
res.tunnel_type = row.type
|
|
|
|
external_ids = row.external_ids
|
|
lport_id = external_ids.get('iface-id')
|
|
if lport_id is not None:
|
|
res.lport = lport_id
|
|
|
|
attached_mac = external_ids.get('attached-mac')
|
|
if attached_mac is not None:
|
|
res.attached_mac = attached_mac
|
|
|
|
return res
|
|
|
|
|
|
class DFIdl(idl.Idl):
|
|
def __init__(self, remote, schema, db_change_callback):
|
|
super(DFIdl, self).__init__(remote, schema)
|
|
self.db_change_callback = db_change_callback
|
|
|
|
def notify(self, event, row, updates=None):
|
|
if not row or not hasattr(row, '_table'):
|
|
return
|
|
if row._table.name != 'Interface':
|
|
return
|
|
|
|
local_interface = _port_from_idl_row(row)
|
|
action = event if event != 'update' else 'set'
|
|
if _is_ovsport_update_valid(action, local_interface):
|
|
self.db_change_callback(
|
|
local_interface.table_name,
|
|
local_interface.id,
|
|
action,
|
|
local_interface.to_json(),
|
|
)
|
|
|
|
|
|
def df_idl_from_server(connection_string, schema_name,
|
|
db_change_callback):
|
|
"""Create the Idl instance by pulling the schema from OVSDB server"""
|
|
helper = idlutils.get_schema_helper(connection_string, schema_name)
|
|
tables = ovsdb_monitor_table_filter_default
|
|
for table_name, columns in tables.items():
|
|
if columns == 'all':
|
|
helper.register_table(table_name)
|
|
else:
|
|
helper.register_columns(table_name, columns)
|
|
return DFIdl(connection_string, helper, db_change_callback)
|
|
|
|
|
|
class DFOvsdbApi(impl_idl.OvsdbIdl):
|
|
"""The command generator of OVS DB operation
|
|
|
|
This is a sub-class of OvsdbIdl, which is defined in neutron. The super
|
|
class OvsdbIdl has defined lots of command. Dragonflow can use
|
|
them. And Dragonflow can extend its own commands in this class.
|
|
"""
|
|
def __init__(self, db_connection, timeout, db_change_callback):
|
|
idl = df_idl_from_server(db_connection, 'Open_vSwitch',
|
|
db_change_callback)
|
|
type(self).ovsdb_connection = None
|
|
ovsdb_connection = connection.Connection(idl, timeout)
|
|
super(DFOvsdbApi, self).__init__(ovsdb_connection)
|
|
|
|
def get_bridge_ports(self, bridge):
|
|
return commands.GetBridgePorts(self, bridge)
|
|
|
|
def add_patch_port(self, bridge, port, peer_port):
|
|
return commands.AddPatchPort(self, bridge, port, peer_port)
|
|
|
|
def add_virtual_tunnel_port(self, tunnel_type):
|
|
return commands.AddVirtualTunnelPort(self, tunnel_type)
|
|
|
|
def create_qos(self, port_id, qos):
|
|
return commands.CreateQos(self, port_id, qos)
|
|
|
|
def update_qos(self, port_id, qos):
|
|
return commands.UpdateQos(self, port_id, qos)
|
|
|
|
def delete_qos(self, port_id):
|
|
return commands.DeleteQos(self, port_id)
|