mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-08-27 04:20:14 +03:00
api endpoint implemented
This commit is contained in:
parent
713dbb7f45
commit
9106ac4522
17
docs/api/examples/controller_get_templateidbaseconfig.txt
Normal file
17
docs/api/examples/controller_get_templateidbaseconfig.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
curl -i -X 'http://localhost:3080/v2/templates/19021f99-e36f-394d-b4a1-8aaa902ab9cc/base-config/vpcs_base_config.txt'
|
||||||
|
|
||||||
|
GET /v2/templates/19021f99-e36f-394d-b4a1-8aaa902ab9cc/base-config/vpcs_base_config.txt HTTP/1.1
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Connection: close
|
||||||
|
X-Route: /v2/templates/{template_id}/base-config/{filename}
|
||||||
|
Server: Python/3.14 GNS3/2.2.60.dev1+713dbb7f
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 144
|
||||||
|
Date: Mon, 18 May 2026 18:34:10 GMT
|
||||||
|
|
||||||
|
{
|
||||||
|
"content": "# test config\n\ndhcp\n",
|
||||||
|
"filename": "vpcs_base_config.txt",
|
||||||
|
"template_id": "19021f99-e36f-394d-b4a1-8aaa902ab9cc"
|
||||||
|
}
|
||||||
44
docs/api/examples/controller_get_templatesbaseconfigs.txt
Normal file
44
docs/api/examples/controller_get_templatesbaseconfigs.txt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
curl -i -X 'http://localhost:3080/v2/templates/base-configs'
|
||||||
|
|
||||||
|
GET /v2/templates/base-configs HTTP/1.1
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Connection: close
|
||||||
|
X-Route: /v2/templates/base-configs
|
||||||
|
Server: Python/3.14 GNS3/2.2.60.dev1+713dbb7f
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 576
|
||||||
|
Date: Mon, 18 May 2026 18:28:53 GMT
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"filename": "config1.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "config2.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "ios_base_startup-config.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "ios_etherswitch_startup-config.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "iou_l2_base_startup-config.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "iou_l3_base_startup-config.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "test_config.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "update_test.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "vpcs_base_config.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "vpcs_base_config2.txt"
|
||||||
|
}
|
||||||
|
]
|
||||||
17
docs/api/examples/controller_put_templateidbaseconfig.txt
Normal file
17
docs/api/examples/controller_put_templateidbaseconfig.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
curl -i -X 'http://localhost:3080/v2/templates/19021f99-e36f-394d-b4a1-8aaa902ab9cc/base-config/vpcs_base_config.txt'
|
||||||
|
|
||||||
|
PUT /v2/templates/19021f99-e36f-394d-b4a1-8aaa902ab9cc/base-config/vpcs_base_config.txt HTTP/1.1
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Connection: close
|
||||||
|
X-Route: /v2/templates/{template_id}/base-config/{filename}
|
||||||
|
Server: Python/3.14 GNS3/2.2.60.dev1+713dbb7f
|
||||||
|
Content-Type: application/json
|
||||||
|
Content-Length: 144
|
||||||
|
Date: Mon, 18 May 2026 18:37:57 GMT
|
||||||
|
|
||||||
|
{
|
||||||
|
"content": "# test config\n\ndhcp\n",
|
||||||
|
"filename": "vpcs_base_config.txt",
|
||||||
|
"template_id": "19021f99-e36f-394d-b4a1-8aaa902ab9cc"
|
||||||
|
}
|
||||||
38
docs/api/v2/controller/template/templatesbaseconfigs.rst
Normal file
38
docs/api/v2/controller/template/templatesbaseconfigs.rst
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
GET /v2/templates/base-configs
|
||||||
|
------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
GET /v2/templates/base-configs
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
List all available base configuration files
|
||||||
|
|
||||||
|
Response status codes
|
||||||
|
**********************
|
||||||
|
- **200**: List of base configuration files returned
|
||||||
|
|
||||||
|
Output
|
||||||
|
*******
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>filename</td>
|
||||||
|
<td>string</td>
|
||||||
|
<td>Name of the base configuration file</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
Sample session
|
||||||
|
***************
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/controller_get_templatesbaseconfigs.txt
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
/v2/templates/{template_id}/base-config/{filename}
|
||||||
|
------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
GET /v2/templates/**{template_id}**/base-configs/**{filename}**
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
List all available base configuration files
|
||||||
|
|
||||||
|
Response status codes
|
||||||
|
**********************
|
||||||
|
- **200**: List of base configuration files returned
|
||||||
|
|
||||||
|
Output
|
||||||
|
*******
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>filename</td>
|
||||||
|
<td>string</td>
|
||||||
|
<td>Name of the base configuration file</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
Sample session
|
||||||
|
***************
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/controller_get_templateidbaseconfig.txt
|
||||||
|
|
||||||
|
|
||||||
|
PUT /v2/templates/**{template_id}**/base-config/**{filename}**
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Update base configuration file content
|
||||||
|
|
||||||
|
Response status codes
|
||||||
|
**********************
|
||||||
|
- **200**: File updated
|
||||||
|
- **404**: File not found
|
||||||
|
|
||||||
|
Input
|
||||||
|
*******
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Mandatory</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr><td>content</td><td>✔</td><td>string</td><td>New file content</td></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
Output
|
||||||
|
*******
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Mandatory</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr><td>template_id</td><td>✔</td><td>string</td><td>Template UUID</td></tr>
|
||||||
|
<tr><td>filename</td><td>✔</td><td>string</td><td>Base configuration filename</td></tr>
|
||||||
|
<tr><td>content</td><td>✔</td><td>string</td><td>Updated file content</td></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
Sample session
|
||||||
|
***************
|
||||||
|
|
||||||
|
.. literalinclude:: ../../../examples/controller_put_templateidbaseconfig.txt
|
||||||
@ -22,6 +22,7 @@ from gns3server.schemas.template import TEMPLATE_USAGE_SCHEMA
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
from gns3server.schemas.template import (
|
from gns3server.schemas.template import (
|
||||||
TEMPLATE_OBJECT_SCHEMA,
|
TEMPLATE_OBJECT_SCHEMA,
|
||||||
@ -173,3 +174,131 @@ class TemplateHandler:
|
|||||||
compute_id=request.json.get("compute_id"))
|
compute_id=request.json.get("compute_id"))
|
||||||
response.set_status(201)
|
response.set_status(201)
|
||||||
response.json(node)
|
response.json(node)
|
||||||
|
|
||||||
|
@Route.get(
|
||||||
|
r"/templates/{template_id}/base-config/{filename}",
|
||||||
|
description="Get base configuration file content",
|
||||||
|
)
|
||||||
|
def get_base_config(request, response):
|
||||||
|
|
||||||
|
controller = Controller.instance()
|
||||||
|
|
||||||
|
template_id = request.match_info["template_id"]
|
||||||
|
|
||||||
|
template = controller.template_manager.get_template(template_id)
|
||||||
|
|
||||||
|
filename = os.path.basename(request.match_info["filename"])
|
||||||
|
|
||||||
|
path = os.path.join(controller.configs_path(), filename)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not os.path.exists(path):
|
||||||
|
response.set_status(404)
|
||||||
|
response.json({"message": "File not found"})
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(path, encoding="utf-8", errors="ignore") as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
response.set_status(500)
|
||||||
|
response.json({"message": str(e)})
|
||||||
|
return
|
||||||
|
|
||||||
|
response.set_status(200)
|
||||||
|
response.json({
|
||||||
|
"template_id": template.id,
|
||||||
|
"filename": filename,
|
||||||
|
"content": content
|
||||||
|
})
|
||||||
|
|
||||||
|
@Route.put(
|
||||||
|
r"/templates/{template_id}/base-config/{filename}",
|
||||||
|
description="Update base configuration file content",
|
||||||
|
parameters={
|
||||||
|
"template_id": "Template UUID",
|
||||||
|
"filename": "Base config filename"
|
||||||
|
},
|
||||||
|
status_codes={
|
||||||
|
200: "File updated",
|
||||||
|
400: "Invalid request",
|
||||||
|
404: "Template or file not found"
|
||||||
|
},
|
||||||
|
)
|
||||||
|
def update_base_config(request, response):
|
||||||
|
|
||||||
|
controller = Controller.instance()
|
||||||
|
|
||||||
|
template_id = request.match_info["template_id"]
|
||||||
|
filename = os.path.basename(request.match_info["filename"])
|
||||||
|
|
||||||
|
try:
|
||||||
|
template = controller.template_manager.get_template(template_id)
|
||||||
|
except Exception:
|
||||||
|
response.set_status(404)
|
||||||
|
response.json({"message": "Template not found"})
|
||||||
|
return
|
||||||
|
|
||||||
|
path = os.path.join(controller.configs_path(), filename)
|
||||||
|
|
||||||
|
body = request.json
|
||||||
|
|
||||||
|
if not body or "content" not in body:
|
||||||
|
response.set_status(400)
|
||||||
|
response.json({"message": "Missing 'content' field"})
|
||||||
|
return
|
||||||
|
|
||||||
|
content = body["content"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.makedirs(controller.configs_path(), exist_ok=True)
|
||||||
|
|
||||||
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
except OSError as e:
|
||||||
|
response.set_status(500)
|
||||||
|
response.json({"message": str(e)})
|
||||||
|
return
|
||||||
|
|
||||||
|
response.set_status(200)
|
||||||
|
response.json({
|
||||||
|
"template_id": template.id,
|
||||||
|
"filename": filename,
|
||||||
|
"content": content
|
||||||
|
})
|
||||||
|
|
||||||
|
@Route.get(
|
||||||
|
r"/templates/base-configs",
|
||||||
|
description="List all available base configuration files",
|
||||||
|
status_codes={
|
||||||
|
200: "List of base configuration files returned"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
def list_base_configs(request, response):
|
||||||
|
|
||||||
|
controller = Controller.instance()
|
||||||
|
|
||||||
|
configs_path = controller.configs_path()
|
||||||
|
|
||||||
|
try:
|
||||||
|
files = []
|
||||||
|
|
||||||
|
if os.path.exists(configs_path):
|
||||||
|
for filename in os.listdir(configs_path):
|
||||||
|
|
||||||
|
path = os.path.join(configs_path, filename)
|
||||||
|
|
||||||
|
if os.path.isfile(path):
|
||||||
|
files.append({"filename": filename})
|
||||||
|
|
||||||
|
except OSError as e:
|
||||||
|
response.set_status(500)
|
||||||
|
response.json({
|
||||||
|
"message": str(e)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
|
||||||
|
files = sorted(files, key=lambda x: x["filename"])
|
||||||
|
response.set_status(200)
|
||||||
|
response.json(files)
|
||||||
|
|||||||
@ -14,8 +14,7 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
import os
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from tests.utils import asyncio_patch
|
from tests.utils import asyncio_patch
|
||||||
@ -955,3 +954,81 @@ async def test_create_node_from_template(controller_api, controller, project):
|
|||||||
mock.assert_called_with(id, x=42, y=12, name=None, compute_id=None)
|
mock.assert_called_with(id, x=42, y=12, name=None, compute_id=None)
|
||||||
assert response.route == "/projects/{project_id}/templates/{template_id}"
|
assert response.route == "/projects/{project_id}/templates/{template_id}"
|
||||||
assert response.status == 201
|
assert response.status == 201
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_base_config(controller_api, controller):
|
||||||
|
|
||||||
|
template_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
controller.template_manager._templates[template_id] = Template(template_id, {
|
||||||
|
"template_type": "vpcs",
|
||||||
|
"category": 0,
|
||||||
|
"name": "test",
|
||||||
|
"symbol": "guest.svg",
|
||||||
|
"default_name_format": "{name}-{0}",
|
||||||
|
"compute_id": "local"
|
||||||
|
})
|
||||||
|
|
||||||
|
config_path = os.path.join(controller.configs_path(), "test_config.txt")
|
||||||
|
|
||||||
|
with open(config_path, "w") as f:
|
||||||
|
f.write("hello config")
|
||||||
|
|
||||||
|
response = await controller_api.get(
|
||||||
|
f"/templates/{template_id}/base-config/test_config.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status == 200
|
||||||
|
assert response.json["filename"] == "test_config.txt"
|
||||||
|
assert response.json["content"] == "hello config"
|
||||||
|
|
||||||
|
async def test_update_base_config(controller_api, controller):
|
||||||
|
|
||||||
|
template_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
controller.template_manager._templates[template_id] = Template(template_id, {
|
||||||
|
"template_type": "vpcs",
|
||||||
|
"category": 0,
|
||||||
|
"name": "test",
|
||||||
|
"symbol": "guest.svg",
|
||||||
|
"default_name_format": "{name}-{0}",
|
||||||
|
"compute_id": "local"
|
||||||
|
})
|
||||||
|
|
||||||
|
config_path = os.path.join(controller.configs_path(), "update_test.txt")
|
||||||
|
|
||||||
|
with open(config_path, "w") as f:
|
||||||
|
f.write("old content")
|
||||||
|
|
||||||
|
response = await controller_api.put(
|
||||||
|
f"/templates/{template_id}/base-config/update_test.txt",
|
||||||
|
{
|
||||||
|
"content": "new updated content"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status == 200
|
||||||
|
assert response.json["content"] == "new updated content"
|
||||||
|
|
||||||
|
with open(config_path) as f:
|
||||||
|
assert f.read() == "new updated content"
|
||||||
|
|
||||||
|
async def test_list_base_configs(controller_api, controller):
|
||||||
|
|
||||||
|
config1 = os.path.join(controller.configs_path(), "config1.txt")
|
||||||
|
config2 = os.path.join(controller.configs_path(), "config2.txt")
|
||||||
|
|
||||||
|
with open(config1, "w") as f:
|
||||||
|
f.write("test1")
|
||||||
|
|
||||||
|
with open(config2, "w") as f:
|
||||||
|
f.write("test2")
|
||||||
|
|
||||||
|
response = await controller_api.get("/templates/base-configs")
|
||||||
|
|
||||||
|
assert response.status == 200
|
||||||
|
|
||||||
|
filenames = [item["filename"] for item in response.json]
|
||||||
|
|
||||||
|
assert "config1.txt" in filenames
|
||||||
|
assert "config2.txt" in filenames
|
||||||
Loading…
x
Reference in New Issue
Block a user