Merge "Add OVN_Northbound API LR, LRP, and LB commands"
This commit is contained in:
commit
6417850d89
@ -70,6 +70,9 @@ class Backend(object):
|
||||
raise
|
||||
|
||||
def _lookup(self, table, record):
|
||||
if record == "":
|
||||
raise TypeError("Cannot look up record by empty string")
|
||||
|
||||
t = self.tables[table]
|
||||
try:
|
||||
if isinstance(record, uuid.UUID):
|
||||
|
@ -17,4 +17,13 @@ DEFAULT_OVNNB_CONNECTION = 'tcp:127.0.0.1:6641'
|
||||
DEFAULT_TIMEOUT = 5
|
||||
DEVICE_NAME_MAX_LEN = 14
|
||||
|
||||
|
||||
ACL_PRIORITY_MAX = 32767
|
||||
|
||||
NAT_SNAT = 'snat'
|
||||
NAT_DNAT = 'dnat'
|
||||
NAT_BOTH = 'dnat_and_snat'
|
||||
NAT_TYPES = (NAT_SNAT, NAT_DNAT, NAT_BOTH)
|
||||
|
||||
PROTO_TCP = 'tcp'
|
||||
PROTO_UDP = 'udp'
|
||||
|
@ -15,6 +15,7 @@ import abc
|
||||
import six
|
||||
|
||||
from ovsdbapp import api
|
||||
from ovsdbapp import constants as const
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
@ -290,6 +291,304 @@ class API(api.API):
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lr_add(self, router=None, may_exist=False, **columns):
|
||||
"""Create a logical router named `router`
|
||||
|
||||
:param router: The optional name or uuid of the router
|
||||
:type router: string or uuid.UUID
|
||||
:param may_exist: If True, don't fail if the router already exists
|
||||
:type may_exist: boolean
|
||||
:param **columns: Additional columns to directly set on the router
|
||||
:returns: :class:`Command` with RowView result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lr_del(self, router, if_exists=False):
|
||||
"""Delete 'router' and all its ports
|
||||
|
||||
:param router: The name or uuid of the router
|
||||
:type router: string or uuid.UUID
|
||||
:param if_exists: If True, don't fail if the router doesn't exist
|
||||
:type if_exists: boolean
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lr_list(self):
|
||||
"""Get the UUIDs of all logical routers
|
||||
|
||||
:returns: :class:`Command` with RowView list result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lrp_add(self, router, port, mac, networks, peer=None, may_exist=False,
|
||||
**columns):
|
||||
"""Add logical port 'port' on 'router'
|
||||
|
||||
:param router: The name or uuid of the router to attach the port
|
||||
:type router: string or uuid.UUID
|
||||
:param mac: The MAC address of the port
|
||||
:type mac: string
|
||||
:param networks: One or more IP address/netmask to assign to the port
|
||||
:type networks: list of strings
|
||||
:param peer: Optional logical router port connected to this one
|
||||
:param may_exist: If True, don't fail if the port already exists
|
||||
:type may_exist: boolean
|
||||
:param **columns: Additional column values to directly set on the port
|
||||
:returns: :class:`Command` with RowView result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lrp_del(self, port, router=None, if_exists=None):
|
||||
"""Delete 'port' from its attached router
|
||||
|
||||
:param port: The name or uuid of the port
|
||||
:type port: string or uuid.UUID
|
||||
:param router: Only delete router if attached to `router`
|
||||
:type router: string or uuiwhd.UUID
|
||||
:param if_exists: If True, don't fail if the port doesn't exist
|
||||
:type if_exists: boolean
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lrp_list(self, router):
|
||||
"""Get the UUIDs of all ports on 'router'
|
||||
|
||||
:param router: The name or uuid of the router
|
||||
:type router: string or uuid.UUID
|
||||
:returns: :class:`Command` with RowView list result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lrp_set_enabled(self, port, is_enabled):
|
||||
"""Set administrative state of 'port'
|
||||
|
||||
:param port: The name or uuid of the port
|
||||
:type port: string or uuid.UUID
|
||||
:param is_enabled: True for enabled, False for disabled
|
||||
:type is_enabled: boolean
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lrp_get_enabled(self, port):
|
||||
"""Get administrative state of 'port'
|
||||
|
||||
:param port: The name or uuid of the port
|
||||
:type port: string or uuid.UUID
|
||||
:returns:
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lr_route_add(self, router, prefix, nexthop, port=None,
|
||||
policy='dst-ip', may_exist=False):
|
||||
"""Add a route to 'router'
|
||||
|
||||
:param router: The name or uuid of the router
|
||||
:type router: string or uuid.UUID
|
||||
:param prefix: an IPv4/6 prefix for this route, e.g. 192.168.1.0/24
|
||||
:type prefix: type string
|
||||
:parm nexthop: The gateway to use for this route, which should be
|
||||
the IP address of one of `router`'s logical router
|
||||
ports or the IP address of a logical port
|
||||
:type nexthop: string
|
||||
:param port: If specified, packets that match this route will be
|
||||
sent out this port. Otherwise OVN infers the output
|
||||
port based on nexthop.
|
||||
:type port: string
|
||||
:param policy: the policy used to make routing decisions
|
||||
:type policy: string, 'dst-ip' or 'src-ip'
|
||||
:param may_exist: If True, don't fail if the route already exists
|
||||
:type may_exist: boolean
|
||||
returns: :class:`Command` with RowView result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lr_route_del(self, router, prefix=None, if_exists=False):
|
||||
"""Remove routes from 'router'
|
||||
|
||||
:param router: The name or uuid of the router
|
||||
:type router: string or uuid.UUID
|
||||
:param prefix: an IPv4/6 prefix to match, e.g. 192.168.1.0/24
|
||||
:type prefix: type string
|
||||
:param if_exists: If True, don't fail if the port doesn't exist
|
||||
:type if_exists: boolean
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lr_route_list(self, router):
|
||||
"""Get the UUIDs of static logical routes from 'router'
|
||||
|
||||
:param router: The name or uuid of the router
|
||||
:type router: string or uuid.UUID
|
||||
:returns: :class:`Command` with RowView list result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lr_nat_add(self, router, nat_type, external_ip, logical_ip,
|
||||
logical_port=None, external_mac=None, may_exist=False):
|
||||
"""Add a NAT to 'router'
|
||||
|
||||
:param router: The name or uuid of the router
|
||||
:type router: string or uuid.UUID
|
||||
:param nat_type: The type of NAT to be done
|
||||
:type nat_type: NAT_SNAT, NAT_DNAT, or NAT_BOTH
|
||||
:param external_ip: Externally visible Ipv4 address
|
||||
:type external_ip: string
|
||||
:param logical_ip: The logical IPv4 network or address with which
|
||||
`external_ip` is NATted
|
||||
:type logical_ip: string
|
||||
:param logical_port: The name of an existing logical switch port where
|
||||
the logical_ip resides
|
||||
:type logical_port: string
|
||||
:param external_mac: ARP replies for the external_ip return the value
|
||||
of `external_mac`. Packets transmitted with
|
||||
source IP address equal to `external_ip` will be
|
||||
sent using `external_mac`.
|
||||
:type external_mac: string
|
||||
:param may_exist: If True, don't fail if the route already exists
|
||||
and if `logical_port` and `external_mac` are
|
||||
specified, they will be updated
|
||||
:type may_exist: boolean
|
||||
:returns: :class:`Command` with RowView result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lr_nat_del(self, router, nat_type=None, match_ip=None, if_exists=None):
|
||||
"""Remove NATs from 'router'
|
||||
|
||||
:param router: The name or uuid of the router
|
||||
:type router: string or uuid.UUID
|
||||
:param nat_type: The type of NAT to match
|
||||
:type nat_type: NAT_SNAT, NAT_DNAT, or NAT_BOTH
|
||||
:param match_ip: The IPv4 address to match on. If
|
||||
`nat_type` is specified and is NAT_SNAT, the IP
|
||||
should be the logical ip, otherwise the IP should
|
||||
be the external IP.
|
||||
:type match_ip: string
|
||||
:param if_exists: If True, don't fail if the port doesn't exist
|
||||
:type if_exists: boolean
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lr_nat_list(self, router):
|
||||
"""Get the NATs on 'router'
|
||||
|
||||
:param router: The name or uuid of the router
|
||||
:type router: string or uuid.UUID
|
||||
:returns: :class:`Command` with RowView list result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lb_add(self, vip, ips, protocol=const.PROTO_TCP, may_exist=False):
|
||||
"""Create a load-balancer or add a VIP to an existing load balancer
|
||||
|
||||
:param lb: The name or uuid of the load-balancer
|
||||
:type lb: string or uuid.UUID
|
||||
:param vip: A virtual IP in the format IP[:PORT]
|
||||
:type vip: string
|
||||
:param ips: A list of ips in the form IP[:PORT]
|
||||
:type ips: string
|
||||
:param protocol: The IP protocol for load balancing
|
||||
:type protocol: PROTO_TCP or PROTO_UDP
|
||||
:param may_exist: If True, don't fail if a LB w/ `vip` exists, and
|
||||
instead, replace the vips on the LB
|
||||
:type may_exist: boolean
|
||||
:returns: :class:`Command` with RowView result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lb_del(self, lb, vip=None, if_exists=False):
|
||||
"""Remove a load balancer or just the VIP from a load balancer
|
||||
|
||||
:param lb: The name or uuid of a load balancer
|
||||
:type lb: string or uuid.UUID
|
||||
:param vip: The VIP on the load balancer to match
|
||||
:type: string
|
||||
:param if_exists: If True, don't fail if the port doesn't exist
|
||||
:type if_exists: boolean
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lb_list(self):
|
||||
"""Get the UUIDs of all load balanacers"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lr_lb_add(self, router, lb, may_exist=False):
|
||||
"""Add a load-balancer to 'router'
|
||||
|
||||
:param router: The name or uuid of the router
|
||||
:type router: string or uuid.UUID
|
||||
:param lb: The name or uuid of the load balancer
|
||||
:type lb: string or uuid.UUID
|
||||
:param may_exist: If True, don't fail if lb already assigned to lr
|
||||
:type may_exist: boolean
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lr_lb_del(self, router, lb=None, if_exists=False):
|
||||
"""Remove load-balancers from 'router'
|
||||
|
||||
:param router: The name or uuid of the router
|
||||
:type router: string or uuid.UUID
|
||||
:param lb: The name or uuid of the load balancer to remove. None
|
||||
to remove all load balancers from the router
|
||||
:type lb: string or uuid.UUID
|
||||
:type if_exists: If True, don't fail if the switch doesn't exist
|
||||
:type if_exists: boolean
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def lr_lb_list(self, router):
|
||||
"""Get UUIDs of load-balancers on 'router'
|
||||
|
||||
:param router: The name or uuid of the router
|
||||
:type router: string or uuid.UUID
|
||||
:returns: :class:`Command` with RowView list result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def ls_lb_add(self, switch, lb, may_exist=False):
|
||||
"""Add a load-balancer to 'switch'
|
||||
|
||||
:param switch: The name or uuid of the switch
|
||||
:type switch: string or uuid.UUID
|
||||
:param lb: The name or uuid of the load balancer
|
||||
:type lb: string or uuid.UUID
|
||||
:param may_exist: If True, don't fail if lb already assigned to lr
|
||||
:type may_exist: boolean
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def ls_lb_del(self, switch, lb=None, if_exists=False):
|
||||
"""Remove load-balancers from 'switch'
|
||||
|
||||
:param switch: The name or uuid of the switch
|
||||
:type switch: string or uuid.UUID
|
||||
:param lb: The name or uuid of the load balancer to remove. None
|
||||
to remove all load balancers from the switch
|
||||
:type lb: string or uuid.UUID
|
||||
:type if_exists: If True, don't fail if the switch doesn't exist
|
||||
:type if_exists: boolean
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def ls_lb_list(self, switch):
|
||||
"""Get UUIDs of load-balancers on 'switch'
|
||||
|
||||
:param switch: The name or uuid of the switch
|
||||
:type switch: string or uuid.UUID
|
||||
:returns: :class:`Command` with RowView list result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def dhcp_options_add(self, cidr, **external_ids):
|
||||
"""Create a DHCP options row with CIDR
|
||||
|
@ -17,6 +17,7 @@ from ovsdbapp.backend import ovs_idl
|
||||
from ovsdbapp.backend.ovs_idl import command as cmd
|
||||
from ovsdbapp.backend.ovs_idl import idlutils
|
||||
from ovsdbapp import constants as const
|
||||
from ovsdbapp import utils
|
||||
|
||||
|
||||
class AddCommand(cmd.BaseCommand):
|
||||
@ -226,33 +227,43 @@ class LspAddCommand(AddCommand):
|
||||
self.result = lsp.uuid
|
||||
|
||||
|
||||
class LspDelCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, port, switch=None, if_exists=False):
|
||||
super(LspDelCommand, self).__init__(api)
|
||||
class PortDelCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, table, port, parent_table, parent=None,
|
||||
if_exists=False):
|
||||
super(PortDelCommand, self).__init__(api)
|
||||
self.table = table
|
||||
self.port = port
|
||||
self.switch = switch
|
||||
self.parent_table = parent_table
|
||||
self.parent = parent
|
||||
self.if_exists = if_exists
|
||||
|
||||
def run_idl(self, txn):
|
||||
try:
|
||||
lsp = self.api.lookup('Logical_Switch_Port', self.port)
|
||||
row = self.api.lookup(self.table, self.port)
|
||||
except idlutils.RowNotFound:
|
||||
if self.if_exists:
|
||||
return
|
||||
raise RuntimeError("%s does not exist" % self.port)
|
||||
|
||||
# We need to delete the port from its switch
|
||||
if self.switch:
|
||||
sw = self.api.lookup('Logical_Switch', self.switch)
|
||||
# We need to delete the port from its parent
|
||||
if self.parent:
|
||||
parent = self.api.lookup(self.parent_table, self.parent)
|
||||
else:
|
||||
sw = next(iter(
|
||||
s for s in self.api.tables['Logical_Switch'].rows.values()
|
||||
if lsp in s.ports), None)
|
||||
if not (sw and lsp in sw.ports):
|
||||
parent = next(iter(
|
||||
p for p in self.api.tables[self.parent_table].rows.values()
|
||||
if row in p.ports), None)
|
||||
if not (parent and row in parent.ports):
|
||||
raise RuntimeError("%s does not exist in %s" % (
|
||||
self.port, self.switch))
|
||||
sw.delvalue('ports', lsp)
|
||||
lsp.delete()
|
||||
self.port, self.parent))
|
||||
parent.delvalue('ports', row)
|
||||
row.delete()
|
||||
|
||||
|
||||
class LspDelCommand(PortDelCommand):
|
||||
def __init__(self, api, port, switch=None, if_exists=False):
|
||||
super(LspDelCommand, self).__init__(
|
||||
api, 'Logical_Switch_Port', port, 'Logical_Switch', switch,
|
||||
if_exists)
|
||||
|
||||
|
||||
class LspListCommand(cmd.BaseCommand):
|
||||
@ -487,3 +498,500 @@ class DhcpOptionsGetOptionsCommand(cmd.BaseCommand):
|
||||
def run_idl(self, txn):
|
||||
dhcpopt = self.api.lookup('DHCP_Options', self.dhcpopt_uuid)
|
||||
self.result = dhcpopt.options
|
||||
|
||||
|
||||
class LrAddCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router=None, may_exist=False, **columns):
|
||||
super(LrAddCommand, self).__init__(api)
|
||||
self.router = router
|
||||
self.may_exist = may_exist
|
||||
self.columns = columns
|
||||
|
||||
def run_idl(self, txn):
|
||||
if self.router:
|
||||
try:
|
||||
lr = self.api.lookup('Logical_Router', self.router)
|
||||
if self.may_exist:
|
||||
self.result = ovs_idl.RowView(lr)
|
||||
return
|
||||
except idlutils.RowNotFound:
|
||||
pass
|
||||
lr = txn.insert(self.api.tables['Logical_Router'])
|
||||
lr.name = self.router if self.router else ""
|
||||
for col, value in self.columns.items():
|
||||
setattr(lr, col, value)
|
||||
self.result = lr.uuid
|
||||
|
||||
def post_commit(self, txn):
|
||||
real_uuid = txn.get_insert_uuid(self.result)
|
||||
if real_uuid:
|
||||
row = self.api.tables['Logical_Router'].rows[real_uuid]
|
||||
self.result = ovs_idl.RowView(row)
|
||||
|
||||
|
||||
class LrDelCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router, if_exists=False):
|
||||
super(LrDelCommand, self).__init__(api)
|
||||
self.router = router
|
||||
self.if_exists = if_exists
|
||||
|
||||
def run_idl(self, txn):
|
||||
try:
|
||||
lr = self.api.lookup('Logical_Router', self.router)
|
||||
lr.delete()
|
||||
except idlutils.RowNotFound:
|
||||
if self.if_exists:
|
||||
return
|
||||
msg = "Logical Router %s does not exist" % self.router
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
class LrListCommand(cmd.BaseCommand):
|
||||
def run_idl(self, txn):
|
||||
self.result = [ovs_idl.RowView(r) for
|
||||
r in self.api.tables['Logical_Router'].rows.values()]
|
||||
|
||||
|
||||
class LrpAddCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router, port, mac, networks,
|
||||
peer=None, may_exist=False, **columns):
|
||||
self.mac = str(netaddr.EUI(mac, dialect=netaddr.mac_unix_expanded))
|
||||
self.networks = [str(netaddr.IPNetwork(net)) for net in networks]
|
||||
self.router = router
|
||||
self.port = port
|
||||
self.peer = peer
|
||||
self.may_exist = may_exist
|
||||
self.columns = columns
|
||||
super(LrpAddCommand, self).__init__(api)
|
||||
|
||||
def run_idl(self, txn):
|
||||
lr = self.api.lookup('Logical_Router', self.router)
|
||||
try:
|
||||
lrp = self.api.lookup('Logical_Router_Port', self.port)
|
||||
if self.may_exist:
|
||||
msg = None
|
||||
if lrp not in lr.ports:
|
||||
msg = "Port %s exists, but is not in router %s" % (
|
||||
self.port, self.router)
|
||||
elif netaddr.EUI(lrp.mac) != netaddr.EUI(self.mac):
|
||||
msg = "Port %s exists with different mac" % (self.port)
|
||||
elif set(self.networks) != set(lrp.networks):
|
||||
msg = "Port %s exists with different networks" % (
|
||||
self.port)
|
||||
elif (not self.peer) != (not lrp.peer) or (
|
||||
self.peer != lrp.peer):
|
||||
msg = "Port %s exists with different peer" % (self.port)
|
||||
if msg:
|
||||
raise RuntimeError(msg)
|
||||
self.result = ovs_idl.RowView(lrp)
|
||||
return
|
||||
except idlutils.RowNotFound:
|
||||
pass
|
||||
lrp = txn.insert(self.api.tables['Logical_Router_Port'])
|
||||
# This is what ovn-nbctl does, though the lookup is by uuid or name
|
||||
lrp.name = self.port
|
||||
lrp.mac = self.mac
|
||||
lrp.networks = self.networks
|
||||
if self.peer:
|
||||
lrp.peer = self.peer
|
||||
lr.addvalue('ports', lrp)
|
||||
for col, value in self.columns.items():
|
||||
setattr(lrp, col, value)
|
||||
self.result = lrp.uuid
|
||||
|
||||
def post_commit(self, txn):
|
||||
real_uuid = txn.get_insert_uuid(self.result)
|
||||
if real_uuid:
|
||||
row = self.api.tables['Logical_Router_Port'].rows[real_uuid]
|
||||
self.result = ovs_idl.RowView(row)
|
||||
|
||||
|
||||
class LrpDelCommand(PortDelCommand):
|
||||
def __init__(self, api, port, router=None, if_exists=False):
|
||||
super(LrpDelCommand, self).__init__(
|
||||
api, 'Logical_Router_Port', port, 'Logical_Router', router,
|
||||
if_exists)
|
||||
|
||||
|
||||
class LrpListCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router):
|
||||
super(LrpListCommand, self).__init__(api)
|
||||
self.router = router
|
||||
|
||||
def run_idl(self, txn):
|
||||
router = self.api.lookup('Logical_Router', self.router)
|
||||
self.result = [ovs_idl.RowView(r) for r in router.ports]
|
||||
|
||||
|
||||
class LrpSetEnabledCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, port, is_enabled):
|
||||
super(LrpSetEnabledCommand, self).__init__(api)
|
||||
self.port = port
|
||||
self.is_enabled = is_enabled
|
||||
|
||||
def run_idl(self, txn):
|
||||
lrp = self.api.lookup('Logical_Router_Port', self.port)
|
||||
lrp.enabled = self.is_enabled
|
||||
|
||||
|
||||
class LrpGetEnabledCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, port):
|
||||
super(LrpGetEnabledCommand, self).__init__(api)
|
||||
self.port = port
|
||||
|
||||
def run_idl(self, txn):
|
||||
lrp = self.api.lookup('Logical_Router_Port', self.port)
|
||||
# enabled is optional, but if not disabled then enabled
|
||||
self.result = next(iter(lrp.enabled), True)
|
||||
|
||||
|
||||
class LrRouteAddCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router, prefix, nexthop, port=None,
|
||||
policy='dst-ip', may_exist=False):
|
||||
prefix = str(netaddr.IPNetwork(prefix))
|
||||
nexthop = str(netaddr.IPAddress(nexthop))
|
||||
super(LrRouteAddCommand, self).__init__(api)
|
||||
self.router = router
|
||||
self.prefix = prefix
|
||||
self.nexthop = nexthop
|
||||
self.port = port
|
||||
self.policy = policy
|
||||
self.may_exist = may_exist
|
||||
|
||||
def run_idl(self, txn):
|
||||
lr = self.api.lookup('Logical_Router', self.router)
|
||||
for route in lr.static_routes:
|
||||
if self.prefix == route.ip_prefix:
|
||||
if not self.may_exist:
|
||||
msg = "Route %s already exists on router %s" % (
|
||||
self.prefix, self.router)
|
||||
raise RuntimeError(msg)
|
||||
route.nexthop = self.nexthop
|
||||
route.policy = self.policy
|
||||
if self.port:
|
||||
route.port = self.port
|
||||
self.result = ovs_idl.RowView(route)
|
||||
return
|
||||
route = txn.insert(self.api.tables['Logical_Router_Static_Route'])
|
||||
route.ip_prefix = self.prefix
|
||||
route.nexthop = self.nexthop
|
||||
route.policy = self.policy
|
||||
if self.port:
|
||||
route.port = self.port
|
||||
lr.addvalue('static_routes', route)
|
||||
self.result = route.uuid
|
||||
|
||||
def post_commit(self, txn):
|
||||
real_uuid = txn.get_insert_uuid(self.result)
|
||||
if real_uuid:
|
||||
table = self.api.tables['Logical_Router_Static_Route']
|
||||
row = table.rows[real_uuid]
|
||||
self.result = ovs_idl.RowView(row)
|
||||
|
||||
|
||||
class LrRouteDelCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router, prefix=None, if_exists=False):
|
||||
if prefix is not None:
|
||||
prefix = str(netaddr.IPNetwork(prefix))
|
||||
super(LrRouteDelCommand, self).__init__(api)
|
||||
self.router = router
|
||||
self.prefix = prefix
|
||||
self.if_exists = if_exists
|
||||
|
||||
def run_idl(self, txn):
|
||||
lr = self.api.lookup('Logical_Router', self.router)
|
||||
if not self.prefix:
|
||||
lr.static_routes = []
|
||||
return
|
||||
for route in lr.static_routes:
|
||||
if self.prefix == route.ip_prefix:
|
||||
lr.delvalue('static_routes', route)
|
||||
# There should only be one possible match
|
||||
return
|
||||
|
||||
if not self.if_exists:
|
||||
msg = "Route for %s in router %s does not exist" % (
|
||||
self.prefix, self.router)
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
class LrRouteListCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router):
|
||||
super(LrRouteListCommand, self).__init__(api)
|
||||
self.router = router
|
||||
|
||||
def run_idl(self, txn):
|
||||
lr = self.api.lookup('Logical_Router', self.router)
|
||||
self.result = [ovs_idl.RowView(r) for r in lr.static_routes]
|
||||
|
||||
|
||||
class LrNatAddCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router, nat_type, external_ip, logical_ip,
|
||||
logical_port=None, external_mac=None, may_exist=False):
|
||||
if nat_type not in const.NAT_TYPES:
|
||||
raise TypeError("nat_type not in %s" % str(const.NAT_TYPES))
|
||||
external_ip = str(netaddr.IPAddress(external_ip))
|
||||
if nat_type == const.NAT_DNAT:
|
||||
logical_ip = str(netaddr.IPAddress(logical_ip))
|
||||
else:
|
||||
net = netaddr.IPNetwork(logical_ip)
|
||||
logical_ip = str(net.ip if net.prefixlen == 32 else net)
|
||||
if (logical_port is None) != (external_mac is None):
|
||||
msg = "logical_port and external_mac must be passed together"
|
||||
raise TypeError(msg)
|
||||
if logical_port and nat_type != const.NAT_BOTH:
|
||||
msg = "logical_port/external_mac only valid for %s" % (
|
||||
const.NAT_BOTH,)
|
||||
raise TypeError(msg)
|
||||
if external_mac:
|
||||
external_mac = str(
|
||||
netaddr.EUI(external_mac, dialect=netaddr.mac_unix_expanded))
|
||||
super(LrNatAddCommand, self).__init__(api)
|
||||
self.router = router
|
||||
self.nat_type = nat_type
|
||||
self.external_ip = external_ip
|
||||
self.logical_ip = logical_ip
|
||||
self.logical_port = logical_port or []
|
||||
self.external_mac = external_mac or []
|
||||
self.may_exist = may_exist
|
||||
|
||||
def run_idl(self, txn):
|
||||
lr = self.api.lookup('Logical_Router', self.router)
|
||||
if self.logical_port:
|
||||
lp = self.api.lookup('Logical_Switch_Port', self.logical_port)
|
||||
for nat in lr.nat:
|
||||
if ((self.nat_type, self.external_ip, self.logical_ip) ==
|
||||
(nat.type, nat.external_ip, nat.logical_ip)):
|
||||
if self.may_exist:
|
||||
nat.logical_port = self.logical_port
|
||||
nat.external_mac = self.external_mac
|
||||
self.result = ovs_idl.RowView(nat)
|
||||
return
|
||||
raise RuntimeError("NAT already exists")
|
||||
nat = txn.insert(self.api.tables['NAT'])
|
||||
nat.type = self.nat_type
|
||||
nat.external_ip = self.external_ip
|
||||
nat.logical_ip = self.logical_ip
|
||||
if self.logical_port:
|
||||
# It seems kind of weird that ovn uses a name string instead of
|
||||
# a ref to a LSP, especially when ovn-nbctl looks the value up by
|
||||
# either name or uuid (and discards the result and store the name).
|
||||
nat.logical_port = lp.name
|
||||
nat.external_mac = self.external_mac
|
||||
lr.addvalue('nat', nat)
|
||||
self.result = nat.uuid
|
||||
|
||||
def post_commit(self, txn):
|
||||
real_uuid = txn.get_insert_uuid(self.result)
|
||||
if real_uuid:
|
||||
row = self.api.tables['NAT'].rows[real_uuid]
|
||||
self.result = ovs_idl.RowView(row)
|
||||
|
||||
|
||||
class LrNatDelCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router, nat_type=None, match_ip=None,
|
||||
if_exists=False):
|
||||
super(LrNatDelCommand, self).__init__(api)
|
||||
self.conditions = []
|
||||
if nat_type:
|
||||
if nat_type not in const.NAT_TYPES:
|
||||
raise TypeError("nat_type not in %s" % str(const.NAT_TYPES))
|
||||
self.conditions += [('type', '=', nat_type)]
|
||||
if match_ip:
|
||||
match_ip = str(netaddr.IPAddress(match_ip))
|
||||
self.col = ('logical_ip' if nat_type == const.NAT_SNAT
|
||||
else 'external_ip')
|
||||
self.conditions += [(self.col, '=', match_ip)]
|
||||
elif match_ip:
|
||||
raise TypeError("must specify nat_type with match_ip")
|
||||
self.router = router
|
||||
self.nat_type = nat_type
|
||||
self.match_ip = match_ip
|
||||
self.if_exists = if_exists
|
||||
|
||||
def run_idl(self, txn):
|
||||
lr = self.api.lookup('Logical_Router', self.router)
|
||||
found = False
|
||||
for nat in [r for r in lr.nat
|
||||
if idlutils.row_match(r, self.conditions)]:
|
||||
found = True
|
||||
lr.delvalue('nat', nat)
|
||||
nat.delete()
|
||||
if self.match_ip:
|
||||
break
|
||||
if self.match_ip and not (found or self.if_exists):
|
||||
raise idlutils.RowNotFound(table='NAT', col=self.col,
|
||||
match=self.match_ip)
|
||||
|
||||
|
||||
class LrNatListCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router):
|
||||
super(LrNatListCommand, self).__init__(api)
|
||||
self.router = router
|
||||
|
||||
def run_idl(self, txn):
|
||||
lr = self.api.lookup('Logical_Router', self.router)
|
||||
self.result = [ovs_idl.RowView(r) for r in lr.nat]
|
||||
|
||||
|
||||
class LbAddCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, lb, vip, ips, protocol=const.PROTO_TCP,
|
||||
may_exist=False, **columns):
|
||||
super(LbAddCommand, self).__init__(api)
|
||||
self.lb = lb
|
||||
self.vip = utils.normalize_ip_port(vip)
|
||||
self.ips = ",".join(utils.normalize_ip(ip) for ip in ips)
|
||||
self.protocol = protocol
|
||||
self.may_exist = may_exist
|
||||
self.columns = columns
|
||||
|
||||
def run_idl(self, txn):
|
||||
try:
|
||||
lb = self.api.lookup('Load_Balancer', self.lb)
|
||||
if lb.vips.get(self.vip):
|
||||
if not self.may_exist:
|
||||
raise RuntimeError("Load Balancer %s exists" % lb.name)
|
||||
# Update load balancer vip
|
||||
lb.setkey('vips', self.vip, self.ips)
|
||||
lb.protocol = self.protocol
|
||||
except idlutils.RowNotFound:
|
||||
# New load balancer
|
||||
lb = txn.insert(self.api.tables['Load_Balancer'])
|
||||
lb.name = self.lb
|
||||
lb.protocol = self.protocol
|
||||
lb.vips = {self.vip: self.ips}
|
||||
for col, val in self.columns.items():
|
||||
setattr(lb, col, val)
|
||||
|
||||
self.result = lb.uuid
|
||||
|
||||
def post_commit(self, txn):
|
||||
real_uuid = txn.get_insert_uuid(self.result) or self.result
|
||||
row = self.api.tables['Load_Balancer'].rows[real_uuid]
|
||||
self.result = ovs_idl.RowView(row)
|
||||
|
||||
|
||||
class LbDelCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, lb, vip=None, if_exists=False):
|
||||
super(LbDelCommand, self).__init__(api)
|
||||
self.lb = lb
|
||||
self.vip = utils.normalize_ip_port(vip) if vip else vip
|
||||
self.if_exists = if_exists
|
||||
|
||||
def run_idl(self, txn):
|
||||
try:
|
||||
lb = self.api.lookup('Load_Balancer', self.lb)
|
||||
if self.vip:
|
||||
if self.vip in lb.vips:
|
||||
if self.if_exists:
|
||||
return
|
||||
lb.delkey('vips', self.vip)
|
||||
else:
|
||||
lb.delete()
|
||||
except idlutils.RowNotFound:
|
||||
if not self.if_exists:
|
||||
raise
|
||||
|
||||
|
||||
class LbListCommand(cmd.BaseCommand):
|
||||
def run_idl(self, txn):
|
||||
self.result = [ovs_idl.RowView(r)
|
||||
for r in self.api.tables['Load_Balancer'].rows.values()]
|
||||
|
||||
|
||||
class LrLbAddCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router, lb, may_exist=False):
|
||||
super(LrLbAddCommand, self).__init__(api)
|
||||
self.router = router
|
||||
self.lb = lb
|
||||
self.may_exist = may_exist
|
||||
|
||||
def run_idl(self, txn):
|
||||
lr = self.api.lookup('Logical_Router', self.router)
|
||||
lb = self.api.lookup('Load_Balancer', self.lb)
|
||||
if lb in lr.load_balancer:
|
||||
if self.may_exist:
|
||||
return
|
||||
raise RuntimeError("LB %s already exist in router %s" % (
|
||||
lb.uuid, lr.uuid))
|
||||
lr.addvalue('load_balancer', lb)
|
||||
|
||||
|
||||
class LrLbDelCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router, lb=None, if_exists=False):
|
||||
super(LrLbDelCommand, self).__init__(api)
|
||||
self.router = router
|
||||
self.lb = lb
|
||||
self.if_exists = if_exists
|
||||
|
||||
def run_idl(self, txn):
|
||||
lr = self.api.lookup('Logical_Router', self.router)
|
||||
if not self.lb:
|
||||
lr.load_balancer = []
|
||||
return
|
||||
try:
|
||||
lb = self.api.lookup('Load_Balancer', self.lb)
|
||||
lr.delvalue('load_balancer', lb)
|
||||
except idlutils.RowNotFound:
|
||||
if self.if_exists:
|
||||
return
|
||||
raise
|
||||
|
||||
|
||||
class LrLbListCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, router):
|
||||
super(LrLbListCommand, self).__init__(api)
|
||||
self.router = router
|
||||
|
||||
def run_idl(self, txn):
|
||||
lr = self.api.lookup('Logical_Router', self.router)
|
||||
self.result = [ovs_idl.RowView(r) for r in lr.load_balancer]
|
||||
|
||||
|
||||
class LsLbAddCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, switch, lb, may_exist=False):
|
||||
super(LsLbAddCommand, self).__init__(api)
|
||||
self.switch = switch
|
||||
self.lb = lb
|
||||
self.may_exist = may_exist
|
||||
|
||||
def run_idl(self, txn):
|
||||
ls = self.api.lookup('Logical_Switch', self.switch)
|
||||
lb = self.api.lookup('Load_Balancer', self.lb)
|
||||
if lb in ls.load_balancer:
|
||||
if self.may_exist:
|
||||
return
|
||||
raise RuntimeError("LB %s alseady exist in switch %s" % (
|
||||
lb.uuid, ls.uuid))
|
||||
ls.addvalue('load_balancer', lb)
|
||||
|
||||
|
||||
class LsLbDelCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, switch, lb=None, if_exists=False):
|
||||
super(LsLbDelCommand, self).__init__(api)
|
||||
self.switch = switch
|
||||
self.lb = lb
|
||||
self.if_exists = if_exists
|
||||
|
||||
def run_idl(self, txn):
|
||||
ls = self.api.lookup('Logical_Switch', self.switch)
|
||||
if not self.lb:
|
||||
ls.load_balancer = []
|
||||
return
|
||||
try:
|
||||
lb = self.api.lookup('Load_Balancer', self.lb)
|
||||
ls.delvalue('load_balancer', lb)
|
||||
except idlutils.RowNotFound:
|
||||
if self.if_exists:
|
||||
return
|
||||
raise
|
||||
|
||||
|
||||
class LsLbListCommand(cmd.BaseCommand):
|
||||
def __init__(self, api, switch):
|
||||
super(LsLbListCommand, self).__init__(api)
|
||||
self.switch = switch
|
||||
|
||||
def run_idl(self, txn):
|
||||
ls = self.api.lookup('Logical_Switch', self.switch)
|
||||
self.result = [ovs_idl.RowView(r) for r in ls.load_balancer]
|
||||
|
@ -15,6 +15,7 @@ import logging
|
||||
from ovsdbapp.backend import ovs_idl
|
||||
from ovsdbapp.backend.ovs_idl import idlutils
|
||||
from ovsdbapp.backend.ovs_idl import transaction
|
||||
from ovsdbapp import constants as const
|
||||
from ovsdbapp import exceptions
|
||||
from ovsdbapp.schema.ovn_northbound import api
|
||||
from ovsdbapp.schema.ovn_northbound import commands as cmd
|
||||
@ -27,6 +28,8 @@ class OvnNbApiIdlImpl(ovs_idl.Backend, api.API):
|
||||
ovsdb_connection = None
|
||||
lookup_table = {
|
||||
'Logical_Switch': idlutils.RowLookup('Logical_Switch', 'name', None),
|
||||
'Logical_Router': idlutils.RowLookup('Logical_Router', 'name', None),
|
||||
'Load_Balancer': idlutils.RowLookup('Load_Balancer', 'name', None),
|
||||
}
|
||||
|
||||
def __init__(self, connection):
|
||||
@ -136,6 +139,85 @@ class OvnNbApiIdlImpl(ovs_idl.Backend, api.API):
|
||||
def lsp_get_dhcpv4_options(self, port):
|
||||
return cmd.LspGetDhcpV4OptionsCommand(self, port)
|
||||
|
||||
def lr_add(self, router=None, may_exist=False, **columns):
|
||||
return cmd.LrAddCommand(self, router, may_exist, **columns)
|
||||
|
||||
def lr_del(self, router, if_exists=False):
|
||||
return cmd.LrDelCommand(self, router, if_exists)
|
||||
|
||||
def lr_list(self):
|
||||
return cmd.LrListCommand(self)
|
||||
|
||||
def lrp_add(self, router, port, mac, networks, peer=None, may_exist=False,
|
||||
**columns):
|
||||
return cmd.LrpAddCommand(self, router, port, mac, networks,
|
||||
peer, may_exist, **columns)
|
||||
|
||||
def lrp_del(self, port, router=None, if_exists=False):
|
||||
return cmd.LrpDelCommand(self, port, router, if_exists)
|
||||
|
||||
def lrp_list(self, router):
|
||||
return cmd.LrpListCommand(self, router)
|
||||
|
||||
def lrp_set_enabled(self, port, is_enabled):
|
||||
return cmd.LrpSetEnabledCommand(self, port, is_enabled)
|
||||
|
||||
def lrp_get_enabled(self, port):
|
||||
return cmd.LrpGetEnabledCommand(self, port)
|
||||
|
||||
def lr_route_add(self, router, prefix, nexthop, port=None,
|
||||
policy='dst-ip', may_exist=False):
|
||||
return cmd.LrRouteAddCommand(self, router, prefix, nexthop, port,
|
||||
policy, may_exist)
|
||||
|
||||
def lr_route_del(self, router, prefix=None, if_exists=False):
|
||||
return cmd.LrRouteDelCommand(self, router, prefix, if_exists)
|
||||
|
||||
def lr_route_list(self, router):
|
||||
return cmd.LrRouteListCommand(self, router)
|
||||
|
||||
def lr_nat_add(self, router, nat_type, external_ip, logical_ip,
|
||||
logical_port=None, external_mac=None, may_exist=False):
|
||||
return cmd.LrNatAddCommand(
|
||||
self, router, nat_type, external_ip, logical_ip, logical_port,
|
||||
external_mac, may_exist)
|
||||
|
||||
def lr_nat_del(self, router, nat_type=None, match_ip=None,
|
||||
if_exists=False):
|
||||
return cmd.LrNatDelCommand(self, router, nat_type, match_ip, if_exists)
|
||||
|
||||
def lr_nat_list(self, router):
|
||||
return cmd.LrNatListCommand(self, router)
|
||||
|
||||
def lb_add(self, lb, vip, ips, protocol=const.PROTO_TCP, may_exist=False,
|
||||
**columns):
|
||||
return cmd.LbAddCommand(self, lb, vip, ips, protocol, may_exist,
|
||||
**columns)
|
||||
|
||||
def lb_del(self, lb, vip=None, if_exists=False):
|
||||
return cmd.LbDelCommand(self, lb, vip, if_exists)
|
||||
|
||||
def lb_list(self):
|
||||
return cmd.LbListCommand(self)
|
||||
|
||||
def lr_lb_add(self, router, lb, may_exist=False):
|
||||
return cmd.LrLbAddCommand(self, router, lb, may_exist)
|
||||
|
||||
def lr_lb_del(self, router, lb=None, if_exists=False):
|
||||
return cmd.LrLbDelCommand(self, router, lb, if_exists)
|
||||
|
||||
def lr_lb_list(self, router):
|
||||
return cmd.LrLbListCommand(self, router)
|
||||
|
||||
def ls_lb_add(self, switch, lb, may_exist=False):
|
||||
return cmd.LsLbAddCommand(self, switch, lb, may_exist)
|
||||
|
||||
def ls_lb_del(self, switch, lb=None, if_exists=False):
|
||||
return cmd.LsLbDelCommand(self, switch, lb, if_exists)
|
||||
|
||||
def ls_lb_list(self, switch):
|
||||
return cmd.LsLbListCommand(self, switch)
|
||||
|
||||
def dhcp_options_add(self, cidr, **external_ids):
|
||||
return cmd.DhcpOptionsAddCommand(self, cidr, **external_ids)
|
||||
|
||||
|
@ -53,3 +53,9 @@ class LogicalRouterFixture(ImplIdlFixture):
|
||||
api = impl_idl.OvnNbApiIdlImpl
|
||||
create = 'lr_add'
|
||||
delete = 'lr_del'
|
||||
|
||||
|
||||
class LoadBalancerFixture(ImplIdlFixture):
|
||||
api = impl_idl.OvnNbApiIdlImpl
|
||||
create = 'lb_add'
|
||||
delete = 'lb_del'
|
||||
|
@ -11,11 +11,15 @@
|
||||
# under the License.
|
||||
|
||||
import netaddr
|
||||
import testscenarios
|
||||
|
||||
from ovsdbapp.backend.ovs_idl import idlutils
|
||||
from ovsdbapp import constants as const
|
||||
from ovsdbapp.schema.ovn_northbound import impl_idl
|
||||
from ovsdbapp.tests.functional import base
|
||||
from ovsdbapp.tests.functional.schema.ovn_northbound import fixtures
|
||||
from ovsdbapp.tests import utils
|
||||
from ovsdbapp import utils as ovsdb_utils
|
||||
|
||||
|
||||
class OvnNorthboundTest(base.FunctionalTestCase):
|
||||
@ -423,3 +427,568 @@ class TestDhcpOptionsOps(OvnNorthboundTest):
|
||||
dhcpopt.uuid, **options).execute(check_error=True)
|
||||
cmd = self.api.dhcp_options_get_options(dhcpopt.uuid)
|
||||
self.assertEqual(options, cmd.execute(check_error=True))
|
||||
|
||||
|
||||
class TestLogicalRouterOps(OvnNorthboundTest):
|
||||
def _lr_add(self, *args, **kwargs):
|
||||
lr = self.useFixture(
|
||||
fixtures.LogicalRouterFixture(*args, **kwargs)).obj
|
||||
self.assertIn(lr.uuid, self.api.tables['Logical_Router'].rows)
|
||||
return lr
|
||||
|
||||
def test_lr_add(self):
|
||||
self._lr_add()
|
||||
|
||||
def test_lr_add_name(self):
|
||||
name = utils.get_rand_device_name()
|
||||
lr = self._lr_add(name)
|
||||
self.assertEqual(name, lr.name)
|
||||
|
||||
def test_lr_add_columns(self):
|
||||
external_ids = {'mykey': 'myvalue', 'yourkey': 'yourvalue'}
|
||||
lr = self._lr_add(external_ids=external_ids)
|
||||
self.assertEqual(external_ids, lr.external_ids)
|
||||
|
||||
def test_lr_del(self):
|
||||
lr = self._lr_add()
|
||||
self.api.lr_del(lr.uuid).execute(check_error=True)
|
||||
self.assertNotIn(lr.uuid,
|
||||
self.api.tables['Logical_Router'].rows.keys())
|
||||
|
||||
def test_lr_del_name(self):
|
||||
lr = self._lr_add(utils.get_rand_device_name())
|
||||
self.api.lr_del(lr.name).execute(check_error=True)
|
||||
self.assertNotIn(lr.uuid,
|
||||
self.api.tables['Logical_Router'].rows.keys())
|
||||
|
||||
def test_lr_list(self):
|
||||
lrs = {self._lr_add() for _ in range(3)}
|
||||
lr_set = set(self.api.lr_list().execute(check_error=True))
|
||||
self.assertTrue(lrs.issubset(lr_set), "%s vs %s" % (lrs, lr_set))
|
||||
|
||||
def _lr_add_route(self, router=None, prefix=None, nexthop=None, port=None,
|
||||
**kwargs):
|
||||
lr = self._lr_add(router or utils.get_rand_device_name(),
|
||||
may_exist=True)
|
||||
prefix = prefix or '192.0.2.0/25'
|
||||
nexthop = nexthop or '192.0.2.254'
|
||||
sr = self.api.lr_route_add(lr.uuid, prefix, nexthop, port,
|
||||
**kwargs).execute(check_error=True)
|
||||
self.assertIn(sr, lr.static_routes)
|
||||
self.assertEqual(prefix, sr.ip_prefix)
|
||||
self.assertEqual(nexthop, sr.nexthop)
|
||||
sr.router = lr
|
||||
return sr
|
||||
|
||||
def test_lr_route_add(self):
|
||||
self._lr_add_route()
|
||||
|
||||
def test_lr_route_add_invalid_prefix(self):
|
||||
self.assertRaises(netaddr.AddrFormatError, self._lr_add_route,
|
||||
prefix='192.168.1.1/40')
|
||||
|
||||
def test_lr_route_add_invalid_nexthop(self):
|
||||
self.assertRaises(netaddr.AddrFormatError, self._lr_add_route,
|
||||
nexthop='256.0.1.3')
|
||||
|
||||
def test_lr_route_add_exist(self):
|
||||
router_name = utils.get_rand_device_name()
|
||||
self._lr_add_route(router_name)
|
||||
self.assertRaises(RuntimeError, self._lr_add_route, router=router_name)
|
||||
|
||||
def test_lr_route_add_may_exist(self):
|
||||
router_name = utils.get_rand_device_name()
|
||||
self._lr_add_route(router_name)
|
||||
self._lr_add_route(router_name, may_exist=True)
|
||||
|
||||
def test_lr_route_del(self):
|
||||
prefix = "192.0.2.0/25"
|
||||
route = self._lr_add_route(prefix=prefix)
|
||||
self.api.lr_route_del(route.router.uuid, prefix).execute(
|
||||
check_error=True)
|
||||
self.assertNotIn(route, route.router.static_routes)
|
||||
|
||||
def test_lr_route_del_all(self):
|
||||
router = self._lr_add()
|
||||
for p in range(3):
|
||||
self._lr_add_route(router.uuid, prefix="192.0.%s.0/24" % p)
|
||||
self.api.lr_route_del(router.uuid).execute(check_error=True)
|
||||
self.assertEqual([], router.static_routes)
|
||||
|
||||
def test_lr_route_del_no_router(self):
|
||||
cmd = self.api.lr_route_del("fake_router", '192.0.2.0/25')
|
||||
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
|
||||
|
||||
def test_lr_route_del_no_exist(self):
|
||||
lr = self._lr_add()
|
||||
cmd = self.api.lr_route_del(lr.uuid, '192.0.2.0/25')
|
||||
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
|
||||
|
||||
def test_lr_route_del_if_exist(self):
|
||||
lr = self._lr_add()
|
||||
self.api.lr_route_del(lr.uuid, '192.0.2.0/25', if_exists=True).execute(
|
||||
check_error=True)
|
||||
|
||||
def test_lr_route_list(self):
|
||||
lr = self._lr_add()
|
||||
routes = {self._lr_add_route(lr.uuid, prefix="192.0.%s.0/25" % p)
|
||||
for p in range(3)}
|
||||
route_set = set(self.api.lr_route_list(lr.uuid).execute(
|
||||
check_error=True))
|
||||
self.assertTrue(routes.issubset(route_set))
|
||||
|
||||
def _lr_nat_add(self, *args, **kwargs):
|
||||
lr = kwargs.pop('router', self._lr_add(utils.get_rand_device_name()))
|
||||
nat = self.api.lr_nat_add(
|
||||
lr.uuid, *args, **kwargs).execute(
|
||||
check_error=True)
|
||||
self.assertIn(nat, lr.nat)
|
||||
nat.router = lr
|
||||
return nat
|
||||
|
||||
def test_lr_nat_add_dnat(self):
|
||||
ext, log = ('10.172.4.1', '192.0.2.1')
|
||||
nat = self._lr_nat_add(const.NAT_DNAT, ext, log)
|
||||
self.assertEqual(ext, nat.external_ip)
|
||||
self.assertEqual(log, nat.logical_ip)
|
||||
|
||||
def test_lr_nat_add_snat(self):
|
||||
ext, log = ('10.172.4.1', '192.0.2.0/24')
|
||||
nat = self._lr_nat_add(const.NAT_SNAT, ext, log)
|
||||
self.assertEqual(ext, nat.external_ip)
|
||||
self.assertEqual(log, nat.logical_ip)
|
||||
|
||||
def test_lr_nat_add_port(self):
|
||||
sw = self.useFixture(
|
||||
fixtures.LogicalSwitchFixture()).obj
|
||||
lsp = self.api.lsp_add(sw.uuid, utils.get_rand_device_name()).execute(
|
||||
check_error=True)
|
||||
lport, mac = (lsp.name, 'de:ad:be:ef:4d:ad')
|
||||
nat = self._lr_nat_add(const.NAT_BOTH, '10.172.4.1', '192.0.2.1',
|
||||
lport, mac)
|
||||
self.assertIn(lport, nat.logical_port) # because optional
|
||||
self.assertIn(mac, nat.external_mac)
|
||||
|
||||
def test_lr_nat_add_port_no_mac(self):
|
||||
# yes, this and other TypeError tests are technically unit tests
|
||||
self.assertRaises(TypeError, self.api.lr_nat_add, 'faker',
|
||||
const.NAT_DNAT, '10.17.4.1', '192.0.2.1', 'fake')
|
||||
|
||||
def test_lr_nat_add_port_wrong_type(self):
|
||||
for nat_type in (const.NAT_DNAT, const.NAT_SNAT):
|
||||
self.assertRaises(
|
||||
TypeError, self.api.lr_nat_add, 'faker', nat_type,
|
||||
'10.17.4.1', '192.0.2.1', 'fake', 'de:ad:be:ef:4d:ad')
|
||||
|
||||
def test_lr_nat_add_exists(self):
|
||||
args = (const.NAT_SNAT, '10.17.4.1', '192.0.2.0/24')
|
||||
nat1 = self._lr_nat_add(*args)
|
||||
cmd = self.api.lr_nat_add(nat1.router.uuid, *args)
|
||||
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
|
||||
|
||||
def test_lr_nat_add_may_exist(self):
|
||||
sw = self.useFixture(
|
||||
fixtures.LogicalSwitchFixture()).obj
|
||||
lsp = self.api.lsp_add(sw.uuid, utils.get_rand_device_name()).execute(
|
||||
check_error=True)
|
||||
args = (const.NAT_BOTH, '10.17.4.1', '192.0.2.1')
|
||||
nat1 = self._lr_nat_add(*args)
|
||||
lp, mac = (lsp.name, 'de:ad:be:ef:4d:ad')
|
||||
nat2 = self.api.lr_nat_add(
|
||||
nat1.router.uuid, *args, logical_port=lp,
|
||||
external_mac=mac, may_exist=True).execute(check_error=True)
|
||||
self.assertEqual(nat1, nat2)
|
||||
self.assertIn(lp, nat2.logical_port) # because optional
|
||||
self.assertIn(mac, nat2.external_mac)
|
||||
|
||||
def test_lr_nat_add_may_exist_remove_port(self):
|
||||
sw = self.useFixture(
|
||||
fixtures.LogicalSwitchFixture()).obj
|
||||
lsp = self.api.lsp_add(sw.uuid, utils.get_rand_device_name()).execute(
|
||||
check_error=True)
|
||||
args = (const.NAT_BOTH, '10.17.4.1', '192.0.2.1')
|
||||
lp, mac = (lsp.name, 'de:ad:be:ef:4d:ad')
|
||||
nat1 = self._lr_nat_add(*args, logical_port=lp, external_mac=mac)
|
||||
nat2 = self.api.lr_nat_add(
|
||||
nat1.router.uuid, *args, may_exist=True).execute(check_error=True)
|
||||
self.assertEqual(nat1, nat2)
|
||||
self.assertEqual([], nat2.logical_port) # because optional
|
||||
self.assertEqual([], nat2.external_mac)
|
||||
|
||||
def _three_nats(self):
|
||||
lr = self._lr_add(utils.get_rand_device_name())
|
||||
for n, nat_type in enumerate((const.NAT_DNAT, const.NAT_SNAT,
|
||||
const.NAT_BOTH)):
|
||||
nat_kwargs = {'router': lr, 'nat_type': nat_type,
|
||||
'logical_ip': '10.17.4.%s' % (n + 1),
|
||||
'external_ip': '192.0.2.%s' % (n + 1)}
|
||||
self._lr_nat_add(**nat_kwargs)
|
||||
return lr
|
||||
|
||||
def _lr_nat_del(self, *args, **kwargs):
|
||||
lr = self._three_nats()
|
||||
self.api.lr_nat_del(lr.name, *args, **kwargs).execute(check_error=True)
|
||||
return lr
|
||||
|
||||
def test_lr_nat_del_all(self):
|
||||
lr = self._lr_nat_del()
|
||||
self.assertEqual([], lr.nat)
|
||||
|
||||
def test_lr_nat_del_type(self):
|
||||
lr = self._lr_nat_del(nat_type=const.NAT_SNAT)
|
||||
types = tuple(nat.type for nat in lr.nat)
|
||||
self.assertNotIn(const.NAT_SNAT, types)
|
||||
self.assertEqual(len(types), len(const.NAT_TYPES) - 1)
|
||||
|
||||
def test_lr_nat_del_specific_dnat(self):
|
||||
lr = self._lr_nat_del(nat_type=const.NAT_DNAT, match_ip='192.0.2.1')
|
||||
self.assertEqual(len(lr.nat), len(const.NAT_TYPES) - 1)
|
||||
for nat in lr.nat:
|
||||
self.assertNotEqual('192.0.2.1', nat.external_ip)
|
||||
self.assertNotEqual(const.NAT_DNAT, nat.type)
|
||||
|
||||
def test_lr_nat_del_specific_snat(self):
|
||||
lr = self._lr_nat_del(nat_type=const.NAT_SNAT, match_ip='10.17.4.2')
|
||||
self.assertEqual(len(lr.nat), len(const.NAT_TYPES) - 1)
|
||||
for nat in lr.nat:
|
||||
self.assertNotEqual('10.17.4.2', nat.external_ip)
|
||||
self.assertNotEqual(const.NAT_SNAT, nat.type)
|
||||
|
||||
def test_lr_nat_del_specific_both(self):
|
||||
lr = self._lr_nat_del(nat_type=const.NAT_BOTH, match_ip='192.0.2.3')
|
||||
self.assertEqual(len(lr.nat), len(const.NAT_TYPES) - 1)
|
||||
for nat in lr.nat:
|
||||
self.assertNotEqual('192.0.2.3', nat.external_ip)
|
||||
self.assertNotEqual(const.NAT_BOTH, nat.type)
|
||||
|
||||
def test_lr_nat_del_specific_not_found(self):
|
||||
self.assertRaises(idlutils.RowNotFound, self._lr_nat_del,
|
||||
nat_type=const.NAT_BOTH, match_ip='10.17.4.2')
|
||||
|
||||
def test_lr_nat_del_specific_if_exists(self):
|
||||
lr = self._lr_nat_del(nat_type=const.NAT_BOTH, match_ip='10.17.4.2',
|
||||
if_exists=True)
|
||||
self.assertEqual(len(lr.nat), len(const.NAT_TYPES))
|
||||
|
||||
def test_lr_nat_list(self):
|
||||
lr = self._three_nats()
|
||||
nats = self.api.lr_nat_list(lr.uuid).execute(check_error=True)
|
||||
self.assertEqual(lr.nat, nats)
|
||||
|
||||
|
||||
class TestLogicalRouterPortOps(OvnNorthboundTest):
|
||||
def setUp(self):
|
||||
super(TestLogicalRouterPortOps, self).setUp()
|
||||
self.lr = self.useFixture(fixtures.LogicalRouterFixture()).obj
|
||||
|
||||
def _lrp_add(self, port, mac='de:ad:be:ef:4d:ad',
|
||||
networks=None, *args, **kwargs):
|
||||
if port is None:
|
||||
port = utils.get_rand_device_name()
|
||||
if networks is None:
|
||||
networks = ['192.0.2.0/24']
|
||||
lrp = self.api.lrp_add(self.lr.uuid, port, mac, networks,
|
||||
*args, **kwargs).execute(check_error=True)
|
||||
self.assertIn(lrp, self.lr.ports)
|
||||
self.assertEqual(mac, lrp.mac)
|
||||
self.assertEqual(set(networks), set(lrp.networks))
|
||||
return lrp
|
||||
|
||||
def test_lrp_add(self):
|
||||
self._lrp_add(None, 'de:ad:be:ef:4d:ad', ['192.0.2.0/24'])
|
||||
|
||||
def test_lpr_add_peer(self):
|
||||
lrp = self._lrp_add(None, 'de:ad:be:ef:4d:ad', ['192.0.2.0/24'],
|
||||
peer='fake_peer')
|
||||
self.assertIn('fake_peer', lrp.peer)
|
||||
|
||||
def test_lpr_add_multiple_networks(self):
|
||||
networks = ['192.0.2.0/24', '192.2.1.0/24']
|
||||
self._lrp_add(None, 'de:ad:be:ef:4d:ad', networks)
|
||||
|
||||
def test_lrp_add_invalid_mac(self):
|
||||
self.assertRaises(
|
||||
netaddr.AddrFormatError,
|
||||
self.api.lrp_add, "fake", "fake", "000:11:22:33:44:55",
|
||||
['192.0.2.0/24'])
|
||||
|
||||
def test_lrp_add_invalid_network(self):
|
||||
self.assertRaises(
|
||||
netaddr.AddrFormatError,
|
||||
self.api.lrp_add, "fake", "fake", "01:02:03:04:05:06",
|
||||
['256.2.0.1/24'])
|
||||
|
||||
def test_lrp_add_exists(self):
|
||||
name = utils.get_rand_device_name()
|
||||
args = (name, 'de:ad:be:ef:4d:ad', ['192.0.2.0/24'])
|
||||
self._lrp_add(*args)
|
||||
self.assertRaises(RuntimeError, self._lrp_add, *args)
|
||||
|
||||
def test_lrp_add_may_exist(self):
|
||||
name = utils.get_rand_device_name()
|
||||
args = (name, 'de:ad:be:ef:4d:ad', ['192.0.2.0/24'])
|
||||
self._lrp_add(*args)
|
||||
self.assertRaises(RuntimeError, self._lrp_add, *args, may_exist=True)
|
||||
|
||||
def test_lrp_add_may_exist_different_router(self):
|
||||
name = utils.get_rand_device_name()
|
||||
args = (name, 'de:ad:be:ef:4d:ad', ['192.0.2.0/24'])
|
||||
lr2 = self.useFixture(fixtures.LogicalRouterFixture()).obj
|
||||
self._lrp_add(*args)
|
||||
cmd = self.api.lrp_add(lr2.uuid, *args, may_exist=True)
|
||||
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
|
||||
|
||||
def test_lrp_add_may_exist_different_mac(self):
|
||||
name = utils.get_rand_device_name()
|
||||
args = {'port': name, 'mac': 'de:ad:be:ef:4d:ad',
|
||||
'networks': ['192.0.2.0/24']}
|
||||
self._lrp_add(**args)
|
||||
args['mac'] = 'da:d4:de:ad:be:ef'
|
||||
self.assertRaises(RuntimeError, self._lrp_add, may_exist=True, **args)
|
||||
|
||||
def test_lrp_add_may_exist_different_networks(self):
|
||||
name = utils.get_rand_device_name()
|
||||
args = (name, 'de:ad:be:ef:4d:ad')
|
||||
self._lrp_add(*args, networks=['192.0.2.0/24'])
|
||||
self.assertRaises(RuntimeError, self._lrp_add, *args,
|
||||
networks=['192.2.1.0/24'], may_exist=True)
|
||||
|
||||
def test_lrp_add_may_exist_different_peer(self):
|
||||
name = utils.get_rand_device_name()
|
||||
args = (name, 'de:ad:be:ef:4d:ad', ['192.0.2.0/24'])
|
||||
self._lrp_add(*args)
|
||||
self.assertRaises(RuntimeError, self._lrp_add, *args,
|
||||
peer='fake', may_exist=True)
|
||||
|
||||
def test_lrp_add_columns(self):
|
||||
options = {'myside': 'yourside'}
|
||||
external_ids = {'myside': 'yourside'}
|
||||
lrp = self._lrp_add(None, options=options, external_ids=external_ids)
|
||||
self.assertEqual(options, lrp.options)
|
||||
self.assertEqual(external_ids, lrp.external_ids)
|
||||
|
||||
def test_lrp_del_uuid(self):
|
||||
lrp = self._lrp_add(None)
|
||||
self.api.lrp_del(lrp.uuid).execute(check_error=True)
|
||||
self.assertNotIn(lrp, self.lr.ports)
|
||||
|
||||
def test_lrp_del_name(self):
|
||||
lrp = self._lrp_add(None)
|
||||
self.api.lrp_del(lrp.name).execute(check_error=True)
|
||||
self.assertNotIn(lrp, self.lr.ports)
|
||||
|
||||
def test_lrp_del_router(self):
|
||||
lrp = self._lrp_add(None)
|
||||
self.api.lrp_del(lrp.uuid, self.lr.uuid).execute(check_error=True)
|
||||
self.assertNotIn(lrp, self.lr.ports)
|
||||
|
||||
def test_lrp_del_router_name(self):
|
||||
lrp = self._lrp_add(None)
|
||||
self.api.lrp_del(lrp.uuid,
|
||||
self.lr.name).execute(check_error=True)
|
||||
self.assertNotIn(lrp, self.lr.ports)
|
||||
|
||||
def test_lrp_del_wrong_router(self):
|
||||
lrp = self._lrp_add(None)
|
||||
sw_id = self.useFixture(fixtures.LogicalSwitchFixture()).obj
|
||||
cmd = self.api.lrp_del(lrp.uuid, sw_id)
|
||||
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
|
||||
|
||||
def test_lrp_del_router_no_exist(self):
|
||||
lrp = self._lrp_add(None)
|
||||
cmd = self.api.lrp_del(lrp.uuid, utils.get_rand_device_name())
|
||||
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
|
||||
|
||||
def test_lrp_del_no_exist(self):
|
||||
cmd = self.api.lrp_del("fake_port")
|
||||
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
|
||||
|
||||
def test_lrp_del_if_exist(self):
|
||||
self.api.lrp_del("fake_port", if_exists=True).execute(check_error=True)
|
||||
|
||||
def test_lrp_list(self):
|
||||
ports = {self._lrp_add(None) for _ in range(3)}
|
||||
port_set = set(self.api.lrp_list(self.lr.uuid).execute(
|
||||
check_error=True))
|
||||
self.assertTrue(ports.issubset(port_set))
|
||||
|
||||
def test_lrp_get_set_enabled(self):
|
||||
lrp = self._lrp_add(None)
|
||||
# default is True
|
||||
self.assertTrue(self.api.lrp_get_enabled(lrp.name).execute(
|
||||
check_error=True))
|
||||
self.api.lrp_set_enabled(lrp.name, False).execute(check_error=True)
|
||||
self.assertFalse(self.api.lrp_get_enabled(lrp.name).execute(
|
||||
check_error=True))
|
||||
self.api.lrp_set_enabled(lrp.name, True).execute(check_error=True)
|
||||
self.assertTrue(self.api.lrp_get_enabled(lrp.name).execute(
|
||||
check_error=True))
|
||||
|
||||
|
||||
class TestLoadBalancerOps(OvnNorthboundTest):
|
||||
|
||||
def _lb_add(self, lb, vip, ips, protocol=const.PROTO_TCP, may_exist=False,
|
||||
**columns):
|
||||
lbal = self.useFixture(fixtures.LoadBalancerFixture(
|
||||
lb, vip, ips, protocol, may_exist, **columns)).obj
|
||||
self.assertEqual(lb, lbal.name)
|
||||
norm_vip = ovsdb_utils.normalize_ip_port(vip)
|
||||
self.assertIn(norm_vip, lbal.vips)
|
||||
self.assertEqual(",".join(ovsdb_utils.normalize_ip(ip) for ip in ips),
|
||||
lbal.vips[norm_vip])
|
||||
self.assertIn(protocol, lbal.protocol) # because optional
|
||||
return lbal
|
||||
|
||||
def test_lb_add(self):
|
||||
vip = '192.0.2.1'
|
||||
ips = ['10.0.0.1', '10.0.0.2', '10.0.0.3']
|
||||
self._lb_add(utils.get_rand_device_name(), vip, ips)
|
||||
|
||||
def test_lb_add_port(self):
|
||||
vip = '192.0.2.1:80'
|
||||
ips = ['10.0.0.1', '10.0.0.2', '10.0.0.3']
|
||||
self._lb_add(utils.get_rand_device_name(), vip, ips)
|
||||
|
||||
def test_lb_add_protocol(self):
|
||||
vip = '192.0.2.1'
|
||||
ips = ['10.0.0.1', '10.0.0.2', '10.0.0.3']
|
||||
self._lb_add(utils.get_rand_device_name(), vip, ips, const.PROTO_UDP)
|
||||
|
||||
def test_lb_add_new_vip(self):
|
||||
name = utils.get_rand_device_name()
|
||||
lb1 = self._lb_add(name, '192.0.2.1', ['10.0.0.1', '10.0.0.2'])
|
||||
lb2 = self._lb_add(name, '192.0.2.2', ['10.1.0.1', '10.1.0.2'])
|
||||
self.assertEqual(lb1, lb2)
|
||||
self.assertEqual(2, len(lb1.vips))
|
||||
|
||||
def test_lb_add_exists(self):
|
||||
name = utils.get_rand_device_name()
|
||||
vip = '192.0.2.1'
|
||||
ips = ['10.0.0.1', '10.0.0.2', '10.0.0.3']
|
||||
self._lb_add(name, vip, ips)
|
||||
cmd = self.api.lb_add(name, vip, ips)
|
||||
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
|
||||
|
||||
def test_lb_add_may_exist(self):
|
||||
name = utils.get_rand_device_name()
|
||||
vip = '192.0.2.1'
|
||||
ips = ['10.0.0.1', '10.0.0.2', '10.0.0.3']
|
||||
lb1 = self._lb_add(name, vip, ips)
|
||||
ips += ['10.0.0.4']
|
||||
lb2 = self.api.lb_add(name, vip, ips, may_exist=True).execute(
|
||||
check_error=True)
|
||||
self.assertEqual(lb1, lb2)
|
||||
self.assertEqual(",".join(ips), lb1.vips[vip])
|
||||
|
||||
def test_lb_add_columns(self):
|
||||
ext_ids = {'one': 'two'}
|
||||
name = utils.get_rand_device_name()
|
||||
lb = self._lb_add(name, '192.0.2.1', ['10.0.0.1', '10.0.0.2'],
|
||||
external_ids=ext_ids)
|
||||
self.assertEqual(ext_ids, lb.external_ids)
|
||||
|
||||
def test_lb_del(self):
|
||||
name = utils.get_rand_device_name()
|
||||
lb = self._lb_add(name, '192.0.2.1', ['10.0.0.1', '10.0.0.2']).uuid
|
||||
self.api.lb_del(lb).execute(check_error=True)
|
||||
self.assertNotIn(lb, self.api.tables['Load_Balancer'].rows)
|
||||
|
||||
def test_lb_del_vip(self):
|
||||
name = utils.get_rand_device_name()
|
||||
lb1 = self._lb_add(name, '192.0.2.1', ['10.0.0.1', '10.0.0.2'])
|
||||
lb2 = self._lb_add(name, '192.0.2.2', ['10.1.0.1', '10.1.0.2'])
|
||||
self.assertEqual(lb1, lb2)
|
||||
self.api.lb_del(lb1.name, '192.0.2.1').execute(check_error=True)
|
||||
self.assertNotIn('192.0.2.1', lb1.vips)
|
||||
self.assertIn('192.0.2.2', lb1.vips)
|
||||
|
||||
def test_lb_del_no_exist(self):
|
||||
cmd = self.api.lb_del(utils.get_rand_device_name())
|
||||
self.assertRaises(idlutils.RowNotFound, cmd.execute, check_error=True)
|
||||
|
||||
def test_lb_del_if_exists(self):
|
||||
self.api.lb_del(utils.get_rand_device_name(), if_exists=True).execute(
|
||||
check_error=True)
|
||||
|
||||
def test_lb_list(self):
|
||||
lbs = {self._lb_add(utils.get_rand_device_name(), '192.0.2.1',
|
||||
['10.0.0.1', '10.0.0.2']) for _ in range(3)}
|
||||
lbset = self.api.lb_list().execute(check_error=True)
|
||||
self.assertTrue(lbs.issubset(lbset))
|
||||
|
||||
|
||||
class TestObLbOps(testscenarios.TestWithScenarios, OvnNorthboundTest):
|
||||
scenarios = [
|
||||
('LrLbOps', dict(fixture=fixtures.LogicalRouterFixture,
|
||||
_add_fn='lr_lb_add', _del_fn='lr_lb_del',
|
||||
_list_fn='lr_lb_list')),
|
||||
('LsLbOps', dict(fixture=fixtures.LogicalSwitchFixture,
|
||||
_add_fn='ls_lb_add', _del_fn='ls_lb_del',
|
||||
_list_fn='ls_lb_list')),
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(TestObLbOps, self).setUp()
|
||||
self.add_fn = getattr(self.api, self._add_fn)
|
||||
self.del_fn = getattr(self.api, self._del_fn)
|
||||
self.list_fn = getattr(self.api, self._list_fn)
|
||||
# They must be in this order because the load balancer
|
||||
# can't be deleted when there is a reference in the router
|
||||
self.lb = self.useFixture(fixtures.LoadBalancerFixture(
|
||||
utils.get_rand_device_name(), '192.0.2.1',
|
||||
['10.0.0.1', '10.0.0.2'])).obj
|
||||
self.lb2 = self.useFixture(fixtures.LoadBalancerFixture(
|
||||
utils.get_rand_device_name(), '192.0.2.2',
|
||||
['10.1.0.1', '10.1.0.2'])).obj
|
||||
self.lr = self.useFixture(self.fixture(
|
||||
utils.get_rand_device_name())).obj
|
||||
|
||||
def test_ob_lb_add(self):
|
||||
self.add_fn(self.lr.name, self.lb.name).execute(
|
||||
check_error=True)
|
||||
self.assertIn(self.lb, self.lr.load_balancer)
|
||||
|
||||
def test_ob_lb_add_exists(self):
|
||||
cmd = self.add_fn(self.lr.name, self.lb.name)
|
||||
cmd.execute(check_error=True)
|
||||
self.assertRaises(RuntimeError, cmd.execute, check_error=True)
|
||||
|
||||
def test_ob_lb_add_may_exist(self):
|
||||
cmd = self.add_fn(self.lr.name, self.lb.name, may_exist=True)
|
||||
lb1 = cmd.execute(check_error=True)
|
||||
lb2 = cmd.execute(check_error=True)
|
||||
self.assertEqual(lb1, lb2)
|
||||
|
||||
def test_ob_lb_del(self):
|
||||
self.add_fn(self.lr.name, self.lb.name).execute(
|
||||
check_error=True)
|
||||
self.assertIn(self.lb, self.lr.load_balancer)
|
||||
self.del_fn(self.lr.name).execute(check_error=True)
|
||||
self.assertEqual(0, len(self.lr.load_balancer))
|
||||
|
||||
def test_ob_lb_del_lb(self):
|
||||
self.add_fn(self.lr.name, self.lb.name).execute(
|
||||
check_error=True)
|
||||
self.add_fn(self.lr.name, self.lb2.name).execute(
|
||||
check_error=True)
|
||||
self.del_fn(self.lr.name, self.lb2.name).execute(
|
||||
check_error=True)
|
||||
self.assertNotIn(self.lb2, self.lr.load_balancer)
|
||||
self.assertIn(self.lb, self.lr.load_balancer)
|
||||
|
||||
def test_ob_lb_del_no_exist(self):
|
||||
cmd = self.del_fn(self.lr.name, 'fake')
|
||||
self.assertRaises(idlutils.RowNotFound, cmd.execute, check_error=True)
|
||||
|
||||
def test_ob_lb_del_if_exists(self):
|
||||
self.del_fn(self.lr.name, 'fake', if_exists=True).execute(
|
||||
check_error=True)
|
||||
|
||||
def test_ob_lb_list(self):
|
||||
self.add_fn(self.lr.name, self.lb.name).execute(
|
||||
check_error=True)
|
||||
self.add_fn(self.lr.name, self.lb2.name).execute(
|
||||
check_error=True)
|
||||
rows = self.list_fn(self.lr.name).execute(check_error=True)
|
||||
self.assertIn(self.lb, rows)
|
||||
self.assertIn(self.lb2, rows)
|
||||
|
53
ovsdbapp/tests/unit/test_utils.py
Normal file
53
ovsdbapp/tests/unit/test_utils.py
Normal file
@ -0,0 +1,53 @@
|
||||
# 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 netaddr
|
||||
|
||||
from ovsdbapp.tests import base
|
||||
from ovsdbapp import utils
|
||||
|
||||
|
||||
class TestUtils(base.TestCase):
|
||||
|
||||
def test_normalize_ip(self):
|
||||
good = [
|
||||
('4.4.4.4', '4.4.4.4'),
|
||||
('10.0.0.0', '10.0.0.0'),
|
||||
('123', '0.0.0.123'),
|
||||
('2001:0db8:85a3:0000:0000:8a2e:0370:7334',
|
||||
'2001:db8:85a3::8a2e:370:7334')
|
||||
]
|
||||
bad = ('256.1.3.2', 'bad', '192.168.1.1:80')
|
||||
for before, after in good:
|
||||
norm = utils.normalize_ip(before)
|
||||
self.assertEqual(after, norm,
|
||||
"%s does not match %s" % (after, norm))
|
||||
for val in bad:
|
||||
self.assertRaises(netaddr.AddrFormatError, utils.normalize_ip, val)
|
||||
|
||||
def test_normalize_ip_port(self):
|
||||
good = [
|
||||
('4.4.4.4:53', '4.4.4.4:53'),
|
||||
('10.0.0.0:7', '10.0.0.0:7'),
|
||||
('123:12', '0.0.0.123:12'),
|
||||
('[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80',
|
||||
'[2001:db8:85a3::8a2e:370:7334]:80')
|
||||
]
|
||||
bad = ('1.2.3.4:0', '1.2.3.4:99000',
|
||||
'2001:0db8:85a3:0000:0000:8a2e:0370:7334:80')
|
||||
for before, after in good:
|
||||
norm = utils.normalize_ip_port(before)
|
||||
self.assertEqual(after, norm,
|
||||
"%s does not match %s" % (after, norm))
|
||||
for val in bad:
|
||||
self.assertRaises(netaddr.AddrFormatError,
|
||||
utils.normalize_ip_port, val)
|
43
ovsdbapp/utils.py
Normal file
43
ovsdbapp/utils.py
Normal file
@ -0,0 +1,43 @@
|
||||
# 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 netaddr
|
||||
|
||||
# NOTE(twilson) Clearly these are silly, but they are good enough for now
|
||||
# I'm happy for someone to replace them with better parsing
|
||||
|
||||
|
||||
def normalize_ip(ip):
|
||||
return str(netaddr.IPAddress(ip))
|
||||
|
||||
|
||||
def normalize_ip_port(ipport):
|
||||
try:
|
||||
return normalize_ip(ipport)
|
||||
except netaddr.AddrFormatError:
|
||||
# maybe we have a port
|
||||
if ipport[0] == '[':
|
||||
# Should be an IPv6 w/ port
|
||||
try:
|
||||
ip, port = ipport[1:].split(']:')
|
||||
except ValueError:
|
||||
raise netaddr.AddrFormatError("Invalid Port")
|
||||
ip = "[%s]" % normalize_ip(ip)
|
||||
else:
|
||||
try:
|
||||
ip, port = ipport.split(':')
|
||||
except ValueError:
|
||||
raise netaddr.AddrFormatError("Invalid Port")
|
||||
ip = normalize_ip(ip)
|
||||
if int(port) <= 0 or int(port) > 65535:
|
||||
raise netaddr.AddrFormatError("Invalid port")
|
||||
return "%s:%s" % (ip, port)
|
Loading…
Reference in New Issue
Block a user