Neutron integration with OVN
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1394 lines
52 KiB

# 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_utils import uuidutils
from ovsdbapp.backend.ovs_idl import command
from ovsdbapp.backend.ovs_idl import idlutils
from ovsdbapp.backend.ovs_idl import rowview
from ovsdbapp import constants as ovsdb_const
from networking_ovn._i18n import _
from networking_ovn.common import constants as ovn_const
from networking_ovn.common import exceptions as ovn_exc
from networking_ovn.common import utils
RESOURCE_TYPE_MAP = {
ovn_const.TYPE_NETWORKS: 'Logical_Switch',
ovn_const.TYPE_PORTS: 'Logical_Switch_Port',
ovn_const.TYPE_ROUTERS: 'Logical_Router',
ovn_const.TYPE_ROUTER_PORTS: 'Logical_Router_Port',
ovn_const.TYPE_FLOATINGIPS: 'NAT',
ovn_const.TYPE_SUBNETS: 'DHCP_Options',
}
def _addvalue_to_list(row, column, new_value):
row.addvalue(column, new_value)
def _delvalue_from_list(row, column, old_value):
row.delvalue(column, old_value)
def _updatevalues_in_list(row, column, new_values=None, old_values=None):
new_values = new_values or []
old_values = old_values or []
for new_value in new_values:
row.addvalue(column, new_value)
for old_value in old_values:
row.delvalue(column, old_value)
def get_lsp_dhcp_options_uuids(lsp, lsp_name):
# Get dhcpv4_options and dhcpv6_options uuids from Logical_Switch_Port,
# which are references of port dhcp options in DHCP_Options table.
uuids = set()
for dhcp_opts in getattr(lsp, 'dhcpv4_options', []):
external_ids = getattr(dhcp_opts, 'external_ids', {})
if external_ids.get('port_id') == lsp_name:
uuids.add(dhcp_opts.uuid)
for dhcp_opts in getattr(lsp, 'dhcpv6_options', []):
external_ids = getattr(dhcp_opts, 'external_ids', {})
if external_ids.get('port_id') == lsp_name:
uuids.add(dhcp_opts.uuid)
return uuids
def _add_gateway_chassis(api, txn, lrp_name, val):
gateway_chassis = api._tables.get('Gateway_Chassis')
if gateway_chassis:
prio = len(val)
uuid_list = []
for chassis in val:
gwc_name = '%s_%s' % (lrp_name, chassis)
try:
gwc = idlutils.row_by_value(api.idl,
'Gateway_Chassis',
'name', gwc_name)
except idlutils.RowNotFound:
gwc = txn.insert(gateway_chassis)
gwc.name = gwc_name
gwc.chassis_name = chassis
gwc.priority = prio
prio = prio - 1
uuid_list.append(gwc.uuid)
return 'gateway_chassis', uuid_list
else:
chassis = {ovn_const.OVN_GATEWAY_CHASSIS_KEY: val[0]}
return 'options', chassis
class LSwitchSetExternalIdsCommand(command.BaseCommand):
def __init__(self, api, name, ext_ids, if_exists):
super(LSwitchSetExternalIdsCommand, self).__init__(api)
self.name = name
self.ext_ids = ext_ids
self.if_exists = if_exists
def run_idl(self, txn):
try:
lswitch = idlutils.row_by_value(self.api.idl, 'Logical_Switch',
'name', self.name)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Logical Switch %s does not exist") % self.name
raise RuntimeError(msg)
lswitch.verify('external_ids')
external_ids = getattr(lswitch, 'external_ids', {})
for key, value in self.ext_ids.items():
external_ids[key] = value
lswitch.external_ids = external_ids
class AddLSwitchPortCommand(command.BaseCommand):
def __init__(self, api, lport, lswitch, may_exist, **columns):
super(AddLSwitchPortCommand, self).__init__(api)
self.lport = lport
self.lswitch = lswitch
self.may_exist = may_exist
self.columns = columns
def run_idl(self, txn):
try:
lswitch = idlutils.row_by_value(self.api.idl, 'Logical_Switch',
'name', self.lswitch)
except idlutils.RowNotFound:
msg = _("Logical Switch %s does not exist") % self.lswitch
raise RuntimeError(msg)
if self.may_exist:
port = idlutils.row_by_value(self.api.idl,
'Logical_Switch_Port', 'name',
self.lport, None)
if port:
return
port = txn.insert(self.api._tables['Logical_Switch_Port'])
port.name = self.lport
dhcpv4_options = self.columns.pop('dhcpv4_options', [])
if isinstance(dhcpv4_options, list):
port.dhcpv4_options = dhcpv4_options
else:
port.dhcpv4_options = [dhcpv4_options.result]
dhcpv6_options = self.columns.pop('dhcpv6_options', [])
if isinstance(dhcpv6_options, list):
port.dhcpv6_options = dhcpv6_options
else:
port.dhcpv6_options = [dhcpv6_options.result]
for col, val in self.columns.items():
setattr(port, col, val)
# add the newly created port to existing lswitch
_addvalue_to_list(lswitch, 'ports', port.uuid)
self.result = port.uuid
def post_commit(self, txn):
self.result = txn.get_insert_uuid(self.result)
class SetLSwitchPortCommand(command.BaseCommand):
def __init__(self, api, lport, if_exists, **columns):
super(SetLSwitchPortCommand, self).__init__(api)
self.lport = lport
self.columns = columns
self.if_exists = if_exists
def run_idl(self, txn):
try:
port = idlutils.row_by_value(self.api.idl, 'Logical_Switch_Port',
'name', self.lport)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Logical Switch Port %s does not exist") % self.lport
raise RuntimeError(msg)
# Delete DHCP_Options records no longer referred by this port.
# The table rows should be consistent for the same transaction.
# After we get DHCP_Options rows uuids from port dhcpv4_options
# and dhcpv6_options references, the rows shouldn't disappear for
# this transaction before we delete it.
cur_port_dhcp_opts = get_lsp_dhcp_options_uuids(
port, self.lport)
new_port_dhcp_opts = set()
dhcpv4_options = self.columns.pop('dhcpv4_options', None)
if dhcpv4_options is None:
new_port_dhcp_opts.update([option.uuid for option in
getattr(port, 'dhcpv4_options', [])])
elif isinstance(dhcpv4_options, list):
new_port_dhcp_opts.update(dhcpv4_options)
port.dhcpv4_options = dhcpv4_options
else:
new_port_dhcp_opts.add(dhcpv4_options.result)
port.dhcpv4_options = [dhcpv4_options.result]
dhcpv6_options = self.columns.pop('dhcpv6_options', None)
if dhcpv6_options is None:
new_port_dhcp_opts.update([option.uuid for option in
getattr(port, 'dhcpv6_options', [])])
elif isinstance(dhcpv6_options, list):
new_port_dhcp_opts.update(dhcpv6_options)
port.dhcpv6_options = dhcpv6_options
else:
new_port_dhcp_opts.add(dhcpv6_options.result)
port.dhcpv6_options = [dhcpv6_options.result]
for uuid in cur_port_dhcp_opts - new_port_dhcp_opts:
self.api._tables['DHCP_Options'].rows[uuid].delete()
for col, val in self.columns.items():
setattr(port, col, val)
class DelLSwitchPortCommand(command.BaseCommand):
def __init__(self, api, lport, lswitch, if_exists):
super(DelLSwitchPortCommand, self).__init__(api)
self.lport = lport
self.lswitch = lswitch
self.if_exists = if_exists
def run_idl(self, txn):
try:
lport = idlutils.row_by_value(self.api.idl, 'Logical_Switch_Port',
'name', self.lport)
lswitch = idlutils.row_by_value(self.api.idl, 'Logical_Switch',
'name', self.lswitch)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Port %s does not exist") % self.lport
raise RuntimeError(msg)
# Delete DHCP_Options records no longer referred by this port.
cur_port_dhcp_opts = get_lsp_dhcp_options_uuids(
lport, self.lport)
for uuid in cur_port_dhcp_opts:
self.api._tables['DHCP_Options'].rows[uuid].delete()
_delvalue_from_list(lswitch, 'ports', lport)
self.api._tables['Logical_Switch_Port'].rows[lport.uuid].delete()
class AddLRouterCommand(command.BaseCommand):
def __init__(self, api, name, may_exist, **columns):
super(AddLRouterCommand, self).__init__(api)
self.name = name
self.columns = columns
self.may_exist = may_exist
def run_idl(self, txn):
if self.may_exist:
lrouter = idlutils.row_by_value(self.api.idl, 'Logical_Router',
'name', self.name, None)
if lrouter:
return
row = txn.insert(self.api._tables['Logical_Router'])
row.name = self.name
for col, val in self.columns.items():
setattr(row, col, val)
class UpdateLRouterCommand(command.BaseCommand):
def __init__(self, api, name, if_exists, **columns):
super(UpdateLRouterCommand, self).__init__(api)
self.name = name
self.columns = columns
self.if_exists = if_exists
def run_idl(self, txn):
try:
lrouter = idlutils.row_by_value(self.api.idl, 'Logical_Router',
'name', self.name, None)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Logical Router %s does not exist") % self.name
raise RuntimeError(msg)
if lrouter:
for col, val in self.columns.items():
setattr(lrouter, col, val)
return
class DelLRouterCommand(command.BaseCommand):
def __init__(self, api, name, if_exists):
super(DelLRouterCommand, self).__init__(api)
self.name = name
self.if_exists = if_exists
def run_idl(self, txn):
try:
lrouter = idlutils.row_by_value(self.api.idl, 'Logical_Router',
'name', self.name)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Logical Router %s does not exist") % self.name
raise RuntimeError(msg)
self.api._tables['Logical_Router'].rows[lrouter.uuid].delete()
class AddLRouterPortCommand(command.BaseCommand):
def __init__(self, api, name, lrouter, may_exist, **columns):
super(AddLRouterPortCommand, self).__init__(api)
self.name = name
self.lrouter = lrouter
self.may_exist = may_exist
self.columns = columns
def run_idl(self, txn):
try:
lrouter = idlutils.row_by_value(self.api.idl, 'Logical_Router',
'name', self.lrouter)
except idlutils.RowNotFound:
msg = _("Logical Router %s does not exist") % self.lrouter
raise RuntimeError(msg)
try:
idlutils.row_by_value(self.api.idl, 'Logical_Router_Port',
'name', self.name)
if self.may_exist:
return
# The LRP entry with certain name has already exist, raise an
# exception to notice caller. It's caller's responsibility to
# call UpdateLRouterPortCommand to get LRP entry processed
# correctly.
msg = _("Logical Router Port with name \"%s\" "
"already exists.") % self.name
raise RuntimeError(msg)
except idlutils.RowNotFound:
lrouter_port = txn.insert(self.api._tables['Logical_Router_Port'])
lrouter_port.name = self.name
for col, val in self.columns.items():
if col == 'gateway_chassis':
col, val = _add_gateway_chassis(self.api, txn, self.name,
val)
setattr(lrouter_port, col, val)
_addvalue_to_list(lrouter, 'ports', lrouter_port)
class UpdateLRouterPortCommand(command.BaseCommand):
def __init__(self, api, name, if_exists, **columns):
super(UpdateLRouterPortCommand, self).__init__(api)
self.name = name
self.columns = columns
self.if_exists = if_exists
def run_idl(self, txn):
try:
lrouter_port = idlutils.row_by_value(self.api.idl,
'Logical_Router_Port',
'name', self.name)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Logical Router Port %s does not exist") % self.name
raise RuntimeError(msg)
# TODO(lucasagomes): Remove this check once we drop the support
# for OVS versions <= 2.8
ipv6_ra_configs_supported = self.api.is_col_present(
'Logical_Router_Port', 'ipv6_ra_configs')
for col, val in self.columns.items():
if col == 'ipv6_ra_configs' and not ipv6_ra_configs_supported:
continue
if col == 'gateway_chassis':
col, val = _add_gateway_chassis(self.api, txn, self.name,
val)
setattr(lrouter_port, col, val)
class DelLRouterPortCommand(command.BaseCommand):
def __init__(self, api, name, lrouter, if_exists):
super(DelLRouterPortCommand, self).__init__(api)
self.name = name
self.lrouter = lrouter
self.if_exists = if_exists
def run_idl(self, txn):
try:
lrouter_port = idlutils.row_by_value(self.api.idl,
'Logical_Router_Port',
'name', self.name)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Logical Router Port %s does not exist") % self.name
raise RuntimeError(msg)
try:
lrouter = idlutils.row_by_value(self.api.idl, 'Logical_Router',
'name', self.lrouter)
except idlutils.RowNotFound:
msg = _("Logical Router %s does not exist") % self.lrouter
raise RuntimeError(msg)
_delvalue_from_list(lrouter, 'ports', lrouter_port)
lrouter_port.delete()
class SetLRouterPortInLSwitchPortCommand(command.BaseCommand):
def __init__(self, api, lswitch_port, lrouter_port, is_gw_port,
if_exists, lsp_address):
super(SetLRouterPortInLSwitchPortCommand, self).__init__(api)
self.lswitch_port = lswitch_port
self.lrouter_port = lrouter_port
self.is_gw_port = is_gw_port
self.if_exists = if_exists
self.lsp_address = lsp_address
def run_idl(self, txn):
try:
port = idlutils.row_by_value(self.api.idl, 'Logical_Switch_Port',
'name', self.lswitch_port)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Logical Switch Port %s does not "
"exist") % self.lswitch_port
raise RuntimeError(msg)
options = {'router-port': self.lrouter_port}
if self.is_gw_port:
options[ovn_const.OVN_GATEWAY_NAT_ADDRESSES_KEY] = 'router'
setattr(port, 'options', options)
setattr(port, 'type', 'router')
setattr(port, 'addresses', self.lsp_address)
class AddACLCommand(command.BaseCommand):
def __init__(self, api, lswitch, lport, **columns):
super(AddACLCommand, self).__init__(api)
self.lswitch = lswitch
self.lport = lport
self.columns = columns
def run_idl(self, txn):
try:
lswitch = idlutils.row_by_value(self.api.idl, 'Logical_Switch',
'name', self.lswitch)
except idlutils.RowNotFound:
msg = _("Logical Switch %s does not exist") % self.lswitch
raise RuntimeError(msg)
row = txn.insert(self.api._tables['ACL'])
for col, val in self.columns.items():
setattr(row, col, val)
_addvalue_to_list(lswitch, 'acls', row.uuid)
class DelACLCommand(command.BaseCommand):
def __init__(self, api, lswitch, lport, if_exists):
super(DelACLCommand, self).__init__(api)
self.lswitch = lswitch
self.lport = lport
self.if_exists = if_exists
def run_idl(self, txn):
try:
lswitch = idlutils.row_by_value(self.api.idl, 'Logical_Switch',
'name', self.lswitch)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Logical Switch %s does not exist") % self.lswitch
raise RuntimeError(msg)
acls_to_del = []
acls = getattr(lswitch, 'acls', [])
for acl in acls:
ext_ids = getattr(acl, 'external_ids', {})
if ext_ids.get('neutron:lport') == self.lport:
acls_to_del.append(acl)
for acl in acls_to_del:
acl.delete()
_updatevalues_in_list(lswitch, 'acls', old_values=acls_to_del)
class UpdateACLsCommand(command.BaseCommand):
def __init__(self, api, lswitch_names, port_list, acl_new_values_dict,
need_compare=True, is_add_acl=True):
"""This command updates the acl list for the logical switches
@param lswitch_names: List of Logical Switch Names
@type lswitch_names: []
@param port_list: Iterator of List of Ports
@type port_list: []
@param acl_new_values_dict: Dictionary of acls indexed by port id
@type acl_new_values_dict: {}
@need_compare: If acl_new_values_dict needs be compared with existing
acls.
@type: Boolean.
@is_add_acl: If updating is caused by acl adding action.
@type: Boolean.
"""
super(UpdateACLsCommand, self).__init__(api)
self.lswitch_names = lswitch_names
self.port_list = port_list
self.acl_new_values_dict = acl_new_values_dict
self.need_compare = need_compare
self.is_add_acl = is_add_acl
def _acl_list_sub(self, acl_list1, acl_list2):
"""Compute the elements in acl_list1 but not in acl_list2.
If acl_list1 and acl_list2 were sets, the result of this routine
could be thought of as acl_list1 - acl_list2. Note that acl_list1
and acl_list2 cannot actually be sets as they contain dictionary
items i.e. set([{'a':1}) doesn't work.
"""
acl_diff = []
for acl in acl_list1:
if acl not in acl_list2:
acl_diff.append(acl)
return acl_diff
def _compute_acl_differences(self, port_list, acl_old_values_dict,
acl_new_values_dict, acl_obj_dict):
"""Compute the difference between the new and old sets of acls
@param port_list: Iterator of a List of ports
@type port_list: []
@param acl_old_values_dict: Dictionary of old acl values indexed
by port id
@param acl_new_values_dict: Dictionary of new acl values indexed
by port id
@param acl_obj_dict: Dictionary of acl objects indexed by the acl
value in string format.
@var acl_del_objs_dict: Dictionary of acl objects to be deleted
indexed by the lswitch.
@var acl_add_values_dict: Dictionary of acl values to be added
indexed by the lswitch.
@return: (acl_del_objs_dict, acl_add_values_dict)
@rtype: ({}, {})
"""
acl_del_objs_dict = {}
acl_add_values_dict = {}
for port in port_list:
lswitch_name = port['network_id']
acls_old = acl_old_values_dict.get(port['id'], [])
acls_new = acl_new_values_dict.get(port['id'], [])
acls_del = self._acl_list_sub(acls_old, acls_new)
acls_add = self._acl_list_sub(acls_new, acls_old)
acl_del_objs = acl_del_objs_dict.setdefault(lswitch_name, [])
for acl in acls_del:
acl_del_objs.append(acl_obj_dict[str(acl)])
acl_add_values = acl_add_values_dict.setdefault(lswitch_name, [])
for acl in acls_add:
# Remove lport and lswitch columns
del acl['lswitch']
del acl['lport']
acl_add_values.append(acl)
return acl_del_objs_dict, acl_add_values_dict
def _get_update_data_without_compare(self):
lswitch_ovsdb_dict = {}
for switch_name in self.lswitch_names:
switch_name = utils.ovn_name(switch_name)
lswitch = idlutils.row_by_value(self.api.idl, 'Logical_Switch',
'name', switch_name)
lswitch_ovsdb_dict[switch_name] = lswitch
if self.is_add_acl:
acl_add_values_dict = {}
for port in self.port_list:
switch_name = utils.ovn_name(port['network_id'])
if switch_name not in acl_add_values_dict:
acl_add_values_dict[switch_name] = []
if port['id'] in self.acl_new_values_dict:
acl_add_values_dict[switch_name].append(
self.acl_new_values_dict[port['id']])
acl_del_objs_dict = {}
else:
acl_add_values_dict = {}
acl_del_objs_dict = {}
del_acl_extids = []
for acl_dict in self.acl_new_values_dict.values():
del_acl_extids.append({acl_dict['match']:
acl_dict['external_ids']})
for switch_name, lswitch in lswitch_ovsdb_dict.items():
if switch_name not in acl_del_objs_dict:
acl_del_objs_dict[switch_name] = []
acls = getattr(lswitch, 'acls', [])
for acl in acls:
match = getattr(acl, 'match')
acl_extids = {match: getattr(acl, 'external_ids')}
if acl_extids in del_acl_extids:
acl_del_objs_dict[switch_name].append(acl)
return lswitch_ovsdb_dict, acl_del_objs_dict, acl_add_values_dict
def run_idl(self, txn):
if self.need_compare:
# Get all relevant ACLs in 1 shot
acl_values_dict, acl_obj_dict, lswitch_ovsdb_dict = \
self.api.get_acls_for_lswitches(self.lswitch_names)
# Compute the difference between the new and old set of ACLs
acl_del_objs_dict, acl_add_values_dict = \
self._compute_acl_differences(
self.port_list, acl_values_dict,
self.acl_new_values_dict, acl_obj_dict)
else:
lswitch_ovsdb_dict, acl_del_objs_dict, acl_add_values_dict = \
self._get_update_data_without_compare()
for lswitch_name, lswitch in lswitch_ovsdb_dict.items():
acl_del_objs = acl_del_objs_dict.get(lswitch_name, [])
acl_add_values = acl_add_values_dict.get(lswitch_name, [])
# Continue if no ACLs to add or delete.
if not acl_del_objs and not acl_add_values:
continue
# Delete old ACLs.
if acl_del_objs:
for acl_del_obj in acl_del_objs:
try:
acl_del_obj.delete()
except AssertionError:
# If we try to delete a row twice, just continue
pass
# Add new ACLs.
acl_add_objs = None
if acl_add_values:
acl_add_objs = []
for acl_value in acl_add_values:
row = txn.insert(self.api._tables['ACL'])
for col, val in acl_value.items():
setattr(row, col, val)
acl_add_objs.append(row.uuid)
# Update logical switch ACLs.
_updatevalues_in_list(lswitch, 'acls',
new_values=acl_add_objs,
old_values=acl_del_objs)
class AddStaticRouteCommand(command.BaseCommand):
def __init__(self, api, lrouter, **columns):
super(AddStaticRouteCommand, self).__init__(api)
self.lrouter = lrouter
self.columns = columns
def run_idl(self, txn):
try:
lrouter = idlutils.row_by_value(self.api.idl, 'Logical_Router',
'name', self.lrouter)
except idlutils.RowNotFound:
msg = _("Logical Router %s does not exist") % self.lrouter
raise RuntimeError(msg)
row = txn.insert(self.api._tables['Logical_Router_Static_Route'])
for col, val in self.columns.items():
setattr(row, col, val)
_addvalue_to_list(lrouter, 'static_routes', row.uuid)
class DelStaticRouteCommand(command.BaseCommand):
def __init__(self, api, lrouter, ip_prefix, nexthop, if_exists):
super(DelStaticRouteCommand, self).__init__(api)
self.lrouter = lrouter
self.ip_prefix = ip_prefix
self.nexthop = nexthop
self.if_exists = if_exists
def run_idl(self, txn):
try:
lrouter = idlutils.row_by_value(self.api.idl, 'Logical_Router',
'name', self.lrouter)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Logical Router %s does not exist") % self.lrouter
raise RuntimeError(msg)
static_routes = getattr(lrouter, 'static_routes', [])
for route in static_routes:
ip_prefix = getattr(route, 'ip_prefix', '')
nexthop = getattr(route, 'nexthop', '')
if self.ip_prefix == ip_prefix and self.nexthop == nexthop:
_delvalue_from_list(lrouter, 'static_routes', route)
route.delete()
break
class AddAddrSetCommand(command.BaseCommand):
def __init__(self, api, name, may_exist, **columns):
super(AddAddrSetCommand, self).__init__(api)
self.name = name
self.columns = columns
self.may_exist = may_exist
def run_idl(self, txn):
if self.may_exist:
addrset = idlutils.row_by_value(self.api.idl, 'Address_Set',
'name', self.name, None)
if addrset:
return
row = txn.insert(self.api._tables['Address_Set'])
row.name = self.name
for col, val in self.columns.items():
setattr(row, col, val)
class DelAddrSetCommand(command.BaseCommand):
def __init__(self, api, name, if_exists):
super(DelAddrSetCommand, self).__init__(api)
self.name = name
self.if_exists = if_exists
def run_idl(self, txn):
try:
addrset = idlutils.row_by_value(self.api.idl, 'Address_Set',
'name', self.name)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Address set %s does not exist. "
"Can't delete.") % self.name
raise RuntimeError(msg)
self.api._tables['Address_Set'].rows[addrset.uuid].delete()
class UpdateAddrSetCommand(command.BaseCommand):
def __init__(self, api, name, addrs_add, addrs_remove, if_exists):
super(UpdateAddrSetCommand, self).__init__(api)
self.name = name
self.addrs_add = addrs_add
self.addrs_remove = addrs_remove
self.if_exists = if_exists
def run_idl(self, txn):
try:
addrset = idlutils.row_by_value(self.api.idl, 'Address_Set',
'name', self.name)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Address set %s does not exist. "
"Can't update addresses") % self.name
raise RuntimeError(msg)
_updatevalues_in_list(
addrset, 'addresses',
new_values=self.addrs_add,
old_values=self.addrs_remove)
class UpdateAddrSetExtIdsCommand(command.BaseCommand):
def __init__(self, api, name, external_ids, if_exists):
super(UpdateAddrSetExtIdsCommand, self).__init__(api)
self.name = name
self.external_ids = external_ids
self.if_exists = if_exists
def run_idl(self, txn):
try:
addrset = idlutils.row_by_value(self.api.idl, 'Address_Set',
'name', self.name)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Address set %s does not exist. "
"Can't update external IDs") % self.name
raise RuntimeError(msg)
addrset.verify('external_ids')
addrset_external_ids = getattr(addrset, 'external_ids', {})
for ext_id_key, ext_id_value in self.external_ids.items():
addrset_external_ids[ext_id_key] = ext_id_value
addrset.external_ids = addrset_external_ids
class UpdateChassisExtIdsCommand(command.BaseCommand):
def __init__(self, api, name, external_ids, if_exists):
super(UpdateChassisExtIdsCommand, self).__init__(api)
self.name = name
self.external_ids = external_ids
self.if_exists = if_exists
def run_idl(self, txn):
try:
chassis = idlutils.row_by_value(self.api.idl, 'Chassis',
'name', self.name)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Chassis %s does not exist. "
"Can't update external IDs") % self.name
raise RuntimeError(msg)
chassis.verify('external_ids')
chassis_external_ids = getattr(chassis, 'external_ids', {})
for ext_id_key, ext_id_value in self.external_ids.items():
chassis_external_ids[ext_id_key] = ext_id_value
chassis.external_ids = chassis_external_ids
class UpdatePortBindingExtIdsCommand(command.BaseCommand):
def __init__(self, api, name, external_ids, if_exists):
super(UpdatePortBindingExtIdsCommand, self).__init__(api)
self.name = name
self.external_ids = external_ids
self.if_exists = if_exists
def run_idl(self, txn):
try:
port = idlutils.row_by_value(self.api.idl, 'Port_Binding',
'logical_port', self.name)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Port %s does not exist. "
"Can't update external IDs") % self.name
raise RuntimeError(msg)
port.verify('external_ids')
port_external_ids = getattr(port, 'external_ids', {})
for ext_id_key, ext_id_value in self.external_ids.items():
port_external_ids[ext_id_key] = ext_id_value
port.external_ids = port_external_ids
class AddDHCPOptionsCommand(command.BaseCommand):
def __init__(self, api, subnet_id, port_id=None, may_exist=True,
**columns):
super(AddDHCPOptionsCommand, self).__init__(api)
self.columns = columns
self.may_exist = may_exist
self.subnet_id = subnet_id
self.port_id = port_id
self.new_insert = False
def _get_dhcp_options_row(self):
for row in self.api._tables['DHCP_Options'].rows.values():
external_ids = getattr(row, 'external_ids', {})
port_id = external_ids.get('port_id')
if self.subnet_id == external_ids.get('subnet_id'):
if self.port_id == port_id:
return row
def run_idl(self, txn):
row = None
if self.may_exist:
row = self._get_dhcp_options_row()
if not row:
row = txn.insert(self.api._tables['DHCP_Options'])
self.new_insert = True
for col, val in self.columns.items():
setattr(row, col, val)
self.result = row.uuid
def post_commit(self, txn):
# Update the result with inserted uuid for new inserted row, or the
# uuid get in run_idl should be real uuid already.
if self.new_insert:
self.result = txn.get_insert_uuid(self.result)
class DelDHCPOptionsCommand(command.BaseCommand):
def __init__(self, api, row_uuid, if_exists=True):
super(DelDHCPOptionsCommand, self).__init__(api)
self.if_exists = if_exists
self.row_uuid = row_uuid
def run_idl(self, txn):
if self.row_uuid not in self.api._tables['DHCP_Options'].rows:
if self.if_exists:
return
msg = _("DHCP Options row %s does not exist") % self.row_uuid
raise RuntimeError(msg)
self.api._tables['DHCP_Options'].rows[self.row_uuid].delete()
class AddNATRuleInLRouterCommand(command.BaseCommand):
# TODO(chandrav): Add unit tests, bug #1638715.
def __init__(self, api, lrouter, **columns):
super(AddNATRuleInLRouterCommand, self).__init__(api)
self.lrouter = lrouter
self.columns = columns
def run_idl(self, txn):
try:
lrouter = idlutils.row_by_value(self.api.idl, 'Logical_Router',
'name', self.lrouter)
except idlutils.RowNotFound:
msg = _("Logical Router %s does not exist") % self.lrouter
raise RuntimeError(msg)
row = txn.insert(self.api._tables['NAT'])
for col, val in self.columns.items():
setattr(row, col, val)
# TODO(chandrav): convert this to ovs transaction mutate
lrouter.verify('nat')
nat = getattr(lrouter, 'nat', [])
nat.append(row.uuid)
setattr(lrouter, 'nat', nat)
class DeleteNATRuleInLRouterCommand(command.BaseCommand):
# TODO(chandrav): Add unit tests, bug #1638715.
def __init__(self, api, lrouter, type, logical_ip, external_ip,
if_exists):
super(DeleteNATRuleInLRouterCommand, self).__init__(api)
self.lrouter = lrouter
self.type = type
self.logical_ip = logical_ip
self.external_ip = external_ip
self.if_exists = if_exists
def run_idl(self, txn):
try:
lrouter = idlutils.row_by_value(self.api.idl, 'Logical_Router',
'name', self.lrouter)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Logical Router %s does not exist") % self.lrouter
raise RuntimeError(msg)
lrouter.verify('nat')
# TODO(chandrav): convert this to ovs transaction mutate
nats = getattr(lrouter, 'nat', [])
for nat in nats:
type = getattr(nat, 'type', '')
external_ip = getattr(nat, 'external_ip', '')
logical_ip = getattr(nat, 'logical_ip', '')
if self.type == type and \
self.external_ip == external_ip and \
self.logical_ip == logical_ip:
nats.remove(nat)
nat.delete()
break
setattr(lrouter, 'nat', nats)
class SetNATRuleInLRouterCommand(command.BaseCommand):
def __init__(self, api, lrouter, nat_rule_uuid, **columns):
super(SetNATRuleInLRouterCommand, self).__init__(api)
self.lrouter = lrouter
self.nat_rule_uuid = nat_rule_uuid
self.columns = columns
def run_idl(self, txn):
try:
lrouter = idlutils.row_by_value(self.api.idl, 'Logical_Router',
'name', self.lrouter)
except idlutils.RowNotFound:
msg = _("Logical Router %s does not exist") % self.lrouter
raise RuntimeError(msg)
lrouter.verify('nat')
nat_rules = getattr(lrouter, 'nat', [])
for nat_rule in nat_rules:
if nat_rule.uuid == self.nat_rule_uuid:
for col, val in self.columns.items():
setattr(nat_rule, col, val)
break
class AddNatIpToLRPortPeerOptionsCommand(command.BaseCommand):
# TODO(chandrav): Add unit tests, bug #1638715.
def __init__(self, api, lport, nat_ip):
super(AddNatIpToLRPortPeerOptionsCommand, self).__init__(api)
self.lport = lport
self.nat_ip = nat_ip
def run_idl(self, txn):
try:
lport = idlutils.row_by_value(self.api.idl, 'Logical_Switch_Port',
'name', self.lport)
except idlutils.RowNotFound:
msg = _("Logical Switch Port %s does not exist") % self.lport
raise RuntimeError(msg)
lport.verify('addresses')
addresses = getattr(lport, 'addresses', [])
lport.verify('options')
options = getattr(lport, 'options', {})
nat_ip_list = options.get('nat-addresses', '').split()
if not nat_ip_list:
nat_ips = addresses[0].split()[0] + ' ' + self.nat_ip
elif self.nat_ip not in nat_ip_list:
nat_ip_list.append(self.nat_ip)
nat_ips = ' '.join(nat_ip_list)
else:
return
options['nat-addresses'] = nat_ips
lport.options = options
class DeleteNatIpFromLRPortPeerOptionsCommand(command.BaseCommand):
# TODO(chandrav): Add unit tests, bug #1638715.
def __init__(self, api, lport, nat_ip):
super(DeleteNatIpFromLRPortPeerOptionsCommand, self).__init__(api)
self.lport = lport
self.nat_ip = nat_ip
def run_idl(self, txn):
try:
lport = idlutils.row_by_value(self.api.idl, 'Logical_Switch_Port',
'name', self.lport)
except idlutils.RowNotFound:
msg = _("Logical Switch Port %s does not exist") % self.lport
raise RuntimeError(msg)
lport.verify('options')
options = getattr(lport, 'options', {})
nat_ip_list = options.get('nat-addresses', '').split()
if self.nat_ip not in nat_ip_list:
return
nat_ip_list.remove(self.nat_ip)
if len(nat_ip_list) is 1:
nat_ips = ''
else:
nat_ips = ' '.join(nat_ip_list)
if not nat_ips:
del options['nat-addresses']
else:
options['nat-addresses'] = nat_ips
lport.options = options
class CheckRevisionNumberCommand(command.BaseCommand):
def __init__(self, api, name, resource, resource_type, if_exists):
super(CheckRevisionNumberCommand, self).__init__(api)
self.name = name
self.resource = resource
self.resource_type = resource_type
self.if_exists = if_exists
def _get_floatingip(self):
# TODO(lucasagomes): We can't use self.api.lookup() because that
# method does not introspect map type columns. We could either:
# 1. Enhance it to look into maps or, 2. Add a new ``name`` column
# to the NAT table so that we can use lookup() just like we do
# for other resources
for nat in self.api._tables['NAT'].rows.values():
if nat.type != 'dnat_and_snat':
continue
ext_ids = getattr(nat, 'external_ids', {})
if ext_ids.get(ovn_const.OVN_FIP_EXT_ID_KEY) == self.name:
return nat
raise idlutils.RowNotFound(
table='NAT', col='external_ids', match=self.name)
def _get_subnet(self):
for dhcp in self.api._tables['DHCP_Options'].rows.values():
ext_ids = getattr(dhcp, 'external_ids', {})
# Ignore ports DHCP Options
if ext_ids.get('port_id'):
continue
if ext_ids.get('subnet_id') == self.name:
return dhcp
raise idlutils.RowNotFound(
table='DHCP_Options', col='external_ids', match=self.name)
def run_idl(self, txn):
try:
ovn_table = RESOURCE_TYPE_MAP[self.resource_type]
# TODO(lucasagomes): After OVS 2.8.2 is released all tables should
# have the external_ids column. We can remove this conditional
# here by then.
if not self.api.is_col_present(ovn_table, 'external_ids'):
return
ovn_resource = None
if self.resource_type == ovn_const.TYPE_FLOATINGIPS:
ovn_resource = self._get_floatingip()
elif self.resource_type == ovn_const.TYPE_SUBNETS:
ovn_resource = self._get_subnet()
else:
ovn_resource = self.api.lookup(ovn_table, self.name)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = (_('Failed to check the revision number for %s: Resource '
'does not exist') % self.name)
raise RuntimeError(msg)
external_ids = getattr(ovn_resource, 'external_ids', {})
ovn_revision = int(external_ids.get(
ovn_const.OVN_REV_NUM_EXT_ID_KEY, -1))
neutron_revision = utils.get_revision_number(self.resource,
self.resource_type)
if ovn_revision > neutron_revision:
raise ovn_exc.RevisionConflict(
resource_id=self.name, resource_type=self.resource_type)
ovn_resource.verify('external_ids')
ovn_resource.setkey('external_ids', ovn_const.OVN_REV_NUM_EXT_ID_KEY,
str(neutron_revision))
def post_commit(self, txn):
self.result = ovn_const.TXN_COMMITTED
class DeleteLRouterExtGwCommand(command.BaseCommand):
def __init__(self, api, lrouter, if_exists):
super(DeleteLRouterExtGwCommand, self).__init__(api)
self.lrouter = lrouter
self.if_exists = if_exists
def run_idl(self, txn):
# TODO(lucasagomes): Remove this check after OVS 2.8.2 is tagged
# (prior to that, the external_ids column didn't exist in this
# table).
if not self.api.is_col_present('Logical_Router_Static_Route',
'external_ids'):
return
try:
lrouter = idlutils.row_by_value(self.api.idl, 'Logical_Router',
'name', self.lrouter)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _("Logical Router %s does not exist") % self.lrouter
raise RuntimeError(msg)
lrouter.verify('static_routes')
static_routes = getattr(lrouter, 'static_routes', [])
for route in static_routes:
external_ids = getattr(route, 'external_ids', {})
if ovn_const.OVN_ROUTER_IS_EXT_GW in external_ids:
_delvalue_from_list(lrouter, 'static_routes', route)
route.delete()
break
lrouter.verify('nat')
nats = getattr(lrouter, 'nat', [])
for nat in nats:
if nat.type != 'snat':
continue
_delvalue_from_list(lrouter, 'nat', nat)
nat.delete()
lrouter_ext_ids = getattr(lrouter, 'external_ids', {})
gw_port_id = lrouter_ext_ids.get(ovn_const.OVN_GW_PORT_EXT_ID_KEY)
if not gw_port_id:
return
try:
lrouter_port = idlutils.row_by_value(
self.api.idl, 'Logical_Router_Port', 'name',
utils.ovn_lrouter_port_name(gw_port_id))
except idlutils.RowNotFound:
return
_delvalue_from_list(lrouter, 'ports', lrouter_port)
class PgAddCommand(command.AddCommand):
table_name = 'Port_Group'
def __init__(self, api, name, may_exist=False, **columns):
super(PgAddCommand, self).__init__(api)
self.name = name
self.may_exist = may_exist
self.columns = columns
def run_idl(self, txn):
if self.may_exist:
try:
pg = self.api.lookup(self.table_name, self.name)
self.result = rowview.RowView(pg)
return
except idlutils.RowNotFound:
pass
pg = txn.insert(self.api._tables[self.table_name])
pg.name = self.name or ""
self.set_columns(pg, **self.columns)
self.result = pg.uuid
class PgDelCommand(command.BaseCommand):
table_name = 'Port_Group'
def __init__(self, api, name, if_exists=False):
super(PgDelCommand, self).__init__(api)
self.name = name
self.if_exists = if_exists
def run_idl(self, txn):
try:
pg = self.api.lookup(self.table_name, self.name)
pg.delete()
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _('Port group %s does not exist') % self.name
raise RuntimeError(msg)
class PgGetCommand(command.BaseGetRowCommand):
table = 'Port_Group'
class _PgUpdatePortsHelper(command.BaseCommand):
method = None
def __init__(self, api, port_group, lsp=None, if_exists=False):
super(_PgUpdatePortsHelper, self).__init__(api)
self.port_group = port_group
self.lsp = [] if lsp is None else self._listify(lsp)
self.if_exists = if_exists
def _listify(self, res):
return res if isinstance(res, (list, tuple)) else [res]
def _run_method(self, pg, port):
if not port:
return
if isinstance(port, command.BaseCommand):
port = port.result
elif uuidutils.is_uuid_like(port):
try:
port = self.api.lookup('Logical_Switch_Port', port)
except idlutils.RowNotFound:
if self.if_exists:
return
msg = _('Port %s does not exist') % port
raise RuntimeError(msg)
getattr(pg, self.method)('ports', port)
def run_idl(self, txn):
try:
pg = self.api.lookup('Port_Group', self.port_group)
except idlutils.RowNotFound:
msg = _('Port group %s does not exist') % self.port_group
raise RuntimeError(msg)
for lsp in self.lsp:
self._run_method(pg, lsp)
class PgAddPortCommand(_PgUpdatePortsHelper):
method = 'addvalue'
class PgDelPortCommand(_PgUpdatePortsHelper):
method = 'delvalue'
class _AclAddHelper(command.AddCommand):
table_name = 'ACL'
def __init__(self, api, entity, direction, priority, match, action,
log=False, may_exist=False, severity=None, name=None,
**external_ids):
if direction not in ('from-lport', 'to-lport'):
msg = _("direction must be either from-lport or to-lport")
raise TypeError(msg)
if not 0 <= priority <= ovsdb_const.ACL_PRIORITY_MAX:
msg = (_("priority must be beween 0 and %s, inclusive") %
ovsdb_const.ACL_PRIORITY_MAX)
raise ValueError(msg)
if action not in ('allow', 'allow-related', 'drop', 'reject'):
msg = _("action must be allow/allow-related/drop/reject")
raise TypeError(msg)
super(_AclAddHelper, self).__init__(api)
self.entity = entity
self.direction = direction
self.priority = priority
self.match = match
self.action = action
self.log = log
self.may_exist = may_exist
self.severity = severity
self.name = name
self.external_ids = external_ids
def acl_match(self, row):
return (self.direction == row.direction and
self.priority == row.priority and
self.match == row.match)
def run_idl(self, txn):
entity = self.api.lookup(self.lookup_table, self.entity)
acls = [acl for acl in entity.acls if self.acl_match(acl)]
if acls:
if self.may_exist:
self.result = rowview.RowView(acls[0])
return
msg = (_("ACL (%(dir)s, %(prio)s, %(match)s) already exists") % {
'dir': self.direction,
'prio': self.priority,
'match': self.match})
raise RuntimeError(msg)
acl = txn.insert(self.api.tables[self.table_name])
acl.direction = self.direction
acl.priority = self.priority
acl.match = self.match
acl.action = self.action
acl.log = self.log
acl.severity = self.severity
acl.name = self.name
entity.addvalue('acls', acl)
for col, value in self.external_ids.items():
acl.setkey('external_ids', col, value)
self.result = acl.uuid
class AclAddCommand(_AclAddHelper):
lookup_table = 'Logical_Switch'
def __init__(self, api, switch, direction, priority, match, action,
log=False, may_exist=False, severity=None, name=None,
**external_ids):
# NOTE: we're overriding the constructor here to not break any
# existing callers before we introduced Port Groups.
super(AclAddCommand, self).__init__(api, switch, direction, priority,
match, action, log, may_exist,
severity, name, **external_ids)
class PgAclAddCommand(_AclAddHelper):
lookup_table = 'Port_Group'
class _AclDelHelper(command.BaseCommand):
def __init__(self, api, entity, direction=None,
priority=None, match=None):
if (priority is None) != (match is None):
msg = _("Must specify priority and match together")
raise TypeError(msg)
if priority is not None and not direction:
msg = _("Cannot specify priority/match without direction")
raise TypeError(msg)
super(_AclDelHelper, self).__init__(api)
self.entity = entity
self.conditions = []
if direction:
self.conditions.append(('direction', '=', direction))
# priority can be 0
if match: # and therefore priority due to the above check
self.conditions += [('priority', '=', priority),
('match', '=', match)]
def run_idl(self, txn):
entity = self.api.lookup(self.lookup_table, self.entity)
for acl in [a for a in entity.acls
if idlutils.row_match(a, self.conditions)]:
entity.delvalue('acls', acl)
acl.delete()
class AclDelCommand(_AclDelHelper):
lookup_table = 'Logical_Switch'
def __init__(self, api, switch, direction=None,
priority=None, match=None):
# NOTE: we're overriding the constructor here to not break any
# existing callers before we introduced Port Groups.
super(AclDelCommand, self).__init__(api, switch, direction, priority,
match)
class PgAclDelCommand(_AclDelHelper):
lookup_table = 'Port_Group'
class _AclListHelper(command.BaseCommand):
def __init__(self, api, entity):
super(_AclListHelper, self).__init__(api)
self.entity = entity
def run_idl(self, txn):
entity = self.api.lookup(self.lookup_table, self.entity)
self.result = [rowview.RowView(acl) for acl in entity.acls]
class AclListCommand(_AclListHelper):
lookup_table = 'Logical_Switch'
class PgAclListCommand(_AclListHelper):
lookup_table = 'Port_Group'