summaryrefslogtreecommitdiff
path: root/src/op_mode/conntrack_sync.py
blob: 54ecd6d0efcb42b0e0af04ed80a4bd20751e5be4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!/usr/bin/env python3
#
# Copyright (C) 2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import os
import syslog
import xmltodict

from argparse import ArgumentParser
from vyos.configquery import CliShellApiConfigQuery
from vyos.configquery import ConfigTreeQuery
from vyos.util import call
from vyos.util import commit_in_progress
from vyos.util import cmd
from vyos.util import run
from vyos.template import render_to_string

conntrackd_bin = '/usr/sbin/conntrackd'
conntrackd_config = '/run/conntrackd/conntrackd.conf'
failover_state_file = '/var/run/vyatta-conntrackd-failover-state'

parser = ArgumentParser(description='Conntrack Sync')
group = parser.add_mutually_exclusive_group()
group.add_argument('--restart', help='Restart connection tracking synchronization service', action='store_true')
group.add_argument('--reset-cache-internal', help='Reset internal cache', action='store_true')
group.add_argument('--reset-cache-external', help='Reset external cache', action='store_true')
group.add_argument('--show-internal', help='Show internal (main) tracking cache', action='store_true')
group.add_argument('--show-external', help='Show external (main) tracking cache', action='store_true')
group.add_argument('--show-internal-expect', help='Show internal (expect) tracking cache', action='store_true')
group.add_argument('--show-external-expect', help='Show external (expect) tracking cache', action='store_true')
group.add_argument('--show-statistics', help='Show connection syncing statistics', action='store_true')
group.add_argument('--show-status', help='Show conntrack-sync status', action='store_true')

def is_configured():
    """ Check if conntrack-sync service is configured """
    config = CliShellApiConfigQuery()
    if not config.exists(['service', 'conntrack-sync']):
        print('Service conntrackd-sync not configured!')
        exit(1)

def send_bulk_update():
    """ send bulk update of internal-cache to other systems """
    tmp = run(f'{conntrackd_bin} -C {conntrackd_config} -B')
    if tmp > 0:
        print('ERROR: failed to send bulk update to other conntrack-sync systems')

def request_sync():
    """ request resynchronization with other systems """
    tmp = run(f'{conntrackd_bin} -C {conntrackd_config} -n')
    if tmp > 0:
        print('ERROR: failed to request resynchronization of external cache')

def flush_cache(direction):
    """ flush conntrackd cache (internal or external) """
    if direction not in ['internal', 'external']:
        raise ValueError()
    tmp = run(f'{conntrackd_bin} -C {conntrackd_config} -f {direction}')
    if tmp > 0:
        print('ERROR: failed to clear {direction} cache')

def xml_to_stdout(xml):
    out = []
    for line in xml.splitlines():
        if line == '\n':
            continue
        parsed = xmltodict.parse(line)
        out.append(parsed)

    print(render_to_string('conntrackd/conntrackd.op-mode.j2', {'data' : out}))

if __name__ == '__main__':
    args = parser.parse_args()
    syslog.openlog(ident='conntrack-tools', logoption=syslog.LOG_PID,
                   facility=syslog.LOG_INFO)

    if args.restart:
        is_configured()
        if commit_in_progress():
            print('Cannot restart conntrackd while a commit is in progress')
            exit(1)

        syslog.syslog('Restarting conntrack sync service...')
        cmd('systemctl restart conntrackd.service')
        # request resynchronization with other systems
        request_sync()
        # send bulk update of internal-cache to other systems
        send_bulk_update()

    elif args.reset_cache_external:
        is_configured()
        syslog.syslog('Resetting external cache of conntrack sync service...')

        # flush the external cache
        flush_cache('external')
        # request resynchronization with other systems
        request_sync()

    elif args.reset_cache_internal:
        is_configured()
        syslog.syslog('Resetting internal cache of conntrack sync service...')
        # flush the internal cache
        flush_cache('internal')

        # request resynchronization of internal cache with kernel conntrack table
        tmp = run(f'{conntrackd_bin} -C {conntrackd_config} -R')
        if tmp > 0:
            print('ERROR: failed to resynchronize internal cache with kernel conntrack table')

        # send bulk update of internal-cache to other systems
        send_bulk_update()

    elif args.show_external or args.show_internal or args.show_external_expect or args.show_internal_expect:
        is_configured()
        opt = ''
        if args.show_external:
            opt = '-e ct'
        elif args.show_external_expect:
            opt = '-e expect'
        elif args.show_internal:
            opt = '-i ct'
        elif args.show_internal_expect:
            opt = '-i expect'

        if args.show_external or args.show_internal:
            print('Main Table Entries:')
        else:
            print('Expect Table Entries:')
        out = cmd(f'sudo {conntrackd_bin} -C {conntrackd_config} {opt} -x')
        xml_to_stdout(out)

    elif args.show_statistics:
        is_configured()
        config = ConfigTreeQuery()
        print('\nMain Table Statistics:\n')
        call(f'sudo {conntrackd_bin} -C {conntrackd_config} -s')
        print()
        if config.exists(['service', 'conntrack-sync', 'expect-sync']):
            print('\nExpect Table Statistics:\n')
            call(f'sudo {conntrackd_bin} -C {conntrackd_config} -s exp')
            print()

    elif args.show_status:
        is_configured()
        config = ConfigTreeQuery()
        ct_sync_intf = config.list_nodes(['service', 'conntrack-sync', 'interface'])
        ct_sync_intf = ', '.join(ct_sync_intf)
        failover_state = "no transition yet!"
        expect_sync_protocols = "disabled"

        if config.exists(['service', 'conntrack-sync', 'failover-mechanism', 'vrrp']):
            failover_mechanism = "vrrp"
            vrrp_sync_grp = config.value(['service', 'conntrack-sync', 'failover-mechanism', 'vrrp', 'sync-group'])

        if os.path.isfile(failover_state_file):
            with open(failover_state_file, "r") as f:
                failover_state = f.readline()

        if config.exists(['service', 'conntrack-sync', 'expect-sync']):
            expect_sync_protocols = config.values(['service', 'conntrack-sync', 'expect-sync'])
            if 'all' in expect_sync_protocols:
                expect_sync_protocols = ["ftp", "sip", "h323", "nfs", "sqlnet"]
            expect_sync_protocols = ', '.join(expect_sync_protocols)

        show_status = (f'\nsync-interface        : {ct_sync_intf}\n'
                       f'failover-mechanism    : {failover_mechanism} [sync-group {vrrp_sync_grp}]\n'
                       f'last state transition : {failover_state}'
                       f'ExpectationSync       : {expect_sync_protocols}')

        print(show_status)

    else:
        parser.print_help()
        exit(1)