[OVN] Import ovsdb related code
This patch imports ovsdb related code from networking_ovn. Previous paths in networking-ovn tree: ./networking_ovn/ovsdb/commands.py -> ./neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/commands.py ./networking_ovn/ovsdb/worker.py -> ./neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/worker.py ./networking_ovn/ovsdb/ovn_api.py -> ./neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/api.py ACL commands will be implemented when the related code is merged and could be tested properly. Related-Blueprint: neutron-ovn-merge Co-Authored-By: Reedip <rbanerje@redhat.com> Co-Authored-By: Numan Siddique <nusiddiq@redhat.com> Co-Authored-By: Flavio Fernandes <flaviof@redhat.com> Co-Authored-By: Terry Wilson <twilson@redhat.com> Co-Authored-By: Daniel Alvarez <dalvarez@redhat.com> Co-Authored-By: Changxun Zhou <zhoucx@dtdream.com> Co-Authored-By: Gal Sagie <gal.sagie@huawei.com> Co-Authored-By: Amitabha Biswas <abiswas@us.ibm.com> Co-Authored-By: Richard Theis <rtheis@us.ibm.com> Co-Authored-By: lzklibj <lzklibj@cn.ibm.com> Co-Authored-By: zhufl <zhu.fanglei@zte.com.cn> Co-Authored-By: Na <nazhu@cn.ibm.com> Co-Authored-By: Chandra S Vejendla <csvejend@us.ibm.com> Co-Authored-By: Gary Kotton <gkotton@vmware.com> Co-Authored-By: Aaron Rosen <aaronorosen@gmail.com> Co-Authored-By: Rodolfo Alonso Hernandez <ralonsoh@redhat.com> Change-Id: I9fe64f954d227efaab5e96c6150df44f36a2530a
This commit is contained in:
parent
65692127f6
commit
4b5cf9e5fb
|
@ -0,0 +1,702 @@
|
|||
# Copyright 2019 Red Hat, Inc.
|
||||
#
|
||||
# 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 abc
|
||||
|
||||
from ovsdbapp import api
|
||||
import six
|
||||
|
||||
from neutron.common.ovn import constants as ovn_const
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class API(api.API):
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_lswitch_ext_ids(self, name, ext_ids, if_exists=True):
|
||||
"""Create a command to set OVN lswitch external ids
|
||||
|
||||
:param name: The name of the lswitch
|
||||
:type name: string
|
||||
:param ext_ids The external ids to set for the lswitch
|
||||
:type ext_ids: dictionary
|
||||
:param if_exists: Do not fail if lswitch does not exist
|
||||
:type if_exists: bool
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_lswitch_port(self, lport_name, lswitch_name, may_exist=True,
|
||||
**columns):
|
||||
"""Create a command to add an OVN logical switch port
|
||||
|
||||
:param lport_name: The name of the lport
|
||||
:type lport_name: string
|
||||
:param lswitch_name: The name of the lswitch the lport is created on
|
||||
:type lswitch_name: string
|
||||
:param may_exist: Do not fail if lport already exists
|
||||
:type may_exist: bool
|
||||
:param columns: Dictionary of port columns
|
||||
Supported columns: macs, external_ids,
|
||||
parent_name, tag, enabled
|
||||
:type columns: dictionary
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_lswitch_port(self, lport_name, if_exists=True, **columns):
|
||||
"""Create a command to set OVN logical switch port fields
|
||||
|
||||
:param lport_name: The name of the lport
|
||||
:type lport_name: string
|
||||
:param columns: Dictionary of port columns
|
||||
Supported columns: macs, external_ids,
|
||||
parent_name, tag, enabled
|
||||
:param if_exists: Do not fail if lport does not exist
|
||||
:type if_exists: bool
|
||||
:type columns: dictionary
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_lswitch_port(self, lport_name=None, lswitch_name=None,
|
||||
ext_id=None, if_exists=True):
|
||||
"""Create a command to delete an OVN logical switch port
|
||||
|
||||
:param lport_name: The name of the lport
|
||||
:type lport_name: string
|
||||
:param lswitch_name: The name of the lswitch
|
||||
:type lswitch_name: string
|
||||
:param ext_id: The external id of the lport
|
||||
:type ext_id: pair of <ext_id_key ,ext_id_value>
|
||||
:param if_exists: Do not fail if the lport does not exist
|
||||
:type if_exists: bool
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_lrouter(self, name, may_exist=True, **columns):
|
||||
"""Create a command to add an OVN lrouter
|
||||
|
||||
:param name: The id of the lrouter
|
||||
:type name: string
|
||||
:param may_exist: Do not fail if lrouter already exists
|
||||
:type may_exist: bool
|
||||
:param columns: Dictionary of lrouter columns
|
||||
Supported columns: external_ids, default_gw, ip
|
||||
:type columns: dictionary
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_lrouter(self, name, if_exists=True, **columns):
|
||||
"""Update a command to add an OVN lrouter
|
||||
|
||||
:param name: The id of the lrouter
|
||||
:type name: string
|
||||
:param if_exists: Do not fail if the lrouter does not exist
|
||||
:type if_exists: bool
|
||||
:param columns: Dictionary of lrouter columns
|
||||
Supported columns: external_ids, default_gw, ip
|
||||
:type columns: dictionary
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_lrouter(self, name, if_exists=True):
|
||||
"""Create a command to delete an OVN lrouter
|
||||
|
||||
:param name: The id of the lrouter
|
||||
:type name: string
|
||||
:param if_exists: Do not fail if the lrouter does not exist
|
||||
:type if_exists: bool
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_lrouter_port(self, name, lrouter, may_exist=True,
|
||||
**columns):
|
||||
"""Create a command to add an OVN lrouter port
|
||||
|
||||
:param name: The unique name of the lrouter port
|
||||
:type name: string
|
||||
:param lrouter: The unique name of the lrouter
|
||||
:type lrouter: string
|
||||
:param lswitch: The unique name of the lswitch
|
||||
:type lswitch: string
|
||||
:param may_exist: If true, do not fail if lrouter port set
|
||||
already exists.
|
||||
:type may_exist: bool
|
||||
:param columns: Dictionary of lrouter columns
|
||||
Supported columns: external_ids, mac, network
|
||||
:type columns: dictionary
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_lrouter_port(self, name, if_exists=True, **columns):
|
||||
"""Update a command to add an OVN lrouter port
|
||||
|
||||
:param name: The unique name of the lrouter port
|
||||
:type name: string
|
||||
:param if_exists: Do not fail if the lrouter port does not exist
|
||||
:type if_exists: bool
|
||||
:param columns: Dictionary of lrouter columns
|
||||
Supported columns: networks
|
||||
:type columns: dictionary
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_lrouter_port(self, name, lrouter, if_exists=True):
|
||||
"""Create a command to delete an OVN lrouter port
|
||||
|
||||
:param name: The unique name of the lport
|
||||
:type name: string
|
||||
:param lrouter: The unique name of the lrouter
|
||||
:type lrouter: string
|
||||
:param if_exists: Do not fail if the lrouter port does not exist
|
||||
:type if_exists: bool
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_lrouter_port_in_lswitch_port(
|
||||
self, lswitch_port, lrouter_port, is_gw_port=False, if_exists=True,
|
||||
lsp_address=ovn_const.DEFAULT_ADDR_FOR_LSP_WITH_PEER):
|
||||
"""Create a command to set lswitch_port as lrouter_port
|
||||
|
||||
:param lswitch_port: The name of logical switch port
|
||||
:type lswitch_port: string
|
||||
:param lrouter_port: The name of logical router port
|
||||
:type lrouter_port: string
|
||||
:param is_gw_port: True if logical router port is gw port
|
||||
:type is_gw_port: bool
|
||||
:param if_exists: Do not fail if the lswitch port does not exist
|
||||
:type if_exists: bool
|
||||
:param lsp_address: logical switch port's addresses to set
|
||||
:type lsp_address: string or list of strings
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_acl(self, lswitch, lport, **columns):
|
||||
"""Create an ACL for a logical port.
|
||||
|
||||
:param lswitch: The logical switch the port is attached to.
|
||||
:type lswitch: string
|
||||
:param lport: The logical port this ACL is associated with.
|
||||
:type lport: string
|
||||
:param columns: Dictionary of ACL columns
|
||||
Supported columns: see ACL table in OVN_Northbound
|
||||
:type columns: dictionary
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_acl(self, lswitch, lport, if_exists=True):
|
||||
"""Delete all ACLs for a logical port.
|
||||
|
||||
:param lswitch: The logical switch the port is attached to.
|
||||
:type lswitch: string
|
||||
:param lport: The logical port this ACL is associated with.
|
||||
:type lport: string
|
||||
:param if_exists: Do not fail if the ACL for this lport does not
|
||||
exist
|
||||
:type if_exists: bool
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_acls(self, lswitch_names, port_list, acl_new_values_dict,
|
||||
need_compare=True, is_add_acl=True):
|
||||
"""Update the list of acls on logical switches with new values.
|
||||
|
||||
:param lswitch_names: List of logical switch names
|
||||
:type lswitch_name: []
|
||||
: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: {}
|
||||
:param need_compare: If acl_new_values_dict need compare
|
||||
with existing acls
|
||||
:type need_compare: bool
|
||||
:is_add_acl: If updating is caused by adding acl
|
||||
:type is_add_acl: bool
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_acl_by_id(self, acl_id):
|
||||
"""Get an ACL by its ID.
|
||||
|
||||
:param acl_id: ID of the ACL to lookup
|
||||
:type acl_id: string
|
||||
:returns The ACL row or None:
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_static_route(self, lrouter, **columns):
|
||||
"""Add static route to logical router.
|
||||
|
||||
:param lrouter: The unique name of the lrouter
|
||||
:type lrouter: string
|
||||
:param columns: Dictionary of static columns
|
||||
Supported columns: prefix, nexthop, valid
|
||||
:type columns: dictionary
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_static_route(self, lrouter, ip_prefix, nexthop, if_exists=True):
|
||||
"""Delete static route from logical router.
|
||||
|
||||
:param lrouter: The unique name of the lrouter
|
||||
:type lrouter: string
|
||||
:param ip_prefix: The prefix of the static route
|
||||
:type ip_prefix: string
|
||||
:param nexthop: The nexthop of the static route
|
||||
:type nexthop: string
|
||||
:param if_exists: Do not fail if router does not exist
|
||||
:type if_exists: bool
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_address_set(self, name, may_exist=True, **columns):
|
||||
"""Create an address set
|
||||
|
||||
:param name: The name of the address set
|
||||
:type name: string
|
||||
:param may_exist: Do not fail if address set already exists
|
||||
:type may_exist: bool
|
||||
:param columns: Dictionary of address set columns
|
||||
Supported columns: external_ids, addresses
|
||||
:type columns: dictionary
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_address_set(self, name, if_exists=True):
|
||||
"""Delete an address set
|
||||
|
||||
:param name: The name of the address set
|
||||
:type name: string
|
||||
:param if_exists: Do not fail if the address set does not exist
|
||||
:type if_exists: bool
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_address_set(self, name, addrs_add, addrs_remove,
|
||||
if_exists=True):
|
||||
"""Updates addresses in an address set
|
||||
|
||||
:param name: The name of the address set
|
||||
:type name: string
|
||||
:param addrs_add: The addresses to be added
|
||||
:type addrs_add: []
|
||||
:param addrs_remove: The addresses to be removed
|
||||
:type addrs_remove: []
|
||||
:param if_exists: Do not fail if the address set does not exist
|
||||
:type if_exists: bool
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_address_set_ext_ids(self, name, external_ids, if_exists=True):
|
||||
"""Update external IDs for an address set
|
||||
|
||||
:param name: The name of the address set
|
||||
:type name: string
|
||||
:param external_ids: The external IDs for the address set
|
||||
:type external_ids: dict
|
||||
:param if_exists: Do not fail if the address set does not exist
|
||||
:type if_exists: bool
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_all_chassis_gateway_bindings(self,
|
||||
chassis_candidate_list=None):
|
||||
"""Return a dictionary of chassis name:list of gateways
|
||||
|
||||
:param chassis_candidate_list: List of possible chassis candidates
|
||||
:type chassis_candidate_list: []
|
||||
:returns: {} of chassis to routers mapping
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_gateway_chassis_binding(self, gateway_id):
|
||||
"""Return the list of chassis to which the gateway is bound to
|
||||
|
||||
As one gateway can be hosted by multiple chassis, this method is
|
||||
returning a list of those chassis ordered by priority. This means
|
||||
that the first element of the list is the chassis hosting the
|
||||
gateway with the highest priority (which will likely be where
|
||||
the router port is going to be active).
|
||||
|
||||
:param gateway_id: The gateway id
|
||||
:type gateway_id: string
|
||||
:returns: a list of strings with the chassis names
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_unhosted_gateways(self, port_physnet_dict, chassis_physnets,
|
||||
gw_chassis):
|
||||
"""Return a list of gateways not hosted on chassis
|
||||
|
||||
:param port_physnet_dict: Dictionary of gateway ports and their physnet
|
||||
:param chassis_physnets: Dictionary of chassis and physnets
|
||||
:param gw_chassis: List of gateway chassis provided by admin
|
||||
through ovn-cms-options
|
||||
:returns: List of gateways not hosted on a valid
|
||||
chassis
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_dhcp_options(self, subnet_id, port_id=None, may_exist=True,
|
||||
**columns):
|
||||
"""Adds the DHCP options specified in the @columns in DHCP_Options
|
||||
|
||||
If the DHCP options already exist in the DHCP_Options table for
|
||||
the @subnet_id (and @lsp_name), updates the row, else creates a new
|
||||
row.
|
||||
|
||||
:param subnet_id: The subnet id to which the DHCP options belong
|
||||
to
|
||||
:type subnet_id: string
|
||||
:param port_id: The port id to which the DHCP options belong to
|
||||
if specified
|
||||
:type port_id: string
|
||||
:param may_exist: If true, checks if the DHCP options for
|
||||
subnet_id exists or not. If it already exists,
|
||||
it updates the row with the columns specified.
|
||||
Else creates a new row.
|
||||
:type may_exist: bool
|
||||
:type columns: Dictionary of DHCP_Options columns
|
||||
Supported columns: see DHCP_Options table in
|
||||
OVN_Northbound
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_dhcp_options(self, row_uuid, if_exists=True):
|
||||
"""Deletes the row in DHCP_Options with the @row_uuid
|
||||
|
||||
:param row_uuid: The UUID of the row to be deleted.
|
||||
:type row_uuid: string
|
||||
:param if_exists: Do not fail if the DHCP_Options row does not
|
||||
exist
|
||||
:type if_exists: bool
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_subnet_dhcp_options(self, subnet_id, with_ports=False):
|
||||
"""Returns the Subnet DHCP options as a dictionary
|
||||
|
||||
:param subnet_id: The subnet id whose DHCP options are returned
|
||||
:type subnet_id: string
|
||||
:param with_ports: If True, also returns the ports DHCP options.
|
||||
:type with_ports: bool
|
||||
:returns: Returns a dictionary containing two keys:
|
||||
subnet and ports.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_subnets_dhcp_options(self, subnet_ids):
|
||||
"""Returns the Subnets DHCP options as list of dictionary
|
||||
|
||||
:param subnet_ids: The subnet ids whose DHCP options are returned
|
||||
:type subnet_ids: list of string
|
||||
:returns: Returns the columns of the DHCP_Options as list
|
||||
of dictionary. Empty list is returned if no
|
||||
DHCP_Options matched found.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_address_sets(self):
|
||||
"""Gets all address sets in the OVN_Northbound DB
|
||||
|
||||
:returns: dictionary indexed by name, DB columns as values
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_port_groups(self):
|
||||
"""Gets all port groups in the OVN_Northbound DB
|
||||
|
||||
:returns: dictionary indexed by name, DB columns as values
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_router_port_options(self, lsp_name):
|
||||
"""Get options set for lsp of type router
|
||||
|
||||
:returns: router port options
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_nat_rule_in_lrouter(self, lrouter, **columns):
|
||||
"""Add NAT rule in logical router
|
||||
|
||||
|
||||
:param lrouter: The unique name of the lrouter
|
||||
:type lrouter: string
|
||||
:param columns: Dictionary of nat columns
|
||||
Supported columns: type, logical_ip, external_ip
|
||||
:type columns: dictionary
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_nat_rule_in_lrouter(self, lrouter, type, logical_ip,
|
||||
external_ip, if_exists=True):
|
||||
"""Delete NAT rule in logical router
|
||||
|
||||
:param lrouter: The unique name of the lrouter
|
||||
:type lrouter: string
|
||||
:param type: Type of nat. Supported values are 'snat', 'dnat'
|
||||
and 'dnat_and_snat'
|
||||
:type type: string
|
||||
:param logical_ip: IP or network that needs to be natted
|
||||
:type logical_ip: string
|
||||
:param external_ip: External IP to be used for nat
|
||||
:type external_ip: string
|
||||
:param if_exists: Do not fail if the Logical_Router row does not
|
||||
exist
|
||||
:type if_exists: bool
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_lrouter_nat_rules(self, lrouter):
|
||||
"""Returns the nat rules of a router
|
||||
|
||||
:param lrouter: The unique name of the router
|
||||
:type lrouter: string
|
||||
:returns: A list of nat rules of the router, with each item
|
||||
as a dict with the keys - 'external_ip', 'logical_ip'
|
||||
'type' and 'uuid' of the row.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_nat_rule_in_lrouter(self, lrouter, nat_rule_uuid, **columns):
|
||||
"""Sets the NAT rule fields
|
||||
|
||||
:param lrouter: The unique name of the router to which this the
|
||||
NAT rule belongs to.
|
||||
:type lrouter: string
|
||||
:param nat_rule_uuid: The uuid of the NAT rule row to be updated.
|
||||
:type nat_rule_uuid: string
|
||||
:type columns: dictionary
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_lswitch(self, lswitch_name):
|
||||
"""Returns the logical switch
|
||||
|
||||
:param lswitch_name: The unique name of the logical switch
|
||||
:type lswitch_name: string
|
||||
:returns: Returns logical switch or None
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_ls_and_dns_record(self, lswitch_name):
|
||||
"""Returns the logical switch and 'dns' records
|
||||
|
||||
:param lswitch_name: The unique name of the logical switch
|
||||
:type lswitch_name: string
|
||||
:returns: Returns logical switch and dns records as a tuple
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_floatingip(self, fip_id):
|
||||
"""Get a Floating IP by its ID
|
||||
|
||||
:param fip_id: The floating IP id
|
||||
:type fip_id: string
|
||||
:returns: The NAT rule row or None
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_floatingip_by_ips(self, router_id, logical_ip, external_ip):
|
||||
"""Get a Floating IP based on it's logical and external IPs.
|
||||
|
||||
DEPRECATED. In the Rocky release of OpenStack this method can be
|
||||
removed and get_floatingip() should be used instead. This method
|
||||
is a backward compatibility layer for the Pike -> Queens release.
|
||||
|
||||
:param router_id: The ID of the router to which the FIP belongs to.
|
||||
:type lrouter: string
|
||||
:param logical_ip: The FIP's logical IP address
|
||||
:type logical_ip: string
|
||||
:param external_ip: The FIP's external IP address
|
||||
:type external_ip: string
|
||||
:returns: The NAT rule row or None
|
||||
"""
|
||||
|
||||
def check_revision_number(self, name, resource, resource_type,
|
||||
if_exists=True):
|
||||
"""Compare the revision number from Neutron and OVN.
|
||||
|
||||
Check if the revision number in OVN is lower than the one from
|
||||
the Neutron resource, otherwise raise RevisionConflict and abort
|
||||
the transaction.
|
||||
|
||||
:param name: The unique name of the resource
|
||||
:type name: string
|
||||
:param resource: The neutron resource object
|
||||
:type resource: dictionary
|
||||
:param resource_type: The resource object type
|
||||
:type resource_type: dictionary
|
||||
:param if_exists: Do not fail if resource does not exist
|
||||
:type if_exists: bool
|
||||
:returns: :class:`Command` with no result
|
||||
:raise: RevisionConflict if the revision number in
|
||||
OVN is equal or higher than the neutron object
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_lswitch_port(self, lsp_name):
|
||||
"""Get a Logical Switch Port by its name.
|
||||
|
||||
:param lsp_name: The Logical Switch Port name
|
||||
:type lsp_name: string
|
||||
:returns: The Logical Switch Port row or None
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_lrouter(self, lrouter_name):
|
||||
"""Get a Logical Router by its name
|
||||
|
||||
:param lrouter_name: The name of the logical router
|
||||
:type lrouter_name: string
|
||||
:returns: The Logical_Router row or None
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_lrouter_ext_gw(self, lrouter_name):
|
||||
"""Delete Logical Router external gateway.
|
||||
|
||||
:param lrouter_name: The name of the logical router
|
||||
:type lrouter_name: string
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_address_set(self, addrset_id, ip_version='ip4'):
|
||||
"""Get a Address Set by its ID.
|
||||
|
||||
:param addrset_id: The Address Set ID
|
||||
:type addrset_id: string
|
||||
:param ip_version: Either "ip4" or "ip6". Defaults to "ip4"
|
||||
:type addr_name: string
|
||||
:returns: The Address Set row or None
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_lswitch_port_to_virtual_type(self, lport_name, vip,
|
||||
virtual_parent, if_exists=True):
|
||||
"""Set the type of a given port to "virtual".
|
||||
|
||||
Set the type of a given port to "virtual" and all its related
|
||||
options.
|
||||
|
||||
:param lport_name: The name of the lport
|
||||
:type lport_name: string
|
||||
:param vip: The virtual ip
|
||||
:type vip: string
|
||||
:param virtual_parent: The name of the parent lport
|
||||
:type virtual_parent: string
|
||||
:param if_exists: Do not fail if lport does not exist
|
||||
:type if_exists: bool
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def unset_lswitch_port_to_virtual_type(self, lport_name,
|
||||
virtual_parent, if_exists=True):
|
||||
"""Unset the type of a given port from "virtual".
|
||||
|
||||
Unset the type of a given port from "virtual" and all its related
|
||||
options.
|
||||
|
||||
:param lport_name: The name of the lport
|
||||
:type lport_name: string
|
||||
:param virtual_parent: The name of the parent lport
|
||||
:type virtual_parent: string
|
||||
:param if_exists: Do not fail if lport does not exist
|
||||
:type if_exists: bool
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class SbAPI(api.API):
|
||||
|
||||
@abc.abstractmethod
|
||||
def chassis_exists(self, hostname):
|
||||
"""Test if chassis for given hostname exists.
|
||||
|
||||
@param hostname: The hostname of the chassis
|
||||
@type hostname: string
|
||||
:returns: True if the chassis exists, else False.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_chassis_hostname_and_physnets(self):
|
||||
"""Return a dict contains hostname and physnets mapping.
|
||||
|
||||
Hostname will be dict key, and a list of physnets will be dict
|
||||
value. And hostname and physnets are related to the same host.
|
||||
"""
|
||||
|
||||
def get_gateway_chassis_from_cms_options(self):
|
||||
"""Get chassis eligible for external connectivity from CMS options.
|
||||
|
||||
When admin wants to enable router gateway on few chassis,
|
||||
he would set the external_ids as
|
||||
|
||||
ovs-vsctl set open .
|
||||
external_ids:ovn-cms-options="enable-chassis-as-gw"
|
||||
In this function, we parse ovn-cms-options and return these chassis
|
||||
:returns: List with chassis names.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_chassis_and_physnets(self):
|
||||
"""Return a dict contains chassis name and physnets mapping.
|
||||
|
||||
Chassis name will be dict key, and a list of physnets will be dict
|
||||
value. And chassis name and physnets are related to the same chassis.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_all_chassis(self, chassis_type=None):
|
||||
"""Return a list of all chassis which match the compute_type
|
||||
|
||||
:param chassis_type: The type of chassis
|
||||
:type chassis_type: string
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_chassis_data_for_ml2_bind_port(self, hostname):
|
||||
"""Return chassis data for ML2 port binding.
|
||||
|
||||
@param hostname: The hostname of the chassis
|
||||
@type hostname: string
|
||||
:returns: Tuple containing the chassis datapath type,
|
||||
iface types and physical networks for the
|
||||
OVN bridge mappings.
|
||||
:raises: RuntimeError exception if an OVN chassis
|
||||
does not exist.
|
||||
"""
|
|
@ -0,0 +1,973 @@
|
|||
# Copyright 2019 Red Hat, Inc.
|
||||
#
|
||||
# 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 timeutils
|
||||
from ovsdbapp.backend.ovs_idl import command
|
||||
from ovsdbapp.backend.ovs_idl import idlutils
|
||||
|
||||
from neutron._i18n import _
|
||||
from neutron.common.ovn import constants as ovn_const
|
||||
from neutron.common.ovn import exceptions as ovn_exc
|
||||
from neutron.common.ovn 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 CheckLivenessCommand(command.BaseCommand):
|
||||
def __init__(self, api):
|
||||
super(CheckLivenessCommand, self).__init__(api)
|
||||
|
||||
def run_idl(self, txn):
|
||||
# txn.pre_commit responsible for updating nb_global.nb_cfg, but
|
||||
# python-ovs will not update nb_cfg if no other changes are made
|
||||
self.api.nb_global.setkey('external_ids',
|
||||
ovn_const.OVN_LIVENESS_CHECK_EXT_ID_KEY,
|
||||
str(timeutils.utcnow(with_timezone=True)))
|
||||
self.result = self.api.nb_global.nb_cfg
|
||||
|
||||
|
||||
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 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 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 SetLSwitchPortToVirtualTypeCommand(command.BaseCommand):
|
||||
def __init__(self, api, lport, vip, parent, if_exists):
|
||||
super(SetLSwitchPortToVirtualTypeCommand, self).__init__(api)
|
||||
self.lport = lport
|
||||
self.vip = vip
|
||||
self.parent = parent
|
||||
self.if_exists = if_exists
|
||||
|
||||
def run_idl(self, txn):
|
||||
try:
|
||||
lsp = 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)
|
||||
|
||||
options = lsp.options
|
||||
options[ovn_const.LSP_OPTIONS_VIRTUAL_IP_KEY] = self.vip
|
||||
virtual_parents = options.get(
|
||||
ovn_const.LSP_OPTIONS_VIRTUAL_PARENTS_KEY, set())
|
||||
if virtual_parents:
|
||||
virtual_parents = set(virtual_parents.split(','))
|
||||
|
||||
virtual_parents.add(self.parent)
|
||||
options[ovn_const.LSP_OPTIONS_VIRTUAL_PARENTS_KEY] = ','.join(
|
||||
virtual_parents)
|
||||
setattr(lsp, 'options', options)
|
||||
setattr(lsp, 'type', ovn_const.LSP_TYPE_VIRTUAL)
|
||||
|
||||
|
||||
class UnsetLSwitchPortToVirtualTypeCommand(command.BaseCommand):
|
||||
def __init__(self, api, lport, parent, if_exists):
|
||||
super(UnsetLSwitchPortToVirtualTypeCommand, self).__init__(api)
|
||||
self.lport = lport
|
||||
self.parent = parent
|
||||
self.if_exists = if_exists
|
||||
|
||||
def run_idl(self, txn):
|
||||
try:
|
||||
lsp = 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)
|
||||
|
||||
options = lsp.options
|
||||
virtual_parents = options.get(
|
||||
ovn_const.LSP_OPTIONS_VIRTUAL_PARENTS_KEY, set())
|
||||
if virtual_parents:
|
||||
virtual_parents = set(virtual_parents.split(','))
|
||||
|
||||
try:
|
||||
virtual_parents.remove(self.parent)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# If virtual-parents is now empty, change the type and remove the
|
||||
# virtual-parents and virtual-ip options
|
||||
if not virtual_parents:
|
||||
options.pop(ovn_const.LSP_OPTIONS_VIRTUAL_PARENTS_KEY, None)
|
||||
options.pop(ovn_const.LSP_OPTIONS_VIRTUAL_IP_KEY, None)
|
||||
setattr(lsp, 'type', '')
|
||||
else:
|
||||
options[ovn_const.LSP_OPTIONS_VIRTUAL_PARENTS_KEY] = ','.join(
|
||||
virtual_parents)
|
||||
|
||||
setattr(lsp, 'options', options)
|
|
@ -0,0 +1,38 @@
|
|||
# Copyright 2019 Red Hat, Inc.
|
||||
#
|
||||
# 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 neutron_lib import worker
|
||||
|
||||
from neutron.common import config
|
||||
|
||||
|
||||
class MaintenanceWorker(worker.BaseWorker):
|
||||
|
||||
def start(self):
|
||||
super(MaintenanceWorker, self).start()
|
||||
# NOTE(twilson) The super class will trigger the post_fork_initialize
|
||||
# in the driver, which starts the connection/IDL notify loop which
|
||||
# keeps the process from exiting
|
||||
|
||||
def stop(self):
|
||||
"""Stop service."""
|
||||
super(MaintenanceWorker, self).stop()
|
||||
|
||||
def wait(self):
|
||||
"""Wait for service to complete."""
|
||||
super(MaintenanceWorker, self).wait()
|
||||
|
||||
@staticmethod
|
||||
def reset():
|
||||
config.reset_service()
|
|
@ -0,0 +1,752 @@
|
|||
#
|
||||
# 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 copy
|
||||
|
||||
import mock
|
||||
from neutron_lib.api.definitions import l3
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron.common.ovn import constants as ovn_const
|
||||
from neutron.common.ovn import utils as ovn_utils
|
||||
|
||||
|
||||
class FakeOvsdbNbOvnIdl(object):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.lswitch_table = FakeOvsdbTable.create_one_ovsdb_table()
|
||||
self.lsp_table = FakeOvsdbTable.create_one_ovsdb_table()
|
||||
self.lrouter_table = FakeOvsdbTable.create_one_ovsdb_table()
|
||||
self.lrouter_static_route_table = \
|
||||
FakeOvsdbTable.create_one_ovsdb_table()
|
||||
self.lrp_table = FakeOvsdbTable.create_one_ovsdb_table()
|
||||
self.addrset_table = FakeOvsdbTable.create_one_ovsdb_table()
|
||||
self.acl_table = FakeOvsdbTable.create_one_ovsdb_table()
|
||||
self.dhcp_options_table = FakeOvsdbTable.create_one_ovsdb_table()
|
||||
self.nat_table = FakeOvsdbTable.create_one_ovsdb_table()
|
||||
self.port_group_table = FakeOvsdbTable.create_one_ovsdb_table()
|
||||
self._tables = {}
|
||||
self._tables['Logical_Switch'] = self.lswitch_table
|
||||
self._tables['Logical_Switch_Port'] = self.lsp_table
|
||||
self._tables['Logical_Router'] = self.lrouter_table
|
||||
self._tables['Logical_Router_Port'] = self.lrp_table
|
||||
self._tables['Logical_Router_Static_Route'] = \
|
||||
self.lrouter_static_route_table
|
||||
self._tables['ACL'] = self.acl_table
|
||||
self._tables['Address_Set'] = self.addrset_table
|
||||
self._tables['DHCP_Options'] = self.dhcp_options_table
|
||||
self._tables['NAT'] = self.nat_table
|
||||
self._tables['Port_Group'] = self.port_group_table
|
||||
self.transaction = mock.MagicMock()
|
||||
self.ls_add = mock.Mock()
|
||||
self.set_lswitch_ext_ids = mock.Mock()
|
||||
self.ls_del = mock.Mock()
|
||||
self.create_lswitch_port = mock.Mock()
|
||||
self.set_lswitch_port = mock.Mock()
|
||||
self.delete_lswitch_port = mock.Mock()
|
||||
self.get_acls_for_lswitches = mock.Mock()
|
||||
self.create_lrouter = mock.Mock()
|
||||
self.lrp_del = mock.Mock()
|
||||
self.update_lrouter = mock.Mock()
|
||||
self.delete_lrouter = mock.Mock()
|
||||
self.add_lrouter_port = mock.Mock()
|
||||
self.update_lrouter_port = mock.Mock()
|
||||
self.delete_lrouter_port = mock.Mock()
|
||||
self.set_lrouter_port_in_lswitch_port = mock.Mock()
|
||||
self.add_acl = mock.Mock()
|
||||
self.delete_acl = mock.Mock()
|
||||
self.update_acls = mock.Mock()
|
||||
self.idl = mock.Mock()
|
||||
self.add_static_route = mock.Mock()
|
||||
self.delete_static_route = mock.Mock()
|
||||
self.create_address_set = mock.Mock()
|
||||
self.update_address_set_ext_ids = mock.Mock()
|
||||
self.delete_address_set = mock.Mock()
|
||||
self.update_address_set = mock.Mock()
|
||||
self.get_all_chassis_gateway_bindings = mock.Mock()
|
||||
self.get_gateway_chassis_binding = mock.Mock()
|
||||
self.get_unhosted_gateways = mock.Mock()
|
||||
self.add_dhcp_options = mock.Mock()
|
||||
self.delete_dhcp_options = mock.Mock()
|
||||
self.get_subnet_dhcp_options = mock.Mock()
|
||||
self.get_subnet_dhcp_options.return_value = {
|
||||
'subnet': None, 'ports': []}
|
||||
self.get_subnets_dhcp_options = mock.Mock()
|
||||
self.get_subnets_dhcp_options.return_value = []
|
||||
self.get_all_dhcp_options = mock.Mock()
|
||||
self.get_router_port_options = mock.MagicMock()
|
||||
self.get_router_port_options.return_value = {}
|
||||
self.add_nat_rule_in_lrouter = mock.Mock()
|
||||
self.delete_nat_rule_in_lrouter = mock.Mock()
|
||||
self.get_lrouter_nat_rules = mock.Mock()
|
||||
self.get_lrouter_nat_rules.return_value = []
|
||||
self.set_nat_rule_in_lrouter = mock.Mock()
|
||||
self.check_for_row_by_value_and_retry = mock.Mock()
|
||||
self.get_parent_port = mock.Mock()
|
||||
self.get_parent_port.return_value = []
|
||||
self.dns_add = mock.Mock()
|
||||
self.get_lswitch = mock.Mock()
|
||||
fake_ovs_row = FakeOvsdbRow.create_one_ovsdb_row()
|
||||
self.get_lswitch.return_value = fake_ovs_row
|
||||
self.get_lswitch_port = mock.Mock()
|
||||
self.get_lswitch_port.return_value = fake_ovs_row
|
||||
self.get_ls_and_dns_record = mock.Mock()
|
||||
self.get_ls_and_dns_record.return_value = (fake_ovs_row, None)
|
||||
self.ls_set_dns_records = mock.Mock()
|
||||
self.get_floatingip = mock.Mock()
|
||||
self.get_floatingip.return_value = None
|
||||
self.check_revision_number = mock.Mock()
|
||||
self.lookup = mock.MagicMock()
|
||||
# TODO(lucasagomes): The get_floatingip_by_ips() method is part
|
||||
# of a backwards compatibility layer for the Pike -> Queens release,
|
||||
# remove it in the Rocky release.
|
||||
self.get_floatingip_by_ips = mock.Mock()
|
||||
self.get_floatingip_by_ips.return_value = None
|
||||
self.is_col_present = mock.Mock()
|
||||
self.is_col_present.return_value = False
|
||||
self.get_lrouter = mock.Mock()
|
||||
self.get_lrouter.return_value = None
|
||||
self.delete_lrouter_ext_gw = mock.Mock()
|
||||
self.delete_lrouter_ext_gw.return_value = None
|
||||
self.is_port_groups_supported = mock.Mock()
|
||||
# TODO(lucasagomes): Flip this return value to True at some point,
|
||||
# port groups should be the default method used by networking-ovn
|
||||
self.is_port_groups_supported.return_value = False
|
||||
self.get_address_set = mock.Mock()
|
||||
self.get_address_set.return_value = None
|
||||
self.pg_acl_add = mock.Mock()
|
||||
self.pg_acl_del = mock.Mock()
|
||||
self.pg_del = mock.Mock()
|
||||
self.pg_add = mock.Mock()
|
||||
self.get_port_group = mock.Mock()
|
||||
self.pg_add_ports = mock.Mock()
|
||||
self.pg_del_ports = mock.Mock()
|
||||
self.lsp_get_up = mock.Mock()
|
||||
self.nb_global = mock.Mock()
|
||||
self.db_list_rows = mock.Mock()
|
||||
self.lsp_list = mock.MagicMock()
|
||||
self.db_find = mock.Mock()
|
||||
self.db_set = mock.Mock()
|
||||
self.db_clear = mock.Mock()
|
||||
self.db_remove = mock.Mock()
|
||||
self.set_lswitch_port_to_virtual_type = mock.Mock()
|
||||
self.unset_lswitch_port_to_virtual_type = mock.Mock()
|
||||
self.ls_get = mock.Mock()
|
||||
|
||||
|
||||
class FakeOvsdbSbOvnIdl(object):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.chassis_exists = mock.Mock()
|
||||
self.chassis_exists.return_value = True
|
||||
self.get_chassis_hostname_and_physnets = mock.Mock()
|
||||
self.get_chassis_hostname_and_physnets.return_value = {}
|
||||
self.get_all_chassis = mock.Mock()
|
||||
self.get_chassis_data_for_ml2_bind_port = mock.Mock()
|
||||
self.get_chassis_data_for_ml2_bind_port.return_value = \
|
||||
('fake', '', ['fake-physnet'])
|
||||
self.get_logical_port_chassis_and_datapath = mock.Mock()
|
||||
self.get_logical_port_chassis_and_datapath.return_value = \
|
||||
('fake', 'fake-dp')
|
||||
self.get_chassis_and_physnets = mock.Mock()
|
||||
self.get_gateway_chassis_from_cms_options = mock.Mock()
|
||||
self.is_col_present = mock.Mock()
|
||||
self.is_col_present.return_value = False
|
||||
|
||||
|
||||
class FakeOvsdbTransaction(object):
|
||||
def __init__(self, **kwargs):
|
||||
self.insert = mock.Mock()
|
||||
|
||||
|
||||
class FakePlugin(object):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.get_ports = mock.Mock()
|
||||
self._get_port_security_group_bindings = mock.Mock()
|
||||
|
||||
|
||||
class FakeResource(dict):
|
||||
|
||||
def __init__(self, manager=None, info=None, loaded=False, methods=None):
|
||||
"""Set attributes and methods for a resource.
|
||||
|
||||
:param manager:
|
||||
The resource manager
|
||||
:param Dictionary info:
|
||||
A dictionary with all attributes
|
||||
:param bool loaded:
|
||||
True if the resource is loaded in memory
|
||||
:param Dictionary methods:
|
||||
A dictionary with all methods
|
||||
"""
|
||||
info = info or {}
|
||||
super(FakeResource, self).__init__(info)
|
||||
methods = methods or {}
|
||||
|
||||
self.__name__ = type(self).__name__
|
||||
self.manager = manager
|
||||
self._info = info
|
||||
self._add_details(info)
|
||||
self._add_methods(methods)
|
||||
self._loaded = loaded
|
||||
# Add a revision number by default
|
||||
setattr(self, 'revision_number', 1)
|
||||
|
||||
@property
|
||||
def db_obj(self):
|
||||
return self
|
||||
|
||||
def _add_details(self, info):
|
||||
for (k, v) in info.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
def _add_methods(self, methods):
|
||||
"""Fake methods with MagicMock objects.
|
||||
|
||||
For each <@key, @value> pairs in methods, add an callable MagicMock
|
||||
object named @key as an attribute, and set the mock's return_value to
|
||||
@value. When users access the attribute with (), @value will be
|
||||
returned, which looks like a function call.
|
||||
"""
|
||||
for (name, ret) in methods.items():
|
||||
method = mock.MagicMock(return_value=ret)
|
||||
setattr(self, name, method)
|
||||
|
||||
def __repr__(self):
|
||||
reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and
|
||||
k != 'manager')
|
||||
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
|
||||
return "<%s %s>" % (self.__class__.__name__, info)
|
||||
|
||||
def keys(self):
|
||||
return self._info.keys()
|
||||
|
||||
def info(self):
|
||||
return self._info
|
||||
|
||||
def update(self, info):
|
||||
super(FakeResource, self).update(info)
|
||||
self._add_details(info)
|
||||
|
||||
|
||||
class FakeNetwork(object):
|
||||
"""Fake one or more networks."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_network(attrs=None):
|
||||
"""Create a fake network.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object faking the network
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attributes.
|
||||
fake_uuid = uuidutils.generate_uuid()
|
||||
network_attrs = {
|
||||
'id': 'network-id-' + fake_uuid,
|
||||
'name': 'network-name-' + fake_uuid,
|
||||
'status': 'ACTIVE',
|
||||
'tenant_id': 'project-id-' + fake_uuid,
|
||||
'admin_state_up': True,
|
||||
'shared': False,
|
||||
'subnets': [],
|
||||
'provider:network_type': 'geneve',
|
||||
'provider:physical_network': None,
|
||||
'provider:segmentation_id': 10,
|
||||
'router:external': False,
|
||||
'availability_zones': [],
|
||||
'availability_zone_hints': [],
|
||||
'is_default': False,
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
network_attrs.update(attrs)
|
||||
|
||||
return FakeResource(info=copy.deepcopy(network_attrs),
|
||||
loaded=True)
|
||||
|
||||
|
||||
class FakeNetworkContext(object):
|
||||
def __init__(self, network, segments):
|
||||
self.fake_network = network
|
||||
self.fake_segments = segments
|
||||
self._plugin_context = mock.MagicMock()
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
return self.fake_network
|
||||
|
||||
@property
|
||||
def original(self):
|
||||
return None
|
||||
|
||||
@property
|
||||
def network_segments(self):
|
||||
return self.fake_segments
|
||||
|
||||
|
||||
class FakeSubnetContext(object):
|
||||
def __init__(self, subnet, original_subnet=None, network=None):
|
||||
self.fake_subnet = subnet
|
||||
self.fake_original_subnet = original_subnet
|
||||
self.fake_network = FakeNetworkContext(network, None)
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
return self.fake_subnet
|
||||
|
||||
@property
|
||||
def original(self):
|
||||
return self.fake_original_subnet
|
||||
|
||||
@property
|
||||
def network(self):
|
||||
return self.fake_network
|
||||
|
||||
|
||||
class FakeOvsdbRow(FakeResource):
|
||||
"""Fake one or more OVSDB rows."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_ovsdb_row(attrs=None, methods=None):
|
||||
"""Create a fake OVSDB row.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:param Dictionary methods:
|
||||
A dictionary with all methods
|
||||
:return:
|
||||
A FakeResource object faking the OVSDB row
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
methods = methods or {}
|
||||
|
||||
# Set default attributes.
|
||||
fake_uuid = uuidutils.generate_uuid()
|
||||
ovsdb_row_attrs = {
|
||||
'uuid': fake_uuid,
|
||||
'name': 'name-' + fake_uuid,
|
||||
'external_ids': {},
|
||||
}
|
||||
|
||||
# Set default methods.
|
||||
ovsdb_row_methods = {
|
||||
'addvalue': None,
|
||||
'delete': None,
|
||||
'delvalue': None,
|
||||
'verify': None,
|
||||
'setkey': None,
|
||||
}
|
||||
|
||||
# Overwrite default attributes and methods.
|
||||
ovsdb_row_attrs.update(attrs)
|
||||
ovsdb_row_methods.update(methods)
|
||||
|
||||
return FakeResource(info=copy.deepcopy(ovsdb_row_attrs),
|
||||
loaded=True,
|
||||
methods=copy.deepcopy(ovsdb_row_methods))
|
||||
|
||||
|
||||
class FakeOvsdbTable(FakeResource):
|
||||
"""Fake one or more OVSDB tables."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_ovsdb_table(attrs=None):
|
||||
"""Create a fake OVSDB table.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object faking the OVSDB table
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attributes.
|
||||
ovsdb_table_attrs = {
|
||||
'rows': {},
|
||||
'columns': {},
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
ovsdb_table_attrs.update(attrs)
|
||||
|
||||
return FakeResource(info=copy.deepcopy(ovsdb_table_attrs),
|
||||
loaded=True)
|
||||
|
||||
|
||||
class FakePort(object):
|
||||
"""Fake one or more ports."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_port(attrs=None):
|
||||
"""Create a fake port.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object faking the port
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attributes.
|
||||
fake_uuid = uuidutils.generate_uuid()
|
||||
port_attrs = {
|
||||
'admin_state_up': True,
|
||||
'allowed_address_pairs': [{}],
|
||||
'binding:host_id': 'binding-host-id-' + fake_uuid,
|
||||
'binding:profile': {},
|
||||
'binding:vif_details': {},
|
||||
'binding:vif_type': 'ovs',
|
||||
'binding:vnic_type': 'normal',
|
||||
'device_id': 'device-id-' + fake_uuid,
|
||||
'device_owner': 'compute:nova',
|
||||
'dns_assignment': [{}],
|
||||
'dns_name': 'dns-name-' + fake_uuid,
|
||||
'extra_dhcp_opts': [{}],
|
||||
'fixed_ips': [{'subnet_id': 'subnet-id-' + fake_uuid,
|
||||
'ip_address': '10.10.10.20'}],
|
||||
'id': 'port-id-' + fake_uuid,
|
||||
'mac_address': 'fa:16:3e:a9:4e:72',
|
||||
'name': 'port-name-' + fake_uuid,
|
||||
'network_id': 'network-id-' + fake_uuid,
|
||||
'port_security_enabled': True,
|
||||
'security_groups': [],
|
||||
'status': 'ACTIVE',
|
||||
'tenant_id': 'project-id-' + fake_uuid,
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
port_attrs.update(attrs)
|
||||
|
||||
return FakeResource(info=copy.deepcopy(port_attrs),
|
||||
loaded=True)
|
||||
|
||||
|
||||
class FakePortContext(object):
|
||||
def __init__(self, port, host, segments_to_bind):
|
||||
self.fake_port = port
|
||||
self.fake_host = host
|
||||
self.fake_segments_to_bind = segments_to_bind
|
||||
self.set_binding = mock.Mock()
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
return self.fake_port
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
return self.fake_host
|
||||
|
||||
@property
|
||||
def segments_to_bind(self):
|
||||
return self.fake_segments_to_bind
|
||||
|
||||
|
||||
class FakeSecurityGroup(object):
|
||||
"""Fake one or more security groups."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_security_group(attrs=None):
|
||||
"""Create a fake security group.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object faking the security group
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attributes.
|
||||
fake_uuid = uuidutils.generate_uuid()
|
||||
security_group_attrs = {
|
||||
'id': 'security-group-id-' + fake_uuid,
|
||||
'name': 'security-group-name-' + fake_uuid,
|
||||
'description': 'security-group-description-' + fake_uuid,
|
||||
'tenant_id': 'project-id-' + fake_uuid,
|
||||
'security_group_rules': [],
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
security_group_attrs.update(attrs)
|
||||
|
||||
return FakeResource(info=copy.deepcopy(security_group_attrs),
|
||||
loaded=True)
|
||||
|
||||
|
||||
class FakeSecurityGroupRule(object):
|
||||
"""Fake one or more security group rules."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_security_group_rule(attrs=None):
|
||||
"""Create a fake security group rule.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object faking the security group rule
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attributes.
|
||||
fake_uuid = uuidutils.generate_uuid()
|
||||
security_group_rule_attrs = {
|
||||
'direction': 'ingress',
|
||||
'ethertype': 'IPv4',
|
||||
'id': 'security-group-rule-id-' + fake_uuid,
|
||||
'port_range_max': 22,
|
||||
'port_range_min': 22,
|
||||
'protocol': 'tcp',
|
||||
'remote_group_id': None,
|
||||
'remote_ip_prefix': '0.0.0.0/0',
|
||||
'security_group_id': 'security-group-id-' + fake_uuid,
|
||||
'tenant_id': 'project-id-' + fake_uuid,
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
security_group_rule_attrs.update(attrs)
|
||||
|
||||
return FakeResource(info=copy.deepcopy(security_group_rule_attrs),
|
||||
loaded=True)
|
||||
|
||||
|
||||
class FakeSegment(object):
|
||||
"""Fake one or more segments."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_segment(attrs=None):
|
||||
"""Create a fake segment.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object faking the segment
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attributes.
|
||||
fake_uuid = uuidutils.generate_uuid()
|
||||
segment_attrs = {
|
||||
'id': 'segment-id-' + fake_uuid,
|
||||
'network_type': 'geneve',
|
||||
'physical_network': None,
|
||||
'segmentation_id': 10,
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
segment_attrs.update(attrs)
|
||||
|
||||
return FakeResource(info=copy.deepcopy(segment_attrs),
|
||||
loaded=True)
|
||||
|
||||
|
||||
class FakeSubnet(object):
|
||||
"""Fake one or more subnets."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_subnet(attrs=None):
|
||||
"""Create a fake subnet.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object faking the subnet
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attributes.
|
||||
fake_uuid = uuidutils.generate_uuid()
|
||||
subnet_attrs = {
|
||||
'id': 'subnet-id-' + fake_uuid,
|
||||
'name': 'subnet-name-' + fake_uuid,
|
||||
'network_id': 'network-id-' + fake_uuid,
|
||||
'cidr': '10.10.10.0/24',
|
||||
'tenant_id': 'project-id-' + fake_uuid,
|
||||
'enable_dhcp': True,
|
||||
'dns_nameservers': [],
|
||||
'allocation_pools': [],
|
||||
'host_routes': [],
|
||||
'ip_version': 4,
|
||||
'gateway_ip': '10.10.10.1',
|
||||
'ipv6_address_mode': 'None',
|
||||
'ipv6_ra_mode': 'None',
|
||||
'subnetpool_id': None,
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
subnet_attrs.update(attrs)
|
||||
|
||||
return FakeResource(info=copy.deepcopy(subnet_attrs),
|
||||
loaded=True)
|
||||
|
||||
|
||||
class FakeFloatingIp(object):
|
||||
"""Fake one or more floating ips."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_fip(attrs=None):
|
||||
"""Create a fake floating ip.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object faking the floating ip
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attributes.
|
||||
fake_uuid = uuidutils.generate_uuid()
|
||||
fip_attrs = {
|
||||
'id': 'fip-id-' + fake_uuid,
|
||||
'tenant_id': '',
|
||||
'fixed_ip_address': '10.0.0.10',
|
||||
'floating_ip_address': '172.21.0.100',
|
||||
'router_id': 'router-id',
|
||||
'port_id': 'port_id',
|
||||
'fixed_port_id': 'port_id',
|
||||
'floating_port_id': 'fip-port-id',
|
||||
'status': 'Active',
|
||||
'floating_network_id': 'fip-net-id',
|
||||
'dns': '',
|
||||
'dns_domain': '',
|
||||
'dns_name': '',
|
||||
'project_id': '',
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
fip_attrs.update(attrs)
|
||||
|
||||
return FakeResource(info=copy.deepcopy(fip_attrs),
|
||||
loaded=True)
|
||||
|
||||
|
||||
class FakeOVNPort(object):
|
||||
"""Fake one or more ports."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_port(attrs=None):
|
||||
"""Create a fake ovn port.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object faking the port
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attributes.
|
||||
fake_uuid = uuidutils.generate_uuid()
|
||||
port_attrs = {
|
||||
'addresses': [],
|
||||
'dhcpv4_options': '',
|
||||
'dhcpv6_options': [],
|
||||
'enabled': True,
|
||||
'external_ids': {},
|
||||
'name': fake_uuid,
|
||||
'options': {},
|
||||
'parent_name': [],
|
||||
'port_security': [],
|
||||
'tag': [],
|
||||
'tag_request': [],
|
||||
'type': '',
|
||||
'up': False,
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
port_attrs.update(attrs)
|
||||
return type('Logical_Switch_Port', (object, ), port_attrs)
|
||||
|
||||
@staticmethod
|
||||
def from_neutron_port(port):
|
||||
"""Create a fake ovn port based on a neutron port."""
|
||||
external_ids = {
|
||||
ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY:
|
||||
ovn_utils.ovn_name(port['network_id']),
|
||||
ovn_const.OVN_SG_IDS_EXT_ID_KEY:
|
||||
' '.join(port['security_groups']),
|
||||
ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY:
|
||||
port.get('device_owner', '')}
|
||||
addresses = [port['mac_address'], ]
|
||||
addresses += [x['ip_address'] for x in port.get('fixed_ips', [])]
|
||||
port_security = (
|
||||
addresses + [x['ip_address'] for x in
|
||||
port.get('allowed_address_pairs', [])])
|
||||
return FakeOVNPort.create_one_port(
|
||||
{'external_ids': external_ids, 'addresses': addresses,
|
||||
'port_security': port_security})
|
||||
|
||||
|
||||
FakeStaticRoute = collections.namedtuple(
|
||||
'Static_Routes', ['ip_prefix', 'nexthop', 'external_ids'])
|
||||
|
||||
|
||||
class FakeOVNRouter(object):
|
||||
|
||||
@staticmethod
|
||||
def create_one_router(attrs=None):
|
||||
router_attrs = {
|
||||
'enabled': False,
|
||||
'external_ids': {},
|
||||
'load_balancer': [],
|
||||
'name': '',
|
||||
'nat': [],
|
||||
'options': {},
|
||||
'ports': [],
|
||||
'static_routes': [],
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
router_attrs.update(attrs)
|
||||
return type('Logical_Router', (object, ), router_attrs)
|
||||
|
||||
@staticmethod
|
||||
def from_neutron_router(router):
|
||||
|
||||
def _get_subnet_id(gw_info):
|
||||
subnet_id = ''
|
||||
ext_ips = gw_info.get('external_fixed_ips', [])
|
||||
if ext_ips:
|
||||
subnet_id = ext_ips[0]['subnet_id']
|
||||
return subnet_id
|
||||
|
||||
external_ids = {
|
||||
ovn_const.OVN_GW_PORT_EXT_ID_KEY: router.get('gw_port_id') or '',
|
||||
ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY:
|
||||
router.get('name', 'no_router_name')}
|
||||
|
||||
# Get the routes
|
||||
routes = []
|
||||
for r in router.get('routes', []):
|
||||
routes.append(FakeStaticRoute(ip_prefix=r['destination'],
|
||||
nexthop=r['nexthop'],
|
||||
external_ids={}))
|
||||
|
||||
gw_info = router.get(l3.EXTERNAL_GW_INFO)
|
||||
if gw_info:
|
||||
external_ids = {
|
||||
ovn_const.OVN_ROUTER_IS_EXT_GW: 'true',
|
||||
ovn_const.OVN_SUBNET_EXT_ID_KEY: _get_subnet_id(gw_info)}
|
||||
routes.append(FakeStaticRoute(
|
||||
ip_prefix='0.0.0.0/0', nexthop='',
|
||||
external_ids=external_ids))
|
||||
|
||||
return FakeOVNRouter.create_one_router(
|
||||
{'external_ids': external_ids,
|
||||
'enabled': router.get('admin_state_up') or False,
|
||||
'name': ovn_utils.ovn_name(router['id']),
|
||||
'static_routes': routes})
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue