
This change adds Strongswan to support VPNaaS in appliance. Change-Id: I1adb74c159eaf4f62950d17ed015856e90a91641 Partial-Blueprint: neutron-vpnaas
1081 lines
33 KiB
Python
1081 lines
33 KiB
Python
# Copyright 2014 DreamHost, LLC
|
|
#
|
|
# Author: DreamHost, LLC
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
|
|
import abc
|
|
import itertools
|
|
import re
|
|
|
|
import netaddr
|
|
|
|
from astara_router import settings
|
|
|
|
GROUP_NAME_LENGTH = 15
|
|
DEFAULT_AS = 64512
|
|
|
|
|
|
class ModelBase(object):
|
|
__metaclass__ = abc.ABCMeta
|
|
|
|
def __eq__(self, other):
|
|
return type(self) == type(other) and vars(self) == vars(other)
|
|
|
|
|
|
class Interface(ModelBase):
|
|
"""
|
|
"""
|
|
def __init__(self, ifname=None, addresses=[], groups=None, flags=None,
|
|
lladdr=None, mtu=None, media=None,
|
|
description=None, management=False, **extra_params):
|
|
self.ifname = ifname
|
|
self.description = description
|
|
self.addresses = addresses
|
|
self.groups = [g[:GROUP_NAME_LENGTH] for g in (groups or [])]
|
|
self.flags = flags or []
|
|
self.lladdr = lladdr
|
|
self.mtu = mtu
|
|
self.media = media
|
|
self.extra_params = extra_params
|
|
self._aliases = []
|
|
self.management = management
|
|
|
|
def __repr__(self):
|
|
return '<Interface: %s %s>' % (self.ifname,
|
|
[str(a) for a in self.addresses])
|
|
|
|
def __eq__(self, other):
|
|
"""Check model equality only on limit fields."""
|
|
return (type(self) == type(other) and
|
|
self.ifname == other.ifname and
|
|
self.all_addresses == other.all_addresses and
|
|
self.description == other.description and
|
|
self.mtu == other.mtu and
|
|
self.groups == other.groups)
|
|
|
|
@property
|
|
def description(self):
|
|
return self._description
|
|
|
|
@description.setter
|
|
def description(self, value):
|
|
if not value:
|
|
self._description = ''
|
|
elif re.match('\w*$', value):
|
|
self._description = value
|
|
else:
|
|
raise ValueError('Description must be chars from [a-zA-Z0-9_]')
|
|
|
|
@property
|
|
def addresses(self):
|
|
return self._addresses
|
|
|
|
@addresses.setter
|
|
def addresses(self, value):
|
|
self._addresses = [netaddr.IPNetwork(a) for a in value]
|
|
|
|
@property
|
|
def aliases(self):
|
|
return self._aliases
|
|
|
|
@aliases.setter
|
|
def aliases(self, value):
|
|
self._aliases = [netaddr.IPNetwork(a) for a in value]
|
|
|
|
@property
|
|
def all_addresses(self):
|
|
return self._addresses + self._aliases
|
|
|
|
@property
|
|
def is_up(self):
|
|
if self.extra_params.get('state', '').lower() == 'up':
|
|
return 'UP'
|
|
return 'UP' in self.flags
|
|
|
|
@property
|
|
def first_v4(self):
|
|
return self._first_addr_for_version(4)
|
|
|
|
@property
|
|
def first_v6(self):
|
|
return self._first_addr_for_version(6)
|
|
|
|
def _first_addr_for_version(self, version):
|
|
addrs = sorted(a.ip for a in self._addresses if a.version == version)
|
|
|
|
if addrs:
|
|
return addrs[0]
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(**d)
|
|
|
|
def to_dict(self, extended=False):
|
|
include = ['ifname', 'groups', 'mtu', 'lladdr', 'media']
|
|
if extended:
|
|
include.extend(['flags', 'extra_params'])
|
|
retval = dict(
|
|
[(k, v) for k, v in vars(self).iteritems() if k in include])
|
|
retval['description'] = self.description
|
|
retval['addresses'] = self.addresses
|
|
retval['state'] = (self.is_up and 'up') or 'down'
|
|
return retval
|
|
|
|
|
|
class FilterRule(ModelBase):
|
|
"""
|
|
"""
|
|
def __init__(self, action=None, direction=None, interface=None,
|
|
family=None, protocol=None, source=None, source_port=None,
|
|
destination_interface=None,
|
|
destination=None, destination_port=None,
|
|
redirect=None, redirect_port=None):
|
|
|
|
self.action = action
|
|
self.direction = direction
|
|
self.interface = interface
|
|
self.family = family
|
|
self.protocol = protocol
|
|
self.source = source
|
|
self.source_port = source_port
|
|
self.destination_interface = destination_interface
|
|
self.destination = destination
|
|
self.destination_port = destination_port
|
|
self.redirect = redirect
|
|
self.redirect_port = redirect_port
|
|
|
|
def __setattr__(self, name, value):
|
|
if name != 'action' and not value:
|
|
pass
|
|
elif name == 'action':
|
|
if value not in ('pass', 'block'):
|
|
raise ValueError("Action must be 'pass' or 'block' not '%s'" %
|
|
value)
|
|
elif name in ('source', 'destination'):
|
|
if '/' in value:
|
|
value = netaddr.IPNetwork(value)
|
|
elif value.lower() == 'any':
|
|
value = None # any is the default so conver to None
|
|
elif name == 'direction':
|
|
if value not in ('in', 'out'):
|
|
raise ValueError(
|
|
"Direction must be 'in' or 'out' not '%s'" % value
|
|
)
|
|
elif name == 'redirect':
|
|
value = netaddr.IPAddress(value)
|
|
elif name.endswith('_port'):
|
|
value = int(value)
|
|
elif name == 'family':
|
|
if value not in ('inet', 'inet6'):
|
|
raise ValueError("Family must be 'inet', 'inet6', None and not"
|
|
" %s" % value)
|
|
elif name == 'protocol':
|
|
if value not in ('tcp', 'udp', 'imcp'):
|
|
raise ValueError("Protocol must be tcp|udp|imcp not '%s'." %
|
|
value)
|
|
|
|
super(FilterRule, self).__setattr__(name, value)
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return FilterRule(**d)
|
|
|
|
@staticmethod
|
|
def _format_ip_or_table(obj):
|
|
if isinstance(obj, netaddr.IPNetwork):
|
|
return str(obj)
|
|
else: # must be table name
|
|
return '<%s>' % obj
|
|
|
|
|
|
class Anchor(ModelBase):
|
|
def __init__(self, name, rules=[]):
|
|
self.name = name
|
|
self.rules = rules
|
|
|
|
|
|
class AddressBookEntry(ModelBase):
|
|
def __init__(self, name, cidrs=[]):
|
|
self.name = name
|
|
self.cidrs = cidrs
|
|
|
|
@property
|
|
def cidrs(self):
|
|
return self._cidrs
|
|
|
|
@cidrs.setter
|
|
def cidrs(self, values):
|
|
self._cidrs = [netaddr.IPNetwork(a) for a in values]
|
|
|
|
def external_table_data(self):
|
|
return '\n'.join(map(str, self.cidrs))
|
|
|
|
|
|
class Allocation(ModelBase):
|
|
def __init__(self, mac_address, ip_addresses, hostname, device_id):
|
|
self.mac_address = mac_address
|
|
self.ip_addresses = ip_addresses or {}
|
|
self.hostname = hostname
|
|
self.device_id = device_id
|
|
|
|
@property
|
|
def dhcp_addresses(self):
|
|
return [ip for ip, dhcp in self.ip_addresses.items() if dhcp]
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(
|
|
d['mac_address'],
|
|
d['ip_addresses'],
|
|
d['hostname'],
|
|
d['device_id'],
|
|
)
|
|
|
|
|
|
class FloatingIP(ModelBase):
|
|
def __init__(self, floating_ip, fixed_ip):
|
|
self.floating_ip = floating_ip
|
|
self.fixed_ip = fixed_ip
|
|
self.network = None
|
|
|
|
@property
|
|
def floating_ip(self):
|
|
return self._floating_ip
|
|
|
|
@floating_ip.setter
|
|
def floating_ip(self, value):
|
|
self._floating_ip = netaddr.IPAddress(value)
|
|
|
|
@property
|
|
def fixed_ip(self):
|
|
return self._fixed_ip
|
|
|
|
@fixed_ip.setter
|
|
def fixed_ip(self, value):
|
|
self._fixed_ip = netaddr.IPAddress(value)
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(
|
|
d['floating_ip'],
|
|
d['fixed_ip']
|
|
)
|
|
|
|
|
|
class StaticRoute(ModelBase):
|
|
def __init__(self, destination, next_hop):
|
|
self.destination = destination
|
|
self.next_hop = next_hop
|
|
|
|
@property
|
|
def destination(self):
|
|
return self._destination
|
|
|
|
@destination.setter
|
|
def destination(self, value):
|
|
self._destination = netaddr.IPNetwork(value)
|
|
|
|
@property
|
|
def next_hop(self):
|
|
return self._next_hop
|
|
|
|
@next_hop.setter
|
|
def next_hop(self, value):
|
|
self._next_hop = netaddr.IPAddress(value)
|
|
|
|
def to_dict(self):
|
|
return dict(destination=self.destination, next_hop=self.next_hop)
|
|
|
|
|
|
class Label(ModelBase):
|
|
def __init__(self, name, cidrs=[]):
|
|
self.name = name
|
|
self.cidrs = cidrs
|
|
|
|
@property
|
|
def cidrs(self):
|
|
return self._cidrs
|
|
|
|
@cidrs.setter
|
|
def cidrs(self, values):
|
|
self._cidrs = [netaddr.IPNetwork(a) for a in values]
|
|
|
|
|
|
class Subnet(ModelBase):
|
|
def __init__(self, id_, cidr, gateway_ip, dhcp_enabled=True,
|
|
dns_nameservers=None, host_routes=None):
|
|
self.id = id_
|
|
self.cidr = cidr
|
|
self.gateway_ip = gateway_ip
|
|
self.dhcp_enabled = bool(dhcp_enabled)
|
|
self.dns_nameservers = dns_nameservers
|
|
self.host_routes = host_routes
|
|
|
|
@property
|
|
def cidr(self):
|
|
return self._cidr
|
|
|
|
@cidr.setter
|
|
def cidr(self, value):
|
|
self._cidr = netaddr.IPNetwork(value)
|
|
|
|
@property
|
|
def gateway_ip(self):
|
|
return self._gateway_ip
|
|
|
|
@gateway_ip.setter
|
|
def gateway_ip(self, value):
|
|
if value:
|
|
self._gateway_ip = netaddr.IPAddress(value)
|
|
else:
|
|
self._gateway_ip = None
|
|
|
|
@property
|
|
def dns_nameservers(self):
|
|
return self._dns_nameservers
|
|
|
|
@dns_nameservers.setter
|
|
def dns_nameservers(self, value):
|
|
self._dns_nameservers = [netaddr.IPAddress(a) for a in value]
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
host_routes = [StaticRoute(r['destination'], r['nexthop'])
|
|
for r in d.get('host_routes', [])]
|
|
return cls(
|
|
d['id'],
|
|
d['cidr'],
|
|
d['gateway_ip'],
|
|
d['dhcp_enabled'],
|
|
d['dns_nameservers'],
|
|
host_routes)
|
|
|
|
|
|
class Network(ModelBase):
|
|
SERVICE_STATIC = 'static'
|
|
SERVICE_RA = 'ra'
|
|
SERVICE_DHCP = 'dhcp'
|
|
TYPE_EXTERNAL = 'external'
|
|
TYPE_INTERNAL = 'internal'
|
|
TYPE_ISOLATED = 'isolated'
|
|
TYPE_MANAGEMENT = 'management'
|
|
TYPE_LOADBALANCER = 'loadbalancer'
|
|
|
|
# TODO(mark): add subnet support for Quantum subnet host routes
|
|
|
|
def __init__(self, id_, interface, name='', network_type=TYPE_ISOLATED,
|
|
v4_conf_service=SERVICE_STATIC,
|
|
v6_conf_service=SERVICE_STATIC,
|
|
address_allocations=None,
|
|
subnets=None, ha=False):
|
|
self.id = id_
|
|
self.interface = interface
|
|
self.name = name
|
|
self.network_type = network_type
|
|
self.v4_conf_service = v4_conf_service
|
|
self.v6_conf_service = v6_conf_service
|
|
self.address_allocations = address_allocations or []
|
|
self.subnets = subnets or []
|
|
self.floating_ips = []
|
|
self.ha = ha
|
|
|
|
@property
|
|
def is_tenant_network(self):
|
|
return self._network_type in (self.TYPE_INTERNAL, self.TYPE_ISOLATED)
|
|
|
|
@property
|
|
def is_internal_network(self):
|
|
return self._network_type == self.TYPE_INTERNAL
|
|
|
|
@property
|
|
def is_external_network(self):
|
|
return self._network_type == self.TYPE_EXTERNAL
|
|
|
|
@property
|
|
def is_management_network(self):
|
|
return self._network_type == self.TYPE_MANAGEMENT
|
|
|
|
@property
|
|
def network_type(self):
|
|
return self._network_type
|
|
|
|
@network_type.setter
|
|
def network_type(self, value):
|
|
network_types = (self.TYPE_EXTERNAL, self.TYPE_INTERNAL,
|
|
self.TYPE_ISOLATED, self.TYPE_MANAGEMENT,
|
|
self.TYPE_LOADBALANCER)
|
|
if value not in network_types:
|
|
msg = ('network must be one of %s not (%s).' %
|
|
('|'.join(network_types), value))
|
|
raise ValueError(msg)
|
|
self._network_type = value
|
|
|
|
@property
|
|
def v4_conf_service(self):
|
|
return self._v4_conf_service
|
|
|
|
@v4_conf_service.setter
|
|
def v4_conf_service(self, value):
|
|
if value not in (self.SERVICE_DHCP, self.SERVICE_STATIC):
|
|
msg = ('v4_conf_service must be one of dhcp|static not (%s).' %
|
|
value)
|
|
raise ValueError(msg)
|
|
self._v4_conf_service = value
|
|
|
|
@property
|
|
def v6_conf_service(self):
|
|
return self._v6_conf_service
|
|
|
|
@v6_conf_service.setter
|
|
def v6_conf_service(self, value):
|
|
if value not in (self.SERVICE_DHCP, self.SERVICE_RA,
|
|
self.SERVICE_STATIC):
|
|
msg = ('v6_conf_service must be one of dhcp|ra|static not (%s).' %
|
|
value)
|
|
raise ValueError(msg)
|
|
self._v6_conf_service = value
|
|
|
|
def to_dict(self):
|
|
return dict(
|
|
network_id=self.id,
|
|
interface=self.interface,
|
|
name=self.name,
|
|
network_type=self.network_type,
|
|
v4_conf_service=self.v4_conf_service,
|
|
v6_conf_service=self.v6_conf_service,
|
|
address_allocations=self.address_allocations
|
|
)
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
missing = []
|
|
for k in ['network_id', 'interface']:
|
|
if not d.get(k):
|
|
missing.append(k)
|
|
if missing:
|
|
raise ValueError('Missing required data: %s.' % missing)
|
|
|
|
if d.get('network_type') == cls.TYPE_MANAGEMENT:
|
|
d['interface']['management'] = True
|
|
else:
|
|
d['interface']['management'] = False
|
|
|
|
return cls(
|
|
d['network_id'],
|
|
interface=Interface.from_dict(d['interface']),
|
|
name=d.get('name', ''),
|
|
network_type=d.get('network_type', cls.TYPE_ISOLATED),
|
|
v6_conf_service=d.get('v6_conf_service', cls.SERVICE_STATIC),
|
|
v4_conf_service=d.get('v4_conf_service', cls.SERVICE_STATIC),
|
|
address_allocations=[
|
|
Allocation.from_dict(a) for a in d.get('allocations', [])],
|
|
subnets=[Subnet.from_dict(s) for s in d.get('subnets', [])],
|
|
ha=d.get('ha', False))
|
|
|
|
|
|
class LoadBalancer(ModelBase):
|
|
def __init__(self, id_, tenant_id, name, admin_state_up, status,
|
|
vip_address, vip_port=None, listeners=()):
|
|
self.id = id_
|
|
self.tenant_id = tenant_id
|
|
self.name = name
|
|
self.admin_state_up = admin_state_up
|
|
self.status = status
|
|
self.vip_address = vip_address
|
|
self.vip_port = vip_port
|
|
self.listeners = listeners
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
if d.get('listeners'):
|
|
d['listeners'] = [
|
|
Listener.from_dict(l) for l in d.get('listeners', [])
|
|
]
|
|
if d.get('vip_port'):
|
|
d['vip_port'] = Port.from_dict(d.get('vip_port'))
|
|
out = cls(
|
|
d['id'],
|
|
d['tenant_id'],
|
|
d['name'],
|
|
d['admin_state_up'],
|
|
d['status'],
|
|
d['vip_address'],
|
|
d['vip_port'],
|
|
d['listeners'],
|
|
)
|
|
return out
|
|
|
|
|
|
class Listener(ModelBase):
|
|
def __init__(self, id_, tenant_id, name, admin_state_up, protocol,
|
|
protocol_port, default_pool=None):
|
|
self.id = id_
|
|
self.tenant_id = tenant_id
|
|
self.name = name
|
|
self.admin_state_up = admin_state_up
|
|
self.protocol = protocol
|
|
self.protocol_port = protocol_port
|
|
self.default_pool = default_pool
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
if d.get('default_pool'):
|
|
def_pool = Pool.from_dict(d['default_pool'])
|
|
else:
|
|
def_pool = None
|
|
|
|
return cls(
|
|
d['id'],
|
|
d['tenant_id'],
|
|
d['name'],
|
|
d['admin_state_up'],
|
|
d['protocol'],
|
|
d['protocol_port'],
|
|
def_pool,
|
|
)
|
|
|
|
def to_dict(self):
|
|
fields = ('id', 'tenant_id', 'name', 'admin_state_up', 'protocol',
|
|
'protocol_port')
|
|
out = dict((f, getattr(self, f)) for f in fields)
|
|
if self.default_pool:
|
|
out['default_pool'] = self.default_pool.to_dict()
|
|
else:
|
|
out['default_pool'] = None
|
|
return out
|
|
|
|
|
|
class Pool(ModelBase):
|
|
def __init__(self, id_, tenant_id, name, admin_state_up, lb_algorithm,
|
|
protocol, healthmonitor=None, session_persistence=None,
|
|
members=()):
|
|
self.id = id_
|
|
self.tenant_id = tenant_id
|
|
self.name = name
|
|
self.admin_state_up = admin_state_up
|
|
self.lb_algorithm = lb_algorithm
|
|
self.protocol = protocol
|
|
self.healthmonitor = healthmonitor
|
|
self.session_persistence = session_persistence
|
|
self.members = members
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(
|
|
d['id'],
|
|
d['tenant_id'],
|
|
d['name'],
|
|
d['admin_state_up'],
|
|
d['lb_algorithm'],
|
|
d['protocol'],
|
|
d.get('healthmonitor'),
|
|
d.get('session_persistence'),
|
|
[Member.from_dict(m) for m in d.get('members', [])],
|
|
)
|
|
|
|
def to_dict(self):
|
|
fields = ('id', 'tenant_id', 'name', 'admin_state_up',
|
|
'lb_algorithm', 'protocol', 'healthmonitor',
|
|
'session_persistence')
|
|
out = dict((f, getattr(self, f)) for f in fields)
|
|
out['members'] = [m.to_dict() for m in self.members]
|
|
return out
|
|
|
|
|
|
class Member(ModelBase):
|
|
def __init__(self, id_, tenant_id, admin_state_up, address, protocol_port,
|
|
weight, subnet=None):
|
|
self.id = id_
|
|
self.tenant_id = tenant_id
|
|
self.admin_state_up = admin_state_up
|
|
self.address = str(netaddr.IPAddress(address))
|
|
self.protocol_port = protocol_port
|
|
self.weight = weight
|
|
self.subnet = subnet
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(
|
|
d['id'],
|
|
d['tenant_id'],
|
|
d['admin_state_up'],
|
|
d['address'],
|
|
d['protocol_port'],
|
|
d['weight'],
|
|
)
|
|
|
|
def to_dict(self):
|
|
fields = ('id', 'tenant_id', 'admin_state_up', 'address',
|
|
'protocol_port', 'weight', 'subnet')
|
|
return dict((f, getattr(self, f)) for f in fields)
|
|
|
|
|
|
class Port(ModelBase):
|
|
def __init__(self, id_, device_id='', fixed_ips=None, mac_address='',
|
|
network_id='', device_owner='', name=''):
|
|
self.id = id_
|
|
self.device_id = device_id
|
|
self.fixed_ips = fixed_ips or []
|
|
self.mac_address = mac_address
|
|
self.network_id = network_id
|
|
self.device_owner = device_owner
|
|
self.name = name
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(
|
|
d['id'],
|
|
d['device_id'],
|
|
fixed_ips=[FixedIp.from_dict(fip) for fip in d['fixed_ips']],
|
|
mac_address=d['mac_address'],
|
|
network_id=d['network_id'],
|
|
device_owner=d['device_owner'],
|
|
name=d['name'])
|
|
|
|
def to_dict(self):
|
|
fields = ('id', 'device_id', 'mac_address', 'network_id',
|
|
'device_owner', 'name')
|
|
out = dict((f, getattr(self, f)) for f in fields)
|
|
out['fixed_ips'] = [fip.to_dict() for fip in self.fixed_ips]
|
|
return out
|
|
|
|
|
|
class FixedIp(ModelBase):
|
|
def __init__(self, subnet_id, ip_address):
|
|
self.subnet_id = subnet_id
|
|
self.ip_address = netaddr.IPAddress(ip_address)
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(d['subnet_id'], d['ip_address'])
|
|
|
|
def to_dict(self):
|
|
fields = ('subnet_id', 'ip_address')
|
|
return dict((f, getattr(self, f)) for f in fields)
|
|
|
|
|
|
class DeadPeerDetection(ModelBase):
|
|
def __init__(self, action, interval, timeout):
|
|
self.action = action
|
|
self.interval = interval
|
|
self.timeout = timeout
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(
|
|
d['action'],
|
|
d['interval'],
|
|
d['timeout']
|
|
)
|
|
|
|
|
|
class Lifetime(ModelBase):
|
|
def __init__(self, units, value):
|
|
self.units = units
|
|
self.value = value
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(
|
|
d['units'],
|
|
d['value']
|
|
)
|
|
|
|
|
|
class EndpointGroup(ModelBase):
|
|
def __init__(self, id_, tenant_id, name, type_, endpoints=()):
|
|
self.id = id_
|
|
self.tenant_id = tenant_id
|
|
self.name = name
|
|
self.type = type_
|
|
if type_ == 'cidr':
|
|
self.endpoints = [netaddr.IPNetwork(ep) for ep in endpoints]
|
|
else:
|
|
self.endpoints = endpoints
|
|
self.subnet_map = {}
|
|
|
|
@property
|
|
def cidrs(self):
|
|
if self.type == 'subnet':
|
|
return [
|
|
self.subnet_map[ep].cidr
|
|
for ep in self.endpoints
|
|
if ep in self.subnet_map
|
|
]
|
|
else:
|
|
return self.endpoints
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(
|
|
d['id'],
|
|
d['tenant_id'],
|
|
d['name'],
|
|
d['type'],
|
|
d['endpoints']
|
|
)
|
|
|
|
|
|
class IkePolicy(ModelBase):
|
|
def __init__(self, id_, tenant_id, name, ike_version, auth_algorithm,
|
|
encryption_algorithm, pfs, phase1_negotiation_mode, lifetime):
|
|
self.id = id_
|
|
self.tenant_id = tenant_id
|
|
self.name = name
|
|
self.ike_version = ike_version
|
|
self.auth_algorithm = auth_algorithm
|
|
self.encryption_algorithm = encryption_algorithm
|
|
self.pfs = pfs
|
|
self.phase1_negotiation_mode = phase1_negotiation_mode
|
|
self.lifetime = lifetime
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(
|
|
d['id'],
|
|
d['tenant_id'],
|
|
d['name'],
|
|
d['ike_version'],
|
|
d['auth_algorithm'],
|
|
d['encryption_algorithm'],
|
|
d['pfs'],
|
|
d['phase1_negotiation_mode'],
|
|
Lifetime.from_dict(d['lifetime'])
|
|
)
|
|
|
|
|
|
class IpsecPolicy(ModelBase):
|
|
def __init__(self, id_, tenant_id, name, transform_protocol,
|
|
auth_algorithm, encryption_algorithm, encapsulation_mode,
|
|
lifetime, pfs):
|
|
self.id = id_
|
|
self.tenant_id = tenant_id
|
|
self.name = name
|
|
self.transform_protocol = transform_protocol
|
|
self.auth_algorithm = auth_algorithm
|
|
self.encryption_algorithm = encryption_algorithm
|
|
self.encapsulation_mode = encapsulation_mode
|
|
self.lifetime = lifetime
|
|
self.pfs = pfs
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(
|
|
d['id'],
|
|
d['tenant_id'],
|
|
d['name'],
|
|
d['transform_protocol'],
|
|
d['auth_algorithm'],
|
|
d['encryption_algorithm'],
|
|
d['encapsulation_mode'],
|
|
Lifetime.from_dict(d['lifetime']),
|
|
d['pfs']
|
|
)
|
|
|
|
|
|
class IpsecSiteConnection(ModelBase):
|
|
def __init__(self, id_, tenant_id, name, peer_address, peer_id,
|
|
admin_state_up, route_mode, mtu, initiator, auth_mode, psk,
|
|
dpd, status, vpnservice_id, local_ep_group=None,
|
|
peer_ep_group=None, peer_cidrs=[], ikepolicy=None,
|
|
ipsecpolicy=None):
|
|
self.id = id_
|
|
self.tenant_id = tenant_id
|
|
self.name = name
|
|
self.peer_address = netaddr.IPAddress(peer_address)
|
|
self.peer_id = peer_id
|
|
self.route_mode = route_mode
|
|
self.mtu = mtu
|
|
self.initiator = initiator
|
|
self.auth_mode = auth_mode
|
|
self.psk = psk
|
|
self.dpd = dpd
|
|
self.status = status
|
|
self.admin_state_up = admin_state_up
|
|
self.vpnservice_id = vpnservice_id
|
|
self.ipsecpolicy = ipsecpolicy
|
|
self.ikepolicy = ikepolicy
|
|
self.local_ep_group = local_ep_group
|
|
self.peer_ep_group = peer_ep_group
|
|
self.peer_cidrs = [netaddr.IPNetwork(pc) for pc in peer_cidrs]
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(
|
|
d['id'],
|
|
d['tenant_id'],
|
|
d['name'],
|
|
d['peer_address'],
|
|
d['peer_id'],
|
|
d['admin_state_up'],
|
|
d['route_mode'],
|
|
d['mtu'],
|
|
d['initiator'],
|
|
d['auth_mode'],
|
|
d['psk'],
|
|
DeadPeerDetection.from_dict(d['dpd']),
|
|
d['status'],
|
|
d['vpnservice_id'],
|
|
peer_cidrs=d['peer_cidrs'],
|
|
ikepolicy=IkePolicy.from_dict(d['ikepolicy']),
|
|
ipsecpolicy=IpsecPolicy.from_dict(d['ipsecpolicy']),
|
|
local_ep_group=EndpointGroup.from_dict(d['local_ep_group']),
|
|
peer_ep_group=EndpointGroup.from_dict(d['peer_ep_group']),
|
|
)
|
|
|
|
|
|
class VpnService(ModelBase):
|
|
def __init__(self, id_, name, status, admin_state_up, external_v4_ip,
|
|
external_v6_ip, router_id, subnet_id=None,
|
|
ipsec_site_connections=()):
|
|
self.id = id_
|
|
self.name = name
|
|
self.status = status
|
|
self.admin_state_up = admin_state_up
|
|
self.external_v4_ip = netaddr.IPAddress(external_v4_ip)
|
|
self.external_v6_ip = netaddr.IPAddress(external_v6_ip)
|
|
self.router_id = router_id
|
|
self.subnet_id = subnet_id
|
|
self.ipsec_site_connections = ipsec_site_connections
|
|
|
|
def get_external_ip(self, peer_ip):
|
|
if peer_ip.version == '6':
|
|
return self.external_v6_ip
|
|
else:
|
|
return self.external_v4_ip
|
|
|
|
@classmethod
|
|
def from_dict(cls, d):
|
|
return cls(
|
|
d['id'],
|
|
d['name'],
|
|
d['status'],
|
|
d['admin_state_up'],
|
|
d['external_v4_ip'],
|
|
d['external_v6_ip'],
|
|
d['router_id'],
|
|
d.get('subnet_id'),
|
|
[IpsecSiteConnection.from_dict(c) for c in d['ipsec_connections']]
|
|
)
|
|
|
|
|
|
class SystemConfiguration(ModelBase):
|
|
service_name = 'system'
|
|
|
|
def __init__(self, conf_dict={}):
|
|
self.tenant_id = conf_dict.get('tenant_id')
|
|
self.hostname = conf_dict.get('hostname')
|
|
self.networks = [
|
|
Network.from_dict(n) for n in conf_dict.get('networks', [])]
|
|
self.ha = conf_dict.get('ha_resource', False)
|
|
self.ha_config = conf_dict.get('ha_config', {})
|
|
|
|
def validate(self):
|
|
# TODO: Improve this interface, it currently sucks.
|
|
errors = []
|
|
for attr in ['tenant_id', 'hostname']:
|
|
if not getattr(self, attr):
|
|
errors.append((attr, 'Config does not contain a %s' % attr))
|
|
return errors
|
|
|
|
@property
|
|
def management_address(self):
|
|
addrs = []
|
|
for net in self.networks:
|
|
if net.is_management_network:
|
|
addrs.extend((net.interface.first_v4, net.interface.first_v6))
|
|
|
|
addrs = sorted(a for a in addrs if a)
|
|
|
|
if addrs:
|
|
return addrs[0]
|
|
|
|
@property
|
|
def interfaces(self):
|
|
return [n.interface for n in self.networks if n.interface]
|
|
|
|
def to_dict(self):
|
|
fields = ('tenant_id', 'hostname', 'management_address', 'interfaces')
|
|
return dict((f, getattr(self, f)) for f in fields)
|
|
|
|
|
|
class RouterConfiguration(SystemConfiguration):
|
|
service_name = 'router'
|
|
|
|
def __init__(self, conf_dict={}):
|
|
super(RouterConfiguration, self).__init__(conf_dict)
|
|
gw = conf_dict.get('default_v4_gateway')
|
|
self.default_v4_gateway = netaddr.IPAddress(gw) if gw else None
|
|
self.asn = conf_dict.get('asn', DEFAULT_AS)
|
|
self.neighbor_asn = conf_dict.get('neighbor_asn', self.asn)
|
|
self.static_routes = [StaticRoute(*r) for r in
|
|
conf_dict.get('static_routes', [])]
|
|
|
|
self.address_book = dict(
|
|
(name, AddressBookEntry(name, cidrs)) for name, cidrs in
|
|
conf_dict.get('address_book', {}).iteritems())
|
|
|
|
self.anchors = [
|
|
Anchor(a['name'], [FilterRule.from_dict(r) for r in a['rules']])
|
|
for a in conf_dict.get('anchors', [])]
|
|
|
|
self.labels = [
|
|
Label(name, cidr) for name, cidr in
|
|
conf_dict.get('labels', {}).iteritems()]
|
|
|
|
orchestrator_conf = conf_dict.get('orchestrator', {})
|
|
self.metadata_address = orchestrator_conf.get(
|
|
'address', settings.ORCHESTRATOR_METADATA_ADDRESS)
|
|
self.metadata_port = orchestrator_conf.get(
|
|
'metadata_port', settings.ORCHESTRATOR_METADATA_PORT)
|
|
|
|
self.floating_ips = [
|
|
FloatingIP.from_dict(fip)
|
|
for fip in conf_dict.get('floating_ips', [])
|
|
]
|
|
|
|
self._attach_floating_ips(self.floating_ips)
|
|
|
|
self.vpn = [
|
|
VpnService.from_dict(s)
|
|
for s in conf_dict.get('vpn', {}).get('ipsec', [])
|
|
]
|
|
|
|
self._link_subnets()
|
|
|
|
def validate(self):
|
|
"""Validate anchor rules to ensure that ifaces and tables exist."""
|
|
interfaces = set(n.interface.ifname for n in self.networks)
|
|
errors = []
|
|
for anchor in self.anchors:
|
|
for rule in anchor.rules:
|
|
for iface in (rule.interface, rule.destination_interface):
|
|
if iface and iface not in interfaces:
|
|
errors.append((rule, '%s does not exist' % iface))
|
|
|
|
for address in (rule.source, rule.destination):
|
|
if not address or isinstance(address, netaddr.IPNetwork):
|
|
pass
|
|
elif address in self.address_book:
|
|
pass
|
|
else:
|
|
reason = '%s is not in the address book' % address
|
|
errors.append((rule, reason))
|
|
|
|
return ["'%s' %s" % e for e in errors]
|
|
|
|
def _attach_floating_ips(self, floating_ips):
|
|
ext_cidr_map = {}
|
|
int_cidr_map = {}
|
|
|
|
for network in self.networks:
|
|
if network.is_external_network:
|
|
m = ext_cidr_map
|
|
elif network.is_internal_network:
|
|
m = int_cidr_map
|
|
else:
|
|
continue
|
|
m.update((s.cidr, network) for s in network.subnets)
|
|
|
|
for fip in floating_ips:
|
|
# add address to external interface
|
|
for ext_cidr, net in ext_cidr_map.items():
|
|
if fip.floating_ip in ext_cidr:
|
|
addr = '%s/%s' % (fip.floating_ip, ext_cidr.prefixlen)
|
|
net.interface.aliases += [netaddr.IPNetwork(addr)]
|
|
net.floating_ips.append(fip)
|
|
break
|
|
|
|
# add to internal
|
|
for int_cidr, net in int_cidr_map.items():
|
|
if fip.fixed_ip in int_cidr:
|
|
fip.network = net
|
|
|
|
def _link_subnets(self):
|
|
subnet_map = {}
|
|
for n in self.networks:
|
|
for s in n.subnets:
|
|
subnet_map[s.id] = s
|
|
|
|
vpn_conn_generator = (v.ipsec_site_connections for v in self.vpn)
|
|
|
|
for conn in itertools.chain.from_iterable(vpn_conn_generator):
|
|
if conn.local_ep_group.type == 'subnet':
|
|
conn.local_ep_group.subnet_map = subnet_map
|
|
|
|
def to_dict(self):
|
|
fields = (
|
|
'networks', 'address_book', 'anchors', 'static_routes', 'vpn'
|
|
)
|
|
return dict((f, getattr(self, f)) for f in fields)
|
|
|
|
@property
|
|
def external_v4_id(self):
|
|
addrs = [n.interface.first_v4
|
|
for n in self.networks if n.is_external_network]
|
|
|
|
# remove any none
|
|
addrs = sorted(a for a in addrs if a)
|
|
|
|
if addrs:
|
|
return addrs[0]
|
|
|
|
|
|
class LoadBalancerConfiguration(SystemConfiguration):
|
|
service_name = 'loadbalancer'
|
|
|
|
def __init__(self, conf_dict={}):
|
|
super(LoadBalancerConfiguration, self).__init__(conf_dict)
|
|
self.id = conf_dict.get('id')
|
|
self.name = conf_dict.get('name')
|
|
if conf_dict:
|
|
self._loadbalancer = LoadBalancer.from_dict(conf_dict)
|
|
self.vip_port = self._loadbalancer.vip_port
|
|
self.vip_address = self._loadbalancer.vip_address
|
|
self.listeners = self._loadbalancer.listeners
|
|
else:
|
|
self.vip_port = None
|
|
self.vip_address = None
|
|
self.listeners = []
|
|
|
|
def validate(self):
|
|
super(LoadBalancerConfiguration, self).validate()
|
|
errors = []
|
|
if not self.id:
|
|
errors.append(['id', 'Missing in config id'])
|
|
return errors
|
|
|
|
def to_dict(self):
|
|
if self.vip_port:
|
|
vip_port = self.vip_port.to_dict()
|
|
else:
|
|
vip_port = {}
|
|
return {
|
|
'id': self.id,
|
|
'name': self.name,
|
|
'vip_port': vip_port,
|
|
'vip_address': self.vip_address,
|
|
'listeners': [l.to_dict() for l in self.listeners],
|
|
}
|
|
|
|
SERVICE_MAP = {
|
|
RouterConfiguration.service_name: RouterConfiguration,
|
|
LoadBalancerConfiguration.service_name: LoadBalancerConfiguration,
|
|
}
|
|
|
|
|
|
def get_config_model(service):
|
|
return SERVICE_MAP[service]
|