Adds ConntrackdManager
Used for setting up conntrackd between two clustered peers. Partially-implements: blueprint appliance-ha Change-Id: Ice3f4dbed02b877bc64ae73879a74acc26cca47e
This commit is contained in:
parent
02383adf64
commit
8633d1a5bc
|
@ -21,6 +21,7 @@
|
||||||
- include: tasks/base.yml
|
- include: tasks/base.yml
|
||||||
- include: tasks/astara.yml
|
- include: tasks/astara.yml
|
||||||
- include: tasks/bird.yml
|
- include: tasks/bird.yml
|
||||||
|
- include: tasks/conntrackd.yml
|
||||||
- include: tasks/dnsmasq.yml
|
- include: tasks/dnsmasq.yml
|
||||||
- include: tasks/extras.yml
|
- include: tasks/extras.yml
|
||||||
when: install_extras
|
when: install_extras
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: install conntrackd
|
||||||
|
apt: name=conntrackd state=installed install_recommends=no
|
||||||
|
|
||||||
|
- name: install conntrackd notify script to /etc/conntrackd
|
||||||
|
copy: src=/usr/share/doc/conntrackd/examples/sync/primary-backup.sh dest=/etc/conntrackd/primary-backup.sh mode=0755
|
|
@ -0,0 +1,38 @@
|
||||||
|
General {
|
||||||
|
HashSize 8192
|
||||||
|
HashLimit 65535
|
||||||
|
Syslog on
|
||||||
|
LockFile /var/lock/conntrackd.lock
|
||||||
|
UNIX {
|
||||||
|
Path /var/run/conntrackd.sock
|
||||||
|
Backlog 20
|
||||||
|
}
|
||||||
|
SocketBufferSize 262142
|
||||||
|
SocketBufferSizeMaxGrown 655355
|
||||||
|
Filter {
|
||||||
|
Protocol Accept {
|
||||||
|
TCP
|
||||||
|
}
|
||||||
|
Address Ignore {
|
||||||
|
IPv4_address 127.0.0.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Sync {
|
||||||
|
Mode FTFW {
|
||||||
|
}
|
||||||
|
UDP Default {
|
||||||
|
{%- if management_ip_version == 4 %}
|
||||||
|
IPv4_address {{ source_address }}
|
||||||
|
IPv4_Destination_Address {{ destination_address }}
|
||||||
|
{%- else %}
|
||||||
|
IPv6_address {{ source_address }}
|
||||||
|
IPv6_Destination_Address {{ destination_address }}
|
||||||
|
{%- endif %}
|
||||||
|
Port 3780
|
||||||
|
Interface {{ interface }}
|
||||||
|
SndSocketBuffer 24985600
|
||||||
|
RcvSocketBuffer 24985600
|
||||||
|
Checksum on
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
# Copyright (c) 2016 Akanda, 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 os
|
||||||
|
|
||||||
|
from astara_router.drivers import base
|
||||||
|
from astara_router import utils
|
||||||
|
|
||||||
|
|
||||||
|
class ConntrackdManager(base.Manager):
|
||||||
|
"""
|
||||||
|
A class to provide facilities to interact with the conntrackd daemon.
|
||||||
|
"""
|
||||||
|
EXECUTABLE = 'service'
|
||||||
|
CONFIG_FILE_TEMPLATE = os.path.join(
|
||||||
|
os.path.dirname(__file__), 'conntrackd.conf.template')
|
||||||
|
|
||||||
|
# Debian defaults
|
||||||
|
CONFIG_FILE = '/etc/conntrackd/conntrackd.conf'
|
||||||
|
|
||||||
|
# Debian installs this to /usr/share/doc/examples/sync but our
|
||||||
|
# DIB recipe will install it here.
|
||||||
|
NOTIFY_SCRIPT = '/etc/conntrackd/primary-backup.sh'
|
||||||
|
|
||||||
|
def __init__(self, root_helper='sudo astara-rootwrap /etc/rootwrap.conf'):
|
||||||
|
"""
|
||||||
|
Initializes ConntrackdManager class.
|
||||||
|
|
||||||
|
:type root_helper: str
|
||||||
|
:param root_helper: System utility used to gain escalate privileges.
|
||||||
|
"""
|
||||||
|
super(ConntrackdManager, self).__init__(root_helper)
|
||||||
|
self._config_templ = utils.load_template(self.CONFIG_FILE_TEMPLATE)
|
||||||
|
self._should_restart = False
|
||||||
|
|
||||||
|
def save_config(self, config, generic_to_host):
|
||||||
|
"""
|
||||||
|
Renders template and writes to the conntrackd file
|
||||||
|
|
||||||
|
:type config: astara_router.models.Configuration
|
||||||
|
:param config: An astara_router.models.Configuration object containing
|
||||||
|
the ha_config configuration.
|
||||||
|
:param generic_to_host: A callable used to resolve generic interface
|
||||||
|
name to system interface name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
mgt_interface = None
|
||||||
|
for interface in config.interfaces:
|
||||||
|
if interface.management:
|
||||||
|
mgt_interface = interface
|
||||||
|
break
|
||||||
|
mgt_addr = mgt_interface.first_v6 or mgt_interface.first_v4
|
||||||
|
ctxt = {
|
||||||
|
'source_address': str(mgt_addr),
|
||||||
|
'management_ip_version': mgt_addr.version,
|
||||||
|
'destination_address': config.ha_config['peers'][0],
|
||||||
|
'interface': generic_to_host(interface.ifname),
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
old_config_hash = utils.hash_file(self.CONFIG_FILE)
|
||||||
|
except IOError:
|
||||||
|
old_config_hash = None
|
||||||
|
|
||||||
|
utils.replace_file(
|
||||||
|
'/tmp/conntrackd.conf',
|
||||||
|
self._config_templ.render(ctxt))
|
||||||
|
utils.execute(
|
||||||
|
['mv', '/tmp/conntrackd.conf', self.CONFIG_FILE],
|
||||||
|
self.root_helper)
|
||||||
|
|
||||||
|
if old_config_hash != utils.hash_file(self.CONFIG_FILE):
|
||||||
|
self._should_restart = True
|
||||||
|
|
||||||
|
def restart(self):
|
||||||
|
"""
|
||||||
|
Restarts the conntrackd daemon if config has been changed
|
||||||
|
"""
|
||||||
|
if not self._should_restart:
|
||||||
|
return
|
||||||
|
self.sudo('conntrackd', 'restart')
|
|
@ -1,3 +1,14 @@
|
||||||
|
vrrp_sync_group astara_vrrp_group {
|
||||||
|
group {
|
||||||
|
{%- for instance in vrrp_instances %}
|
||||||
|
{{ instance.name }}
|
||||||
|
{%- endfor %}
|
||||||
|
}
|
||||||
|
notify_master "{{ notify_script }} primary"
|
||||||
|
notify_backup "{{ notify_script }} backup"
|
||||||
|
notify_fault "{{ notify_script }} fault"
|
||||||
|
}
|
||||||
|
|
||||||
{%- for instance in vrrp_instances %}
|
{%- for instance in vrrp_instances %}
|
||||||
vrrp_instance {{ instance.name }} {
|
vrrp_instance {{ instance.name }} {
|
||||||
native_ipv6
|
native_ipv6
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from astara_router.drivers import base
|
from astara_router.drivers import base, conntrackd
|
||||||
from astara_router import utils
|
from astara_router import utils
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,6 +84,7 @@ class KeepalivedManager(base.Manager):
|
||||||
self.config_tmpl = utils.load_template(self.CONFIG_FILE_TEMPLATE)
|
self.config_tmpl = utils.load_template(self.CONFIG_FILE_TEMPLATE)
|
||||||
self.peers = []
|
self.peers = []
|
||||||
self.priority = 0
|
self.priority = 0
|
||||||
|
self.notify_script = conntrackd.ConntrackdManager.NOTIFY_SCRIPT
|
||||||
self._last_config_hash = None
|
self._last_config_hash = None
|
||||||
|
|
||||||
def set_management_address(self, address):
|
def set_management_address(self, address):
|
||||||
|
@ -123,6 +124,7 @@ class KeepalivedManager(base.Manager):
|
||||||
return self.config_tmpl.render(
|
return self.config_tmpl.render(
|
||||||
priority=self.priority,
|
priority=self.priority,
|
||||||
peers=self.peers,
|
peers=self.peers,
|
||||||
|
notify_script=self.notify_script,
|
||||||
vrrp_instances=self.instances.values())
|
vrrp_instances=self.instances.values())
|
||||||
|
|
||||||
def reload(self):
|
def reload(self):
|
||||||
|
|
|
@ -20,7 +20,7 @@ import re
|
||||||
|
|
||||||
from astara_router import models
|
from astara_router import models
|
||||||
from astara_router import settings
|
from astara_router import settings
|
||||||
from astara_router.drivers import (bird, dnsmasq, ip, metadata,
|
from astara_router.drivers import (bird, conntrackd, dnsmasq, ip, metadata,
|
||||||
iptables, arp, hostname, loadbalancer)
|
iptables, arp, hostname, loadbalancer)
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,8 +113,16 @@ class RouterManager(ServiceManagerBase):
|
||||||
self.update_firewall()
|
self.update_firewall()
|
||||||
self.update_routes(cache)
|
self.update_routes(cache)
|
||||||
self.update_arp()
|
self.update_arp()
|
||||||
|
self.update_conntrackd()
|
||||||
self.reload_config()
|
self.reload_config()
|
||||||
|
|
||||||
|
def update_conntrackd(self):
|
||||||
|
if not self._config.ha:
|
||||||
|
return
|
||||||
|
mgr = conntrackd.ConntrackdManager()
|
||||||
|
mgr.save_config(self._config, self.ip_mgr.generic_to_host)
|
||||||
|
mgr.restart()
|
||||||
|
|
||||||
def update_dhcp(self):
|
def update_dhcp(self):
|
||||||
mgr = dnsmasq.DHCPManager()
|
mgr = dnsmasq.DHCPManager()
|
||||||
mgr.delete_all_config()
|
mgr.delete_all_config()
|
||||||
|
|
|
@ -9,6 +9,9 @@ mv_bird: RegExpFilter, mv, root, mv, /tmp/bird6\.conf, /etc/bird/bird6\.conf
|
||||||
arp: CommandFilter, /usr/sbin/arp, root
|
arp: CommandFilter, /usr/sbin/arp, root
|
||||||
astara_gratuitous_arp: CommandFilter, astara-gratuitous-arp, root
|
astara_gratuitous_arp: CommandFilter, astara-gratuitous-arp, root
|
||||||
|
|
||||||
|
# astara_router/drivers/conntrackd.py:
|
||||||
|
mv_conntrackd: RegExpFilter, mv, root, mv, /tmp/conntrackd\.conf, /etc/conntrackd/conntrackd\.conf
|
||||||
|
|
||||||
# astara_router/drivers/dnsmasq.py:
|
# astara_router/drivers/dnsmasq.py:
|
||||||
mv_dnsmasq: RegExpFilter, mv, root, mv, /tmp/dnsmasq\.conf, /etc/dnsmasq\.d/.*\.conf
|
mv_dnsmasq: RegExpFilter, mv, root, mv, /tmp/dnsmasq\.conf, /etc/dnsmasq\.d/.*\.conf
|
||||||
rm: CommandFilter, rm, root
|
rm: CommandFilter, rm, root
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
features:
|
||||||
|
- The appliance is now built with conntrackd installed and supports configuring
|
||||||
|
the connection tracking service among pairs of clustered HA router appliances.
|
|
@ -0,0 +1,75 @@
|
||||||
|
# Copyright 2016 Akanda, Inc.
|
||||||
|
#
|
||||||
|
# Author: 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 __builtin__
|
||||||
|
|
||||||
|
from unittest2 import TestCase
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from test.unit import fakes
|
||||||
|
|
||||||
|
from astara_router.drivers import conntrackd
|
||||||
|
|
||||||
|
|
||||||
|
class ConntrackddManagerTestCase(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(ConntrackddManagerTestCase, self).setUp()
|
||||||
|
self.mgr = conntrackd.ConntrackdManager()
|
||||||
|
self.mgr._config_templ = mock.Mock(
|
||||||
|
render=mock.Mock()
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch('astara_router.utils.execute')
|
||||||
|
@mock.patch('astara_router.utils.replace_file')
|
||||||
|
@mock.patch('astara_router.utils.hash_file')
|
||||||
|
def test_save_config(self, fake_hash, fake_replace, fake_execute):
|
||||||
|
fake_generic_to_host = mock.Mock(return_value='eth0')
|
||||||
|
fake_interface = fakes.fake_interface()
|
||||||
|
fake_mgt_interface = fakes.fake_mgt_interface()
|
||||||
|
ha_config = {
|
||||||
|
'peers': ['10.0.0.2'],
|
||||||
|
}
|
||||||
|
fake_config = mock.Mock(
|
||||||
|
interfaces=[fake_interface, fake_mgt_interface],
|
||||||
|
ha_config=ha_config,
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_hash.side_effect = ['hash1', 'hash2']
|
||||||
|
self.mgr._config_templ.render.return_value = 'new_config'
|
||||||
|
self.mgr.save_config(fake_config, fake_generic_to_host)
|
||||||
|
self.mgr._config_templ.render.assert_called_with(dict(
|
||||||
|
source_address=str(fake_mgt_interface.addresses[0].ip),
|
||||||
|
management_ip_version=4,
|
||||||
|
destination_address='10.0.0.2',
|
||||||
|
interface='eth0',
|
||||||
|
))
|
||||||
|
self.assertTrue(self.mgr._should_restart)
|
||||||
|
fake_replace.assert_called_with('/tmp/conntrackd.conf', 'new_config')
|
||||||
|
fake_execute.assert_called_with(
|
||||||
|
['mv', '/tmp/conntrackd.conf', '/etc/conntrackd/conntrackd.conf'],
|
||||||
|
self.mgr.root_helper)
|
||||||
|
|
||||||
|
@mock.patch.object(conntrackd.ConntrackdManager, 'sudo')
|
||||||
|
def test_restart(self, fake_sudo):
|
||||||
|
self.mgr._should_restart = True
|
||||||
|
self.mgr.restart()
|
||||||
|
fake_sudo.assert_called_with('conntrackd', 'restart')
|
||||||
|
|
||||||
|
@mock.patch.object(conntrackd.ConntrackdManager, 'sudo')
|
||||||
|
def test_restart_skip(self, fake_sudo):
|
||||||
|
self.mgr._should_restart = False
|
||||||
|
self.mgr.restart()
|
||||||
|
self.assertFalse(fake_sudo.called)
|
Loading…
Reference in New Issue