You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
FreeDMR/tests/test_hdstack.py

737 lines
27 KiB

import configparser
import tempfile
import unittest
from pathlib import Path
import config
import hdstack_runtime
ROOT = Path(__file__).resolve().parents[1]
DOCKER_CONFIGS = ROOT / 'docker-configs'
class HDStackRuntimeTests(unittest.TestCase):
def setUp(self):
self.temporary = tempfile.TemporaryDirectory()
self.addCleanup(self.temporary.cleanup)
self.root = Path(self.temporary.name)
self.main_config = self.root / 'freedmr.cfg'
self.bridge_config = self.root / 'freedmr-bridge.cfg'
self.bridge_rules = self.root / 'rules.py'
self.main_config.write_text(MAIN_CONFIG, encoding='utf-8')
self.bridge_config.write_text(BRIDGE_CONFIG, encoding='utf-8')
self.bridge_rules.write_text('BRIDGES = {}\n', encoding='utf-8')
def generate(self, instances=2, base_id=23400, bridge_enabled=True):
return hdstack_runtime.generate_hdstack(
self.main_config,
self.bridge_config,
self.bridge_rules,
self.root / 'generated',
instances,
base_id,
bridge_enabled=bridge_enabled,
)
def test_two_worker_layout_uses_base_id_plus_one_and_disjoint_ranges(self):
layout = self.generate()
aggregator = _read_config(layout.aggregator_config)
workers = [_read_config(path) for path in layout.worker_configs]
self.assertEqual(layout.backend_ranges, ((54000, 54099), (54100, 54199)))
self.assertEqual(aggregator.getint('GLOBAL', 'SERVER_ID'), 23400)
self.assertEqual(
[worker.getint('GLOBAL', 'SERVER_ID') for worker in workers],
[23401, 23402],
)
self.assertEqual(aggregator.getint('GLOBAL', 'HDSTACK_INSTANCES'), 2)
self.assertEqual(
aggregator.getint('GLOBAL', 'HDSTACK_BASE_SERVER_ID'),
23400,
)
self.assertTrue(all(
worker.getint('GLOBAL', 'HDSTACK_INSTANCES') == 2
and worker.getint('GLOBAL', 'HDSTACK_BASE_SERVER_ID') == 23400
for worker in workers
))
self.assertEqual(
[worker.getint('SYSTEM', 'PORT') for worker in workers],
[54000, 54100],
)
self.assertEqual(
[worker.getint('SYSTEM', 'GENERATOR') for worker in workers],
[100, 100],
)
def test_five_worker_layout_is_derived_without_hard_coded_second_worker(self):
layout = self.generate(instances=5)
self.assertEqual(
layout.backend_ranges,
(
(54000, 54099),
(54100, 54199),
(54200, 54299),
(54300, 54399),
(54400, 54499),
),
)
self.assertEqual(len(layout.worker_configs), 5)
self.assertEqual(layout.worker_report_ports, (4322, 4323, 4324, 4325, 4326))
def test_aggregator_has_only_external_and_internal_openbridge_systems(self):
layout = self.generate()
aggregator = _read_config(layout.aggregator_config)
self.assertFalse(aggregator.getboolean('REPORTS', 'REPORT'))
self.assertTrue(aggregator.getboolean('GLOBAL', 'GEN_STAT_BRIDGES'))
self.assertNotIn('SYSTEM', aggregator.sections())
self.assertNotIn('ECHO', aggregator.sections())
self.assertIn('EXTERNAL-FBP', aggregator.sections())
self.assertEqual(aggregator.getint('EXTERNAL-FBP', 'PORT'), 62044)
protocol_sections = [
section for section in aggregator.sections()
if aggregator.has_option(section, 'MODE')
]
self.assertTrue(protocol_sections)
self.assertTrue(all(aggregator.get(section, 'MODE') == 'OPENBRIDGE'
for section in protocol_sections))
def test_workers_copy_system_policy_and_only_add_aggregator_link(self):
source = _read_config(self.main_config)
layout = self.generate()
workers = [_read_config(path) for path in layout.worker_configs]
for number, worker in enumerate(workers, 1):
expected = dict(source.items('SYSTEM'))
expected['port'] = str(54000 + (number - 1) * 100)
self.assertEqual(dict(worker.items('SYSTEM')), expected)
self.assertNotIn('EXTERNAL-FBP', worker.sections())
self.assertEqual(
[
section for section in worker.sections()
if worker.has_option(section, 'MODE')
],
['SYSTEM', 'ECHO', 'OBP-HDSTACK-AGGREGATOR'],
)
self.assertEqual(
worker.getint('OBP-HDSTACK-AGGREGATOR', 'NETWORK_ID'),
23400,
)
self.assertFalse(worker.getboolean('GLOBAL', 'ENABLE_API'))
self.assertFalse(worker.getboolean('GLOBAL', 'DATA_GATEWAY'))
def test_data_gateway_is_connected_directly_to_each_worker(self):
self.main_config.write_text(
MAIN_CONFIG + DATA_GATEWAY_CONFIG,
encoding='utf-8',
)
layout = self.generate()
aggregator = _read_config(layout.aggregator_config)
workers = [_read_config(path) for path in layout.worker_configs]
self.assertFalse(aggregator.getboolean('GLOBAL', 'DATA_GATEWAY'))
self.assertNotIn('DATA-GATEWAY', aggregator.sections())
for number, worker in enumerate(workers, 1):
self.assertTrue(worker.getboolean('GLOBAL', 'DATA_GATEWAY'))
self.assertIn('DATA-GATEWAY', worker.sections())
self.assertEqual(
worker.getint('DATA-GATEWAY', 'PORT'),
62040 + number,
)
self.assertEqual(
worker.getint('DATA-GATEWAY', 'TARGET_PORT'),
62030 + number,
)
self.assertEqual(
worker.get('DATA-GATEWAY', 'TARGET_IP'),
'data-gateway',
)
self.assertEqual(worker.getint('DATA-GATEWAY', 'PROTO_VER'), 5)
self.assertTrue(worker.getboolean('DATA-GATEWAY', 'ENHANCED_OBP'))
self.assertNotIn('EXTERNAL-FBP', worker.sections())
def test_data_gateway_requires_fbp5_fbcp_and_valid_derived_ports(self):
invalid_profiles = (
DATA_GATEWAY_CONFIG.replace("MODE: OPENBRIDGE", "MODE: MASTER"),
DATA_GATEWAY_CONFIG.replace("PROTO_VER: 5", "PROTO_VER: 1"),
DATA_GATEWAY_CONFIG.replace("ENHANCED_OBP: True", "ENHANCED_OBP: False"),
)
for data_gateway_config in invalid_profiles:
with self.subTest(config=data_gateway_config):
self.main_config.write_text(
MAIN_CONFIG + data_gateway_config,
encoding="utf-8",
)
with self.assertRaises(hdstack_runtime.HDStackConfigurationError):
self.generate()
for data_gateway_config in (
DATA_GATEWAY_CONFIG.replace("PORT: 62041", "PORT: 54000"),
DATA_GATEWAY_CONFIG.replace("TARGET_PORT: 62031", "TARGET_PORT: 65535"),
):
with self.subTest(config=data_gateway_config):
self.main_config.write_text(
MAIN_CONFIG + data_gateway_config,
encoding="utf-8",
)
with self.assertRaises(hdstack_runtime.HDStackConfigurationError):
self.generate()
def test_internal_fbp_links_are_reciprocal_and_use_expected_ids(self):
layout = self.generate()
aggregator = _read_config(layout.aggregator_config)
workers = [_read_config(path) for path in layout.worker_configs]
for number, worker in enumerate(workers, 1):
aggregator_link = 'OBP-HDSTACK-WORKER-{}'.format(number)
self.assertEqual(
(
aggregator.getint(aggregator_link, 'PORT'),
aggregator.getint(aggregator_link, 'TARGET_PORT'),
aggregator.getint(aggregator_link, 'NETWORK_ID'),
),
(7100 + number, 7000 + number, 23400 + number),
)
self.assertEqual(
(
worker.getint('OBP-HDSTACK-AGGREGATOR', 'PORT'),
worker.getint('OBP-HDSTACK-AGGREGATOR', 'TARGET_PORT'),
worker.getint('OBP-HDSTACK-AGGREGATOR', 'NETWORK_ID'),
),
(7000 + number, 7100 + number, 23400),
)
self.assertEqual(
aggregator.getint(aggregator_link, 'PROTO_VER'),
5,
)
self.assertTrue(
aggregator.getboolean(aggregator_link, 'ENHANCED_OBP')
)
self.assertEqual(worker.getint('OBP-HDSTACK-AGGREGATOR', 'PROTO_VER'), 5)
self.assertTrue(
worker.getboolean('OBP-HDSTACK-AGGREGATOR', 'ENHANCED_OBP')
)
self.assertFalse(
aggregator.getboolean(aggregator_link, 'RELAX_CHECKS')
)
self.assertFalse(
worker.getboolean('OBP-HDSTACK-AGGREGATOR', 'RELAX_CHECKS')
)
def test_bridge_link_is_reciprocal_fbp_v5_with_expected_ids(self):
layout = self.generate()
aggregator = _read_config(layout.aggregator_config)
bridge = _read_config(layout.bridge_config)
self.assertEqual(
(
aggregator.getint('OBP-HDSTACK-BRIDGE', 'PORT'),
aggregator.getint('OBP-HDSTACK-BRIDGE', 'TARGET_PORT'),
aggregator.getint('OBP-HDSTACK-BRIDGE', 'NETWORK_ID'),
),
(7200, 7201, 23500),
)
self.assertEqual(
(
bridge.getint('OBP-HDSTACK-AGGREGATOR', 'PORT'),
bridge.getint('OBP-HDSTACK-AGGREGATOR', 'TARGET_PORT'),
bridge.getint('OBP-HDSTACK-AGGREGATOR', 'NETWORK_ID'),
),
(7201, 7200, 23400),
)
for parser, section in (
(aggregator, 'OBP-HDSTACK-BRIDGE'),
(bridge, 'OBP-HDSTACK-AGGREGATOR'),
):
self.assertEqual(parser.get(section, 'MODE'), 'OPENBRIDGE')
self.assertEqual(parser.getint(section, 'PROTO_VER'), 5)
self.assertTrue(parser.getboolean(section, 'ENHANCED_OBP'))
self.assertFalse(parser.getboolean(section, 'RELAX_CHECKS'))
self.assertEqual(parser.get(section, 'PASSPHRASE'), 'internal')
def test_each_worker_has_an_isolated_loro_peer(self):
layout = self.generate(instances=3)
workers = [_read_config(path) for path in layout.worker_configs]
loros = [_read_config(path) for path in layout.loro_configs]
self.assertEqual(len(loros), 3)
for number, (worker, loro) in enumerate(zip(workers, loros), 1):
expected_peer_port = 54916 + (number - 1) * 2
expected_master_port = 54915 + (number - 1) * 2
self.assertEqual(worker.get('ECHO', 'IP'), '127.0.0.1')
self.assertEqual(worker.getint('ECHO', 'PORT'), expected_peer_port)
self.assertEqual(worker.get('ECHO', 'MASTER_IP'), '127.0.0.1')
self.assertEqual(
worker.getint('ECHO', 'MASTER_PORT'),
expected_master_port,
)
self.assertEqual(loro.get('PARROT', 'IP'), '127.0.0.1')
self.assertEqual(loro.getint('PARROT', 'PORT'), expected_master_port)
self.assertEqual(loro.getint('PARROT', 'MAX_PEERS'), 1)
self.assertEqual(
loro.get('PARROT', 'PASSPHRASE'),
worker.get('ECHO', 'PASSPHRASE'),
)
self.assertFalse(loro.getboolean('REPORTS', 'REPORT'))
self.assertEqual(
loro.getint('REPORTS', 'REPORT_PORT'),
4821 + number - 1,
)
self.assertEqual(loro.get('LOGGER', 'LOG_NAME'), 'ECHO-{}'.format(number))
def test_reporting_mux_uses_workers_only_and_preserves_client_acl(self):
layout = self.generate()
supervisor = layout.supervisor_config.read_text(encoding='utf-8')
workers = [_read_config(path) for path in layout.worker_configs]
self.assertIn('--listen-port 4321', supervisor)
self.assertIn('--client 127.0.0.1', supervisor)
self.assertIn('--client 192.0.2.20', supervisor)
self.assertIn('--backend 1,127.0.0.1,4322', supervisor)
self.assertIn('--backend 2,127.0.0.1,4323', supervisor)
self.assertNotIn('--backend 0,', supervisor)
self.assertNotIn('--backend 3,', supervisor)
self.assertEqual(
[worker.getint('REPORTS', 'REPORT_PORT') for worker in workers],
[4322, 4323],
)
self.assertTrue(all(
worker.get('REPORTS', 'REPORT_CLIENTS') == '127.0.0.1'
for worker in workers
))
def test_supervisor_runs_aggregator_workers_bridge_proxy_mux_and_playback(self):
layout = self.generate()
supervisor = layout.supervisor_config.read_text(encoding='utf-8')
for program in (
'hdstack-aggregator',
'hdstack-worker-1',
'hdstack-worker-2',
'bridge',
'playback-1',
'playback-2',
'proxy',
'report-mux',
):
self.assertIn('[program:{}]'.format(program), supervisor)
self.assertNotIn('[program:playback]', supervisor)
self.assertIn(
'bridge.py -c {} -r {}'.format(layout.bridge_config, self.bridge_rules),
supervisor,
)
self.assertIn(
'FDPROXY_BACKEND_PORT_RANGES="[[54000,54099],[54100,54199]]"',
supervisor,
)
def test_bridge_can_be_disabled_without_bridge_files(self):
self.bridge_config.unlink()
self.bridge_rules.unlink()
layout = self.generate(bridge_enabled=False)
supervisor = layout.supervisor_config.read_text(encoding='utf-8')
self.assertNotIn('[program:bridge]', supervisor)
for program in (
'hdstack-aggregator',
'hdstack-worker-1',
'hdstack-worker-2',
'playback-1',
'playback-2',
'proxy',
'report-mux',
):
self.assertIn('[program:{}]'.format(program), supervisor)
self.assertIsNone(layout.bridge_config)
def test_reporting_disabled_omits_mux_and_worker_reporting(self):
self.main_config.write_text(
MAIN_CONFIG.replace('REPORT: True', 'REPORT: False'),
encoding='utf-8',
)
layout = self.generate()
supervisor = layout.supervisor_config.read_text(encoding='utf-8')
self.assertNotIn('[program:report-mux]', supervisor)
for path in layout.worker_configs:
self.assertFalse(_read_config(path).getboolean('REPORTS', 'REPORT'))
def test_generated_configs_are_accepted_by_current_freedmr_parser(self):
layout = self.generate()
generated = (
(layout.aggregator_config,)
+ layout.worker_configs
+ layout.loro_configs
+ (layout.bridge_config,)
)
for path in generated:
parsed = config.build_config(str(path))
self.assertIn('GLOBAL', parsed)
self.assertIn('SYSTEMS', parsed)
def test_mutable_worker_state_files_are_distinct(self):
layout = self.generate(instances=3)
workers = [_read_config(path) for path in layout.worker_configs]
self.assertEqual(
[worker.get('ALIASES', 'SUB_MAP_FILE') for worker in workers],
['sub_map-hdstack-1.pkl', 'sub_map-hdstack-2.pkl', 'sub_map-hdstack-3.pkl'],
)
self.assertEqual(
[worker.get('ALIASES', 'KEYS_FILE') for worker in workers],
['keys-hdstack-1.json', 'keys-hdstack-2.json', 'keys-hdstack-3.json'],
)
self.assertTrue(all(
not worker.getboolean('ALIASES', 'TRY_DOWNLOAD') for worker in workers
))
def test_generation_does_not_modify_supplied_configuration_or_rules(self):
before = (
self.main_config.read_bytes(),
self.bridge_config.read_bytes(),
self.bridge_rules.read_bytes(),
)
self.generate()
self.assertEqual(
before,
(
self.main_config.read_bytes(),
self.bridge_config.read_bytes(),
self.bridge_rules.read_bytes(),
),
)
def test_invalid_instance_count_base_id_and_system_template_are_rejected(self):
for instances in (1, 6):
with self.subTest(instances=instances):
with self.assertRaises(hdstack_runtime.HDStackConfigurationError):
self.generate(instances=instances)
with self.assertRaises(hdstack_runtime.HDStackConfigurationError):
self.generate(base_id=0xFFFFFFFF - 1)
self.main_config.write_text(
MAIN_CONFIG.replace('MODE: MASTER', 'MODE: PEER'),
encoding='utf-8',
)
with self.assertRaises(hdstack_runtime.HDStackConfigurationError):
self.generate()
with self.assertRaises(hdstack_runtime.HDStackConfigurationError):
self.generate(bridge_enabled=1)
def test_enabled_bridge_requires_a_distinct_nonzero_server_id(self):
for bridge_id in (0, 23400, 23401, 23402):
with self.subTest(bridge_id=bridge_id):
self.bridge_config.write_text(
BRIDGE_CONFIG.replace(
'SERVER_ID: 23500',
'SERVER_ID: {}'.format(bridge_id),
),
encoding='utf-8',
)
with self.assertRaises(hdstack_runtime.HDStackConfigurationError):
self.generate()
self.bridge_config.write_text(
BRIDGE_CONFIG.replace('SERVER_ID: 23500', 'SERVER_ID: 23399'),
encoding='utf-8',
)
self.assertIsNotNone(self.generate().bridge_config)
def test_missing_bridge_files_and_port_collisions_fail_before_generation(self):
self.bridge_rules.unlink()
with self.assertRaises(hdstack_runtime.HDStackConfigurationError):
self.generate()
self.bridge_rules.write_text('BRIDGES = {}\n', encoding='utf-8')
self.main_config.write_text(
MAIN_CONFIG.replace('PORT: 62044', 'PORT: 7001'),
encoding='utf-8',
)
with self.assertRaises(hdstack_runtime.HDStackConfigurationError):
self.generate()
class HDStackContainerTests(unittest.TestCase):
def test_standard_entrypoint_derives_default_and_preserves_explicit_single_mode(self):
entrypoint = (DOCKER_CONFIGS / 'entrypoint-proxy').read_text(encoding='utf-8')
self.assertIn('HDSTACK_COUNT="${HDSTACK:-}"', entrypoint)
self.assertIn('getconf _NPROCESSORS_ONLN', entrypoint)
self.assertIn('HDSTACK_COUNT=$((CPU_COUNT - 1))', entrypoint)
self.assertIn('HDSTACK_COUNT=5', entrypoint)
self.assertIn('[ "$HDSTACK_COUNT" = 1 ]', entrypoint)
self.assertIn('/etc/supervisor/conf.d/supervisord.conf', entrypoint)
self.assertIn('[ "$HDSTACK_COUNT" -ge 2 ]', entrypoint)
self.assertIn('[ "$HDSTACK_COUNT" -le 5 ]', entrypoint)
self.assertIn('--base-id "$HDSTACK_BASEID"', entrypoint)
self.assertIn('HDSTACK_BRIDGE="${HDSTACK_BRIDGE:-0}"', entrypoint)
self.assertIn('--bridge-enabled "$HDSTACK_BRIDGE"', entrypoint)
self.assertIn('--loro-config /opt/freedmr/loro.cfg', entrypoint)
self.assertIn('HDSTACK_BRIDGE_CONFIG', entrypoint)
self.assertIn('HDSTACK_BRIDGE_RULES', entrypoint)
self.assertIn('/^\\[GLOBAL\\]/', entrypoint)
def test_standard_compose_enables_cpu_derived_hdstack_and_skeleton_mounts(self):
compose = (DOCKER_CONFIGS / 'docker-compose.yml').read_text(encoding='utf-8')
self.assertIn("#- HDSTACK=5", compose)
self.assertIn("#- HDSTACK_BASEID=23400", compose)
self.assertIn("- HDSTACK_BRIDGE=0", compose)
self.assertIn('freedmr:latest', compose)
self.assertIn(
'/etc/freedmr/freedmr-bridge.cfg:/opt/freedmr/freedmr-bridge.cfg:ro',
compose,
)
self.assertIn(
'/etc/freedmr/rules-bridge.py:/opt/freedmr/rules.py:ro',
compose,
)
self.assertIn('#- HDSTACK_BRIDGE_CONFIG=', compose)
self.assertIn('#- HDSTACK_BRIDGE_RULES=', compose)
self.assertIn(
' tmpfs: /tmp\n read_only: "true"',
compose,
)
self.assertIn("62041-62045:62041-62045/udp", compose)
self.assertIn("container_name: freedmr-data-gateway", compose)
self.assertIn("freedmr-data-gateway:development-latest", compose)
self.assertIn("FREEDMR_GATEWAY_INGRESS_MODE: fbp", compose)
self.assertIn("FREEDMR_GATEWAY_FBP_RELATIONSHIPS:", compose)
self.assertIn("/etc/freedmr/json/:/data/:ro", compose)
def test_installer_records_guided_hdstack_and_bridge_choices(self):
installer = (
DOCKER_CONFIGS / 'docker-compose_install.sh'
).read_text(encoding='utf-8')
self.assertIn('Assigned Server ID, or your own DMR ID', installer)
self.assertIn('HDStack allows FreeDMR to use more than one CPU core', installer)
self.assertIn('DEFAULT_HDSTACK=$((CPU_COUNT - 1))', installer)
self.assertIn("prompt_value 'Number of HDStack workers (1-5)'", installer)
self.assertIn("prompt_value 'Enable the separate bridge server?", installer)
self.assertIn("prompt_value 'Bridge server ID'", installer)
self.assertIn('docker-configs/freedmr-bridge.cfg', installer)
self.assertIn('docker-configs/rules-bridge.py', installer)
self.assertIn('- HDSTACK=$HDSTACK_COUNT', installer)
self.assertIn('- HDSTACK_BASEID=$HDSTACK_BASEID', installer)
self.assertIn('- HDSTACK_BRIDGE=$HDSTACK_BRIDGE_VALUE', installer)
self.assertIn("prompt_value 'APRS-IS login callsign'", installer)
self.assertIn("prompt_value 'APRS-IS passcode'", installer)
self.assertIn('build_data_gateway_relationships', installer)
self.assertIn('\\"bind_port\\":${bind_port}', installer)
self.assertIn('\\"peer_port\\":${peer_port}', installer)
self.assertIn('DATA_GATEWAY: True', installer)
self.assertIn('ENABLED: True', installer)
self.assertIn('TARGET_IP: freedmr-data-gateway', installer)
self.assertIn('s/^SERVER_ID:.*/SERVER_ID: $HDSTACK_BASEID/', installer)
self.assertIn('s/^SERVER_ID:.*/SERVER_ID: $HDSTACK_BRIDGE_ID/', installer)
self.assertNotIn('/etc/freedmr/.env', installer)
def test_shipped_config_keeps_native_gateway_disabled_until_install(self):
for path in (
ROOT / 'FreeDMR.cfg',
ROOT / 'freedmr.cfg',
DOCKER_CONFIGS / 'freedmr.cfg',
):
with self.subTest(path=path):
config = _read_config(path)
self.assertFalse(config.getboolean('GLOBAL', 'DATA_GATEWAY'))
self.assertFalse(config.getboolean('DATA-GATEWAY', 'ENABLED'))
self.assertEqual(config.get('DATA-GATEWAY', 'MODE'), 'OPENBRIDGE')
self.assertEqual(config.getint('DATA-GATEWAY', 'PROTO_VER'), 5)
self.assertTrue(config.getboolean('DATA-GATEWAY', 'ENHANCED_OBP'))
def test_bridge_templates_are_valid_and_idle(self):
bridge = _read_config(DOCKER_CONFIGS / 'freedmr-bridge.cfg')
rules_text = (
DOCKER_CONFIGS / 'rules-bridge.py'
).read_text(encoding='utf-8')
rules = {}
exec(compile(rules_text, 'rules-bridge.py', 'exec'), rules)
self.assertEqual(bridge.getint('GLOBAL', 'SERVER_ID'), 0)
self.assertFalse(bridge.getboolean('REPORTS', 'REPORT'))
self.assertFalse(any(
bridge.has_option(section, 'MODE')
for section in bridge.sections()
))
self.assertEqual(rules['BRIDGES'], {})
def test_image_has_no_obsolete_static_hdstack_supervisor(self):
dockerfile = (DOCKER_CONFIGS / 'Dockerfile-ci').read_text(encoding='utf-8')
self.assertNotIn('supervisord-hdstack.conf', dockerfile)
self.assertFalse((DOCKER_CONFIGS / 'supervisord-hdstack.conf').exists())
self.assertFalse((DOCKER_CONFIGS / 'docker-compose-hdstack.yml').exists())
def _read_config(path):
parser = configparser.ConfigParser(interpolation=None)
loaded = parser.read(path)
if not loaded:
raise AssertionError('could not read {}'.format(path))
return parser
MAIN_CONFIG = """
[GLOBAL]
SERVER_ID: 9999
GEN_STAT_BRIDGES: False
ENABLE_API: True
DATA_GATEWAY: True
VALIDATE_SERVER_IDS: False
USE_ACL: True
REG_ACL: PERMIT:ALL
SUB_ACL: DENY:1
TGID_TS1_ACL: PERMIT:ALL
TGID_TS2_ACL: PERMIT:ALL
[REPORTS]
REPORT: True
REPORT_PORT: 4321
REPORT_CLIENTS: 127.0.0.1,192.0.2.20
[LOGGER]
LOG_FILE: /dev/null
LOG_HANDLERS: console-timed
LOG_LEVEL: INFO
LOG_NAME: FreeDMR
[ALIASES]
TRY_DOWNLOAD: True
PATH: ./json/
SUB_MAP_FILE: sub_map.pkl
KEYS_FILE: keys.json
[ALLSTAR]
ENABLED: False
[EXTERNAL-FBP]
MODE: OPENBRIDGE
ENABLED: True
IP: 127.0.0.1
PORT: 62044
NETWORK_ID: 23499
PASSPHRASE: external
TARGET_IP: 127.0.0.1
TARGET_PORT: 62045
USE_ACL: True
SUB_ACL: DENY:1
TGID_ACL: PERMIT:ALL
RELAX_CHECKS: True
ENHANCED_OBP: True
PROTO_VER: 5
[SYSTEM]
MODE: MASTER
ENABLED: True
REPEAT: True
MAX_PEERS: 1
IP: 127.0.0.1
PORT: 54000
PASSPHRASE:
GROUP_HANGTIME: 5
USE_ACL: True
REG_ACL: DENY:1
SUB_ACL: DENY:1
TGID_TS1_ACL: PERMIT:ALL
TGID_TS2_ACL: PERMIT:ALL
DEFAULT_UA_TIMER: 10
SINGLE_MODE: True
VOICE_IDENT: True
DIAL_A_TG: True
DYNAMIC_TG_ROUTING: True
NETWORK_DIRECT_DIAL_SLOT: 1
TS1_STATIC:
TS2_STATIC: 2350
GENERATOR: 100
ALLOW_UNREG_ID: True
PROXY_CONTROL: True
ANNOUNCEMENT_LANGUAGE: en_GB
[ECHO]
MODE: PEER
ENABLED: True
LOOSE: False
EXPORT_AMBE: False
IP: 127.0.0.1
PORT: 54916
MASTER_IP: 127.0.0.1
MASTER_PORT: 54915
PASSPHRASE: passw0rd
CALLSIGN: TEST
RADIO_ID: 2340210
RX_FREQ: 449000000
TX_FREQ: 444000000
TX_POWER: 25
COLORCODE: 1
SLOTS: 1
LATITUDE: 00.0000
LONGITUDE: 000.0000
HEIGHT: 0
LOCATION: Test
DESCRIPTION: ECHO
URL: example.invalid
SOFTWARE_ID: test
PACKAGE_ID: test
GROUP_HANGTIME: 5
OPTIONS:
USE_ACL: True
SUB_ACL: DENY:1
TGID_TS1_ACL: PERMIT:ALL
TGID_TS2_ACL: PERMIT:ALL
ANNOUNCEMENT_LANGUAGE: en_GB
""".lstrip()
DATA_GATEWAY_CONFIG = """
[DATA-GATEWAY]
MODE: OPENBRIDGE
ENABLED: True
IP: 0.0.0.0
PORT: 62041
NETWORK_ID: 0
PASSPHRASE: gateway-test
TARGET_IP: data-gateway
TARGET_PORT: 62031
USE_ACL: False
SUB_ACL: PERMIT:ALL
TGID_ACL: PERMIT:ALL
RELAX_CHECKS: False
ENHANCED_OBP: True
PROTO_VER: 5
""".lstrip()
BRIDGE_CONFIG = """
[GLOBAL]
SERVER_ID: 23500
USE_ACL: True
REG_ACL: PERMIT:ALL
SUB_ACL: DENY:1
TGID_TS1_ACL: PERMIT:ALL
TGID_TS2_ACL: PERMIT:ALL
[REPORTS]
REPORT: False
REPORT_PORT: 4421
[LOGGER]
[ALIASES]
TRY_DOWNLOAD: False
[ALLSTAR]
ENABLED: False
""".lstrip()
if __name__ == '__main__':
unittest.main()

Powered by TurnKey Linux.