From e267f8a8b88603f0931ddcec172c2a400a65c942 Mon Sep 17 00:00:00 2001 From: ziajka Date: Fri, 4 May 2018 14:34:44 +0200 Subject: [PATCH] Project global variables --- .gitignore | 1 + gns3server/compute/docker/docker_vm.py | 4 ++ gns3server/compute/project.py | 14 +++++- gns3server/controller/project.py | 39 +++++++++++++++- gns3server/schemas/project.py | 46 +++++++++++++++++-- tests/compute/docker/test_docker_vm.py | 22 +++++++++ tests/compute/test_project.py | 22 ++++++++- tests/controller/test_project.py | 2 + tests/handlers/api/controller/test_project.py | 25 ++++++++++ 9 files changed, 167 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index e4ff9349..bf694717 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ nosetests.xml .project .pydevproject .settings +.vscode # Pycharm .idea diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 6b7c3c7f..7318b755 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -325,6 +325,10 @@ class DockerVM(BaseNode): # Give the information to the container the list of volume path mounted params["Env"].append("GNS3_VOLUMES={}".format(":".join(self._volumes))) + if self.project.variables: + for var in self.project.variables: + params["Env"].append("{}={}".format(var["name"], var.get('value', ''))) + if self._environment: for e in self._environment.strip().split("\n"): e = e.strip() diff --git a/gns3server/compute/project.py b/gns3server/compute/project.py index 8be8fb1e..3dc6723f 100644 --- a/gns3server/compute/project.py +++ b/gns3server/compute/project.py @@ -46,7 +46,7 @@ class Project: :param path: path of the project. (None use the standard directory) """ - def __init__(self, name=None, project_id=None, path=None): + def __init__(self, name=None, project_id=None, path=None, variables=None): self._name = name if project_id: @@ -61,6 +61,7 @@ class Project: self._nodes = set() self._used_tcp_ports = set() self._used_udp_ports = set() + self._variables = variables if path is None: location = get_default_project_directory() @@ -83,7 +84,8 @@ class Project: return { "name": self._name, - "project_id": self._id + "project_id": self._id, + "variables": self._variables } def _config(self): @@ -131,6 +133,14 @@ class Project: return self._nodes + @property + def variables(self): + return self._variables + + @variables.setter + def variables(self, variables): + self._variables = variables + def record_tcp_port(self, port): """ Associate a reserved TCP port number with this project. diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index 24306cb6..ceee6939 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -68,7 +68,7 @@ class Project: def __init__(self, name=None, project_id=None, path=None, controller=None, status="opened", filename=None, auto_start=False, auto_open=False, auto_close=True, scene_height=1000, scene_width=2000, zoom=100, show_layers=False, snap_to_grid=False, show_grid=False, - grid_size=0, show_interface_labels=False): + grid_size=0, show_interface_labels=False, variables=None, supplier=None): self._controller = controller assert name is not None @@ -85,6 +85,9 @@ class Project: self._show_grid = show_grid self._grid_size = grid_size self._show_interface_labels = show_interface_labels + self._variables = variables + self._supplier = supplier + self._loading = False # Disallow overwrite of existing project @@ -266,6 +269,36 @@ class Project: """ self._show_interface_labels = show_interface_labels + @property + def variables(self): + """ + Variables applied to the project + :return: list + """ + return self._variables + + @variables.setter + def variables(self, variables): + """ + Setter for variables applied to the project + """ + self._variables = variables + + @property + def supplier(self): + """ + Supplier of the project + :return: dict + """ + return self._supplier + + @supplier.setter + def supplier(self, supplier): + """ + Setter for supplier of the project + """ + self._supplier = supplier + @property def auto_start(self): """ @@ -1012,7 +1045,9 @@ class Project: "snap_to_grid": self._snap_to_grid, "show_grid": self._show_grid, "grid_size": self._grid_size, - "show_interface_labels": self._show_interface_labels + "show_interface_labels": self._show_interface_labels, + "supplier": self._supplier, + "variables": self._variables } def __repr__(self): diff --git a/gns3server/schemas/project.py b/gns3server/schemas/project.py index 242f5e5b..c232f8ce 100644 --- a/gns3server/schemas/project.py +++ b/gns3server/schemas/project.py @@ -15,6 +15,40 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +SUPPLIER_OBJECT_SCHEMA = { + "type": ["object", "null"], + "description": "Supplier of the project", + "properties": { + "logo": { + "type": "string", + "description": "Path to the project supplier logo" + }, + "url": { + "type": "string", + "description": "URL to the project supplier site" + } + } +} + + +VARIABLES_OBJECT_SCHEMA = { + "type": ["array", "null"], + "description": "Variables required to run the project", + "items": { + "properties": { + "name": { + "type": "string", + "description": "Variable name" + }, + "value": { + "type": "string", + "description": "Variable value" + } + }, + "required": ["name"] + } +} + PROJECT_CREATE_SCHEMA = { "$schema": "http://json-schema.org/draft-04/schema#", @@ -73,7 +107,9 @@ PROJECT_CREATE_SCHEMA = { "show_interface_labels": { "type": "boolean", "description": "Show interface labels on the drawing area" - } + }, + "supplier": SUPPLIER_OBJECT_SCHEMA, + "variables": VARIABLES_OBJECT_SCHEMA }, "additionalProperties": False, "required": ["name"] @@ -136,7 +172,9 @@ PROJECT_UPDATE_SCHEMA = { "show_interface_labels": { "type": "boolean", "description": "Show interface labels on the drawing area" - } + }, + "supplier": SUPPLIER_OBJECT_SCHEMA, + "variables": VARIABLES_OBJECT_SCHEMA }, "additionalProperties": False, } @@ -215,7 +253,9 @@ PROJECT_OBJECT_SCHEMA = { "show_interface_labels": { "type": "boolean", "description": "Show interface labels on the drawing area" - } + }, + "supplier": SUPPLIER_OBJECT_SCHEMA, + "variables": VARIABLES_OBJECT_SCHEMA }, "additionalProperties": False, "required": ["project_id"] diff --git a/tests/compute/docker/test_docker_vm.py b/tests/compute/docker/test_docker_vm.py index 8360b2ff..8e2075c5 100644 --- a/tests/compute/docker/test_docker_vm.py +++ b/tests/compute/docker/test_docker_vm.py @@ -251,6 +251,28 @@ def test_create_with_empty_extra_hosts(loop, project, manager): assert len([ e for e in called_kwargs["data"]["Env"] if "GNS3_EXTRA_HOSTS" in e]) == 0 +def test_create_with_project_variables(loop, project_with_variables, manager): + response = { + "Id": "e90e34656806", + "Warnings": [] + } + + project.variables = [ + {"name": "VAR1"}, + {"name": "VAR2", "value": "VAL1"} + ] + + with asyncio_patch("gns3server.compute.docker.Docker.list_images", return_value=[{"image": "ubuntu"}]): + with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: + vm = DockerVM("test", str(uuid.uuid4()), project, manager, "ubuntu") + loop.run_until_complete(asyncio.async(vm.create())) + called_kwargs = mock.call_args[1] + assert "VAR1=" in called_kwargs["data"]["Env"] + assert "VAR2=VAL1" in called_kwargs["data"]["Env"] + assert vm._extra_hosts == extra_hosts + + project.variables = None + def test_create_start_cmd(loop, project, manager): response = { diff --git a/tests/compute/test_project.py b/tests/compute/test_project.py index 1b349354..5713ebf4 100644 --- a/tests/compute/test_project.py +++ b/tests/compute/test_project.py @@ -92,9 +92,29 @@ def test_changing_path_not_allowed(tmpdir): p.path = str(tmpdir) +def test_variables(tmpdir): + variables = [{"name": "VAR1", "value": "VAL1"}] + p = Project(project_id=str(uuid4()), variables=variables) + assert p.variables == variables + + def test_json(tmpdir): p = Project(project_id=str(uuid4())) - assert p.__json__() == {"name": p.name, "project_id": p.id} + assert p.__json__() == { + "name": p.name, + "project_id": p.id, + "variables": None + } + + +def test_json_with_variables(tmpdir): + variables = [{"name": "VAR1", "value": "VAL1"}] + p = Project(project_id=str(uuid4()), variables=variables) + assert p.__json__() == { + "name": p.name, + "project_id": p.id, + "variables": variables + } def test_node_working_directory(tmpdir, node): diff --git a/tests/controller/test_project.py b/tests/controller/test_project.py index 77f8e799..6d0550ab 100644 --- a/tests/controller/test_project.py +++ b/tests/controller/test_project.py @@ -77,6 +77,8 @@ def test_json(tmpdir): "show_layers": False, "snap_to_grid": False, "grid_size": 0, + "supplier": None, + "variables": [] } diff --git a/tests/handlers/api/controller/test_project.py b/tests/handlers/api/controller/test_project.py index e75511c5..4e70f04d 100644 --- a/tests/handlers/api/controller/test_project.py +++ b/tests/handlers/api/controller/test_project.py @@ -67,6 +67,31 @@ def test_create_project_with_uuid(http_controller): assert response.json["name"] == "test" +def test_create_project_with_variables(http_controller): + variables = [ + {"name": "TEST1"}, + {"name": "TEST2", "value": "value1"} + ] + query = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "variables": variables} + response = http_controller.post("/projects", query) + assert response.status == 201 + assert response.json["variables"] == [ + {"name": "TEST1"}, + {"name": "TEST2", "value": "value1"} + ] + + +def test_create_project_with_supplier(http_controller): + supplier = { + 'logo': 'logo.png', + 'url': 'http://example.com' + } + query = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f", "supplier": supplier} + response = http_controller.post("/projects", query) + assert response.status == 201 + assert response.json["supplier"] == supplier + + def test_update_project(http_controller): query = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"} response = http_controller.post("/projects", query)