Skip to content

Commit

Permalink
[feature] Use SIGUSR1 to update configuration on device openwisp#478
Browse files Browse the repository at this point in the history
Use SIGUSR1 signal if the openwisp-config version is higher than 0.6.0a.
The old behavior is maintained for old versions. 

Closes openwisp#478
  • Loading branch information
pandafy committed Jun 8, 2021
1 parent f16e74d commit dbb64eb
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 5 deletions.
24 changes: 24 additions & 0 deletions openwisp_controller/connection/connectors/openwrt/ssh.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,36 @@
import logging

from packaging import version

from ..ssh import Ssh

logger = logging.getLogger(__name__)


class OpenWrt(Ssh):
def update_config(self):
try:
output, exit_code = self.exec_command('openwisp_config --version')
except Exception as error:
logger.error('Unable to get version of openwisp_config')
raise error
else:
ow_config_version = output.split(' ')[-1]
if version.parse(ow_config_version) >= version.parse('0.6.0a'):
self.exec_signal_reload()
else:
self.exec_legacy_restart()

def exec_signal_reload(self):
self.exec_command(
(
'OW_CONFIG_PID=$(ps | grep "openwisp_config" | '
'grep -v "grep" | awk \'{print $1}\'); '
'kill -SIGUSR1 $OW_CONFIG_PID'
)
)

def exec_legacy_restart(self):
_, exit_code = self.exec_command(
'test -f /tmp/openwisp/applying_conf', exit_codes=[0, 1]
)
Expand Down
60 changes: 55 additions & 5 deletions openwisp_controller/connection/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -739,27 +739,77 @@ def test_device_config_created(self, mocked_sleep, mocked_connect):
@mock.patch(_connect_path)
@mock.patch('time.sleep')
def test_device_config_update(self, mocked_sleep, mocked_connect):
def _assert_version_check_command(mocked_exec):
args, _ = mocked_exec.call_args_list[0]
self.assertEqual(args[0], 'openwisp_config --version')

def _assert_applying_conf_test_command(mocked_exec):
args, _ = mocked_exec_command.call_args_list[1]
self.assertEqual(
args[0], 'test -f /tmp/openwisp/applying_conf',
)

conf = self._prepare_conf_object()

with self.subTest('exit_code 0'):
with self.subTest('Unable to get openwisp_config version'):
with mock.patch(_exec_command_path) as mocked_exec_command:
mocked_exec_command.return_value = self._exec_command_return_value()
mocked_exec_command.return_value = self._exec_command_return_value(
exit_code=1
)
conf.save()
mocked_exec_command.assert_called_once()
self.assertEqual(mocked_exec_command.call_count, 1)
_assert_version_check_command(mocked_exec_command)
conf.refresh_from_db()
self.assertEqual(conf.status, 'modified')

with self.subTest('openwisp_config >= 0.6.0a'):
conf.config = '{"dns_servers": []}'
conf.full_clean()
with mock.patch(_exec_command_path) as mocked_exec_command:
mocked_exec_command.return_value = self._exec_command_return_value(
stdout='openwisp_config 0.6.0a'
)
conf.save()
self.assertEqual(mocked_exec_command.call_count, 2)
_assert_version_check_command(mocked_exec_command)
args, _ = mocked_exec_command.call_args_list[1]
self.assertIn('OW_CONFIG_PID', args[0])
conf.refresh_from_db()
self.assertEqual(conf.status, 'applied')

with self.subTest('exit_code 1'):
with self.subTest('openwisp_config < 0.6.0a: exit_code 0'):
conf.config = '{"interfaces": []}'
conf.full_clean()
with mock.patch(_exec_command_path) as mocked_exec_command:
mocked_exec_command.return_value = self._exec_command_return_value(
exit_code=1
stdout='openwisp_config 0.5.0'
)
conf.save()
self.assertEqual(mocked_exec_command.call_count, 2)
_assert_version_check_command(mocked_exec_command)
_assert_applying_conf_test_command(mocked_exec_command)
conf.refresh_from_db()
self.assertEqual(conf.status, 'applied')

with self.subTest('openwisp_config < 0.6.0a: exit_code 1'):
conf.config = '{"radios": []}'
conf.full_clean()
with mock.patch(_exec_command_path) as mocked_exec_command:
stdin, stdout, stderr = self._exec_command_return_value(
stdout='openwisp_config 0.5.0'
)
# An iterable side effect is required for different exit codes:
# 1. Checking openwisp_config returns with 0
# 2. Testing presence of /tmp/openwisp/applying_conf returns with 1
# 3. Restarting openwisp_config returns with 0 exit code
stdout.channel.recv_exit_status.side_effect = [0, 1, 1]
mocked_exec_command.return_value = (stdin, stdout, stderr)
conf.save()
self.assertEqual(mocked_exec_command.call_count, 3)
_assert_version_check_command(mocked_exec_command)
_assert_applying_conf_test_command(mocked_exec_command)
args, _ = mocked_exec_command.call_args_list[2]
self.assertEqual(args[0], '/etc/init.d/openwisp_config restart')
conf.refresh_from_db()
# exit code 1 considers the update not successful
self.assertEqual(conf.status, 'modified')
Expand Down

0 comments on commit dbb64eb

Please sign in to comment.