Add support for StrongSwan VPN to router
This change adds Strongswan to support VPNaaS in appliance. Change-Id: I1adb74c159eaf4f62950d17ed015856e90a91641 Partial-Blueprint: neutron-vpnaas
This commit is contained in:
parent
8633d1a5bc
commit
920954e31d
@ -6,6 +6,8 @@
|
||||
bird_enable: False
|
||||
bird6_enable: True
|
||||
bird_enable_service: True
|
||||
strongswan_enable: True
|
||||
strongswan_enable_service: False
|
||||
dnsmasq_conf_dir: /etc/dnsmasq.d
|
||||
dnsmasq_conf_file: /etc/dnsmasq.conf
|
||||
install_extras: False
|
||||
@ -22,6 +24,7 @@
|
||||
- include: tasks/astara.yml
|
||||
- include: tasks/bird.yml
|
||||
- include: tasks/conntrackd.yml
|
||||
- include: tasks/strongswan.yml
|
||||
- include: tasks/dnsmasq.yml
|
||||
- include: tasks/extras.yml
|
||||
when: install_extras
|
||||
|
@ -29,8 +29,9 @@
|
||||
- name: install gunicorn config file
|
||||
template: src=gunicorn.j2 dest=/etc/astara_gunicorn_config.py
|
||||
|
||||
|
||||
- name: add gunicorn user
|
||||
command: useradd -r gunicorn
|
||||
action: user name=gunicorn state=present
|
||||
|
||||
- name: install init.d files
|
||||
copy: src={{playbook_dir}}/../scripts/etc/init.d/{{item}} dest=/etc/init.d/{{item}} mode=0555
|
||||
@ -90,4 +91,7 @@
|
||||
command: apt-get -y autoremove
|
||||
when: do_cleanup
|
||||
|
||||
- name: Ensure gunicorn is restarted
|
||||
service: name=astara-router-api-server state=restarted enabled=yes
|
||||
|
||||
|
||||
|
9
ansible/tasks/strongswan.yml
Normal file
9
ansible/tasks/strongswan.yml
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
|
||||
- name: install strongswan
|
||||
apt: name=strongswan state=installed install_recommends=yes
|
||||
when: strongswan_enable
|
||||
|
||||
- name: Ensure strongswan is started
|
||||
service: name=strongswan state=started enabled=yes
|
||||
when: strongswan_enable and strongswan_enable_service
|
0
astara_router/drivers/vpn/__init__.py
Normal file
0
astara_router/drivers/vpn/__init__.py
Normal file
89
astara_router/drivers/vpn/ipsec.py
Normal file
89
astara_router/drivers/vpn/ipsec.py
Normal file
@ -0,0 +1,89 @@
|
||||
# Copyright 2016 Akanda, Inc
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
|
||||
import jinja2
|
||||
|
||||
from astara_router.drivers import base
|
||||
from astara_router import utils
|
||||
|
||||
TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), 'templates')
|
||||
|
||||
|
||||
class StrongswanManager(base.Manager):
|
||||
"""
|
||||
A class to interact with strongswan, an IPSEC VPN daemon.
|
||||
"""
|
||||
def __init__(self, root_helper='sudo astara-rootwrap /etc/rootwrap.conf'):
|
||||
"""
|
||||
Initializes StrongswanManager class.
|
||||
|
||||
:type root_helper: string
|
||||
:param root_helper: System utility used to gain escalate privileges.
|
||||
"""
|
||||
super(StrongswanManager, self).__init__(root_helper)
|
||||
|
||||
def save_config(self, config):
|
||||
"""
|
||||
Writes config file for strongswan daemon.
|
||||
|
||||
:type config: astara_router.models.Configuration
|
||||
:param config:
|
||||
{'ge0': 'eth0', 'ge1': 'eth1'}
|
||||
"""
|
||||
|
||||
templates = ('ipsec.conf', 'ipsec.secrets')
|
||||
|
||||
for template_name in templates:
|
||||
tmpl = jinja2.Template(
|
||||
open(os.path.join(TEMPLATE_DIR, template_name+'.j2')).read()
|
||||
)
|
||||
|
||||
tmp = os.path.join('/tmp', template_name)
|
||||
open(tmp, 'w').write(tmpl.render(vpnservices=config.vpn))
|
||||
|
||||
for template_name in templates:
|
||||
tmp = os.path.join('/tmp', template_name)
|
||||
etc = os.path.join('/etc', template_name)
|
||||
utils.execute(['mv', tmp, etc], self.root_helper)
|
||||
|
||||
def restart(self):
|
||||
"""
|
||||
Restart the Strongswan daemon using the system provided init scripts.
|
||||
"""
|
||||
try:
|
||||
utils.execute(
|
||||
['service', 'strongswan', 'status'],
|
||||
self.root_helper
|
||||
)
|
||||
except: # pragma no cover
|
||||
utils.execute(['service', 'strongswan', 'start'], self.root_helper)
|
||||
else: # pragma no cover
|
||||
utils.execute(
|
||||
['service', 'strongswan', 'reload'],
|
||||
self.root_helper
|
||||
)
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Stop the Strongswan daemon using the system provided init scripts.
|
||||
"""
|
||||
try:
|
||||
utils.execute(
|
||||
['service', 'strongswan', 'stop'],
|
||||
self.root_helper
|
||||
)
|
||||
except: # pragma no cover
|
||||
pass
|
3
astara_router/drivers/vpn/templates/README
Normal file
3
astara_router/drivers/vpn/templates/README
Normal file
@ -0,0 +1,3 @@
|
||||
These templates were originally copied from the upstream neutron-vpaas [1].
|
||||
|
||||
[1] http://git.openstack.org/cgit/openstack/neutron-vpnaas/tree/neutron_vpnaas/services/vpn/device_drivers/template/strongswan
|
25
astara_router/drivers/vpn/templates/ipsec.conf.j2
Normal file
25
astara_router/drivers/vpn/templates/ipsec.conf.j2
Normal file
@ -0,0 +1,25 @@
|
||||
config setup
|
||||
|
||||
conn %default
|
||||
ikelifetime=60m
|
||||
keylife=20m
|
||||
rekeymargin=3m
|
||||
keyingtries=1
|
||||
authby=psk
|
||||
mobike=no
|
||||
{% for vpnservice in vpnservices %}
|
||||
# Configuration for {{vpnservice.name}}
|
||||
{% for ipsec_site_connection in vpnservice.ipsec_site_connections%}
|
||||
conn {{ipsec_site_connection.id}}
|
||||
keyexchange=ike{{ipsec_site_connection.ikepolicy.ike_version}}
|
||||
left={{vpnservice.get_external_ip(ipsec_site_connection.peer_address)}}
|
||||
leftsubnet={{ipsec_site_connection.local_ep_group.cidrs|join(',')}}
|
||||
leftid={{vpnservice.get_external_ip(ipsec_site_connection.peer_address)}}
|
||||
leftfirewall=yes
|
||||
right={{ipsec_site_connection.peer_address}}
|
||||
rightsubnet={{ipsec_site_connection.peer_ep_group.cidrs|join(',')}}
|
||||
rightid={{ipsec_site_connection.peer_id}}
|
||||
auto=route
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
6
astara_router/drivers/vpn/templates/ipsec.secrets.j2
Normal file
6
astara_router/drivers/vpn/templates/ipsec.secrets.j2
Normal file
@ -0,0 +1,6 @@
|
||||
{% for vpnservice in vpnservices %}
|
||||
# Configuration for {{vpnservice.name}}
|
||||
{% for ipsec_site_connection in vpnservice.ipsec_site_connections %}
|
||||
{{ipsec_site_connection.external_ip}} {{ipsec_site_connection.peer_id}} : PSK "{{ipsec_site_connection.psk}}"
|
||||
{% endfor %}
|
||||
{% endfor %}
|
@ -22,6 +22,7 @@ from astara_router import models
|
||||
from astara_router import settings
|
||||
from astara_router.drivers import (bird, conntrackd, dnsmasq, ip, metadata,
|
||||
iptables, arp, hostname, loadbalancer)
|
||||
from astara_router.drivers.vpn import ipsec
|
||||
|
||||
|
||||
class ServiceManagerBase(object):
|
||||
@ -114,6 +115,7 @@ class RouterManager(ServiceManagerBase):
|
||||
self.update_routes(cache)
|
||||
self.update_arp()
|
||||
self.update_conntrackd()
|
||||
self.update_ipsec_vpn()
|
||||
self.reload_config()
|
||||
|
||||
def update_conntrackd(self):
|
||||
@ -162,6 +164,15 @@ class RouterManager(ServiceManagerBase):
|
||||
)
|
||||
mgr.remove_stale_entries(self._config)
|
||||
|
||||
def update_ipsec_vpn(self):
|
||||
mgr = ipsec.StrongswanManager()
|
||||
|
||||
if self._config.vpn:
|
||||
mgr.save_config(self._config)
|
||||
mgr.restart()
|
||||
else:
|
||||
mgr.stop()
|
||||
|
||||
def get_interfaces(self):
|
||||
return self.ip_mgr.get_interfaces()
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
|
||||
import abc
|
||||
import itertools
|
||||
import re
|
||||
|
||||
import netaddr
|
||||
@ -313,8 +314,9 @@ class Label(ModelBase):
|
||||
|
||||
|
||||
class Subnet(ModelBase):
|
||||
def __init__(self, cidr, gateway_ip, dhcp_enabled=True,
|
||||
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)
|
||||
@ -353,6 +355,7 @@ class Subnet(ModelBase):
|
||||
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'],
|
||||
@ -663,6 +666,211 @@ class FixedIp(ModelBase):
|
||||
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'
|
||||
|
||||
@ -740,6 +948,13 @@ class RouterConfiguration(SystemConfiguration):
|
||||
|
||||
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)
|
||||
@ -788,8 +1003,22 @@ class RouterConfiguration(SystemConfiguration):
|
||||
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')
|
||||
fields = (
|
||||
'networks', 'address_book', 'anchors', 'static_routes', 'vpn'
|
||||
)
|
||||
return dict((f, getattr(self, f)) for f in fields)
|
||||
|
||||
@property
|
||||
|
@ -41,5 +41,8 @@ ip6tables: CommandFilter, ip6tables, root
|
||||
# astara_router/drivers/metadata.py:
|
||||
mv_metadata: RegExpFilter, mv, root, mv, /tmp/metadata\.conf, /etc/metadata\.conf
|
||||
|
||||
# astara_router/drivers/vpn/ipsec.py:
|
||||
mv_strongswan: RegExpFilter, mv, root, mv, /tmp/ipsec.*, /etc/ipsec.*
|
||||
|
||||
# astara services
|
||||
services: CommandFilter, service, root
|
||||
|
@ -114,7 +114,8 @@ class SystemAPITestCase(unittest.TestCase):
|
||||
'interfaces': [],
|
||||
'management_address': None,
|
||||
'tenant_id': None
|
||||
}
|
||||
},
|
||||
'vpn': [],
|
||||
}
|
||||
}
|
||||
self.assertEqual(json.loads(result.data), expected)
|
||||
|
@ -103,6 +103,7 @@ class ARPTest(unittest2.TestCase):
|
||||
'name': 'ext',
|
||||
},
|
||||
'subnets': [{
|
||||
'id': 'theid',
|
||||
'cidr': '172.16.77.0/24',
|
||||
'gateway_ip': '172.16.77.1',
|
||||
'dhcp_enabled': True,
|
||||
|
@ -31,6 +31,7 @@ CONFIG = models.RouterConfiguration({
|
||||
'name': 'ext',
|
||||
'network_type': models.Network.TYPE_EXTERNAL,
|
||||
'subnets': [{
|
||||
'id': 'theid',
|
||||
'cidr': '172.16.77.0/24',
|
||||
'gateway_ip': '172.16.77.1',
|
||||
'dhcp_enabled': True,
|
||||
@ -48,6 +49,7 @@ CONFIG = models.RouterConfiguration({
|
||||
'name': 'internal',
|
||||
'network_type': models.Network.TYPE_INTERNAL,
|
||||
'subnets': [{
|
||||
'id': 'theid',
|
||||
'cidr': '192.168.0.0/24',
|
||||
'gateway_ip': '192.168.0.1',
|
||||
'dhcp_enabled': True,
|
||||
|
@ -176,6 +176,7 @@ class RouteTest(unittest2.TestCase):
|
||||
|
||||
def test_update_default_v4_from_subnet(self):
|
||||
subnet = dict(
|
||||
id='theid',
|
||||
cidr='192.168.89.0/24',
|
||||
gateway_ip='192.168.89.1',
|
||||
dhcp_enabled=True,
|
||||
@ -198,12 +199,14 @@ class RouteTest(unittest2.TestCase):
|
||||
|
||||
def test_update_multiple_v4_subnets(self):
|
||||
subnet = dict(
|
||||
id='id-1',
|
||||
cidr='192.168.89.0/24',
|
||||
gateway_ip='192.168.89.1',
|
||||
dhcp_enabled=True,
|
||||
dns_nameservers=[],
|
||||
)
|
||||
subnet2 = dict(
|
||||
id='id-2',
|
||||
cidr='192.168.71.0/24',
|
||||
gateway_ip='192.168.71.1',
|
||||
dhcp_enabled=True,
|
||||
@ -226,6 +229,7 @@ class RouteTest(unittest2.TestCase):
|
||||
|
||||
def test_update_default_v6(self):
|
||||
subnet = dict(
|
||||
id='theid',
|
||||
cidr='fe80::1/64',
|
||||
gateway_ip='fe80::1',
|
||||
dhcp_enabled=True,
|
||||
@ -248,12 +252,14 @@ class RouteTest(unittest2.TestCase):
|
||||
|
||||
def test_update_default_multiple_v6(self):
|
||||
subnet = dict(
|
||||
id='id-1',
|
||||
cidr='fe80::1/64',
|
||||
gateway_ip='fe80::1',
|
||||
dhcp_enabled=True,
|
||||
dns_nameservers=[],
|
||||
)
|
||||
subnet2 = dict(
|
||||
id='id-2',
|
||||
cidr='fe89::1/64',
|
||||
gateway_ip='fe89::1',
|
||||
dhcp_enabled=True,
|
||||
@ -278,6 +284,7 @@ class RouteTest(unittest2.TestCase):
|
||||
lambda *a, **kw: None)
|
||||
def test_custom_host_routes(self):
|
||||
subnet = dict(
|
||||
id='theid',
|
||||
cidr='192.168.89.0/24',
|
||||
gateway_ip='192.168.89.1',
|
||||
dhcp_enabled=True,
|
||||
@ -367,6 +374,7 @@ class RouteTest(unittest2.TestCase):
|
||||
self.assertEqual(len(cache.get('host_routes')), 1)
|
||||
sudo.reset_mock()
|
||||
network['subnets'].append(dict(
|
||||
id='add-1',
|
||||
cidr='192.168.90.0/24',
|
||||
gateway_ip='192.168.90.1',
|
||||
dhcp_enabled=True,
|
||||
@ -402,6 +410,7 @@ class RouteTest(unittest2.TestCase):
|
||||
|
||||
def test_custom_host_routes_failure(self):
|
||||
subnet = dict(
|
||||
id='theid',
|
||||
cidr='192.168.89.0/24',
|
||||
gateway_ip='192.168.89.1',
|
||||
dhcp_enabled=True,
|
||||
|
@ -289,9 +289,16 @@ class StaticRouteTestCase(TestCase):
|
||||
|
||||
class SubnetTestCase(TestCase):
|
||||
def test_subnet(self):
|
||||
s = models.Subnet('192.168.1.0/24', '192.168.1.1', True, ['8.8.8.8'],
|
||||
[])
|
||||
s = models.Subnet(
|
||||
'id',
|
||||
'192.168.1.0/24',
|
||||
'192.168.1.1',
|
||||
True,
|
||||
['8.8.8.8'],
|
||||
[]
|
||||
)
|
||||
|
||||
self.assertEqual(s.id, 'id')
|
||||
self.assertEqual(s.cidr, netaddr.IPNetwork('192.168.1.0/24'))
|
||||
self.assertEqual(s.gateway_ip, netaddr.IPAddress('192.168.1.1'))
|
||||
self.assertTrue(s.dhcp_enabled)
|
||||
@ -299,12 +306,12 @@ class SubnetTestCase(TestCase):
|
||||
self.assertEqual(s.host_routes, [])
|
||||
|
||||
def test_gateway_ip_empty(self):
|
||||
s = models.Subnet('192.168.1.0/24', '', True, ['8.8.8.8'],
|
||||
s = models.Subnet('id', '192.168.1.0/24', '', True, ['8.8.8.8'],
|
||||
[])
|
||||
self.assertIsNone(s.gateway_ip)
|
||||
|
||||
def test_gateway_ip_none(self):
|
||||
s = models.Subnet('192.168.1.0/24', None, True, ['8.8.8.8'],
|
||||
s = models.Subnet('id', '192.168.1.0/24', None, True, ['8.8.8.8'],
|
||||
[])
|
||||
self.assertIsNone(s.gateway_ip)
|
||||
|
||||
@ -365,6 +372,7 @@ class NetworkTestCase(TestCase):
|
||||
class RouterConfigurationTestCase(TestCase):
|
||||
def test_init_only_networks(self):
|
||||
subnet = dict(
|
||||
id='id',
|
||||
cidr='192.168.1.0/24',
|
||||
gateway_ip='192.168.1.1',
|
||||
dhcp_enabled=True,
|
||||
@ -542,7 +550,8 @@ class RouterConfigurationTestCase(TestCase):
|
||||
expected = dict(networks=[],
|
||||
address_book={},
|
||||
static_routes=[],
|
||||
anchors=[])
|
||||
anchors=[],
|
||||
vpn=[])
|
||||
|
||||
self.assertEqual(c.to_dict(), expected)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user