Source code for octavia.amphorae.backends.utils.interface_file

# Copyright 2020 Red Hat, Inc. All rights reserved.
#
# 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 ipaddress
import os
import stat

from oslo_config import cfg
import simplejson

from octavia.common import constants as consts

CONF = cfg.CONF


[docs] class InterfaceFile: def __init__(self, name, if_type, mtu=None, addresses=None, routes=None, rules=None, scripts=None, is_sriov=False): self.name = name self.if_type = if_type self.mtu = mtu self.addresses = addresses or [] self.routes = routes or [] self.rules = rules or [] self.scripts = scripts or { consts.IFACE_UP: [], consts.IFACE_DOWN: [] } self.is_sriov = is_sriov
[docs] @classmethod def get_extensions(cls): return [".json"]
[docs] @classmethod def load(cls, fp): return simplejson.load(fp)
[docs] @classmethod def dump(cls, obj): return simplejson.dumps(obj)
[docs] @classmethod def from_file(cls, filename): with open(filename, encoding='utf-8') as fp: config = cls.load(fp) return InterfaceFile(**config)
[docs] @classmethod def get_directory(cls): return (CONF.amphora_agent.agent_server_network_dir or consts.AMP_NET_DIR_TEMPLATE)
[docs] @classmethod def get_host_routes(cls, routes, **kwargs): host_routes = [] if routes: for hr in routes: route = { consts.DST: hr['destination'], consts.GATEWAY: hr['nexthop'], consts.FLAGS: [consts.ONLINK] } route.update(kwargs) host_routes.append(route) return host_routes
[docs] def write(self): mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC net_dir = self.get_directory() try: os.makedirs(net_dir) except OSError: pass interface_file = f"{self.name}.json" with os.fdopen(os.open(os.path.join(net_dir, interface_file), flags, mode), 'w') as fp: interface = { consts.NAME: self.name, consts.IF_TYPE: self.if_type, consts.ADDRESSES: self.addresses, consts.ROUTES: self.routes, consts.RULES: self.rules, consts.SCRIPTS: self.scripts, consts.IS_SRIOV: self.is_sriov } if self.mtu: interface[consts.MTU] = self.mtu fp.write(self.dump(interface))
[docs] class VIPInterfaceFile(InterfaceFile): def __init__(self, name, mtu, vips, vrrp_info, fixed_ips, topology, is_sriov=False): super().__init__(name, if_type=consts.VIP, mtu=mtu, is_sriov=is_sriov) has_ipv4 = any(vip['ip_version'] == 4 for vip in vips) has_ipv6 = any(vip['ip_version'] == 6 for vip in vips) if vrrp_info: self.addresses.append({ consts.ADDRESS: vrrp_info['ip'], consts.PREFIXLEN: vrrp_info['prefixlen'] }) else: if has_ipv4: self.addresses.append({ consts.DHCP: True }) if has_ipv6: self.addresses.append({ consts.IPV6AUTO: True }) ip_versions = set() for vip in vips: gateway = vip.get('gateway') ip_version = vip['ip_version'] ip_versions.add(ip_version) if gateway: # Add default routes if there's a gateway self.routes.append({ consts.DST: ( "::/0" if ip_version == 6 else "0.0.0.0/0"), consts.GATEWAY: gateway, consts.FLAGS: [consts.ONLINK] }) if topology != consts.TOPOLOGY_ACTIVE_STANDBY: self.routes.append({ consts.DST: ( "::/0" if ip_version == 6 else "0.0.0.0/0"), consts.GATEWAY: gateway, consts.FLAGS: [consts.ONLINK], consts.TABLE: 1, }) # In ACTIVE_STANDBY topology, keepalived configures the VIP # address. Keep track of it in the interface file but mark it with # a special flag so the amphora-interface would not add/delete # keepalived-maintained things. self.addresses.append({ consts.ADDRESS: vip['ip_address'], consts.PREFIXLEN: 128 if ip_version == 6 else 32, # OCTAVIA_OWNED = False when this address is managed by another # tool (keepalived) consts.OCTAVIA_OWNED: ( topology != consts.TOPOLOGY_ACTIVE_STANDBY) }) vip_cidr = ipaddress.ip_network( "{}/{}".format(vip['ip_address'], vip['prefixlen']), strict=False) self.routes.append({ consts.DST: vip_cidr.exploded, consts.SCOPE: 'link' }) if topology != consts.TOPOLOGY_ACTIVE_STANDBY: self.routes.append({ consts.DST: vip_cidr.exploded, consts.PREFSRC: vip['ip_address'], consts.SCOPE: 'link', consts.TABLE: 1 }) self.rules.append({ consts.SRC: vip['ip_address'], consts.SRC_LEN: 128 if ip_version == 6 else 32, consts.TABLE: 1 }) self.routes.extend(self.get_host_routes(vip['host_routes'])) self.routes.extend(self.get_host_routes(vip['host_routes'], table=1)) for fixed_ip in fixed_ips or (): ip_addr = fixed_ip['ip_address'] cidr = fixed_ip['subnet_cidr'] ip = ipaddress.ip_address(ip_addr) network = ipaddress.ip_network(cidr) prefixlen = network.prefixlen self.addresses.append({ consts.ADDRESS: fixed_ip['ip_address'], consts.PREFIXLEN: prefixlen, }) ip_versions.add(ip.version) gateway = fixed_ip.get('gateway') if gateway: # Add default routes if there's a gateway self.routes.append({ consts.DST: ( "::/0" if ip.version == 6 else "0.0.0.0/0"), consts.GATEWAY: gateway, consts.FLAGS: [consts.ONLINK] }) if topology != consts.TOPOLOGY_ACTIVE_STANDBY: self.routes.append({ consts.DST: ( "::/0" if ip.version == 6 else "0.0.0.0/0"), consts.GATEWAY: gateway, consts.FLAGS: [consts.ONLINK], consts.TABLE: 1, }) host_routes = self.get_host_routes( fixed_ip.get('host_routes', [])) self.routes.extend(host_routes) for ip_v in ip_versions: self.scripts[consts.IFACE_UP].append({ consts.COMMAND: ( "/usr/local/bin/lvs-masquerade.sh add {} {}".format( 'ipv6' if ip_v == 6 else 'ipv4', name)) }) self.scripts[consts.IFACE_DOWN].append({ consts.COMMAND: ( "/usr/local/bin/lvs-masquerade.sh delete {} {}".format( 'ipv6' if ip_v == 6 else 'ipv4', name)) })
[docs] class PortInterfaceFile(InterfaceFile): def __init__(self, name, mtu, fixed_ips): super().__init__(name, if_type=consts.BACKEND, mtu=mtu) if fixed_ips: ip_versions = set() for fixed_ip in fixed_ips: ip_addr = fixed_ip['ip_address'] cidr = fixed_ip['subnet_cidr'] ip = ipaddress.ip_address(ip_addr) network = ipaddress.ip_network(cidr) prefixlen = network.prefixlen self.addresses.append({ consts.ADDRESS: fixed_ip['ip_address'], consts.PREFIXLEN: prefixlen, }) ip_versions.add(ip.version) host_routes = self.get_host_routes( fixed_ip.get('host_routes', [])) self.routes.extend(host_routes) else: ip_versions = {4, 6} self.addresses.append({ consts.DHCP: True, consts.IPV6AUTO: True }) for ip_version in ip_versions: self.scripts[consts.IFACE_UP].append({ consts.COMMAND: ( "/usr/local/bin/lvs-masquerade.sh add {} {}".format( 'ipv6' if ip_version == 6 else 'ipv4', name)) }) self.scripts[consts.IFACE_DOWN].append({ consts.COMMAND: ( "/usr/local/bin/lvs-masquerade.sh delete {} {}".format( 'ipv6' if ip_version == 6 else 'ipv4', name)) })