diff --git a/docs/api/notifications/link.updated.json b/docs/api/notifications/link.updated.json
index 02d45efa..6fb0157e 100644
--- a/docs/api/notifications/link.updated.json
+++ b/docs/api/notifications/link.updated.json
@@ -11,7 +11,8 @@
10
]
},
- "link_id": "b76bd8b1-2171-4361-9228-801713d23079",
+ "link_id": "c3f1b38a-160d-4a67-baa0-34fc3d9759ed",
+ "link_style": {},
"link_type": "ethernet",
"nodes": [
{
@@ -21,7 +22,7 @@
"x": 64,
"y": 0
},
- "node_id": "8b77b480-361e-488b-96b1-a769890e11ec",
+ "node_id": "481e5898-cd97-4351-8b1e-0c9e77fc7c58",
"port_number": 3
},
{
@@ -30,10 +31,10 @@
"style": "font-family: TypeWriter;font-size: 10.0;font-weight: bold;fill: #000000;fill-opacity: 1.0;",
"text": "2/4"
},
- "node_id": "b4688a3d-0af8-4ddc-b85f-e26dc1031c4c",
+ "node_id": "63f7b8ab-146d-4142-b8c5-cf387ac1d963",
"port_number": 4
}
],
- "project_id": "85333131-b83a-4112-9a51-184ba0c536a8",
+ "project_id": "292936d2-9f54-4e29-959f-b59ed9ac5e6f",
"suspend": false
}
\ No newline at end of file
diff --git a/docs/api/notifications/log.warning.json b/docs/api/notifications/log.warning.json
index 5d630354..dd724322 100644
--- a/docs/api/notifications/log.warning.json
+++ b/docs/api/notifications/log.warning.json
@@ -1,3 +1,3 @@
{
- "message": "Warning ASA 8 is not officialy supported by GNS3"
+ "message": "Warning ASA 8 is not officially supported by GNS3"
}
\ No newline at end of file
diff --git a/docs/api/notifications/project.created.json b/docs/api/notifications/project.created.json
new file mode 100644
index 00000000..54428224
--- /dev/null
+++ b/docs/api/notifications/project.created.json
@@ -0,0 +1,21 @@
+{
+ "auto_close": true,
+ "auto_open": false,
+ "auto_start": false,
+ "drawing_grid_size": 25,
+ "filename": "Test.gns3",
+ "grid_size": 75,
+ "name": "Test",
+ "path": "/tmp/tmprusds8mt/projects/87d4b692-52b4-4b4c-8828-13666306a68a",
+ "project_id": "87d4b692-52b4-4b4c-8828-13666306a68a",
+ "scene_height": 1000,
+ "scene_width": 2000,
+ "show_grid": false,
+ "show_interface_labels": false,
+ "show_layers": false,
+ "snap_to_grid": false,
+ "status": "opened",
+ "supplier": null,
+ "variables": null,
+ "zoom": 100
+}
\ No newline at end of file
diff --git a/docs/api/notifications/project.deleted.json b/docs/api/notifications/project.deleted.json
new file mode 100644
index 00000000..0c93406f
--- /dev/null
+++ b/docs/api/notifications/project.deleted.json
@@ -0,0 +1,21 @@
+{
+ "auto_close": true,
+ "auto_open": false,
+ "auto_start": false,
+ "drawing_grid_size": 25,
+ "filename": "Test.gns3",
+ "grid_size": 75,
+ "name": "Test",
+ "path": "/tmp/tmpvqf8d5mx/projects/6f01ee8c-5fe7-47a2-95ab-a0f4c0a355f9",
+ "project_id": "6f01ee8c-5fe7-47a2-95ab-a0f4c0a355f9",
+ "scene_height": 1000,
+ "scene_width": 2000,
+ "show_grid": false,
+ "show_interface_labels": false,
+ "show_layers": false,
+ "snap_to_grid": false,
+ "status": "closed",
+ "supplier": null,
+ "variables": null,
+ "zoom": 100
+}
\ No newline at end of file
diff --git a/docs/api/notifications/project.opened.json b/docs/api/notifications/project.opened.json
new file mode 100644
index 00000000..e7c9cc8e
--- /dev/null
+++ b/docs/api/notifications/project.opened.json
@@ -0,0 +1,21 @@
+{
+ "auto_close": true,
+ "auto_open": false,
+ "auto_start": false,
+ "drawing_grid_size": 25,
+ "filename": "test.gns3",
+ "grid_size": 75,
+ "name": "test",
+ "path": "/tmp/tmp7swwxptj/projects/e5b0b37a-a74e-40a2-9adb-42908f146fba",
+ "project_id": "e5b0b37a-a74e-40a2-9adb-42908f146fba",
+ "scene_height": 1000,
+ "scene_width": 2000,
+ "show_grid": false,
+ "show_interface_labels": false,
+ "show_layers": false,
+ "snap_to_grid": false,
+ "status": "opened",
+ "supplier": null,
+ "variables": null,
+ "zoom": 100
+}
\ No newline at end of file
diff --git a/docs/api/v2/compute/ethernet_switch/projectsprojectidethernetswitchnodes.rst b/docs/api/v2/compute/ethernet_switch/projectsprojectidethernetswitchnodes.rst
index dcbfcb5d..e855f049 100644
--- a/docs/api/v2/compute/ethernet_switch/projectsprojectidethernetswitchnodes.rst
+++ b/docs/api/v2/compute/ethernet_switch/projectsprojectidethernetswitchnodes.rst
@@ -23,7 +23,7 @@ Types
+++++++++
EthernetSwitchPort
^^^^^^^^^^^^^^^^^^^^^^
-Ethernet port
+Ethernet switch port
.. raw:: html
diff --git a/docs/api/v2/compute/ethernet_switch/projectsprojectidethernetswitchnodesnodeid.rst b/docs/api/v2/compute/ethernet_switch/projectsprojectidethernetswitchnodesnodeid.rst
index 8a56fe64..efa5d250 100644
--- a/docs/api/v2/compute/ethernet_switch/projectsprojectidethernetswitchnodesnodeid.rst
+++ b/docs/api/v2/compute/ethernet_switch/projectsprojectidethernetswitchnodesnodeid.rst
@@ -56,7 +56,7 @@ Types
+++++++++
EthernetSwitchPort
^^^^^^^^^^^^^^^^^^^^^^
-Ethernet port
+Ethernet switch port
.. raw:: html
diff --git a/docs/api/v2/compute/iou/projectsprojectidiounodes.rst b/docs/api/v2/compute/iou/projectsprojectidiounodes.rst
index 4eee67da..5ef4495c 100644
--- a/docs/api/v2/compute/iou/projectsprojectidiounodes.rst
+++ b/docs/api/v2/compute/iou/projectsprojectidiounodes.rst
@@ -23,7 +23,7 @@ Input
Name | Mandatory | Type | Description |
- application_id | | ['integer', 'null'] | Application ID for running IOU image |
+ application_id | ✔ | ['integer', 'null'] | Application ID for running IOU image |
console | | ['integer', 'null'] | Console TCP port |
console_type | | enum | Possible values: telnet, none |
ethernet_adapters | | integer | How many ethernet adapters are connected to the IOU |
diff --git a/docs/api/v2/compute/project/projects.rst b/docs/api/v2/compute/project/projects.rst
index ed7b6a5e..3d1abea5 100644
--- a/docs/api/v2/compute/project/projects.rst
+++ b/docs/api/v2/compute/project/projects.rst
@@ -35,6 +35,8 @@ Input
Name | Mandatory | Type | Description |
auto_close | | boolean | Project auto close |
+ auto_open | | boolean | Project open when GNS3 start |
+ auto_start | | boolean | Project start when opened |
drawing_grid_size | | integer | Grid size for the drawing area for drawings |
grid_size | | integer | Grid size for the drawing area for nodes |
name | ✔ | ['string', 'null'] | Project name |
diff --git a/docs/api/v2/compute/qemu/projectsprojectidqemunodes.rst b/docs/api/v2/compute/qemu/projectsprojectidqemunodes.rst
index d39588a5..7ef0486d 100644
--- a/docs/api/v2/compute/qemu/projectsprojectidqemunodes.rst
+++ b/docs/api/v2/compute/qemu/projectsprojectidqemunodes.rst
@@ -34,6 +34,7 @@ Input
console_type | | enum | Possible values: telnet, vnc, spice, spice+agent, none |
cpu_throttling | | ['integer', 'null'] | Percentage of CPU allowed for QEMU |
cpus | | ['integer', 'null'] | Number of vCPUs |
+ create_config_disk | | ['boolean', 'null'] | Automatically create a config disk on HDD disk interface (secondary slave) |
custom_adapters | | array | |
hda_disk_image | | string | QEMU hda disk image path |
hda_disk_image_md5sum | | ['string', 'null'] | QEMU hda disk image checksum |
@@ -63,6 +64,9 @@ Input
process_priority | | enum | Possible values: realtime, very high, high, normal, low, very low, null |
qemu_path | | ['string', 'null'] | Path to QEMU |
ram | | ['integer', 'null'] | Amount of RAM in MB |
+ replicate_network_connection_state | | ['boolean', 'null'] | Replicate the network connection state for links in Qemu |
+ tpm | | ['boolean', 'null'] | Enable the Trusted Platform Module (TPM) in Qemu |
+ uefi | | ['boolean', 'null'] | Enable the UEFI boot mode in Qemu |
usage | | string | How to use the Qemu VM |
@@ -84,6 +88,7 @@ Output
console_type | ✔ | enum | Possible values: telnet, vnc, spice, spice+agent, none |
cpu_throttling | ✔ | integer | Percentage of CPU allowed for QEMU |
cpus | ✔ | ['integer', 'null'] | Number of vCPUs |
+ create_config_disk | ✔ | ['boolean', 'null'] | Automatically create a config disk on HDD disk interface (secondary slave) |
hda_disk_image | ✔ | string | QEMU hda disk image path |
hda_disk_image_md5sum | ✔ | ['string', 'null'] | QEMU hda disk image checksum |
hda_disk_interface | ✔ | string | QEMU hda interface |
@@ -113,8 +118,11 @@ Output
project_id | ✔ | string | Project UUID |
qemu_path | ✔ | string | Path to QEMU |
ram | ✔ | integer | Amount of RAM in MB |
+ replicate_network_connection_state | ✔ | boolean | Replicate the network connection state for links in Qemu |
save_vm_state | | ['boolean', 'null'] | Save VM state support |
status | ✔ | enum | Possible values: started, stopped, suspended |
+ tpm | ✔ | boolean | Enable the Trusted Platform Module (TPM) in Qemu |
+ uefi | ✔ | boolean | Enable the UEFI boot mode in Qemu |
usage | ✔ | string | How to use the QEMU VM |
diff --git a/docs/api/v2/compute/qemu/projectsprojectidqemunodesnodeid.rst b/docs/api/v2/compute/qemu/projectsprojectidqemunodesnodeid.rst
index fa458038..3a4073f2 100644
--- a/docs/api/v2/compute/qemu/projectsprojectidqemunodesnodeid.rst
+++ b/docs/api/v2/compute/qemu/projectsprojectidqemunodesnodeid.rst
@@ -36,6 +36,7 @@ Output
console_type | ✔ | enum | Possible values: telnet, vnc, spice, spice+agent, none |
cpu_throttling | ✔ | integer | Percentage of CPU allowed for QEMU |
cpus | ✔ | ['integer', 'null'] | Number of vCPUs |
+ create_config_disk | ✔ | ['boolean', 'null'] | Automatically create a config disk on HDD disk interface (secondary slave) |
hda_disk_image | ✔ | string | QEMU hda disk image path |
hda_disk_image_md5sum | ✔ | ['string', 'null'] | QEMU hda disk image checksum |
hda_disk_interface | ✔ | string | QEMU hda interface |
@@ -65,8 +66,11 @@ Output
project_id | ✔ | string | Project UUID |
qemu_path | ✔ | string | Path to QEMU |
ram | ✔ | integer | Amount of RAM in MB |
+ replicate_network_connection_state | ✔ | boolean | Replicate the network connection state for links in Qemu |
save_vm_state | | ['boolean', 'null'] | Save VM state support |
status | ✔ | enum | Possible values: started, stopped, suspended |
+ tpm | ✔ | boolean | Enable the Trusted Platform Module (TPM) in Qemu |
+ uefi | ✔ | boolean | Enable the UEFI boot mode in Qemu |
usage | ✔ | string | How to use the QEMU VM |
@@ -110,6 +114,7 @@ Input
console_type | | enum | Possible values: telnet, vnc, spice, spice+agent, none |
cpu_throttling | | ['integer', 'null'] | Percentage of CPU allowed for QEMU |
cpus | | ['integer', 'null'] | Number of vCPUs |
+ create_config_disk | | ['boolean', 'null'] | Automatically create a config disk on HDD disk interface (secondary slave) |
custom_adapters | | array | |
hda_disk_image | | string | QEMU hda disk image path |
hda_disk_image_md5sum | | ['string', 'null'] | QEMU hda disk image checksum |
@@ -138,6 +143,9 @@ Input
process_priority | | enum | Possible values: realtime, very high, high, normal, low, very low, null |
qemu_path | | ['string', 'null'] | Path to QEMU |
ram | | ['integer', 'null'] | Amount of RAM in MB |
+ replicate_network_connection_state | | ['boolean', 'null'] | Replicate the network connection state for links in Qemu |
+ tpm | | ['boolean', 'null'] | Enable the Trusted Platform Module (TPM) in Qemu |
+ uefi | | ['boolean', 'null'] | Enable the UEFI boot mode in Qemu |
usage | | string | How to use the QEMU VM |
@@ -159,6 +167,7 @@ Output
console_type | ✔ | enum | Possible values: telnet, vnc, spice, spice+agent, none |
cpu_throttling | ✔ | integer | Percentage of CPU allowed for QEMU |
cpus | ✔ | ['integer', 'null'] | Number of vCPUs |
+ create_config_disk | ✔ | ['boolean', 'null'] | Automatically create a config disk on HDD disk interface (secondary slave) |
hda_disk_image | ✔ | string | QEMU hda disk image path |
hda_disk_image_md5sum | ✔ | ['string', 'null'] | QEMU hda disk image checksum |
hda_disk_interface | ✔ | string | QEMU hda interface |
@@ -188,8 +197,11 @@ Output
project_id | ✔ | string | Project UUID |
qemu_path | ✔ | string | Path to QEMU |
ram | ✔ | integer | Amount of RAM in MB |
+ replicate_network_connection_state | ✔ | boolean | Replicate the network connection state for links in Qemu |
save_vm_state | | ['boolean', 'null'] | Save VM state support |
status | ✔ | enum | Possible values: started, stopped, suspended |
+ tpm | ✔ | boolean | Enable the Trusted Platform Module (TPM) in Qemu |
+ uefi | ✔ | boolean | Enable the UEFI boot mode in Qemu |
usage | ✔ | string | How to use the QEMU VM |
diff --git a/docs/api/v2/compute/qemu/projectsprojectidqemunodesnodeidstart.rst b/docs/api/v2/compute/qemu/projectsprojectidqemunodesnodeidstart.rst
index 5df86864..1f9b05a4 100644
--- a/docs/api/v2/compute/qemu/projectsprojectidqemunodesnodeidstart.rst
+++ b/docs/api/v2/compute/qemu/projectsprojectidqemunodesnodeidstart.rst
@@ -36,6 +36,7 @@ Output
console_type | ✔ | enum | Possible values: telnet, vnc, spice, spice+agent, none |
cpu_throttling | ✔ | integer | Percentage of CPU allowed for QEMU |
cpus | ✔ | ['integer', 'null'] | Number of vCPUs |
+ create_config_disk | ✔ | ['boolean', 'null'] | Automatically create a config disk on HDD disk interface (secondary slave) |
hda_disk_image | ✔ | string | QEMU hda disk image path |
hda_disk_image_md5sum | ✔ | ['string', 'null'] | QEMU hda disk image checksum |
hda_disk_interface | ✔ | string | QEMU hda interface |
@@ -65,8 +66,11 @@ Output
project_id | ✔ | string | Project UUID |
qemu_path | ✔ | string | Path to QEMU |
ram | ✔ | integer | Amount of RAM in MB |
+ replicate_network_connection_state | ✔ | boolean | Replicate the network connection state for links in Qemu |
save_vm_state | | ['boolean', 'null'] | Save VM state support |
status | ✔ | enum | Possible values: started, stopped, suspended |
+ tpm | ✔ | boolean | Enable the Trusted Platform Module (TPM) in Qemu |
+ uefi | ✔ | boolean | Enable the UEFI boot mode in Qemu |
usage | ✔ | string | How to use the QEMU VM |
diff --git a/docs/api/v2/controller/link/projectsprojectidlinks.rst b/docs/api/v2/controller/link/projectsprojectidlinks.rst
index 8022478f..f5e5ff7d 100644
--- a/docs/api/v2/controller/link/projectsprojectidlinks.rst
+++ b/docs/api/v2/controller/link/projectsprojectidlinks.rst
@@ -47,6 +47,7 @@ Input
capturing | | boolean | Read only property. True if a capture running on the link |
filters | | object | Packet filter. This allow to simulate latency and errors |
link_id | | string | Link UUID |
+ link_style | | object | Link line style |
link_type | | enum | Possible values: ethernet, serial |
nodes | | array | List of the VMS |
project_id | | string | Project UUID |
@@ -65,6 +66,7 @@ Output
capturing | | boolean | Read only property. True if a capture running on the link |
filters | | object | Packet filter. This allow to simulate latency and errors |
link_id | | string | Link UUID |
+ link_style | | object | Link line style |
link_type | | enum | Possible values: ethernet, serial |
nodes | | array | List of the VMS |
project_id | | string | Project UUID |
diff --git a/docs/api/v2/controller/link/projectsprojectidlinkslinkid.rst b/docs/api/v2/controller/link/projectsprojectidlinkslinkid.rst
index 14e82bd2..3da82ff1 100644
--- a/docs/api/v2/controller/link/projectsprojectidlinkslinkid.rst
+++ b/docs/api/v2/controller/link/projectsprojectidlinkslinkid.rst
@@ -30,6 +30,7 @@ Output
capturing | | boolean | Read only property. True if a capture running on the link |
filters | | object | Packet filter. This allow to simulate latency and errors |
link_id | | string | Link UUID |
+ link_style | | object | Link line style |
link_type | | enum | Possible values: ethernet, serial |
nodes | | array | List of the VMS |
project_id | | string | Project UUID |
@@ -69,6 +70,7 @@ Input
capturing | | boolean | Read only property. True if a capture running on the link |
filters | | object | Packet filter. This allow to simulate latency and errors |
link_id | | string | Link UUID |
+ link_style | | object | Link line style |
link_type | | enum | Possible values: ethernet, serial |
nodes | | array | List of the VMS |
project_id | | string | Project UUID |
@@ -87,6 +89,7 @@ Output
capturing | | boolean | Read only property. True if a capture running on the link |
filters | | object | Packet filter. This allow to simulate latency and errors |
link_id | | string | Link UUID |
+ link_style | | object | Link line style |
link_type | | enum | Possible values: ethernet, serial |
nodes | | array | List of the VMS |
project_id | | string | Project UUID |
diff --git a/docs/api/v2/controller/link/projectsprojectidlinkslinkidstartcapture.rst b/docs/api/v2/controller/link/projectsprojectidlinkslinkidstartcapture.rst
index 7db0a05a..bc8f6c42 100644
--- a/docs/api/v2/controller/link/projectsprojectidlinkslinkidstartcapture.rst
+++ b/docs/api/v2/controller/link/projectsprojectidlinkslinkidstartcapture.rst
@@ -39,6 +39,7 @@ Output
capturing | | boolean | Read only property. True if a capture running on the link |
filters | | object | Packet filter. This allow to simulate latency and errors |
link_id | | string | Link UUID |
+ link_style | | object | Link line style |
link_type | | enum | Possible values: ethernet, serial |
nodes | | array | List of the VMS |
project_id | | string | Project UUID |
diff --git a/docs/api/v2/controller/project/projects.rst b/docs/api/v2/controller/project/projects.rst
index fc6c8bd2..9b954366 100644
--- a/docs/api/v2/controller/project/projects.rst
+++ b/docs/api/v2/controller/project/projects.rst
@@ -19,6 +19,8 @@ Input
Name | Mandatory | Type | Description |
auto_close | | boolean | Project auto close |
+ auto_open | | boolean | Project open when GNS3 start |
+ auto_start | | boolean | Project start when opened |
drawing_grid_size | | integer | Grid size for the drawing area for drawings |
grid_size | | integer | Grid size for the drawing area for nodes |
name | ✔ | ['string', 'null'] | Project name |
diff --git a/docs/api/v2/controller/project/projectsprojectidduplicate.rst b/docs/api/v2/controller/project/projectsprojectidduplicate.rst
index 13e63c0f..ddfca566 100644
--- a/docs/api/v2/controller/project/projectsprojectidduplicate.rst
+++ b/docs/api/v2/controller/project/projectsprojectidduplicate.rst
@@ -24,11 +24,14 @@ Input
Name | Mandatory | Type | Description |
auto_close | | boolean | Project auto close |
+ auto_open | | boolean | Project open when GNS3 start |
+ auto_start | | boolean | Project start when opened |
drawing_grid_size | | integer | Grid size for the drawing area for drawings |
grid_size | | integer | Grid size for the drawing area for nodes |
name | ✔ | ['string', 'null'] | Project name |
path | | ['string', 'null'] | Project directory |
project_id | | ['string', 'null'] | Project UUID |
+ reset_mac_addresses | | boolean | Reset MAC addresses for this project |
scene_height | | integer | Height of the drawing area |
scene_width | | integer | Width of the drawing area |
show_grid | | boolean | Show the grid on the drawing area |
diff --git a/docs/controller_notifications.rst b/docs/controller_notifications.rst
index e21caa54..5d7765e0 100644
--- a/docs/controller_notifications.rst
+++ b/docs/controller_notifications.rst
@@ -50,6 +50,38 @@ A compute has been deleted.
.. literalinclude:: api/notifications/compute.deleted.json
+project.created
+---------------
+
+A project has been created.
+
+.. literalinclude:: api/notifications/project.created.json
+
+
+project.updated
+---------------
+
+A project has been updated.
+
+.. literalinclude:: api/notifications/project.updated.json
+
+
+project.closed
+---------------
+
+A project has been closed.
+
+.. literalinclude:: api/notifications/project.closed.json
+
+
+project.deleted
+---------------
+
+A project has been deleted.
+
+.. literalinclude:: api/notifications/project.deleted.json
+
+
template.created
-----------------
diff --git a/docs/gns3_file.json b/docs/gns3_file.json
index 825a9e87..a90d89b8 100644
--- a/docs/gns3_file.json
+++ b/docs/gns3_file.json
@@ -103,7 +103,8 @@
"properties": {
"name": {
"type": "string",
- "description": "Variable name"
+ "description": "Variable name",
+ "minLength": 1
},
"value": {
"type": "string",
@@ -387,6 +388,27 @@
"type": "boolean",
"description": "Suspend the link"
},
+ "link_style": {
+ "type": "object",
+ "description": "Link line style",
+ "items": {
+ "type": "object",
+ "properties": {
+ "color": {
+ "description": "Link line color",
+ "type": "string"
+ },
+ "width": {
+ "description": "Link line width",
+ "type": "integer"
+ },
+ "type": {
+ "description": "Link line type",
+ "type": "integer"
+ }
+ }
+ }
+ },
"filters": {
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Packet filter. This allow to simulate latency and errors",
diff --git a/docs/project_notifications.rst b/docs/project_notifications.rst
index f37dd07e..58ab1861 100644
--- a/docs/project_notifications.rst
+++ b/docs/project_notifications.rst
@@ -100,22 +100,6 @@ A drawing has been deleted.
.. literalinclude:: api/notifications/drawing.deleted.json
-project.updated
----------------
-
-A project has been updated.
-
-.. literalinclude:: api/notifications/project.updated.json
-
-
-project.closed
----------------
-
-A project has been closed.
-
-.. literalinclude:: api/notifications/project.closed.json
-
-
snapshot.restored
--------------------------
diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py
index c4dc3b95..eed15198 100644
--- a/gns3server/controller/project.py
+++ b/gns3server/controller/project.py
@@ -130,16 +130,27 @@ class Project:
self._iou_id_lock = asyncio.Lock()
log.debug('Project "{name}" [{id}] loaded'.format(name=self.name, id=self._id))
+ self.emit_controller_notification("project.created", self.__json__())
def emit_notification(self, action, event):
"""
- Emit a notification to all clients using this project.
+ Emit a project notification to all clients using this project.
:param action: Action name
:param event: Event to send
"""
- self.controller.notification.project_emit(action, event, project_id=self.id)
+ self._controller.notification.project_emit(action, event, project_id=self.id)
+
+ def emit_controller_notification(self, action, event):
+ """
+ Emit a controller notification, all clients will see it.
+
+ :param action: Action name
+ :param event: Event to send
+ """
+
+ self._controller.notification.controller_emit(action, event)
async def update(self, **kwargs):
"""
@@ -154,7 +165,7 @@ class Project:
# We send notif only if object has changed
if old_json != self.__json__():
- self.emit_notification("project.updated", self.__json__())
+ self.emit_controller_notification("project.updated", self.__json__())
self.dump()
# update on computes
@@ -803,7 +814,8 @@ class Project:
self._clean_pictures()
self._status = "closed"
if not ignore_notification:
- self.emit_notification("project.closed", self.__json__())
+ self.emit_controller_notification("project.closed", self.__json__())
+
self.reset()
self._closing = False
@@ -857,6 +869,7 @@ class Project:
shutil.rmtree(self.path)
except OSError as e:
raise aiohttp.web.HTTPConflict(text="Cannot delete project directory {}: {}".format(self.path, str(e)))
+ self.emit_controller_notification("project.deleted", self.__json__())
async def delete_on_computes(self):
"""
@@ -976,7 +989,7 @@ class Project:
await self.add_drawing(dump=False, **drawing_data)
self.dump()
- # We catch all error to be able to rollback the .gns3 to the previous state
+ # We catch all error to be able to roll back the .gns3 to the previous state
except Exception as e:
for compute in list(self._project_created_on_compute):
try:
@@ -1001,6 +1014,7 @@ class Project:
pass
self._loading = False
+ self.emit_controller_notification("project.opened", self.__json__())
# Should we start the nodes when project is open
if self._auto_start:
# Start all in the background without waiting for completion
diff --git a/readthedocs.yml b/readthedocs.yml
index 87fa516b..754f9590 100644
--- a/readthedocs.yml
+++ b/readthedocs.yml
@@ -1,5 +1,5 @@
+version: 2
build:
- image: latest
-
-python:
- version: 3.6
+ os: "ubuntu-22.04"
+ tools:
+ python: "3.11"
diff --git a/tests/controller/test_compute.py b/tests/controller/test_compute.py
index d7f5329c..b9b911b3 100644
--- a/tests/controller/test_compute.py
+++ b/tests/controller/test_compute.py
@@ -213,7 +213,9 @@ async def test_compute_httpQuery_project(compute):
response = MagicMock()
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
response.status = 200
- project = Project(name="Test")
+ with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
+ project = Project(name="Test")
+ mock_notification.assert_called()
await compute.post("/projects", project)
mock.assert_called_with("POST", "https://example.com:84/v2/compute/projects", data=json.dumps(project.__json__()), headers={'content-type': 'application/json'}, auth=None, chunked=None, timeout=20)
await compute.close()
diff --git a/tests/controller/test_project.py b/tests/controller/test_project.py
index bc729fee..fb8bcfeb 100644
--- a/tests/controller/test_project.py
+++ b/tests/controller/test_project.py
@@ -47,15 +47,19 @@ async def node(controller, project):
async def test_affect_uuid():
- p = Project(name="Test")
- assert len(p.id) == 36
- p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test 2")
- assert p.id == '00010203-0405-0607-0809-0a0b0c0d0e0f'
+ with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
+ p = Project(name="Test")
+ mock_notification.assert_called()
+ assert len(p.id) == 36
+ p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test 2")
+ assert p.id == '00010203-0405-0607-0809-0a0b0c0d0e0f'
async def test_json():
- p = Project(name="Test")
+ with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
+ p = Project(name="Test")
+ mock_notification.assert_called()
assert p.__json__() == {
"name": "Test",
@@ -83,11 +87,11 @@ async def test_json():
async def test_update(controller):
project = Project(controller=controller, name="Hello")
- project.emit_notification = MagicMock()
+ project.emit_controller_notification = MagicMock()
assert project.name == "Hello"
await project.update(name="World")
assert project.name == "World"
- project.emit_notification.assert_any_call("project.updated", project.__json__())
+ project.emit_controller_notification.assert_any_call("project.updated", project.__json__())
async def test_update_on_compute(controller):
@@ -106,7 +110,9 @@ async def test_path(projects_dir):
directory = projects_dir
with patch("gns3server.utils.path.get_default_project_directory", return_value=directory):
- p = Project(project_id=str(uuid4()), name="Test")
+ with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
+ p = Project(project_id=str(uuid4()), name="Test")
+ mock_notification.assert_called()
assert p.path == os.path.join(directory, p.id)
assert os.path.exists(os.path.join(directory, p.id))
@@ -124,23 +130,27 @@ def test_path_exist(tmpdir):
async def test_init_path(tmpdir):
- p = Project(path=str(tmpdir), project_id=str(uuid4()), name="Test")
- assert p.path == str(tmpdir)
+ with patch('gns3server.controller.project.Project.emit_controller_notification') as mock_notification:
+ p = Project(path=str(tmpdir), project_id=str(uuid4()), name="Test")
+ mock_notification.assert_called()
+ assert p.path == str(tmpdir)
@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows")
async def test_changing_path_with_quote_not_allowed(tmpdir):
with pytest.raises(aiohttp.web.HTTPForbidden):
- p = Project(project_id=str(uuid4()), name="Test")
- p.path = str(tmpdir / "project\"53")
+ with patch('gns3server.controller.project.Project.emit_controller_notification'):
+ p = Project(project_id=str(uuid4()), name="Test")
+ p.path = str(tmpdir / "project\"53")
async def test_captures_directory(tmpdir):
- p = Project(path=str(tmpdir / "capturestest"), name="Test")
- assert p.captures_directory == str(tmpdir / "capturestest" / "project-files" / "captures")
- assert os.path.exists(p.captures_directory)
+ with patch('gns3server.controller.project.Project.emit_controller_notification'):
+ p = Project(path=str(tmpdir / "capturestest"), name="Test")
+ assert p.captures_directory == str(tmpdir / "capturestest" / "project-files" / "captures")
+ assert os.path.exists(p.captures_directory)
async def test_add_node_local(controller):
@@ -649,36 +659,39 @@ async def test_dump(projects_dir):
directory = projects_dir
with patch("gns3server.utils.path.get_default_project_directory", return_value=directory):
- p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test")
- p.dump()
- with open(os.path.join(directory, p.id, "Test.gns3")) as f:
- content = f.read()
- assert "00010203-0405-0607-0809-0a0b0c0d0e0f" in content
+ with patch('gns3server.controller.project.Project.emit_controller_notification'):
+ p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f', name="Test")
+ p.dump()
+ with open(os.path.join(directory, p.id, "Test.gns3")) as f:
+ content = f.read()
+ assert "00010203-0405-0607-0809-0a0b0c0d0e0f" in content
async def test_open_close(controller):
- project = Project(controller=controller, name="Test")
- assert project.status == "opened"
- await project.close()
- project.start_all = AsyncioMagicMock()
- await project.open()
- assert not project.start_all.called
- assert project.status == "opened"
- project.emit_notification = MagicMock()
- await project.close()
- assert project.status == "closed"
- project.emit_notification.assert_any_call("project.closed", project.__json__())
+ with patch('gns3server.controller.project.Project.emit_controller_notification'):
+ project = Project(controller=controller, name="Test")
+ assert project.status == "opened"
+ await project.close()
+ project.start_all = AsyncioMagicMock()
+ await project.open()
+ assert not project.start_all.called
+ assert project.status == "opened"
+ project.emit_controller_notification = MagicMock()
+ await project.close()
+ assert project.status == "closed"
+ project.emit_controller_notification.assert_any_call("project.closed", project.__json__())
async def test_open_auto_start(controller):
- project = Project(controller=controller, name="Test", auto_start=True)
- assert project.status == "opened"
- await project.close()
- project.start_all = AsyncioMagicMock()
- await project.open()
- assert project.start_all.called
+ with patch('gns3server.controller.project.Project.emit_controller_notification'):
+ project = Project(controller=controller, name="Test", auto_start=True)
+ assert project.status == "opened"
+ await project.close()
+ project.start_all = AsyncioMagicMock()
+ await project.open()
+ assert project.start_all.called
def test_is_running(project, node):
diff --git a/tests/controller/test_topology.py b/tests/controller/test_topology.py
index 4ac24259..4499ddce 100644
--- a/tests/controller/test_topology.py
+++ b/tests/controller/test_topology.py
@@ -19,7 +19,7 @@ import json
import uuid
import pytest
import aiohttp
-from unittest.mock import MagicMock
+from unittest.mock import MagicMock, patch
from tests.utils import asyncio_patch
from gns3server.controller.project import Project
@@ -30,35 +30,36 @@ from gns3server.version import __version__
async def test_project_to_topology_empty(tmpdir):
- project = Project(name="Test")
- topo = project_to_topology(project)
- assert topo == {
- "project_id": project.id,
- "name": "Test",
- "auto_start": False,
- "auto_close": True,
- "auto_open": False,
- "scene_width": 2000,
- "scene_height": 1000,
- "revision": GNS3_FILE_FORMAT_REVISION,
- "zoom": 100,
- "show_grid": False,
- "show_interface_labels": False,
- "show_layers": False,
- "snap_to_grid": False,
- "grid_size": 75,
- "drawing_grid_size": 25,
- "topology": {
- "nodes": [],
- "links": [],
- "computes": [],
- "drawings": []
- },
- "type": "topology",
- "supplier": None,
- "variables": None,
- "version": __version__
- }
+ with patch('gns3server.controller.project.Project.emit_controller_notification'):
+ project = Project(name="Test")
+ topo = project_to_topology(project)
+ assert topo == {
+ "project_id": project.id,
+ "name": "Test",
+ "auto_start": False,
+ "auto_close": True,
+ "auto_open": False,
+ "scene_width": 2000,
+ "scene_height": 1000,
+ "revision": GNS3_FILE_FORMAT_REVISION,
+ "zoom": 100,
+ "show_grid": False,
+ "show_interface_labels": False,
+ "show_layers": False,
+ "snap_to_grid": False,
+ "grid_size": 75,
+ "drawing_grid_size": 25,
+ "topology": {
+ "nodes": [],
+ "links": [],
+ "computes": [],
+ "drawings": []
+ },
+ "type": "topology",
+ "supplier": None,
+ "variables": None,
+ "version": __version__
+ }
async def test_basic_topology(controller):