diff --git a/gns3server/config.py b/gns3server/config.py index 494ac743..cf6062d8 100644 --- a/gns3server/config.py +++ b/gns3server/config.py @@ -98,28 +98,15 @@ class Config(object): self.read_config() self._cloud_config = configparser.ConfigParser() self.read_cloud_config() - self._watch_config_file() - def _watch_config_file(self): - asyncio.get_event_loop().call_later(1, self._check_config_file_change) - - def _check_config_file_change(self): + def reload(self): """ - Check if configuration file has changed on the disk + Reload configuration """ - changed = False - for file in self._watched_files: - try: - if os.stat(file).st_mtime != self._watched_files[file]: - changed = True - except OSError: - continue - if changed: - self.read_config() + self.read_config() for section in self._override_config: self.set_section_config(section, self._override_config[section]) - self._watch_config_file() def list_cloud_config_file(self): return self._cloud_file diff --git a/gns3server/handlers/__init__.py b/gns3server/handlers/__init__.py index 3db429a1..325e8ba4 100644 --- a/gns3server/handlers/__init__.py +++ b/gns3server/handlers/__init__.py @@ -25,6 +25,7 @@ from gns3server.handlers.api.dynamips_vm_handler import DynamipsVMHandler from gns3server.handlers.api.qemu_handler import QEMUHandler from gns3server.handlers.api.virtualbox_handler import VirtualBoxHandler from gns3server.handlers.api.vpcs_handler import VPCSHandler +from gns3server.handlers.api.config_handler import ConfigHandler from gns3server.handlers.upload_handler import UploadHandler if sys.platform.startswith("linux") or hasattr(sys, "_called_from_test"): diff --git a/gns3server/handlers/api/config_handler.py b/gns3server/handlers/api/config_handler.py new file mode 100644 index 00000000..0025731e --- /dev/null +++ b/gns3server/handlers/api/config_handler.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2015 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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 . + +from ...web.route import Route +from ...config import Config +from aiohttp.web import HTTPForbidden + +import asyncio + + +class ConfigHandler: + + @classmethod + @Route.post( + r"/config/reload", + description="Check if version is the same as the server", + status_codes={ + 201: "Config reload", + 403: "Config reload refused" + }) + def reload(request, response): + + config = Config.instance() + if config.get_section_config("Server").getboolean("local", False) is False: + raise HTTPForbidden(text="You can only reload the configuration for a local server") + config.reload() + response.set_status(201) diff --git a/tests/handlers/api/test_config.py b/tests/handlers/api/test_config.py new file mode 100644 index 00000000..b081ca13 --- /dev/null +++ b/tests/handlers/api/test_config.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2015 GNS3 Technologies Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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 . + +from configparser import ConfigParser +from unittest.mock import patch, MagicMock + + +def test_reload_accepted(server): + + gns_config = MagicMock() + config = ConfigParser() + config.add_section("Server") + config.set("Server", "local", "true") + gns_config.get_section_config.return_value = config["Server"] + + with patch("gns3server.config.Config.instance", return_value=gns_config): + response = server.post('/config/reload', example=True) + + assert response.status == 201 + assert gns_config.reload.called + + +def test_reload_forbidden(server): + + gns_config = MagicMock() + config = ConfigParser() + config.add_section("Server") + config.set("Server", "local", "false") + gns_config.get_section_config.return_value = config["Server"] + + with patch("gns3server.config.Config.instance", return_value=gns_config): + response = server.post('/config/reload') + + assert response.status == 403 diff --git a/tests/test_config.py b/tests/test_config.py index d12683b1..3f119619 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -77,7 +77,7 @@ def test_set_section_config(tmpdir): assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"} -def test_check_config_file_change(tmpdir): +def test_reload(tmpdir): config = load_config(tmpdir, { "Server": { @@ -91,13 +91,12 @@ def test_check_config_file_change(tmpdir): "host": "192.168.1.1" } }) - os.utime(path, (time.time() + 1, time.time() + 1)) - config._check_config_file_change() + config.reload() assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"} -def test_check_config_file_change_override_cmdline(tmpdir): +def test_reload(tmpdir): config = load_config(tmpdir, { "Server": { @@ -114,7 +113,6 @@ def test_check_config_file_change_override_cmdline(tmpdir): "host": "192.168.1.2" } }) - os.utime(path, (time.time() + 1, time.time() + 1)) - config._check_config_file_change() + config.reload() assert dict(config.get_section_config("Server")) == {"host": "192.168.1.1"}