zebra: Implement database for Zebra protocol service
Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
parent
dc57ff5b47
commit
1b486a0634
@ -78,6 +78,7 @@ DEFAULT_ZSERV_PORT = 2600
|
|||||||
DEFAULT_ZSERV_VERSION = 2 # Version of Ubuntu 16.04 LTS packaged Quagga
|
DEFAULT_ZSERV_VERSION = 2 # Version of Ubuntu 16.04 LTS packaged Quagga
|
||||||
DEFAULT_ZSERV_CLIENT_ROUTE_TYPE = 'BGP'
|
DEFAULT_ZSERV_CLIENT_ROUTE_TYPE = 'BGP'
|
||||||
DEFAULT_ZSERV_INTERVAL = 10
|
DEFAULT_ZSERV_INTERVAL = 10
|
||||||
|
DEFAULT_ZSERV_DATABASE = 'sqlite:///zebra.db'
|
||||||
|
|
||||||
CONF.register_cli_opts([
|
CONF.register_cli_opts([
|
||||||
cfg.StrOpt(
|
cfg.StrOpt(
|
||||||
@ -101,4 +102,8 @@ CONF.register_cli_opts([
|
|||||||
'retry-interval', default=DEFAULT_ZSERV_INTERVAL,
|
'retry-interval', default=DEFAULT_ZSERV_INTERVAL,
|
||||||
help='Retry interval connecting to Zebra server '
|
help='Retry interval connecting to Zebra server '
|
||||||
'(default: %s)' % DEFAULT_ZSERV_INTERVAL),
|
'(default: %s)' % DEFAULT_ZSERV_INTERVAL),
|
||||||
|
cfg.StrOpt(
|
||||||
|
'db-url', default=DEFAULT_ZSERV_DATABASE,
|
||||||
|
help='URL to database used by Zebra protocol service '
|
||||||
|
'(default: %s)' % DEFAULT_ZSERV_DATABASE),
|
||||||
], group='zapi')
|
], group='zapi')
|
||||||
|
42
ryu/services/protocols/zebra/db/__init__.py
Normal file
42
ryu/services/protocols/zebra/db/__init__.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Copyright (C) 2017 Nippon Telegraph and Telephone Corporation.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Database implementation for Zebra protocol service.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
from ryu import cfg
|
||||||
|
|
||||||
|
# Configuration parameters for Zebra service
|
||||||
|
CONF = cfg.CONF['zapi']
|
||||||
|
|
||||||
|
# Connect to database
|
||||||
|
ENGINE = create_engine(CONF.db_url)
|
||||||
|
|
||||||
|
Session = sessionmaker(bind=ENGINE)
|
||||||
|
"""
|
||||||
|
Session class connecting to database
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Create all tables
|
||||||
|
from . import base
|
||||||
|
from . import interface
|
||||||
|
from . import route
|
||||||
|
base.Base.metadata.create_all(ENGINE)
|
70
ryu/services/protocols/zebra/db/base.py
Normal file
70
ryu/services/protocols/zebra/db/base.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# Copyright (C) 2017 Nippon Telegraph and Telephone Corporation.
|
||||||
|
#
|
||||||
|
# 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 __future__ import absolute_import
|
||||||
|
|
||||||
|
import functools
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
"""
|
||||||
|
Base class for Zebra protocol database tables.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _repr(self):
|
||||||
|
m = ', '.join(
|
||||||
|
['%s=%r' % (k, v)
|
||||||
|
for k, v in self.__dict__.items() if not k.startswith('_')])
|
||||||
|
return "%s(%s)" % (self.__class__.__name__, m)
|
||||||
|
|
||||||
|
Base.__repr__ = _repr
|
||||||
|
|
||||||
|
|
||||||
|
def sql_function(func):
|
||||||
|
"""
|
||||||
|
Decorator for wrapping the given function in order to manipulate (CRUD)
|
||||||
|
the records safely.
|
||||||
|
|
||||||
|
For the adding/updating/deleting records function, this decorator
|
||||||
|
invokes "Session.commit()" after the given function.
|
||||||
|
If any exception while modifying records raised, this decorator invokes
|
||||||
|
"Session.rollbacks()".
|
||||||
|
"""
|
||||||
|
@functools.wraps(func)
|
||||||
|
def _wrapper(session, *args, **kwargs):
|
||||||
|
ret = None
|
||||||
|
try:
|
||||||
|
ret = func(session, *args, **kwargs)
|
||||||
|
if session.dirty:
|
||||||
|
# If the given function has any update to records,
|
||||||
|
# commits them.
|
||||||
|
session.commit()
|
||||||
|
except Exception as e:
|
||||||
|
# If any exception raised, rollbacks the transaction.
|
||||||
|
LOG.error('Error in %s: %s', func.__name__, e)
|
||||||
|
if session.dirty:
|
||||||
|
LOG.error('Do rolling back %s table',
|
||||||
|
session.dirty[0].__tablename__)
|
||||||
|
session.rollback()
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
return _wrapper
|
271
ryu/services/protocols/zebra/db/interface.py
Normal file
271
ryu/services/protocols/zebra/db/interface.py
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
# Copyright (C) 2017 Nippon Telegraph and Telephone Corporation.
|
||||||
|
#
|
||||||
|
# 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 __future__ import absolute_import
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sqlalchemy import Column
|
||||||
|
from sqlalchemy import Integer
|
||||||
|
from sqlalchemy import String
|
||||||
|
|
||||||
|
from ryu.lib import netdevice
|
||||||
|
from ryu.lib import ip
|
||||||
|
from ryu.lib.packet import zebra
|
||||||
|
|
||||||
|
from . import base
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Default value for ethernet interface
|
||||||
|
DEFAULT_ETH_FLAGS = (
|
||||||
|
netdevice.IFF_UP
|
||||||
|
| netdevice.IFF_BROADCAST
|
||||||
|
| netdevice.IFF_RUNNING
|
||||||
|
| netdevice.IFF_MULTICAST)
|
||||||
|
DEFAULT_ETH_MTU = 1500
|
||||||
|
|
||||||
|
|
||||||
|
class Interface(base.Base):
|
||||||
|
"""
|
||||||
|
Interface table for Zebra protocol service.
|
||||||
|
|
||||||
|
The default value for each fields suppose "Loopback" interface.
|
||||||
|
|
||||||
|
``ifindex``: Number of index.
|
||||||
|
|
||||||
|
``ifname``: Name of this interface.
|
||||||
|
|
||||||
|
``status``: A combination of flags
|
||||||
|
"ryu.lib.packet.zebra.ZEBRA_INTERFACE_*".
|
||||||
|
The default value shows "active" and "link-detect".
|
||||||
|
|
||||||
|
``flags``: A combination of flags "ryu.lib.netdevice.IFF_*".
|
||||||
|
The default value show "up", "loopback" and "running".
|
||||||
|
|
||||||
|
``metric``: Metric of this interface.
|
||||||
|
|
||||||
|
``ifmtu``: IPv4 MTU of this interface.
|
||||||
|
|
||||||
|
``ifmtu6``: IPv6 MTU of this interface.
|
||||||
|
|
||||||
|
``bandwidth``: Bandwidth of this interface.
|
||||||
|
|
||||||
|
``ll_type``: Link Layer Type.
|
||||||
|
One of "ryu.lib.packet.zebra.ZEBRA_LLT_*" types.
|
||||||
|
|
||||||
|
``hw_addr``: Hardware address of this interface (mostly, MAC address).
|
||||||
|
|
||||||
|
``inet``: List of IPv4 addresses separated by a comma.
|
||||||
|
(e.g., "192.168.1.100/24,192.168.2.100/24)".
|
||||||
|
|
||||||
|
``inet6``: List of IPv6 addresses separated by a comma.
|
||||||
|
"""
|
||||||
|
__tablename__ = 'interface'
|
||||||
|
|
||||||
|
ifindex = Column(Integer, primary_key=True)
|
||||||
|
ifname = Column(String, default="lo")
|
||||||
|
status = Column(
|
||||||
|
Integer,
|
||||||
|
default=(
|
||||||
|
zebra.ZEBRA_INTERFACE_ACTIVE
|
||||||
|
| zebra.ZEBRA_INTERFACE_LINKDETECTION))
|
||||||
|
flags = Column(
|
||||||
|
Integer,
|
||||||
|
default=(
|
||||||
|
netdevice.IFF_UP
|
||||||
|
| netdevice.IFF_LOOPBACK
|
||||||
|
| netdevice.IFF_RUNNING))
|
||||||
|
metric = Column(Integer, default=1)
|
||||||
|
ifmtu = Column(Integer, default=0x10000)
|
||||||
|
ifmtu6 = Column(Integer, default=0x10000)
|
||||||
|
bandwidth = Column(Integer, default=0)
|
||||||
|
ll_type = Column(Integer, default=zebra.ZEBRA_LLT_ETHER)
|
||||||
|
hw_addr = Column(String, default='00:00:00:00:00:00')
|
||||||
|
# Note: Only the PostgreSQL backend has support sqlalchemy.ARRAY,
|
||||||
|
# we use the comma separated string as array instead.
|
||||||
|
inet = Column(String, default='')
|
||||||
|
inet6 = Column(String, default='')
|
||||||
|
|
||||||
|
|
||||||
|
@base.sql_function
|
||||||
|
def ip_link_show(session, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns a first interface record matching the given filtering rules.
|
||||||
|
|
||||||
|
The arguments for "kwargs" is the same with Interface class.
|
||||||
|
|
||||||
|
:param session: Session instance connecting to database.
|
||||||
|
:param kwargs: Filtering rules to query.
|
||||||
|
:return: An instance of Interface record.
|
||||||
|
"""
|
||||||
|
return session.query(Interface).filter_by(**kwargs).first()
|
||||||
|
|
||||||
|
|
||||||
|
@base.sql_function
|
||||||
|
def ip_link_show_all(session, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns all interface records matching the given filtering rules.
|
||||||
|
|
||||||
|
The arguments for "kwargs" is the same with Interface class.
|
||||||
|
|
||||||
|
:param session: Session instance connecting to database.
|
||||||
|
:param kwargs: Filtering rules to query.
|
||||||
|
:return: A list of Interface records.
|
||||||
|
"""
|
||||||
|
return session.query(Interface).filter_by(**kwargs).all()
|
||||||
|
|
||||||
|
|
||||||
|
@base.sql_function
|
||||||
|
def ip_link_add(session, name, type_='loopback', lladdr='00:00:00:00:00:00'):
|
||||||
|
"""
|
||||||
|
Adds an interface record into Zebra protocol service database.
|
||||||
|
|
||||||
|
The arguments are similar to "ip link add" command of iproute2.
|
||||||
|
|
||||||
|
:param session: Session instance connecting to database.
|
||||||
|
:param name: Name of interface.
|
||||||
|
:param type_: Type of interface. 'loopback' or 'ethernet'.
|
||||||
|
:param lladdr: Link layer address. Mostly MAC address.
|
||||||
|
:return: Instance of added record or already existing record.
|
||||||
|
"""
|
||||||
|
intf = ip_link_show(session, ifname=name)
|
||||||
|
if intf:
|
||||||
|
LOG.debug('Interface "%s" already exists: %s', intf.ifname, intf)
|
||||||
|
return intf
|
||||||
|
|
||||||
|
if type_ == 'ethernet':
|
||||||
|
intf = Interface(
|
||||||
|
ifname=name,
|
||||||
|
flags=DEFAULT_ETH_FLAGS,
|
||||||
|
ifmtu=DEFAULT_ETH_MTU,
|
||||||
|
ifmtu6=DEFAULT_ETH_MTU,
|
||||||
|
hw_addr=lladdr)
|
||||||
|
else: # type_ == 'loopback':
|
||||||
|
intf = Interface(
|
||||||
|
ifname=name,
|
||||||
|
inet='127.0.0.1/8',
|
||||||
|
inet6='::1/128')
|
||||||
|
|
||||||
|
session.add(intf)
|
||||||
|
|
||||||
|
return intf
|
||||||
|
|
||||||
|
|
||||||
|
@base.sql_function
|
||||||
|
def ip_link_delete(session, name):
|
||||||
|
"""
|
||||||
|
Deletes an interface record from Zebra protocol service database.
|
||||||
|
|
||||||
|
The arguments are similar to "ip link delete" command of iproute2.
|
||||||
|
|
||||||
|
:param session: Session instance connecting to database.
|
||||||
|
:param name: Name of interface.
|
||||||
|
:return: Name of interface which was deleted. None if failed.
|
||||||
|
"""
|
||||||
|
intf = ip_link_show(session, ifname=name)
|
||||||
|
if not intf:
|
||||||
|
LOG.debug('Interface "%s" does not exist', name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
session.delete(intf)
|
||||||
|
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
# Currently, functions corresponding to "ip link show" and "ip address show"
|
||||||
|
# have the same implementation.
|
||||||
|
ip_address_show = ip_link_show
|
||||||
|
ip_address_show_all = ip_link_show_all
|
||||||
|
|
||||||
|
|
||||||
|
@base.sql_function
|
||||||
|
def ip_address_add(session, ifname, ifaddr):
|
||||||
|
"""
|
||||||
|
Adds an IP address to interface record identified with the given "ifname".
|
||||||
|
|
||||||
|
The arguments are similar to "ip address add" command of iproute2.
|
||||||
|
|
||||||
|
:param session: Session instance connecting to database.
|
||||||
|
:param ifname: Name of interface.
|
||||||
|
:param ifaddr: IPv4 or IPv6 address.
|
||||||
|
:return: Instance of record or "None" if failed.
|
||||||
|
"""
|
||||||
|
def _append_inet_addr(intf_inet, addr):
|
||||||
|
addr_list = intf_inet.split(',')
|
||||||
|
if addr in addr_list:
|
||||||
|
LOG.debug(
|
||||||
|
'Interface "%s" has already "ifaddr": %s',
|
||||||
|
intf.ifname, addr)
|
||||||
|
return intf_inet
|
||||||
|
else:
|
||||||
|
addr_list.append(addr)
|
||||||
|
return ','.join(addr_list)
|
||||||
|
|
||||||
|
intf = ip_link_show(session, ifname=ifname)
|
||||||
|
if not intf:
|
||||||
|
LOG.debug('Interface "%s" does not exist', ifname)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if ip.valid_ipv4(ifaddr):
|
||||||
|
intf.inet = _append_inet_addr(intf.inet, ifaddr)
|
||||||
|
elif ip.valid_ipv6(ifaddr):
|
||||||
|
intf.inet6 = _append_inet_addr(intf.inet6, ifaddr)
|
||||||
|
else:
|
||||||
|
LOG.debug('Invalid IP address for "ifaddr": %s', ifaddr)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return intf
|
||||||
|
|
||||||
|
|
||||||
|
@base.sql_function
|
||||||
|
def ip_address_delete(session, ifname, ifaddr):
|
||||||
|
"""
|
||||||
|
Deletes an IP address from interface record identified with the given
|
||||||
|
"ifname".
|
||||||
|
|
||||||
|
The arguments are similar to "ip address delete" command of iproute2.
|
||||||
|
|
||||||
|
:param session: Session instance connecting to database.
|
||||||
|
:param ifname: Name of interface.
|
||||||
|
:param ifaddr: IPv4 or IPv6 address.
|
||||||
|
:return: Instance of record or "None" if failed.
|
||||||
|
"""
|
||||||
|
def _remove_inet_addr(intf_inet, addr):
|
||||||
|
addr_list = intf_inet.split(',')
|
||||||
|
if addr not in addr_list:
|
||||||
|
LOG.debug(
|
||||||
|
'Interface "%s" does not have "ifaddr": %s',
|
||||||
|
intf.ifname, addr)
|
||||||
|
return intf_inet
|
||||||
|
else:
|
||||||
|
addr_list.remove(addr)
|
||||||
|
return ','.join(addr_list)
|
||||||
|
|
||||||
|
intf = ip_link_show(session, ifname=ifname)
|
||||||
|
if not intf:
|
||||||
|
LOG.debug('Interface "%s" does not exist', ifname)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if ip.valid_ipv4(ifaddr):
|
||||||
|
intf.inet = _remove_inet_addr(intf.inet, ifaddr)
|
||||||
|
elif ip.valid_ipv6(ifaddr):
|
||||||
|
intf.inet6 = _remove_inet_addr(intf.inet6, ifaddr)
|
||||||
|
else:
|
||||||
|
LOG.debug('Invalid IP address for "ifaddr": %s', ifaddr)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return intf
|
201
ryu/services/protocols/zebra/db/route.py
Normal file
201
ryu/services/protocols/zebra/db/route.py
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
# Copyright (C) 2017 Nippon Telegraph and Telephone Corporation.
|
||||||
|
#
|
||||||
|
# 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 __future__ import absolute_import
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import socket
|
||||||
|
|
||||||
|
import netaddr
|
||||||
|
from sqlalchemy import Column
|
||||||
|
from sqlalchemy import Boolean
|
||||||
|
from sqlalchemy import Integer
|
||||||
|
from sqlalchemy import String
|
||||||
|
|
||||||
|
from ryu.lib.packet import safi as packet_safi
|
||||||
|
from ryu.lib.packet import zebra
|
||||||
|
|
||||||
|
from . import base
|
||||||
|
from . import interface
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Route(base.Base):
|
||||||
|
"""
|
||||||
|
Route table (like routing table) for Zebra protocol service.
|
||||||
|
|
||||||
|
``id``: (Primary Key) ID of this route.
|
||||||
|
|
||||||
|
``family``: Address Family, not AFI (Address Family Identifiers).
|
||||||
|
Mostly, "socket.AF_INET" or "socket.AF_INET6".
|
||||||
|
|
||||||
|
``safi``: Subsequent Address Family Identifiers.
|
||||||
|
|
||||||
|
``destination``: Destination prefix of this route.
|
||||||
|
|
||||||
|
``gateway``: Next hop address of this route.
|
||||||
|
The default is "" (empty string).
|
||||||
|
|
||||||
|
``ifindex``: Index of interface to forward packets.
|
||||||
|
|
||||||
|
``source``: Source IP address of this route, which should be an
|
||||||
|
address assigned to the local interface.
|
||||||
|
|
||||||
|
``route_type``: Route Type of this route.
|
||||||
|
This type shows which daemon (or kernel) generated this route.
|
||||||
|
|
||||||
|
``is_selected``: Whether this route is selected for "destination".
|
||||||
|
"""
|
||||||
|
__tablename__ = 'route'
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
family = Column(Integer, default=socket.AF_INET)
|
||||||
|
safi = Column(Integer, default=packet_safi.UNICAST)
|
||||||
|
destination = Column(String, default='0.0.0.0/0')
|
||||||
|
gateway = Column(String, default='')
|
||||||
|
ifindex = Column(Integer, default=0)
|
||||||
|
source = Column(String, default='')
|
||||||
|
route_type = Column(Integer, default=zebra.ZEBRA_ROUTE_KERNEL)
|
||||||
|
is_selected = Column(Boolean, default=False)
|
||||||
|
|
||||||
|
|
||||||
|
@base.sql_function
|
||||||
|
def ip_route_show(session, destination, device, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns a selected route record matching the given filtering rules.
|
||||||
|
|
||||||
|
The arguments are similar to "ip route showdump" command of iproute2.
|
||||||
|
|
||||||
|
:param session: Session instance connecting to database.
|
||||||
|
:param destination: Destination prefix.
|
||||||
|
:param device: Source device.
|
||||||
|
:param kwargs: Filtering rules to query.
|
||||||
|
:return: Instance of route record or "None" if failed.
|
||||||
|
"""
|
||||||
|
intf = interface.ip_link_show(session, ifname=device)
|
||||||
|
if not intf:
|
||||||
|
LOG.debug('Interface "%s" does not exist', device)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return session.query(Route).filter_by(
|
||||||
|
destination=destination, ifindex=intf.ifindex, **kwargs).first()
|
||||||
|
|
||||||
|
|
||||||
|
@base.sql_function
|
||||||
|
def ip_route_show_all(session, **kwargs):
|
||||||
|
"""
|
||||||
|
Returns a selected route record matching the given filtering rules.
|
||||||
|
|
||||||
|
The arguments are similar to "ip route showdump" command of iproute2.
|
||||||
|
|
||||||
|
If "is_selected=True", disables the existing selected route for the
|
||||||
|
given destination.
|
||||||
|
|
||||||
|
:param session: Session instance connecting to database.
|
||||||
|
:param kwargs: Filtering rules to query.
|
||||||
|
:return: A list of route records.
|
||||||
|
"""
|
||||||
|
return session.query(Route).filter_by(**kwargs).all()
|
||||||
|
|
||||||
|
|
||||||
|
@base.sql_function
|
||||||
|
def ip_route_add(session, destination, device=None, gateway='', source='',
|
||||||
|
ifindex=0, route_type=zebra.ZEBRA_ROUTE_KERNEL,
|
||||||
|
is_selected=True):
|
||||||
|
"""
|
||||||
|
Adds a route record into Zebra protocol service database.
|
||||||
|
|
||||||
|
The arguments are similar to "ip route add" command of iproute2.
|
||||||
|
|
||||||
|
If "is_selected=True", disables the existing selected route for the
|
||||||
|
given destination.
|
||||||
|
|
||||||
|
:param session: Session instance connecting to database.
|
||||||
|
:param destination: Destination prefix.
|
||||||
|
:param device: Source device.
|
||||||
|
:param gateway: Gateway IP address.
|
||||||
|
:param source: Source IP address.
|
||||||
|
:param ifindex: Index of source device.
|
||||||
|
:param route_type: Route type of daemon (or kernel).
|
||||||
|
:param is_selected: If select the given route as "in use" or not.
|
||||||
|
:return: Instance of record or "None" if failed.
|
||||||
|
"""
|
||||||
|
if device:
|
||||||
|
intf = interface.ip_link_show(session, ifname=device)
|
||||||
|
if not intf:
|
||||||
|
LOG.debug('Interface "%s" does not exist', device)
|
||||||
|
return None
|
||||||
|
ifindex = ifindex or intf.ifindex
|
||||||
|
|
||||||
|
route = ip_route_show(session, destination=destination, device=device)
|
||||||
|
if route:
|
||||||
|
LOG.debug(
|
||||||
|
'Route to "%s" already exists on "%s" device',
|
||||||
|
destination, device)
|
||||||
|
return route
|
||||||
|
|
||||||
|
dest_addr, dest_prefix_num = destination.split('/')
|
||||||
|
dest_prefix_num = int(dest_prefix_num)
|
||||||
|
if netaddr.valid_ipv4(dest_addr) and 0 <= dest_prefix_num <= 32:
|
||||||
|
family = socket.AF_INET
|
||||||
|
elif netaddr.valid_ipv6(dest_addr) and 0 <= dest_prefix_num <= 128:
|
||||||
|
family = socket.AF_INET6
|
||||||
|
else:
|
||||||
|
LOG.debug('Invalid IP address for "prefix": %s', destination)
|
||||||
|
return None
|
||||||
|
safi = packet_safi.UNICAST
|
||||||
|
|
||||||
|
if is_selected:
|
||||||
|
old_routes = ip_route_show_all(
|
||||||
|
session, destination=destination, is_selected=True)
|
||||||
|
for old_route in old_routes:
|
||||||
|
if old_route:
|
||||||
|
LOG.debug('Set existing route to unselected: %s', old_route)
|
||||||
|
old_route.is_selected = False
|
||||||
|
|
||||||
|
new_route = Route(
|
||||||
|
family=family,
|
||||||
|
safi=safi,
|
||||||
|
destination=destination,
|
||||||
|
gateway=gateway,
|
||||||
|
ifindex=ifindex,
|
||||||
|
source=source,
|
||||||
|
route_type=route_type,
|
||||||
|
is_selected=is_selected)
|
||||||
|
|
||||||
|
session.add(new_route)
|
||||||
|
|
||||||
|
return new_route
|
||||||
|
|
||||||
|
|
||||||
|
@base.sql_function
|
||||||
|
def ip_route_delete(session, destination, **kwargs):
|
||||||
|
"""
|
||||||
|
Deletes route record(s) from Zebra protocol service database.
|
||||||
|
|
||||||
|
The arguments are similar to "ip route delete" command of iproute2.
|
||||||
|
|
||||||
|
:param session: Session instance connecting to database.
|
||||||
|
:param destination: Destination prefix.
|
||||||
|
:param kwargs: Filtering rules to query.
|
||||||
|
:return: Records which are deleted.
|
||||||
|
"""
|
||||||
|
routes = ip_route_show_all(session, destination=destination, **kwargs)
|
||||||
|
for route in routes:
|
||||||
|
session.delete(route)
|
||||||
|
|
||||||
|
return routes
|
Loading…
Reference in New Issue
Block a user