gns3-server 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of gns3-server might be problematic. Click here for more details.

Files changed (1251) hide show
  1. gns3_server-3.0.0.dist-info/LICENSE +674 -0
  2. gns3_server-3.0.0.dist-info/METADATA +868 -0
  3. gns3_server-3.0.0.dist-info/RECORD +1251 -0
  4. gns3_server-3.0.0.dist-info/WHEEL +5 -0
  5. gns3_server-3.0.0.dist-info/entry_points.txt +3 -0
  6. gns3_server-3.0.0.dist-info/top_level.txt +1 -0
  7. gns3server/__init__.py +17 -0
  8. gns3server/__main__.py +20 -0
  9. gns3server/alembic.ini +103 -0
  10. gns3server/api/__init__.py +0 -0
  11. gns3server/api/routes/__init__.py +0 -0
  12. gns3server/api/routes/compute/__init__.py +267 -0
  13. gns3server/api/routes/compute/atm_switch_nodes.py +233 -0
  14. gns3server/api/routes/compute/capabilities.py +51 -0
  15. gns3server/api/routes/compute/cloud_nodes.py +249 -0
  16. gns3server/api/routes/compute/compute.py +143 -0
  17. gns3server/api/routes/compute/dependencies/__init__.py +0 -0
  18. gns3server/api/routes/compute/dependencies/authentication.py +83 -0
  19. gns3server/api/routes/compute/docker_nodes.py +393 -0
  20. gns3server/api/routes/compute/dynamips_nodes.py +367 -0
  21. gns3server/api/routes/compute/ethernet_hub_nodes.py +236 -0
  22. gns3server/api/routes/compute/ethernet_switch_nodes.py +246 -0
  23. gns3server/api/routes/compute/frame_relay_switch_nodes.py +240 -0
  24. gns3server/api/routes/compute/images.py +156 -0
  25. gns3server/api/routes/compute/iou_nodes.py +344 -0
  26. gns3server/api/routes/compute/nat_nodes.py +244 -0
  27. gns3server/api/routes/compute/notifications.py +88 -0
  28. gns3server/api/routes/compute/projects.py +243 -0
  29. gns3server/api/routes/compute/qemu_nodes.py +412 -0
  30. gns3server/api/routes/compute/virtualbox_nodes.py +377 -0
  31. gns3server/api/routes/compute/vmware_nodes.py +355 -0
  32. gns3server/api/routes/compute/vpcs_nodes.py +343 -0
  33. gns3server/api/routes/controller/__init__.py +147 -0
  34. gns3server/api/routes/controller/acl.py +268 -0
  35. gns3server/api/routes/controller/appliances.py +150 -0
  36. gns3server/api/routes/controller/computes.py +231 -0
  37. gns3server/api/routes/controller/controller.py +319 -0
  38. gns3server/api/routes/controller/dependencies/__init__.py +0 -0
  39. gns3server/api/routes/controller/dependencies/authentication.py +117 -0
  40. gns3server/api/routes/controller/dependencies/database.py +38 -0
  41. gns3server/api/routes/controller/dependencies/rbac.py +78 -0
  42. gns3server/api/routes/controller/drawings.py +131 -0
  43. gns3server/api/routes/controller/gns3vm.py +71 -0
  44. gns3server/api/routes/controller/groups.py +231 -0
  45. gns3server/api/routes/controller/images.py +255 -0
  46. gns3server/api/routes/controller/links.py +287 -0
  47. gns3server/api/routes/controller/nodes.py +619 -0
  48. gns3server/api/routes/controller/pools.py +239 -0
  49. gns3server/api/routes/controller/privileges.py +43 -0
  50. gns3server/api/routes/controller/projects.py +595 -0
  51. gns3server/api/routes/controller/roles.py +224 -0
  52. gns3server/api/routes/controller/snapshots.py +124 -0
  53. gns3server/api/routes/controller/symbols.py +133 -0
  54. gns3server/api/routes/controller/templates.py +192 -0
  55. gns3server/api/routes/controller/users.py +254 -0
  56. gns3server/api/routes/index.py +93 -0
  57. gns3server/api/server.py +224 -0
  58. gns3server/appliances/6wind-turbo-router.gns3a +47 -0
  59. gns3server/appliances/IPCop.gns3a +52 -0
  60. gns3server/appliances/a10-vthunder.gns3a +72 -0
  61. gns3server/appliances/aaa.gns3a +20 -0
  62. gns3server/appliances/alcatel-7750.gns3a +74 -0
  63. gns3server/appliances/almalinux.gns3a +84 -0
  64. gns3server/appliances/alpine-linux-virt.gns3a +59 -0
  65. gns3server/appliances/alpine-linux.gns3a +21 -0
  66. gns3server/appliances/arista-ceos.gns3a +20 -0
  67. gns3server/appliances/arista-veos.gns3a +577 -0
  68. gns3server/appliances/aruba-arubaoscx.gns3a +233 -0
  69. gns3server/appliances/aruba-vgw.gns3a +60 -0
  70. gns3server/appliances/aruba-vmc.gns3a +56 -0
  71. gns3server/appliances/asterfusion-vAsterNOS.gns3a +49 -0
  72. gns3server/appliances/asterisk.gns3a +114 -0
  73. gns3server/appliances/bigswitch-bigcloud-fabric.gns3a +42 -0
  74. gns3server/appliances/bird.gns3a +42 -0
  75. gns3server/appliances/bird2.gns3a +57 -0
  76. gns3server/appliances/brocade-vadx.gns3a +43 -0
  77. gns3server/appliances/brocade-vrouter.gns3a +138 -0
  78. gns3server/appliances/brocade-vtm.gns3a +136 -0
  79. gns3server/appliances/bsdrp.gns3a +117 -0
  80. gns3server/appliances/centos-cloud.gns3a +115 -0
  81. gns3server/appliances/centos7.gns3a +72 -0
  82. gns3server/appliances/checkpoint-gaia.gns3a +81 -0
  83. gns3server/appliances/chromium.gns3a +19 -0
  84. gns3server/appliances/cisco-1700.gns3a +39 -0
  85. gns3server/appliances/cisco-2600.gns3a +38 -0
  86. gns3server/appliances/cisco-2691.gns3a +38 -0
  87. gns3server/appliances/cisco-3620.gns3a +38 -0
  88. gns3server/appliances/cisco-3640.gns3a +38 -0
  89. gns3server/appliances/cisco-3660.gns3a +38 -0
  90. gns3server/appliances/cisco-3725.gns3a +38 -0
  91. gns3server/appliances/cisco-3745.gns3a +45 -0
  92. gns3server/appliances/cisco-7200.gns3a +92 -0
  93. gns3server/appliances/cisco-asa.gns3a +86 -0
  94. gns3server/appliances/cisco-asav.gns3a +227 -0
  95. gns3server/appliances/cisco-c8000v.gns3a +108 -0
  96. gns3server/appliances/cisco-cat9k.gns3a +58 -0
  97. gns3server/appliances/cisco-csr1000v.gns3a +316 -0
  98. gns3server/appliances/cisco-dcnm.gns3a +96 -0
  99. gns3server/appliances/cisco-fcnf.gns3a +71 -0
  100. gns3server/appliances/cisco-fmcv.gns3a +139 -0
  101. gns3server/appliances/cisco-fsve.gns3a +57 -0
  102. gns3server/appliances/cisco-ftdv.gns3a +99 -0
  103. gns3server/appliances/cisco-iosv.gns3a +178 -0
  104. gns3server/appliances/cisco-iosvl2.gns3a +95 -0
  105. gns3server/appliances/cisco-iosxrv.gns3a +57 -0
  106. gns3server/appliances/cisco-iosxrv9k.gns3a +188 -0
  107. gns3server/appliances/cisco-iou-l2.gns3a +84 -0
  108. gns3server/appliances/cisco-iou-l3.gns3a +84 -0
  109. gns3server/appliances/cisco-ise.gns3a +154 -0
  110. gns3server/appliances/cisco-ngipsv.gns3a +73 -0
  111. gns3server/appliances/cisco-nxosv.gns3a +56 -0
  112. gns3server/appliances/cisco-nxosv9k.gns3a +433 -0
  113. gns3server/appliances/cisco-pyats.gns3a +19 -0
  114. gns3server/appliances/cisco-smc.gns3a +71 -0
  115. gns3server/appliances/cisco-vWLC.gns3a +140 -0
  116. gns3server/appliances/cisco-wsav.gns3a +44 -0
  117. gns3server/appliances/citrix-netscaler-vpx.gns3a +72 -0
  118. gns3server/appliances/citrix-sd-wan.gns3a +46 -0
  119. gns3server/appliances/citrix-sdwan-center.gns3a +46 -0
  120. gns3server/appliances/clavister-netsheild.gns3a +59 -0
  121. gns3server/appliances/clavister-netwall.gns3a +71 -0
  122. gns3server/appliances/clearos.gns3a +107 -0
  123. gns3server/appliances/cloudrouter.gns3a +72 -0
  124. gns3server/appliances/coreos.gns3a +329 -0
  125. gns3server/appliances/cumulus-vx.gns3a +440 -0
  126. gns3server/appliances/danos.gns3a +57 -0
  127. gns3server/appliances/debian.gns3a +85 -0
  128. gns3server/appliances/deft-linux.gns3a +96 -0
  129. gns3server/appliances/dell-ftos.gns3a +71 -0
  130. gns3server/appliances/dns.gns3a +20 -0
  131. gns3server/appliances/empty-vm.gns3a +199 -0
  132. gns3server/appliances/endhost.gns3a +17 -0
  133. gns3server/appliances/exos.gns3a +51 -0
  134. gns3server/appliances/extreme-networks-voss.gns3a +152 -0
  135. gns3server/appliances/f5-bigip.gns3a +322 -0
  136. gns3server/appliances/f5-bigiq.gns3a +165 -0
  137. gns3server/appliances/fedora-cloud.gns3a +70 -0
  138. gns3server/appliances/firefox.gns3a +59 -0
  139. gns3server/appliances/fortiadc-manager.gns3a +54 -0
  140. gns3server/appliances/fortiadc.gns3a +502 -0
  141. gns3server/appliances/fortianalyzer.gns3a +519 -0
  142. gns3server/appliances/fortiauthenticator.gns3a +251 -0
  143. gns3server/appliances/forticache.gns3a +210 -0
  144. gns3server/appliances/fortigate.gns3a +742 -0
  145. gns3server/appliances/fortimail.gns3a +392 -0
  146. gns3server/appliances/fortimanager.gns3a +519 -0
  147. gns3server/appliances/fortiproxy.gns3a +83 -0
  148. gns3server/appliances/fortirecorder.gns3a +69 -0
  149. gns3server/appliances/fortisandbox.gns3a +210 -0
  150. gns3server/appliances/fortisiem-super_worker.gns3a +210 -0
  151. gns3server/appliances/fortiweb.gns3a +308 -0
  152. gns3server/appliances/freeRouter.gns3a +44 -0
  153. gns3server/appliances/freebsd.gns3a +75 -0
  154. gns3server/appliances/freenas.gns3a +183 -0
  155. gns3server/appliances/frr.gns3a +84 -0
  156. gns3server/appliances/haproxy.gns3a +18 -0
  157. gns3server/appliances/hbcd-pe.gns3a +62 -0
  158. gns3server/appliances/hp-vsr1001.gns3a +149 -0
  159. gns3server/appliances/huawei-ar1kv.gns3a +58 -0
  160. gns3server/appliances/huawei-ce12800.gns3a +43 -0
  161. gns3server/appliances/huawei-ne40e.gns3a +94 -0
  162. gns3server/appliances/huawei-usg6kv.gns3a +50 -0
  163. gns3server/appliances/innovaphone-app.gns3a +50 -0
  164. gns3server/appliances/innovaphone-ipva.gns3a +78 -0
  165. gns3server/appliances/ipfire.gns3a +181 -0
  166. gns3server/appliances/ipterm.gns3a +19 -0
  167. gns3server/appliances/ipxe.gns3a +47 -0
  168. gns3server/appliances/juniper-junos-space.gns3a +46 -0
  169. gns3server/appliances/juniper-vmx-legacy.gns3a +55 -0
  170. gns3server/appliances/juniper-vmx-vcp.gns3a +520 -0
  171. gns3server/appliances/juniper-vmx-vfp.gns3a +281 -0
  172. gns3server/appliances/juniper-vqfx-pfe.gns3a +71 -0
  173. gns3server/appliances/juniper-vqfx-re.gns3a +85 -0
  174. gns3server/appliances/juniper-vrr.gns3a +66 -0
  175. gns3server/appliances/juniper-vsrx.gns3a +332 -0
  176. gns3server/appliances/jupyter.gns3a +20 -0
  177. gns3server/appliances/jupyter27.gns3a +20 -0
  178. gns3server/appliances/kali-linux-cli.gns3a +18 -0
  179. gns3server/appliances/kali-linux.gns3a +248 -0
  180. gns3server/appliances/kemp-vlm.gns3a +111 -0
  181. gns3server/appliances/kerio-connect.gns3a +131 -0
  182. gns3server/appliances/kerio-control.gns3a +143 -0
  183. gns3server/appliances/kerio-operator.gns3a +130 -0
  184. gns3server/appliances/lancom-vrouter.gns3a +45 -0
  185. gns3server/appliances/loadbalancer_org-va.gns3a +56 -0
  186. gns3server/appliances/macos-install.gns3a +72 -0
  187. gns3server/appliances/mcjoin.gns3a +19 -0
  188. gns3server/appliances/microcore-linux.gns3a +72 -0
  189. gns3server/appliances/mikrotik-ccr1036-8g-2s+.gns3a +165 -0
  190. gns3server/appliances/mikrotik-ccr1072-1g-8s+.gns3a +161 -0
  191. gns3server/appliances/mikrotik-chr.gns3a +108 -0
  192. gns3server/appliances/mikrotik-crs328-24p-4s+.gns3a +173 -0
  193. gns3server/appliances/mikrotik-crs328-4c-20s-4s+.gns3a +189 -0
  194. gns3server/appliances/mikrotik-rb1100ahx4-dude-edition.gns3a +155 -0
  195. gns3server/appliances/mikrotik-rb2011uias.gns3a +161 -0
  196. gns3server/appliances/mikrotik-rb3011uias.gns3a +161 -0
  197. gns3server/appliances/mikrotik-rb4011igs+.gns3a +161 -0
  198. gns3server/appliances/mikrotik-rb450g.gns3a +155 -0
  199. gns3server/appliances/mikrotik-rb450gx4.gns3a +155 -0
  200. gns3server/appliances/mikrotik-winbox.gns3a +19 -0
  201. gns3server/appliances/mininet.gns3a +47 -0
  202. gns3server/appliances/net_toolbox.gns3a +20 -0
  203. gns3server/appliances/netapp-ontapsim.gns3a +185 -0
  204. gns3server/appliances/netem.gns3a +44 -0
  205. gns3server/appliances/network_automation.gns3a +19 -0
  206. gns3server/appliances/nixos.gns3a +52 -0
  207. gns3server/appliances/nokia-vsim.gns3a +47 -0
  208. gns3server/appliances/ntopng.gns3a +24 -0
  209. gns3server/appliances/onos.gns3a +19 -0
  210. gns3server/appliances/op5-monitor.gns3a +72 -0
  211. gns3server/appliances/open-media-vault.gns3a +120 -0
  212. gns3server/appliances/openbsd.gns3a +44 -0
  213. gns3server/appliances/opennac.gns3a +45 -0
  214. gns3server/appliances/opensuse.gns3a +47 -0
  215. gns3server/appliances/openvswitch-management.gns3a +21 -0
  216. gns3server/appliances/openvswitch.gns3a +21 -0
  217. gns3server/appliances/openwrt-realview.gns3a +56 -0
  218. gns3server/appliances/openwrt.gns3a +359 -0
  219. gns3server/appliances/opnsense.gns3a +122 -0
  220. gns3server/appliances/oracle-linux-cloud.gns3a +115 -0
  221. gns3server/appliances/ostinato-wireshark.gns3a +20 -0
  222. gns3server/appliances/ostinato.gns3a +75 -0
  223. gns3server/appliances/ovs-snmp.gns3a +18 -0
  224. gns3server/appliances/packetfence-zen.gns3a +151 -0
  225. gns3server/appliances/pan-vm-fw.gns3a +241 -0
  226. gns3server/appliances/parrot-os.gns3a +68 -0
  227. gns3server/appliances/pfsense.gns3a +122 -0
  228. gns3server/appliances/proxmox-mg.gns3a +69 -0
  229. gns3server/appliances/puppy-linux.gns3a +82 -0
  230. gns3server/appliances/python-go-perl-php.gns3a +17 -0
  231. gns3server/appliances/raspian.gns3a +79 -0
  232. gns3server/appliances/reactos.gns3a +67 -0
  233. gns3server/appliances/rhel.gns3a +207 -0
  234. gns3server/appliances/riverbed-steelhead-ng-vcx.gns3a +68 -0
  235. gns3server/appliances/rockylinux.gns3a +100 -0
  236. gns3server/appliances/security-onion.gns3a +108 -0
  237. gns3server/appliances/smoothwall.gns3a +54 -0
  238. gns3server/appliances/sophos-iview.gns3a +66 -0
  239. gns3server/appliances/sophos-utm.gns3a +263 -0
  240. gns3server/appliances/sophos-xg.gns3a +198 -0
  241. gns3server/appliances/stonework.gns3a +21 -0
  242. gns3server/appliances/tacacs-gui.gns3a +57 -0
  243. gns3server/appliances/tinycore-linux.gns3a +73 -0
  244. gns3server/appliances/trendmicro-imsva.gns3a +54 -0
  245. gns3server/appliances/trendmicro-iwsva.gns3a +53 -0
  246. gns3server/appliances/truenas.gns3a +104 -0
  247. gns3server/appliances/turnkey-wordpress.gns3a +20 -0
  248. gns3server/appliances/ubuntu-cloud.gns3a +116 -0
  249. gns3server/appliances/ubuntu-docker.gns3a +20 -0
  250. gns3server/appliances/ubuntu-gui.gns3a +98 -0
  251. gns3server/appliances/untangle.gns3a +208 -0
  252. gns3server/appliances/viptela-edge-genericx86-64.gns3a +84 -0
  253. gns3server/appliances/viptela-smart-genericx86-64.gns3a +82 -0
  254. gns3server/appliances/viptela-vmanage-genericx86-64.gns3a +95 -0
  255. gns3server/appliances/vpp.gns3a +45 -0
  256. gns3server/appliances/vrin.gns3a +118 -0
  257. gns3server/appliances/vyos.gns3a +179 -0
  258. gns3server/appliances/watchguard-fireboxv.gns3a +45 -0
  259. gns3server/appliances/watchguard-xtmv.gns3a +45 -0
  260. gns3server/appliances/webterm.gns3a +20 -0
  261. gns3server/appliances/windows-11-dev-env.gns3a +74 -0
  262. gns3server/appliances/windows-xp+ie.gns3a +52 -0
  263. gns3server/appliances/windows.gns3a +125 -0
  264. gns3server/appliances/windows_server.gns3a +83 -0
  265. gns3server/appliances/zentyal-server.gns3a +159 -0
  266. gns3server/appliances/zeroshell.gns3a +103 -0
  267. gns3server/compute/__init__.py +38 -0
  268. gns3server/compute/adapters/__init__.py +0 -0
  269. gns3server/compute/adapters/adapter.py +104 -0
  270. gns3server/compute/adapters/ethernet_adapter.py +33 -0
  271. gns3server/compute/adapters/serial_adapter.py +32 -0
  272. gns3server/compute/base_manager.py +547 -0
  273. gns3server/compute/base_node.py +979 -0
  274. gns3server/compute/builtin/__init__.py +46 -0
  275. gns3server/compute/builtin/builtin_node_factory.py +42 -0
  276. gns3server/compute/builtin/nodes/__init__.py +0 -0
  277. gns3server/compute/builtin/nodes/cloud.py +548 -0
  278. gns3server/compute/builtin/nodes/ethernet_hub.py +104 -0
  279. gns3server/compute/builtin/nodes/ethernet_switch.py +104 -0
  280. gns3server/compute/builtin/nodes/nat.py +97 -0
  281. gns3server/compute/compute_error.py +48 -0
  282. gns3server/compute/docker/__init__.py +320 -0
  283. gns3server/compute/docker/docker_error.py +33 -0
  284. gns3server/compute/docker/docker_vm.py +1378 -0
  285. gns3server/compute/docker/resources/bin/udhcpc +15 -0
  286. gns3server/compute/docker/resources/etc/udhcpc/default.script +138 -0
  287. gns3server/compute/docker/resources/init.sh +102 -0
  288. gns3server/compute/docker/resources/run-cmd.sh +6 -0
  289. gns3server/compute/dynamips/__init__.py +661 -0
  290. gns3server/compute/dynamips/adapters/__init__.py +0 -0
  291. gns3server/compute/dynamips/adapters/adapter.py +165 -0
  292. gns3server/compute/dynamips/adapters/c1700_mb_1fe.py +36 -0
  293. gns3server/compute/dynamips/adapters/c1700_mb_wic1.py +37 -0
  294. gns3server/compute/dynamips/adapters/c2600_mb_1e.py +36 -0
  295. gns3server/compute/dynamips/adapters/c2600_mb_1fe.py +37 -0
  296. gns3server/compute/dynamips/adapters/c2600_mb_2e.py +36 -0
  297. gns3server/compute/dynamips/adapters/c2600_mb_2fe.py +36 -0
  298. gns3server/compute/dynamips/adapters/c7200_io_2fe.py +36 -0
  299. gns3server/compute/dynamips/adapters/c7200_io_fe.py +36 -0
  300. gns3server/compute/dynamips/adapters/c7200_io_ge_e.py +36 -0
  301. gns3server/compute/dynamips/adapters/gt96100_fe.py +31 -0
  302. gns3server/compute/dynamips/adapters/leopard_2fe.py +37 -0
  303. gns3server/compute/dynamips/adapters/nm_16esw.py +32 -0
  304. gns3server/compute/dynamips/adapters/nm_1e.py +32 -0
  305. gns3server/compute/dynamips/adapters/nm_1fe_tx.py +32 -0
  306. gns3server/compute/dynamips/adapters/nm_4e.py +32 -0
  307. gns3server/compute/dynamips/adapters/nm_4t.py +32 -0
  308. gns3server/compute/dynamips/adapters/pa_2fe_tx.py +32 -0
  309. gns3server/compute/dynamips/adapters/pa_4e.py +32 -0
  310. gns3server/compute/dynamips/adapters/pa_4t.py +32 -0
  311. gns3server/compute/dynamips/adapters/pa_8e.py +32 -0
  312. gns3server/compute/dynamips/adapters/pa_8t.py +32 -0
  313. gns3server/compute/dynamips/adapters/pa_a1.py +32 -0
  314. gns3server/compute/dynamips/adapters/pa_fe_tx.py +32 -0
  315. gns3server/compute/dynamips/adapters/pa_ge.py +32 -0
  316. gns3server/compute/dynamips/adapters/pa_pos_oc3.py +32 -0
  317. gns3server/compute/dynamips/adapters/wic_1enet.py +40 -0
  318. gns3server/compute/dynamips/adapters/wic_1t.py +40 -0
  319. gns3server/compute/dynamips/adapters/wic_2t.py +40 -0
  320. gns3server/compute/dynamips/dynamips_error.py +26 -0
  321. gns3server/compute/dynamips/dynamips_factory.py +71 -0
  322. gns3server/compute/dynamips/dynamips_hypervisor.py +333 -0
  323. gns3server/compute/dynamips/hypervisor.py +210 -0
  324. gns3server/compute/dynamips/nios/__init__.py +0 -0
  325. gns3server/compute/dynamips/nios/nio.py +352 -0
  326. gns3server/compute/dynamips/nios/nio_generic_ethernet.py +73 -0
  327. gns3server/compute/dynamips/nios/nio_linux_ethernet.py +72 -0
  328. gns3server/compute/dynamips/nios/nio_null.py +51 -0
  329. gns3server/compute/dynamips/nios/nio_tap.py +66 -0
  330. gns3server/compute/dynamips/nios/nio_udp.py +136 -0
  331. gns3server/compute/dynamips/nios/nio_unix.py +88 -0
  332. gns3server/compute/dynamips/nios/nio_vde.py +88 -0
  333. gns3server/compute/dynamips/nodes/__init__.py +0 -0
  334. gns3server/compute/dynamips/nodes/atm_switch.py +514 -0
  335. gns3server/compute/dynamips/nodes/bridge.py +107 -0
  336. gns3server/compute/dynamips/nodes/c1700.py +160 -0
  337. gns3server/compute/dynamips/nodes/c2600.py +171 -0
  338. gns3server/compute/dynamips/nodes/c2691.py +112 -0
  339. gns3server/compute/dynamips/nodes/c3600.py +155 -0
  340. gns3server/compute/dynamips/nodes/c3725.py +112 -0
  341. gns3server/compute/dynamips/nodes/c3745.py +112 -0
  342. gns3server/compute/dynamips/nodes/c7200.py +268 -0
  343. gns3server/compute/dynamips/nodes/device.py +134 -0
  344. gns3server/compute/dynamips/nodes/ethernet_hub.py +241 -0
  345. gns3server/compute/dynamips/nodes/ethernet_switch.py +496 -0
  346. gns3server/compute/dynamips/nodes/frame_relay_switch.py +379 -0
  347. gns3server/compute/dynamips/nodes/router.py +1797 -0
  348. gns3server/compute/error.py +40 -0
  349. gns3server/compute/iou/__init__.py +64 -0
  350. gns3server/compute/iou/iou_error.py +25 -0
  351. gns3server/compute/iou/iou_vm.py +1482 -0
  352. gns3server/compute/iou/utils/__init__.py +0 -0
  353. gns3server/compute/iou/utils/iou_export.py +233 -0
  354. gns3server/compute/iou/utils/iou_import.py +232 -0
  355. gns3server/compute/nios/__init__.py +0 -0
  356. gns3server/compute/nios/nio.py +120 -0
  357. gns3server/compute/nios/nio_ethernet.py +56 -0
  358. gns3server/compute/nios/nio_tap.py +56 -0
  359. gns3server/compute/nios/nio_udp.py +82 -0
  360. gns3server/compute/notification_manager.py +73 -0
  361. gns3server/compute/port_manager.py +402 -0
  362. gns3server/compute/project.py +434 -0
  363. gns3server/compute/project_manager.py +122 -0
  364. gns3server/compute/qemu/__init__.py +304 -0
  365. gns3server/compute/qemu/qemu_error.py +26 -0
  366. gns3server/compute/qemu/qemu_vm.py +2697 -0
  367. gns3server/compute/qemu/resources/config.img.zip +0 -0
  368. gns3server/compute/qemu/utils/__init__.py +0 -0
  369. gns3server/compute/qemu/utils/guest_cid.py +38 -0
  370. gns3server/compute/qemu/utils/qcow2.py +138 -0
  371. gns3server/compute/qemu/utils/ziputils.py +55 -0
  372. gns3server/compute/ubridge/__init__.py +0 -0
  373. gns3server/compute/ubridge/hypervisor.py +256 -0
  374. gns3server/compute/ubridge/ubridge_error.py +32 -0
  375. gns3server/compute/ubridge/ubridge_hypervisor.py +285 -0
  376. gns3server/compute/virtualbox/__init__.py +212 -0
  377. gns3server/compute/virtualbox/virtualbox_error.py +26 -0
  378. gns3server/compute/virtualbox/virtualbox_vm.py +1255 -0
  379. gns3server/compute/vmware/__init__.py +725 -0
  380. gns3server/compute/vmware/vmware_error.py +26 -0
  381. gns3server/compute/vmware/vmware_vm.py +1007 -0
  382. gns3server/compute/vpcs/__init__.py +91 -0
  383. gns3server/compute/vpcs/vpcs_error.py +26 -0
  384. gns3server/compute/vpcs/vpcs_vm.py +595 -0
  385. gns3server/config.py +284 -0
  386. gns3server/config_samples/gns3_server.conf +160 -0
  387. gns3server/configs/ios_base_startup-config.txt +26 -0
  388. gns3server/configs/ios_etherswitch_startup-config.txt +181 -0
  389. gns3server/configs/iou_l2_base_startup-config.txt +133 -0
  390. gns3server/configs/iou_l3_base_startup-config.txt +109 -0
  391. gns3server/configs/vpcs_base_config.txt +9 -0
  392. gns3server/controller/__init__.py +691 -0
  393. gns3server/controller/appliance.py +86 -0
  394. gns3server/controller/appliance_manager.py +486 -0
  395. gns3server/controller/appliance_to_template.py +132 -0
  396. gns3server/controller/compute.py +677 -0
  397. gns3server/controller/controller_error.py +77 -0
  398. gns3server/controller/drawing.py +229 -0
  399. gns3server/controller/export_project.py +336 -0
  400. gns3server/controller/gns3vm/__init__.py +412 -0
  401. gns3server/controller/gns3vm/base_gns3_vm.py +303 -0
  402. gns3server/controller/gns3vm/gns3_vm_error.py +28 -0
  403. gns3server/controller/gns3vm/hyperv_gns3_vm.py +346 -0
  404. gns3server/controller/gns3vm/remote_gns3_vm.py +73 -0
  405. gns3server/controller/gns3vm/virtualbox_gns3_vm.py +480 -0
  406. gns3server/controller/gns3vm/vmware_gns3_vm.py +216 -0
  407. gns3server/controller/import_project.py +332 -0
  408. gns3server/controller/link.py +461 -0
  409. gns3server/controller/node.py +840 -0
  410. gns3server/controller/notification.py +149 -0
  411. gns3server/controller/ports/__init__.py +0 -0
  412. gns3server/controller/ports/atm_port.py +51 -0
  413. gns3server/controller/ports/ethernet_port.py +49 -0
  414. gns3server/controller/ports/fastethernet_port.py +43 -0
  415. gns3server/controller/ports/frame_relay_port.py +43 -0
  416. gns3server/controller/ports/gigabitethernet_port.py +43 -0
  417. gns3server/controller/ports/port.py +109 -0
  418. gns3server/controller/ports/port_factory.py +213 -0
  419. gns3server/controller/ports/pos_port.py +52 -0
  420. gns3server/controller/ports/serial_port.py +63 -0
  421. gns3server/controller/project.py +1399 -0
  422. gns3server/controller/snapshot.py +140 -0
  423. gns3server/controller/symbol_themes.py +153 -0
  424. gns3server/controller/symbols.py +168 -0
  425. gns3server/controller/topology.py +788 -0
  426. gns3server/controller/udp_link.py +245 -0
  427. gns3server/core/__init__.py +0 -0
  428. gns3server/core/tasks.py +99 -0
  429. gns3server/crash_report.py +167 -0
  430. gns3server/db/__init__.py +0 -0
  431. gns3server/db/models/__init__.py +38 -0
  432. gns3server/db/models/acl.py +46 -0
  433. gns3server/db/models/base.py +117 -0
  434. gns3server/db/models/computes.py +33 -0
  435. gns3server/db/models/images.py +43 -0
  436. gns3server/db/models/pools.py +52 -0
  437. gns3server/db/models/privileges.py +359 -0
  438. gns3server/db/models/roles.py +57 -0
  439. gns3server/db/models/templates.py +272 -0
  440. gns3server/db/models/users.py +94 -0
  441. gns3server/db/repositories/__init__.py +0 -0
  442. gns3server/db/repositories/base.py +24 -0
  443. gns3server/db/repositories/computes.py +93 -0
  444. gns3server/db/repositories/images.py +141 -0
  445. gns3server/db/repositories/pools.py +218 -0
  446. gns3server/db/repositories/rbac.py +430 -0
  447. gns3server/db/repositories/templates.py +172 -0
  448. gns3server/db/repositories/users.py +289 -0
  449. gns3server/db/tasks.py +230 -0
  450. gns3server/db_migrations/README +5 -0
  451. gns3server/db_migrations/env.py +101 -0
  452. gns3server/db_migrations/script.py.mako +24 -0
  453. gns3server/db_migrations/versions/7ceeddd9c9a8_init.py +28 -0
  454. gns3server/db_migrations/versions/9a5292aa4389_add_mac_address_field_in_docker_.py +27 -0
  455. gns3server/disks/OVMF.fd +0 -0
  456. gns3server/disks/OVMF_CODE.fd +0 -0
  457. gns3server/disks/OVMF_VARS.fd +0 -0
  458. gns3server/disks/empty100G.qcow2 +0 -0
  459. gns3server/disks/empty10G.qcow2 +0 -0
  460. gns3server/disks/empty150G.qcow2 +0 -0
  461. gns3server/disks/empty1T.qcow2 +0 -0
  462. gns3server/disks/empty200G.qcow2 +0 -0
  463. gns3server/disks/empty20G.qcow2 +0 -0
  464. gns3server/disks/empty250G.qcow2 +0 -0
  465. gns3server/disks/empty30G.qcow2 +0 -0
  466. gns3server/disks/empty40G.qcow2 +0 -0
  467. gns3server/disks/empty500G.qcow2 +0 -0
  468. gns3server/disks/empty50G.qcow2 +0 -0
  469. gns3server/disks/empty8G.qcow2 +0 -0
  470. gns3server/gns3_server.egg-info/PKG-INFO +307 -0
  471. gns3server/gns3_server.egg-info/SOURCES.txt +0 -0
  472. gns3server/gns3_server.egg-info/dependency_links.txt +1 -0
  473. gns3server/gns3_server.egg-info/entry_points.txt +4 -0
  474. gns3server/gns3_server.egg-info/not-zip-safe +1 -0
  475. gns3server/gns3_server.egg-info/requires.txt +31 -0
  476. gns3server/gns3_server.egg-info/top_level.txt +2 -0
  477. gns3server/logger.py +164 -0
  478. gns3server/main.py +81 -0
  479. gns3server/schemas/__init__.py +85 -0
  480. gns3server/schemas/common.py +71 -0
  481. gns3server/schemas/compute/__init__.py +0 -0
  482. gns3server/schemas/compute/atm_switch_nodes.py +55 -0
  483. gns3server/schemas/compute/cloud_nodes.py +137 -0
  484. gns3server/schemas/compute/docker_nodes.py +75 -0
  485. gns3server/schemas/compute/dynamips_nodes.py +197 -0
  486. gns3server/schemas/compute/ethernet_hub_nodes.py +63 -0
  487. gns3server/schemas/compute/ethernet_switch_nodes.py +100 -0
  488. gns3server/schemas/compute/frame_relay_switch_nodes.py +55 -0
  489. gns3server/schemas/compute/iou_nodes.py +76 -0
  490. gns3server/schemas/compute/nat_nodes.py +122 -0
  491. gns3server/schemas/compute/nios.py +66 -0
  492. gns3server/schemas/compute/qemu_nodes.py +245 -0
  493. gns3server/schemas/compute/virtualbox_nodes.py +97 -0
  494. gns3server/schemas/compute/vmware_nodes.py +102 -0
  495. gns3server/schemas/compute/vpcs_nodes.py +68 -0
  496. gns3server/schemas/config.py +197 -0
  497. gns3server/schemas/controller/__init__.py +0 -0
  498. gns3server/schemas/controller/appliances.py +466 -0
  499. gns3server/schemas/controller/base.py +25 -0
  500. gns3server/schemas/controller/capabilities.py +34 -0
  501. gns3server/schemas/controller/computes.py +168 -0
  502. gns3server/schemas/controller/drawings.py +34 -0
  503. gns3server/schemas/controller/gns3vm.py +56 -0
  504. gns3server/schemas/controller/images.py +44 -0
  505. gns3server/schemas/controller/iou_license.py +23 -0
  506. gns3server/schemas/controller/labels.py +30 -0
  507. gns3server/schemas/controller/links.py +94 -0
  508. gns3server/schemas/controller/nodes.py +177 -0
  509. gns3server/schemas/controller/pools.py +81 -0
  510. gns3server/schemas/controller/projects.py +116 -0
  511. gns3server/schemas/controller/rbac.py +113 -0
  512. gns3server/schemas/controller/snapshots.py +42 -0
  513. gns3server/schemas/controller/templates/__init__.py +83 -0
  514. gns3server/schemas/controller/templates/cloud_templates.py +44 -0
  515. gns3server/schemas/controller/templates/docker_templates.py +59 -0
  516. gns3server/schemas/controller/templates/dynamips_templates.py +199 -0
  517. gns3server/schemas/controller/templates/ethernet_hub_templates.py +46 -0
  518. gns3server/schemas/controller/templates/ethernet_switch_templates.py +57 -0
  519. gns3server/schemas/controller/templates/iou_templates.py +47 -0
  520. gns3server/schemas/controller/templates/qemu_templates.py +94 -0
  521. gns3server/schemas/controller/templates/virtualbox_templates.py +64 -0
  522. gns3server/schemas/controller/templates/vmware_templates.py +61 -0
  523. gns3server/schemas/controller/templates/vpcs_templates.py +39 -0
  524. gns3server/schemas/controller/tokens.py +29 -0
  525. gns3server/schemas/controller/topology.py +82 -0
  526. gns3server/schemas/controller/users.py +105 -0
  527. gns3server/schemas/link.py +0 -0
  528. gns3server/schemas/qemu_disk_image.py +103 -0
  529. gns3server/schemas/version.py +25 -0
  530. gns3server/server.py +348 -0
  531. gns3server/services/__init__.py +19 -0
  532. gns3server/services/authentication.py +83 -0
  533. gns3server/services/computes.py +82 -0
  534. gns3server/services/templates.py +329 -0
  535. gns3server/static/.gitkeep +0 -0
  536. gns3server/static/favicon.ico +0 -0
  537. gns3server/static/redoc.standalone.js +1782 -0
  538. gns3server/static/swagger-ui-bundle.js +2 -0
  539. gns3server/static/swagger-ui.css +3 -0
  540. gns3server/static/web-ui/3rdpartylicenses.txt +2604 -0
  541. gns3server/static/web-ui/465.92c7ab880f2504d3.js +1 -0
  542. gns3server/static/web-ui/MaterialIcons-Regular.196fa4a92dd6fa73.ttf +0 -0
  543. gns3server/static/web-ui/MaterialIcons-Regular.1e50f5c2ffa6aba4.eot +0 -0
  544. gns3server/static/web-ui/MaterialIcons-Regular.7ea2023eeca07427.woff2 +0 -0
  545. gns3server/static/web-ui/MaterialIcons-Regular.db852539204b1a34.woff +0 -0
  546. gns3server/static/web-ui/NotoSans-Bold.635e9291df1a5a00.woff2 +0 -0
  547. gns3server/static/web-ui/NotoSans-Bold.885427cced6d8c94.woff +0 -0
  548. gns3server/static/web-ui/NotoSans-Bold.93203a43bc93ad9c.svg +8453 -0
  549. gns3server/static/web-ui/NotoSans-Bold.a7ab189a45f553b4.eot +0 -0
  550. gns3server/static/web-ui/NotoSans-Bold.e989c11744b36f58.ttf +0 -0
  551. gns3server/static/web-ui/NotoSans-BoldItalic.2baf33b639fc86a4.woff +0 -0
  552. gns3server/static/web-ui/NotoSans-BoldItalic.2eea9ba02e25c1d3.ttf +0 -0
  553. gns3server/static/web-ui/NotoSans-BoldItalic.46a5eec91da0d900.svg +8453 -0
  554. gns3server/static/web-ui/NotoSans-BoldItalic.7fdef73b477daa73.eot +0 -0
  555. gns3server/static/web-ui/NotoSans-BoldItalic.c62f70f121f271aa.woff2 +0 -0
  556. gns3server/static/web-ui/NotoSans-Italic.219ee88e5bc4e168.ttf +0 -0
  557. gns3server/static/web-ui/NotoSans-Italic.3d6f7cf772169c8d.svg +8453 -0
  558. gns3server/static/web-ui/NotoSans-Italic.77cd3818b5a2cb97.woff +0 -0
  559. gns3server/static/web-ui/NotoSans-Italic.85194585ae9d8c36.eot +0 -0
  560. gns3server/static/web-ui/NotoSans-Italic.eee02500e81db981.woff2 +0 -0
  561. gns3server/static/web-ui/NotoSans-Regular.156b7584192d70ed.eot +0 -0
  562. gns3server/static/web-ui/NotoSans-Regular.374d132a6803e279.woff2 +0 -0
  563. gns3server/static/web-ui/NotoSans-Regular.595e3f5876719740.woff +0 -0
  564. gns3server/static/web-ui/NotoSans-Regular.bb384defbe36eaec.svg +8453 -0
  565. gns3server/static/web-ui/NotoSans-Regular.e6139cb1663a1c57.ttf +0 -0
  566. gns3server/static/web-ui/ReleaseNotes.txt +179 -0
  567. gns3server/static/web-ui/assets/favicon.ico +0 -0
  568. gns3server/static/web-ui/assets/gns3_icon.svg +88 -0
  569. gns3server/static/web-ui/assets/gns3_icon_black.svg +88 -0
  570. gns3server/static/web-ui/assets/icons/mac/icon.icns +0 -0
  571. gns3server/static/web-ui/assets/icons/png/1024x1024.png +0 -0
  572. gns3server/static/web-ui/assets/icons/png/128x128.png +0 -0
  573. gns3server/static/web-ui/assets/icons/png/16x16.png +0 -0
  574. gns3server/static/web-ui/assets/icons/png/24x24.png +0 -0
  575. gns3server/static/web-ui/assets/icons/png/256x256.png +0 -0
  576. gns3server/static/web-ui/assets/icons/png/32x32.png +0 -0
  577. gns3server/static/web-ui/assets/icons/png/48x48.png +0 -0
  578. gns3server/static/web-ui/assets/icons/png/512x512.png +0 -0
  579. gns3server/static/web-ui/assets/icons/png/64x64.png +0 -0
  580. gns3server/static/web-ui/assets/icons/win/icon.ico +0 -0
  581. gns3server/static/web-ui/assets/logo-header.png +0 -0
  582. gns3server/static/web-ui/assets/resources/images/filter-capture.svg +428 -0
  583. gns3server/static/web-ui/assets/resources/images/filter.svg +708 -0
  584. gns3server/static/web-ui/assets/resources/images/inspect.svg +312 -0
  585. gns3server/static/web-ui/assets/resources/images/pause.svg +752 -0
  586. gns3server/static/web-ui/favicon.ico +0 -0
  587. gns3server/static/web-ui/index.html +51 -0
  588. gns3server/static/web-ui/main.f802edd2b8c6db1d.js +1 -0
  589. gns3server/static/web-ui/polyfills.319c79dd175e50d0.js +1 -0
  590. gns3server/static/web-ui/roboto-latin-100.539f0a96b40596f7.woff2 +0 -0
  591. gns3server/static/web-ui/roboto-latin-100.5ba994dac3e79ea8.woff +0 -0
  592. gns3server/static/web-ui/roboto-latin-100italic.41ba64219cb743c9.woff2 +0 -0
  593. gns3server/static/web-ui/roboto-latin-100italic.d61e7e8b07c0638c.woff +0 -0
  594. gns3server/static/web-ui/roboto-latin-300.4d8f8086236bad80.woff2 +0 -0
  595. gns3server/static/web-ui/roboto-latin-300.6c1bc461047e61f5.woff +0 -0
  596. gns3server/static/web-ui/roboto-latin-300italic.3a529751a590d3c1.woff +0 -0
  597. gns3server/static/web-ui/roboto-latin-300italic.45164643b3bd5824.woff2 +0 -0
  598. gns3server/static/web-ui/roboto-latin-400.1e2d4d3a272629cd.woff2 +0 -0
  599. gns3server/static/web-ui/roboto-latin-400.7e4a045b9373d9c1.woff +0 -0
  600. gns3server/static/web-ui/roboto-latin-400italic.68431199e5b90ea8.woff +0 -0
  601. gns3server/static/web-ui/roboto-latin-400italic.bb3c6955c4334c8a.woff2 +0 -0
  602. gns3server/static/web-ui/roboto-latin-500.1dfbc3dbf815e3f3.woff2 +0 -0
  603. gns3server/static/web-ui/roboto-latin-500.e21fe97fd2329ff7.woff +0 -0
  604. gns3server/static/web-ui/roboto-latin-500italic.7543a42b12b1452f.woff2 +0 -0
  605. gns3server/static/web-ui/roboto-latin-500italic.aaff6867154023d3.woff +0 -0
  606. gns3server/static/web-ui/roboto-latin-700.02633003129d1e63.woff +0 -0
  607. gns3server/static/web-ui/roboto-latin-700.12893bfc0762b0f3.woff2 +0 -0
  608. gns3server/static/web-ui/roboto-latin-700italic.bc7179e004e40113.woff2 +0 -0
  609. gns3server/static/web-ui/roboto-latin-700italic.e53062e27c63fafb.woff +0 -0
  610. gns3server/static/web-ui/roboto-latin-900.282ba77fda1349f3.woff2 +0 -0
  611. gns3server/static/web-ui/roboto-latin-900.4962e810cbb4d3e5.woff +0 -0
  612. gns3server/static/web-ui/roboto-latin-900italic.2394134a78bbaef8.woff +0 -0
  613. gns3server/static/web-ui/roboto-latin-900italic.9c1f80840351fa12.woff2 +0 -0
  614. gns3server/static/web-ui/runtime.24fa95b7061d7056.js +1 -0
  615. gns3server/static/web-ui/styles.6e966072487df0b7.css +5 -0
  616. gns3server/symbols/.gitkeep +0 -0
  617. gns3server/symbols/affinity/circle/blue/atm.svg +5 -0
  618. gns3server/symbols/affinity/circle/blue/bug.svg +8 -0
  619. gns3server/symbols/affinity/circle/blue/camera.svg +5 -0
  620. gns3server/symbols/affinity/circle/blue/camera_dome.svg +8 -0
  621. gns3server/symbols/affinity/circle/blue/client.svg +5 -0
  622. gns3server/symbols/affinity/circle/blue/client_vm.svg +6 -0
  623. gns3server/symbols/affinity/circle/blue/cloud.svg +5 -0
  624. gns3server/symbols/affinity/circle/blue/coffee.svg +6 -0
  625. gns3server/symbols/affinity/circle/blue/cog.svg +5 -0
  626. gns3server/symbols/affinity/circle/blue/communications.svg +5 -0
  627. gns3server/symbols/affinity/circle/blue/conversation.svg +12 -0
  628. gns3server/symbols/affinity/circle/blue/dna.svg +7 -0
  629. gns3server/symbols/affinity/circle/blue/dna2.svg +7 -0
  630. gns3server/symbols/affinity/circle/blue/docker.svg +11 -0
  631. gns3server/symbols/affinity/circle/blue/dslam.svg +5 -0
  632. gns3server/symbols/affinity/circle/blue/factory.svg +6 -0
  633. gns3server/symbols/affinity/circle/blue/fingerprint.svg +5 -0
  634. gns3server/symbols/affinity/circle/blue/firewall.svg +5 -0
  635. gns3server/symbols/affinity/circle/blue/firewall3.svg +5 -0
  636. gns3server/symbols/affinity/circle/blue/globe.svg +5 -0
  637. gns3server/symbols/affinity/circle/blue/grid.svg +5 -0
  638. gns3server/symbols/affinity/circle/blue/grid2.svg +5 -0
  639. gns3server/symbols/affinity/circle/blue/health.svg +5 -0
  640. gns3server/symbols/affinity/circle/blue/house.svg +9 -0
  641. gns3server/symbols/affinity/circle/blue/hub.svg +5 -0
  642. gns3server/symbols/affinity/circle/blue/inspect.svg +7 -0
  643. gns3server/symbols/affinity/circle/blue/inspect2.svg +6 -0
  644. gns3server/symbols/affinity/circle/blue/interconnect.svg +5 -0
  645. gns3server/symbols/affinity/circle/blue/ip_phone.svg +21 -0
  646. gns3server/symbols/affinity/circle/blue/isdn.svg +5 -0
  647. gns3server/symbols/affinity/circle/blue/laptop.svg +6 -0
  648. gns3server/symbols/affinity/circle/blue/ldap.svg +5 -0
  649. gns3server/symbols/affinity/circle/blue/light_bulb.svg +11 -0
  650. gns3server/symbols/affinity/circle/blue/link.svg +5 -0
  651. gns3server/symbols/affinity/circle/blue/loadbalancer.svg +5 -0
  652. gns3server/symbols/affinity/circle/blue/nas.svg +5 -0
  653. gns3server/symbols/affinity/circle/blue/nat.svg +60 -0
  654. gns3server/symbols/affinity/circle/blue/nat2.svg +55 -0
  655. gns3server/symbols/affinity/circle/blue/office.svg +5 -0
  656. gns3server/symbols/affinity/circle/blue/optical.svg +8 -0
  657. gns3server/symbols/affinity/circle/blue/phone_cloud.svg +6 -0
  658. gns3server/symbols/affinity/circle/blue/phone_old.svg +5 -0
  659. gns3server/symbols/affinity/circle/blue/phone_wireless.svg +8 -0
  660. gns3server/symbols/affinity/circle/blue/pinpoint.svg +5 -0
  661. gns3server/symbols/affinity/circle/blue/printer.svg +7 -0
  662. gns3server/symbols/affinity/circle/blue/rj45.svg +5 -0
  663. gns3server/symbols/affinity/circle/blue/router.svg +5 -0
  664. gns3server/symbols/affinity/circle/blue/router2.svg +5 -0
  665. gns3server/symbols/affinity/circle/blue/router_cloud.svg +6 -0
  666. gns3server/symbols/affinity/circle/blue/satellite.svg +6 -0
  667. gns3server/symbols/affinity/circle/blue/satellite_dish.svg +7 -0
  668. gns3server/symbols/affinity/circle/blue/scull.svg +8 -0
  669. gns3server/symbols/affinity/circle/blue/server.svg +5 -0
  670. gns3server/symbols/affinity/circle/blue/server_cluster.svg +7 -0
  671. gns3server/symbols/affinity/circle/blue/shield.svg +7 -0
  672. gns3server/symbols/affinity/circle/blue/statistics.svg +7 -0
  673. gns3server/symbols/affinity/circle/blue/storage.svg +5 -0
  674. gns3server/symbols/affinity/circle/blue/switch.svg +8 -0
  675. gns3server/symbols/affinity/circle/blue/switch_multilayer.svg +5 -0
  676. gns3server/symbols/affinity/circle/blue/tablet.svg +11 -0
  677. gns3server/symbols/affinity/circle/blue/template.svg +4 -0
  678. gns3server/symbols/affinity/circle/blue/tree.svg +5 -0
  679. gns3server/symbols/affinity/circle/blue/user.svg +7 -0
  680. gns3server/symbols/affinity/circle/blue/virtualbox.svg +7 -0
  681. gns3server/symbols/affinity/circle/blue/vm.svg +7 -0
  682. gns3server/symbols/affinity/circle/blue/vmware.svg +7 -0
  683. gns3server/symbols/affinity/circle/blue/vrf.svg +8 -0
  684. gns3server/symbols/affinity/circle/blue/wifi.svg +5 -0
  685. gns3server/symbols/affinity/circle/blue/wlc.svg +5 -0
  686. gns3server/symbols/affinity/circle/blue/xml.svg +5 -0
  687. gns3server/symbols/affinity/circle/gray/atm.svg +5 -0
  688. gns3server/symbols/affinity/circle/gray/bug.svg +8 -0
  689. gns3server/symbols/affinity/circle/gray/camera.svg +5 -0
  690. gns3server/symbols/affinity/circle/gray/camera_dome.svg +8 -0
  691. gns3server/symbols/affinity/circle/gray/client.svg +5 -0
  692. gns3server/symbols/affinity/circle/gray/client_vm.svg +6 -0
  693. gns3server/symbols/affinity/circle/gray/cloud.svg +5 -0
  694. gns3server/symbols/affinity/circle/gray/coffee.svg +6 -0
  695. gns3server/symbols/affinity/circle/gray/cog.svg +5 -0
  696. gns3server/symbols/affinity/circle/gray/communications.svg +5 -0
  697. gns3server/symbols/affinity/circle/gray/conversation.svg +12 -0
  698. gns3server/symbols/affinity/circle/gray/dna.svg +7 -0
  699. gns3server/symbols/affinity/circle/gray/dna2.svg +7 -0
  700. gns3server/symbols/affinity/circle/gray/docker.svg +11 -0
  701. gns3server/symbols/affinity/circle/gray/dslam.svg +5 -0
  702. gns3server/symbols/affinity/circle/gray/factory.svg +7 -0
  703. gns3server/symbols/affinity/circle/gray/fingerprint.svg +5 -0
  704. gns3server/symbols/affinity/circle/gray/firewall.svg +5 -0
  705. gns3server/symbols/affinity/circle/gray/firewall3.svg +5 -0
  706. gns3server/symbols/affinity/circle/gray/globe.svg +5 -0
  707. gns3server/symbols/affinity/circle/gray/grid.svg +5 -0
  708. gns3server/symbols/affinity/circle/gray/grid2.svg +5 -0
  709. gns3server/symbols/affinity/circle/gray/health.svg +5 -0
  710. gns3server/symbols/affinity/circle/gray/house.svg +9 -0
  711. gns3server/symbols/affinity/circle/gray/hub.svg +5 -0
  712. gns3server/symbols/affinity/circle/gray/inspect.svg +7 -0
  713. gns3server/symbols/affinity/circle/gray/inspect2.svg +6 -0
  714. gns3server/symbols/affinity/circle/gray/interconnect.svg +5 -0
  715. gns3server/symbols/affinity/circle/gray/ip_phone.svg +21 -0
  716. gns3server/symbols/affinity/circle/gray/isdn.svg +5 -0
  717. gns3server/symbols/affinity/circle/gray/laptop.svg +6 -0
  718. gns3server/symbols/affinity/circle/gray/ldap.svg +5 -0
  719. gns3server/symbols/affinity/circle/gray/light_bulb.svg +11 -0
  720. gns3server/symbols/affinity/circle/gray/link.svg +5 -0
  721. gns3server/symbols/affinity/circle/gray/loadbalancer.svg +5 -0
  722. gns3server/symbols/affinity/circle/gray/nas.svg +5 -0
  723. gns3server/symbols/affinity/circle/gray/nat.svg +60 -0
  724. gns3server/symbols/affinity/circle/gray/nat2.svg +55 -0
  725. gns3server/symbols/affinity/circle/gray/office.svg +5 -0
  726. gns3server/symbols/affinity/circle/gray/optical.svg +8 -0
  727. gns3server/symbols/affinity/circle/gray/phone_cloud.svg +6 -0
  728. gns3server/symbols/affinity/circle/gray/phone_old.svg +5 -0
  729. gns3server/symbols/affinity/circle/gray/phone_wireless.svg +8 -0
  730. gns3server/symbols/affinity/circle/gray/pinpoint.svg +5 -0
  731. gns3server/symbols/affinity/circle/gray/printer.svg +25 -0
  732. gns3server/symbols/affinity/circle/gray/rj45.svg +5 -0
  733. gns3server/symbols/affinity/circle/gray/router.svg +5 -0
  734. gns3server/symbols/affinity/circle/gray/router2.svg +5 -0
  735. gns3server/symbols/affinity/circle/gray/router_cloud.svg +6 -0
  736. gns3server/symbols/affinity/circle/gray/satellite.svg +6 -0
  737. gns3server/symbols/affinity/circle/gray/satellite_dish.svg +7 -0
  738. gns3server/symbols/affinity/circle/gray/scull.svg +8 -0
  739. gns3server/symbols/affinity/circle/gray/server.svg +5 -0
  740. gns3server/symbols/affinity/circle/gray/server_cluster.svg +7 -0
  741. gns3server/symbols/affinity/circle/gray/shield.svg +7 -0
  742. gns3server/symbols/affinity/circle/gray/statistics.svg +7 -0
  743. gns3server/symbols/affinity/circle/gray/storage.svg +5 -0
  744. gns3server/symbols/affinity/circle/gray/switch.svg +8 -0
  745. gns3server/symbols/affinity/circle/gray/switch_multilayer.svg +5 -0
  746. gns3server/symbols/affinity/circle/gray/tablet.svg +11 -0
  747. gns3server/symbols/affinity/circle/gray/template.svg +4 -0
  748. gns3server/symbols/affinity/circle/gray/tree.svg +5 -0
  749. gns3server/symbols/affinity/circle/gray/user.svg +7 -0
  750. gns3server/symbols/affinity/circle/gray/virtualbox.svg +7 -0
  751. gns3server/symbols/affinity/circle/gray/vm.svg +7 -0
  752. gns3server/symbols/affinity/circle/gray/vmware.svg +7 -0
  753. gns3server/symbols/affinity/circle/gray/vrf.svg +8 -0
  754. gns3server/symbols/affinity/circle/gray/wifi.svg +5 -0
  755. gns3server/symbols/affinity/circle/gray/wlc.svg +5 -0
  756. gns3server/symbols/affinity/circle/gray/xml.svg +5 -0
  757. gns3server/symbols/affinity/circle/green/atm.svg +5 -0
  758. gns3server/symbols/affinity/circle/green/bug.svg +8 -0
  759. gns3server/symbols/affinity/circle/green/camera.svg +5 -0
  760. gns3server/symbols/affinity/circle/green/camera_dome.svg +8 -0
  761. gns3server/symbols/affinity/circle/green/client.svg +5 -0
  762. gns3server/symbols/affinity/circle/green/client_vm.svg +6 -0
  763. gns3server/symbols/affinity/circle/green/cloud.svg +5 -0
  764. gns3server/symbols/affinity/circle/green/coffee.svg +6 -0
  765. gns3server/symbols/affinity/circle/green/cog.svg +5 -0
  766. gns3server/symbols/affinity/circle/green/communications.svg +5 -0
  767. gns3server/symbols/affinity/circle/green/conversation.svg +12 -0
  768. gns3server/symbols/affinity/circle/green/dna.svg +7 -0
  769. gns3server/symbols/affinity/circle/green/dna2.svg +7 -0
  770. gns3server/symbols/affinity/circle/green/docker.svg +11 -0
  771. gns3server/symbols/affinity/circle/green/dslam.svg +5 -0
  772. gns3server/symbols/affinity/circle/green/factory.svg +6 -0
  773. gns3server/symbols/affinity/circle/green/fingerprint.svg +5 -0
  774. gns3server/symbols/affinity/circle/green/firewall.svg +5 -0
  775. gns3server/symbols/affinity/circle/green/firewall3.svg +5 -0
  776. gns3server/symbols/affinity/circle/green/globe.svg +5 -0
  777. gns3server/symbols/affinity/circle/green/grid.svg +5 -0
  778. gns3server/symbols/affinity/circle/green/grid2.svg +5 -0
  779. gns3server/symbols/affinity/circle/green/health.svg +5 -0
  780. gns3server/symbols/affinity/circle/green/house.svg +9 -0
  781. gns3server/symbols/affinity/circle/green/hub.svg +5 -0
  782. gns3server/symbols/affinity/circle/green/inspect.svg +7 -0
  783. gns3server/symbols/affinity/circle/green/inspect2.svg +6 -0
  784. gns3server/symbols/affinity/circle/green/interconnect.svg +5 -0
  785. gns3server/symbols/affinity/circle/green/ip_phone.svg +21 -0
  786. gns3server/symbols/affinity/circle/green/isdn.svg +5 -0
  787. gns3server/symbols/affinity/circle/green/laptop.svg +6 -0
  788. gns3server/symbols/affinity/circle/green/ldap.svg +5 -0
  789. gns3server/symbols/affinity/circle/green/light_bulb.svg +11 -0
  790. gns3server/symbols/affinity/circle/green/link.svg +5 -0
  791. gns3server/symbols/affinity/circle/green/loadbalancer.svg +5 -0
  792. gns3server/symbols/affinity/circle/green/nas.svg +5 -0
  793. gns3server/symbols/affinity/circle/green/nat.svg +60 -0
  794. gns3server/symbols/affinity/circle/green/nat2.svg +55 -0
  795. gns3server/symbols/affinity/circle/green/office.svg +5 -0
  796. gns3server/symbols/affinity/circle/green/optical.svg +8 -0
  797. gns3server/symbols/affinity/circle/green/phone_cloud.svg +6 -0
  798. gns3server/symbols/affinity/circle/green/phone_old.svg +5 -0
  799. gns3server/symbols/affinity/circle/green/phone_wireless.svg +8 -0
  800. gns3server/symbols/affinity/circle/green/pinpoint.svg +5 -0
  801. gns3server/symbols/affinity/circle/green/printer.svg +7 -0
  802. gns3server/symbols/affinity/circle/green/rj45.svg +5 -0
  803. gns3server/symbols/affinity/circle/green/router.svg +5 -0
  804. gns3server/symbols/affinity/circle/green/router2.svg +5 -0
  805. gns3server/symbols/affinity/circle/green/router_cloud.svg +6 -0
  806. gns3server/symbols/affinity/circle/green/satellite.svg +6 -0
  807. gns3server/symbols/affinity/circle/green/satellite_dish.svg +7 -0
  808. gns3server/symbols/affinity/circle/green/scull.svg +8 -0
  809. gns3server/symbols/affinity/circle/green/server.svg +5 -0
  810. gns3server/symbols/affinity/circle/green/server_cluster.svg +7 -0
  811. gns3server/symbols/affinity/circle/green/shield.svg +7 -0
  812. gns3server/symbols/affinity/circle/green/statistics.svg +7 -0
  813. gns3server/symbols/affinity/circle/green/storage.svg +5 -0
  814. gns3server/symbols/affinity/circle/green/switch.svg +8 -0
  815. gns3server/symbols/affinity/circle/green/switch_multilayer.svg +5 -0
  816. gns3server/symbols/affinity/circle/green/tablet.svg +11 -0
  817. gns3server/symbols/affinity/circle/green/template.svg +4 -0
  818. gns3server/symbols/affinity/circle/green/tree.svg +5 -0
  819. gns3server/symbols/affinity/circle/green/user.svg +7 -0
  820. gns3server/symbols/affinity/circle/green/virtualbox.svg +7 -0
  821. gns3server/symbols/affinity/circle/green/vm.svg +7 -0
  822. gns3server/symbols/affinity/circle/green/vmware.svg +7 -0
  823. gns3server/symbols/affinity/circle/green/vrf.svg +8 -0
  824. gns3server/symbols/affinity/circle/green/wifi.svg +5 -0
  825. gns3server/symbols/affinity/circle/green/wlc.svg +5 -0
  826. gns3server/symbols/affinity/circle/green/xml.svg +5 -0
  827. gns3server/symbols/affinity/circle/red/atm.svg +5 -0
  828. gns3server/symbols/affinity/circle/red/bug.svg +8 -0
  829. gns3server/symbols/affinity/circle/red/camera.svg +5 -0
  830. gns3server/symbols/affinity/circle/red/camera_dome.svg +8 -0
  831. gns3server/symbols/affinity/circle/red/client.svg +5 -0
  832. gns3server/symbols/affinity/circle/red/client_vm.svg +6 -0
  833. gns3server/symbols/affinity/circle/red/cloud.svg +5 -0
  834. gns3server/symbols/affinity/circle/red/coffee.svg +6 -0
  835. gns3server/symbols/affinity/circle/red/cog.svg +5 -0
  836. gns3server/symbols/affinity/circle/red/communications.svg +5 -0
  837. gns3server/symbols/affinity/circle/red/conversation.svg +12 -0
  838. gns3server/symbols/affinity/circle/red/dna.svg +7 -0
  839. gns3server/symbols/affinity/circle/red/dna2.svg +7 -0
  840. gns3server/symbols/affinity/circle/red/docker.svg +11 -0
  841. gns3server/symbols/affinity/circle/red/dslam.svg +5 -0
  842. gns3server/symbols/affinity/circle/red/factory.svg +6 -0
  843. gns3server/symbols/affinity/circle/red/fingerprint.svg +5 -0
  844. gns3server/symbols/affinity/circle/red/firewall.svg +5 -0
  845. gns3server/symbols/affinity/circle/red/firewall3.svg +5 -0
  846. gns3server/symbols/affinity/circle/red/globe.svg +5 -0
  847. gns3server/symbols/affinity/circle/red/grid.svg +5 -0
  848. gns3server/symbols/affinity/circle/red/grid2.svg +5 -0
  849. gns3server/symbols/affinity/circle/red/health.svg +5 -0
  850. gns3server/symbols/affinity/circle/red/house.svg +9 -0
  851. gns3server/symbols/affinity/circle/red/hub.svg +5 -0
  852. gns3server/symbols/affinity/circle/red/inspect.svg +7 -0
  853. gns3server/symbols/affinity/circle/red/inspect2.svg +6 -0
  854. gns3server/symbols/affinity/circle/red/interconnect.svg +5 -0
  855. gns3server/symbols/affinity/circle/red/ip_phone.svg +21 -0
  856. gns3server/symbols/affinity/circle/red/isdn.svg +5 -0
  857. gns3server/symbols/affinity/circle/red/laptop.svg +6 -0
  858. gns3server/symbols/affinity/circle/red/ldap.svg +5 -0
  859. gns3server/symbols/affinity/circle/red/light_bulb.svg +11 -0
  860. gns3server/symbols/affinity/circle/red/link.svg +5 -0
  861. gns3server/symbols/affinity/circle/red/loadbalancer.svg +5 -0
  862. gns3server/symbols/affinity/circle/red/nas.svg +5 -0
  863. gns3server/symbols/affinity/circle/red/nat.svg +60 -0
  864. gns3server/symbols/affinity/circle/red/nat2.svg +55 -0
  865. gns3server/symbols/affinity/circle/red/office.svg +5 -0
  866. gns3server/symbols/affinity/circle/red/optical.svg +8 -0
  867. gns3server/symbols/affinity/circle/red/phone_cloud.svg +6 -0
  868. gns3server/symbols/affinity/circle/red/phone_old.svg +5 -0
  869. gns3server/symbols/affinity/circle/red/phone_wireless.svg +8 -0
  870. gns3server/symbols/affinity/circle/red/pinpoint.svg +5 -0
  871. gns3server/symbols/affinity/circle/red/printer.svg +7 -0
  872. gns3server/symbols/affinity/circle/red/rj45.svg +5 -0
  873. gns3server/symbols/affinity/circle/red/router.svg +5 -0
  874. gns3server/symbols/affinity/circle/red/router2.svg +5 -0
  875. gns3server/symbols/affinity/circle/red/router_cloud.svg +6 -0
  876. gns3server/symbols/affinity/circle/red/satellite.svg +6 -0
  877. gns3server/symbols/affinity/circle/red/satellite_dish.svg +7 -0
  878. gns3server/symbols/affinity/circle/red/scull.svg +8 -0
  879. gns3server/symbols/affinity/circle/red/server.svg +5 -0
  880. gns3server/symbols/affinity/circle/red/server_cluster.svg +7 -0
  881. gns3server/symbols/affinity/circle/red/shield.svg +7 -0
  882. gns3server/symbols/affinity/circle/red/statistics.svg +7 -0
  883. gns3server/symbols/affinity/circle/red/storage.svg +5 -0
  884. gns3server/symbols/affinity/circle/red/switch.svg +8 -0
  885. gns3server/symbols/affinity/circle/red/switch_multilayer.svg +5 -0
  886. gns3server/symbols/affinity/circle/red/tablet.svg +11 -0
  887. gns3server/symbols/affinity/circle/red/template.svg +4 -0
  888. gns3server/symbols/affinity/circle/red/tree.svg +5 -0
  889. gns3server/symbols/affinity/circle/red/user.svg +7 -0
  890. gns3server/symbols/affinity/circle/red/virtualbox.svg +7 -0
  891. gns3server/symbols/affinity/circle/red/vm.svg +7 -0
  892. gns3server/symbols/affinity/circle/red/vmware.svg +7 -0
  893. gns3server/symbols/affinity/circle/red/vrf.svg +8 -0
  894. gns3server/symbols/affinity/circle/red/wifi.svg +5 -0
  895. gns3server/symbols/affinity/circle/red/wlc.svg +5 -0
  896. gns3server/symbols/affinity/circle/red/xml.svg +5 -0
  897. gns3server/symbols/affinity/square/blue/atm.svg +5 -0
  898. gns3server/symbols/affinity/square/blue/bug.svg +5 -0
  899. gns3server/symbols/affinity/square/blue/camera.svg +12 -0
  900. gns3server/symbols/affinity/square/blue/camera_dome.svg +8 -0
  901. gns3server/symbols/affinity/square/blue/client.svg +8 -0
  902. gns3server/symbols/affinity/square/blue/client_vm.svg +8 -0
  903. gns3server/symbols/affinity/square/blue/cloud.svg +12 -0
  904. gns3server/symbols/affinity/square/blue/coffee.svg +5 -0
  905. gns3server/symbols/affinity/square/blue/cog.svg +8 -0
  906. gns3server/symbols/affinity/square/blue/communications.svg +8 -0
  907. gns3server/symbols/affinity/square/blue/conversation.svg +12 -0
  908. gns3server/symbols/affinity/square/blue/dna.svg +7 -0
  909. gns3server/symbols/affinity/square/blue/dna2.svg +7 -0
  910. gns3server/symbols/affinity/square/blue/docker.svg +11 -0
  911. gns3server/symbols/affinity/square/blue/dslam.svg +8 -0
  912. gns3server/symbols/affinity/square/blue/factory.svg +5 -0
  913. gns3server/symbols/affinity/square/blue/fingerprint.svg +9 -0
  914. gns3server/symbols/affinity/square/blue/firewall.svg +7 -0
  915. gns3server/symbols/affinity/square/blue/firewall3.svg +6 -0
  916. gns3server/symbols/affinity/square/blue/globe.svg +5 -0
  917. gns3server/symbols/affinity/square/blue/grid.svg +7 -0
  918. gns3server/symbols/affinity/square/blue/grid2.svg +7 -0
  919. gns3server/symbols/affinity/square/blue/health.svg +5 -0
  920. gns3server/symbols/affinity/square/blue/house.svg +6 -0
  921. gns3server/symbols/affinity/square/blue/hub.svg +7 -0
  922. gns3server/symbols/affinity/square/blue/inspect.svg +5 -0
  923. gns3server/symbols/affinity/square/blue/inspect2.svg +9 -0
  924. gns3server/symbols/affinity/square/blue/interconnect.svg +8 -0
  925. gns3server/symbols/affinity/square/blue/ip_phone.svg +23 -0
  926. gns3server/symbols/affinity/square/blue/isdn.svg +7 -0
  927. gns3server/symbols/affinity/square/blue/laptop.svg +8 -0
  928. gns3server/symbols/affinity/square/blue/ldap.svg +5 -0
  929. gns3server/symbols/affinity/square/blue/light_bulb.svg +11 -0
  930. gns3server/symbols/affinity/square/blue/link.svg +5 -0
  931. gns3server/symbols/affinity/square/blue/loadbalancer.svg +5 -0
  932. gns3server/symbols/affinity/square/blue/nas.svg +7 -0
  933. gns3server/symbols/affinity/square/blue/nat.svg +58 -0
  934. gns3server/symbols/affinity/square/blue/nat2.svg +74 -0
  935. gns3server/symbols/affinity/square/blue/office.svg +5 -0
  936. gns3server/symbols/affinity/square/blue/optical.svg +8 -0
  937. gns3server/symbols/affinity/square/blue/phone_cloud.svg +8 -0
  938. gns3server/symbols/affinity/square/blue/phone_old.svg +7 -0
  939. gns3server/symbols/affinity/square/blue/phone_wireless.svg +10 -0
  940. gns3server/symbols/affinity/square/blue/pinpoint.svg +9 -0
  941. gns3server/symbols/affinity/square/blue/printer.svg +27 -0
  942. gns3server/symbols/affinity/square/blue/rj45.svg +14 -0
  943. gns3server/symbols/affinity/square/blue/router.svg +7 -0
  944. gns3server/symbols/affinity/square/blue/router2.svg +5 -0
  945. gns3server/symbols/affinity/square/blue/router_cloud.svg +7 -0
  946. gns3server/symbols/affinity/square/blue/satellite.svg +8 -0
  947. gns3server/symbols/affinity/square/blue/satellite_dish.svg +7 -0
  948. gns3server/symbols/affinity/square/blue/scull.svg +10 -0
  949. gns3server/symbols/affinity/square/blue/server.svg +5 -0
  950. gns3server/symbols/affinity/square/blue/server_cluster.svg +9 -0
  951. gns3server/symbols/affinity/square/blue/shield.svg +5 -0
  952. gns3server/symbols/affinity/square/blue/statistics.svg +7 -0
  953. gns3server/symbols/affinity/square/blue/storage.svg +7 -0
  954. gns3server/symbols/affinity/square/blue/switch.svg +10 -0
  955. gns3server/symbols/affinity/square/blue/switch_multilayer.svg +8 -0
  956. gns3server/symbols/affinity/square/blue/tablet.svg +9 -0
  957. gns3server/symbols/affinity/square/blue/template.svg +4 -0
  958. gns3server/symbols/affinity/square/blue/tree.svg +5 -0
  959. gns3server/symbols/affinity/square/blue/user.svg +7 -0
  960. gns3server/symbols/affinity/square/blue/virtualbox.svg +7 -0
  961. gns3server/symbols/affinity/square/blue/vm.svg +7 -0
  962. gns3server/symbols/affinity/square/blue/vmware.svg +7 -0
  963. gns3server/symbols/affinity/square/blue/vrf.svg +5 -0
  964. gns3server/symbols/affinity/square/blue/wifi.svg +13 -0
  965. gns3server/symbols/affinity/square/blue/wlc.svg +7 -0
  966. gns3server/symbols/affinity/square/blue/xml.svg +10 -0
  967. gns3server/symbols/affinity/square/gray/atm.svg +5 -0
  968. gns3server/symbols/affinity/square/gray/bug.svg +5 -0
  969. gns3server/symbols/affinity/square/gray/camera.svg +12 -0
  970. gns3server/symbols/affinity/square/gray/camera_dome.svg +8 -0
  971. gns3server/symbols/affinity/square/gray/client.svg +8 -0
  972. gns3server/symbols/affinity/square/gray/client_vm.svg +8 -0
  973. gns3server/symbols/affinity/square/gray/cloud.svg +12 -0
  974. gns3server/symbols/affinity/square/gray/coffee.svg +5 -0
  975. gns3server/symbols/affinity/square/gray/cog.svg +8 -0
  976. gns3server/symbols/affinity/square/gray/communications.svg +8 -0
  977. gns3server/symbols/affinity/square/gray/conversation.svg +12 -0
  978. gns3server/symbols/affinity/square/gray/dna.svg +7 -0
  979. gns3server/symbols/affinity/square/gray/dna2.svg +7 -0
  980. gns3server/symbols/affinity/square/gray/docker.svg +11 -0
  981. gns3server/symbols/affinity/square/gray/dslam.svg +8 -0
  982. gns3server/symbols/affinity/square/gray/factory.svg +5 -0
  983. gns3server/symbols/affinity/square/gray/fingerprint.svg +9 -0
  984. gns3server/symbols/affinity/square/gray/firewall.svg +7 -0
  985. gns3server/symbols/affinity/square/gray/firewall3.svg +6 -0
  986. gns3server/symbols/affinity/square/gray/globe.svg +5 -0
  987. gns3server/symbols/affinity/square/gray/grid.svg +7 -0
  988. gns3server/symbols/affinity/square/gray/grid2.svg +7 -0
  989. gns3server/symbols/affinity/square/gray/health.svg +5 -0
  990. gns3server/symbols/affinity/square/gray/house.svg +6 -0
  991. gns3server/symbols/affinity/square/gray/hub.svg +7 -0
  992. gns3server/symbols/affinity/square/gray/inspect.svg +5 -0
  993. gns3server/symbols/affinity/square/gray/inspect2.svg +9 -0
  994. gns3server/symbols/affinity/square/gray/interconnect.svg +8 -0
  995. gns3server/symbols/affinity/square/gray/ip_phone.svg +23 -0
  996. gns3server/symbols/affinity/square/gray/isdn.svg +7 -0
  997. gns3server/symbols/affinity/square/gray/laptop.svg +8 -0
  998. gns3server/symbols/affinity/square/gray/ldap.svg +5 -0
  999. gns3server/symbols/affinity/square/gray/light_bulb.svg +11 -0
  1000. gns3server/symbols/affinity/square/gray/link.svg +5 -0
  1001. gns3server/symbols/affinity/square/gray/loadbalancer.svg +5 -0
  1002. gns3server/symbols/affinity/square/gray/nas.svg +7 -0
  1003. gns3server/symbols/affinity/square/gray/nat.svg +58 -0
  1004. gns3server/symbols/affinity/square/gray/nat2.svg +74 -0
  1005. gns3server/symbols/affinity/square/gray/office.svg +5 -0
  1006. gns3server/symbols/affinity/square/gray/optical.svg +8 -0
  1007. gns3server/symbols/affinity/square/gray/phone_cloud.svg +8 -0
  1008. gns3server/symbols/affinity/square/gray/phone_old.svg +7 -0
  1009. gns3server/symbols/affinity/square/gray/phone_wireless.svg +10 -0
  1010. gns3server/symbols/affinity/square/gray/pinpoint.svg +9 -0
  1011. gns3server/symbols/affinity/square/gray/printer.svg +27 -0
  1012. gns3server/symbols/affinity/square/gray/rj45.svg +14 -0
  1013. gns3server/symbols/affinity/square/gray/router.svg +7 -0
  1014. gns3server/symbols/affinity/square/gray/router2.svg +5 -0
  1015. gns3server/symbols/affinity/square/gray/router_cloud.svg +7 -0
  1016. gns3server/symbols/affinity/square/gray/satellite.svg +8 -0
  1017. gns3server/symbols/affinity/square/gray/satellite_dish.svg +7 -0
  1018. gns3server/symbols/affinity/square/gray/scull.svg +10 -0
  1019. gns3server/symbols/affinity/square/gray/server.svg +5 -0
  1020. gns3server/symbols/affinity/square/gray/server_cluster.svg +9 -0
  1021. gns3server/symbols/affinity/square/gray/shield.svg +5 -0
  1022. gns3server/symbols/affinity/square/gray/statistics.svg +7 -0
  1023. gns3server/symbols/affinity/square/gray/storage.svg +5 -0
  1024. gns3server/symbols/affinity/square/gray/switch.svg +10 -0
  1025. gns3server/symbols/affinity/square/gray/switch_multilayer.svg +9 -0
  1026. gns3server/symbols/affinity/square/gray/tablet.svg +9 -0
  1027. gns3server/symbols/affinity/square/gray/template.svg +4 -0
  1028. gns3server/symbols/affinity/square/gray/tree.svg +5 -0
  1029. gns3server/symbols/affinity/square/gray/user.svg +7 -0
  1030. gns3server/symbols/affinity/square/gray/virtualbox.svg +7 -0
  1031. gns3server/symbols/affinity/square/gray/vm.svg +7 -0
  1032. gns3server/symbols/affinity/square/gray/vmware.svg +7 -0
  1033. gns3server/symbols/affinity/square/gray/vrf.svg +5 -0
  1034. gns3server/symbols/affinity/square/gray/wifi.svg +13 -0
  1035. gns3server/symbols/affinity/square/gray/wlc.svg +7 -0
  1036. gns3server/symbols/affinity/square/gray/xml.svg +10 -0
  1037. gns3server/symbols/affinity/square/green/atm.svg +5 -0
  1038. gns3server/symbols/affinity/square/green/bug.svg +5 -0
  1039. gns3server/symbols/affinity/square/green/camera.svg +5 -0
  1040. gns3server/symbols/affinity/square/green/camera_dome.svg +8 -0
  1041. gns3server/symbols/affinity/square/green/client.svg +5 -0
  1042. gns3server/symbols/affinity/square/green/client_vm.svg +6 -0
  1043. gns3server/symbols/affinity/square/green/cloud.svg +12 -0
  1044. gns3server/symbols/affinity/square/green/coffee.svg +5 -0
  1045. gns3server/symbols/affinity/square/green/cog.svg +5 -0
  1046. gns3server/symbols/affinity/square/green/communications.svg +5 -0
  1047. gns3server/symbols/affinity/square/green/conversation.svg +12 -0
  1048. gns3server/symbols/affinity/square/green/dna.svg +7 -0
  1049. gns3server/symbols/affinity/square/green/dna2.svg +7 -0
  1050. gns3server/symbols/affinity/square/green/docker.svg +11 -0
  1051. gns3server/symbols/affinity/square/green/dslam.svg +5 -0
  1052. gns3server/symbols/affinity/square/green/factory.svg +5 -0
  1053. gns3server/symbols/affinity/square/green/fingerprint.svg +5 -0
  1054. gns3server/symbols/affinity/square/green/firewall.svg +5 -0
  1055. gns3server/symbols/affinity/square/green/firewall3.svg +5 -0
  1056. gns3server/symbols/affinity/square/green/globe.svg +5 -0
  1057. gns3server/symbols/affinity/square/green/grid.svg +5 -0
  1058. gns3server/symbols/affinity/square/green/grid2.svg +5 -0
  1059. gns3server/symbols/affinity/square/green/health.svg +5 -0
  1060. gns3server/symbols/affinity/square/green/house.svg +6 -0
  1061. gns3server/symbols/affinity/square/green/hub.svg +5 -0
  1062. gns3server/symbols/affinity/square/green/inspect.svg +5 -0
  1063. gns3server/symbols/affinity/square/green/inspect2.svg +5 -0
  1064. gns3server/symbols/affinity/square/green/interconnect.svg +5 -0
  1065. gns3server/symbols/affinity/square/green/ip_phone.svg +7 -0
  1066. gns3server/symbols/affinity/square/green/isdn.svg +5 -0
  1067. gns3server/symbols/affinity/square/green/laptop.svg +8 -0
  1068. gns3server/symbols/affinity/square/green/ldap.svg +5 -0
  1069. gns3server/symbols/affinity/square/green/light_bulb.svg +5 -0
  1070. gns3server/symbols/affinity/square/green/link.svg +5 -0
  1071. gns3server/symbols/affinity/square/green/loadbalancer.svg +5 -0
  1072. gns3server/symbols/affinity/square/green/nas.svg +5 -0
  1073. gns3server/symbols/affinity/square/green/nat.svg +58 -0
  1074. gns3server/symbols/affinity/square/green/nat2.svg +74 -0
  1075. gns3server/symbols/affinity/square/green/office.svg +5 -0
  1076. gns3server/symbols/affinity/square/green/optical.svg +8 -0
  1077. gns3server/symbols/affinity/square/green/phone_cloud.svg +6 -0
  1078. gns3server/symbols/affinity/square/green/phone_old.svg +5 -0
  1079. gns3server/symbols/affinity/square/green/phone_wireless.svg +5 -0
  1080. gns3server/symbols/affinity/square/green/pinpoint.svg +5 -0
  1081. gns3server/symbols/affinity/square/green/printer.svg +27 -0
  1082. gns3server/symbols/affinity/square/green/rj45.svg +5 -0
  1083. gns3server/symbols/affinity/square/green/router.svg +5 -0
  1084. gns3server/symbols/affinity/square/green/router2.svg +5 -0
  1085. gns3server/symbols/affinity/square/green/router_cloud.svg +6 -0
  1086. gns3server/symbols/affinity/square/green/satellite.svg +5 -0
  1087. gns3server/symbols/affinity/square/green/satellite_dish.svg +5 -0
  1088. gns3server/symbols/affinity/square/green/scull.svg +8 -0
  1089. gns3server/symbols/affinity/square/green/server.svg +5 -0
  1090. gns3server/symbols/affinity/square/green/server_cluster.svg +7 -0
  1091. gns3server/symbols/affinity/square/green/shield.svg +5 -0
  1092. gns3server/symbols/affinity/square/green/statistics.svg +5 -0
  1093. gns3server/symbols/affinity/square/green/storage.svg +5 -0
  1094. gns3server/symbols/affinity/square/green/switch.svg +10 -0
  1095. gns3server/symbols/affinity/square/green/switch_multilayer.svg +5 -0
  1096. gns3server/symbols/affinity/square/green/tablet.svg +9 -0
  1097. gns3server/symbols/affinity/square/green/template.svg +4 -0
  1098. gns3server/symbols/affinity/square/green/tree.svg +5 -0
  1099. gns3server/symbols/affinity/square/green/user.svg +7 -0
  1100. gns3server/symbols/affinity/square/green/virtualbox.svg +7 -0
  1101. gns3server/symbols/affinity/square/green/vm.svg +7 -0
  1102. gns3server/symbols/affinity/square/green/vmware.svg +7 -0
  1103. gns3server/symbols/affinity/square/green/vrf.svg +5 -0
  1104. gns3server/symbols/affinity/square/green/wifi.svg +5 -0
  1105. gns3server/symbols/affinity/square/green/wlc.svg +5 -0
  1106. gns3server/symbols/affinity/square/green/xml.svg +5 -0
  1107. gns3server/symbols/affinity/square/red/atm.svg +5 -0
  1108. gns3server/symbols/affinity/square/red/bug.svg +5 -0
  1109. gns3server/symbols/affinity/square/red/camera.svg +12 -0
  1110. gns3server/symbols/affinity/square/red/camera_dome.svg +8 -0
  1111. gns3server/symbols/affinity/square/red/client.svg +8 -0
  1112. gns3server/symbols/affinity/square/red/client_vm.svg +8 -0
  1113. gns3server/symbols/affinity/square/red/cloud.svg +12 -0
  1114. gns3server/symbols/affinity/square/red/coffee.svg +5 -0
  1115. gns3server/symbols/affinity/square/red/cog.svg +8 -0
  1116. gns3server/symbols/affinity/square/red/communications.svg +8 -0
  1117. gns3server/symbols/affinity/square/red/conversation.svg +12 -0
  1118. gns3server/symbols/affinity/square/red/dna.svg +7 -0
  1119. gns3server/symbols/affinity/square/red/dna2.svg +7 -0
  1120. gns3server/symbols/affinity/square/red/docker.svg +11 -0
  1121. gns3server/symbols/affinity/square/red/dslam.svg +8 -0
  1122. gns3server/symbols/affinity/square/red/factory.svg +5 -0
  1123. gns3server/symbols/affinity/square/red/fingerprint.svg +9 -0
  1124. gns3server/symbols/affinity/square/red/firewall.svg +7 -0
  1125. gns3server/symbols/affinity/square/red/firewall3.svg +5 -0
  1126. gns3server/symbols/affinity/square/red/globe.svg +5 -0
  1127. gns3server/symbols/affinity/square/red/grid.svg +7 -0
  1128. gns3server/symbols/affinity/square/red/grid2.svg +7 -0
  1129. gns3server/symbols/affinity/square/red/health.svg +5 -0
  1130. gns3server/symbols/affinity/square/red/house.svg +6 -0
  1131. gns3server/symbols/affinity/square/red/hub.svg +7 -0
  1132. gns3server/symbols/affinity/square/red/inspect.svg +5 -0
  1133. gns3server/symbols/affinity/square/red/inspect2.svg +9 -0
  1134. gns3server/symbols/affinity/square/red/interconnect.svg +8 -0
  1135. gns3server/symbols/affinity/square/red/ip_phone.svg +7 -0
  1136. gns3server/symbols/affinity/square/red/isdn.svg +7 -0
  1137. gns3server/symbols/affinity/square/red/laptop.svg +8 -0
  1138. gns3server/symbols/affinity/square/red/ldap.svg +5 -0
  1139. gns3server/symbols/affinity/square/red/light_bulb.svg +5 -0
  1140. gns3server/symbols/affinity/square/red/link.svg +5 -0
  1141. gns3server/symbols/affinity/square/red/loadbalancer.svg +5 -0
  1142. gns3server/symbols/affinity/square/red/nas.svg +7 -0
  1143. gns3server/symbols/affinity/square/red/nat.svg +58 -0
  1144. gns3server/symbols/affinity/square/red/nat2.svg +74 -0
  1145. gns3server/symbols/affinity/square/red/office.svg +5 -0
  1146. gns3server/symbols/affinity/square/red/optical.svg +8 -0
  1147. gns3server/symbols/affinity/square/red/phone_cloud.svg +6 -0
  1148. gns3server/symbols/affinity/square/red/phone_old.svg +5 -0
  1149. gns3server/symbols/affinity/square/red/phone_wireless.svg +5 -0
  1150. gns3server/symbols/affinity/square/red/pinpoint.svg +9 -0
  1151. gns3server/symbols/affinity/square/red/printer.svg +27 -0
  1152. gns3server/symbols/affinity/square/red/rj45.svg +14 -0
  1153. gns3server/symbols/affinity/square/red/router.svg +7 -0
  1154. gns3server/symbols/affinity/square/red/router2.svg +5 -0
  1155. gns3server/symbols/affinity/square/red/router_cloud.svg +6 -0
  1156. gns3server/symbols/affinity/square/red/satellite.svg +8 -0
  1157. gns3server/symbols/affinity/square/red/satellite_dish.svg +5 -0
  1158. gns3server/symbols/affinity/square/red/scull.svg +10 -0
  1159. gns3server/symbols/affinity/square/red/server.svg +5 -0
  1160. gns3server/symbols/affinity/square/red/server_cluster.svg +9 -0
  1161. gns3server/symbols/affinity/square/red/shield.svg +5 -0
  1162. gns3server/symbols/affinity/square/red/statistics.svg +5 -0
  1163. gns3server/symbols/affinity/square/red/storage.svg +7 -0
  1164. gns3server/symbols/affinity/square/red/switch.svg +10 -0
  1165. gns3server/symbols/affinity/square/red/switch_multilayer.svg +9 -0
  1166. gns3server/symbols/affinity/square/red/tablet.svg +9 -0
  1167. gns3server/symbols/affinity/square/red/template.svg +4 -0
  1168. gns3server/symbols/affinity/square/red/tree.svg +5 -0
  1169. gns3server/symbols/affinity/square/red/user.svg +7 -0
  1170. gns3server/symbols/affinity/square/red/virtualbox.svg +7 -0
  1171. gns3server/symbols/affinity/square/red/vm.svg +7 -0
  1172. gns3server/symbols/affinity/square/red/vmware.svg +7 -0
  1173. gns3server/symbols/affinity/square/red/vrf.svg +5 -0
  1174. gns3server/symbols/affinity/square/red/wifi.svg +13 -0
  1175. gns3server/symbols/affinity/square/red/wlc.svg +7 -0
  1176. gns3server/symbols/affinity/square/red/xml.svg +10 -0
  1177. gns3server/symbols/classic/PBX.svg +304 -0
  1178. gns3server/symbols/classic/PIX_firewall.svg +187 -0
  1179. gns3server/symbols/classic/access_point.svg +516 -0
  1180. gns3server/symbols/classic/access_server.svg +270 -0
  1181. gns3server/symbols/classic/asa.svg +424 -0
  1182. gns3server/symbols/classic/atm_bridge.svg +219 -0
  1183. gns3server/symbols/classic/atm_switch.svg +243 -0
  1184. gns3server/symbols/classic/call_manager.svg +228 -0
  1185. gns3server/symbols/classic/cloud.svg +163 -0
  1186. gns3server/symbols/classic/computer.svg +560 -0
  1187. gns3server/symbols/classic/docker_guest.svg +657 -0
  1188. gns3server/symbols/classic/dslam.svg +155 -0
  1189. gns3server/symbols/classic/edge_label_switch_router.svg +216 -0
  1190. gns3server/symbols/classic/ethernet_switch.svg +228 -0
  1191. gns3server/symbols/classic/firewall.svg +187 -0
  1192. gns3server/symbols/classic/frame_relay_switch.svg +263 -0
  1193. gns3server/symbols/classic/gateway.svg +309 -0
  1194. gns3server/symbols/classic/hub.svg +204 -0
  1195. gns3server/symbols/classic/ids.svg +310 -0
  1196. gns3server/symbols/classic/iosv_l2_virl.svg +286 -0
  1197. gns3server/symbols/classic/iosv_virl.svg +394 -0
  1198. gns3server/symbols/classic/ip_phone.svg +171 -0
  1199. gns3server/symbols/classic/label_switch_router.svg +223 -0
  1200. gns3server/symbols/classic/lightweight_ap.svg +536 -0
  1201. gns3server/symbols/classic/multilayer_switch.svg +317 -0
  1202. gns3server/symbols/classic/nat.svg +207 -0
  1203. gns3server/symbols/classic/optical_router.svg +207 -0
  1204. gns3server/symbols/classic/printer.svg +178 -0
  1205. gns3server/symbols/classic/qemu_guest.svg +1387 -0
  1206. gns3server/symbols/classic/route_switch_processor.svg +395 -0
  1207. gns3server/symbols/classic/router.awp.svg +362 -0
  1208. gns3server/symbols/classic/router.svg +203 -0
  1209. gns3server/symbols/classic/router_firewall.svg +395 -0
  1210. gns3server/symbols/classic/router_netflow.svg +215 -0
  1211. gns3server/symbols/classic/server.svg +226 -0
  1212. gns3server/symbols/classic/sip_server.svg +305 -0
  1213. gns3server/symbols/classic/traceng.svg +610 -0
  1214. gns3server/symbols/classic/vbox_guest.svg +7691 -0
  1215. gns3server/symbols/classic/vmware_guest.svg +572 -0
  1216. gns3server/symbols/classic/voice_access_server.svg +289 -0
  1217. gns3server/symbols/classic/voice_router.svg +223 -0
  1218. gns3server/symbols/classic/vpcs_guest.svg +574 -0
  1219. gns3server/symbols/classic/wlan_controller.svg +528 -0
  1220. gns3server/templates/compute.html +62 -0
  1221. gns3server/templates/controller.html +55 -0
  1222. gns3server/templates/index.html +12 -0
  1223. gns3server/templates/layout.html +20 -0
  1224. gns3server/templates/project.html +91 -0
  1225. gns3server/templates/upload.html +6 -0
  1226. gns3server/utils/__init__.py +123 -0
  1227. gns3server/utils/application_id.py +49 -0
  1228. gns3server/utils/asyncio/__init__.py +148 -0
  1229. gns3server/utils/asyncio/aiozipstream.py +528 -0
  1230. gns3server/utils/asyncio/embed_shell.py +369 -0
  1231. gns3server/utils/asyncio/input_stream.py +408 -0
  1232. gns3server/utils/asyncio/pool.py +69 -0
  1233. gns3server/utils/asyncio/raw_command_server.py +140 -0
  1234. gns3server/utils/asyncio/serial.py +87 -0
  1235. gns3server/utils/asyncio/telnet_server.py +450 -0
  1236. gns3server/utils/cpu_percent.py +46 -0
  1237. gns3server/utils/file_watcher.py +103 -0
  1238. gns3server/utils/get_resource.py +48 -0
  1239. gns3server/utils/hostname.py +112 -0
  1240. gns3server/utils/http_client.py +71 -0
  1241. gns3server/utils/images.py +378 -0
  1242. gns3server/utils/interfaces.py +254 -0
  1243. gns3server/utils/notification_queue.py +79 -0
  1244. gns3server/utils/path.py +76 -0
  1245. gns3server/utils/picture.py +146 -0
  1246. gns3server/utils/qt.py +44 -0
  1247. gns3server/utils/vmnet.py +277 -0
  1248. gns3server/utils/zipfile_zstd/__init__.py +10 -0
  1249. gns3server/utils/zipfile_zstd/_patcher.py +20 -0
  1250. gns3server/utils/zipfile_zstd/_zipfile.py +64 -0
  1251. gns3server/version.py +36 -0
@@ -0,0 +1,2697 @@
1
+ #
2
+ # Copyright (C) 2014 GNS3 Technologies Inc.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ """
18
+ QEMU VM management (creates command line, processes, files etc.) in
19
+ order to run a QEMU VM.
20
+ """
21
+
22
+ import sys
23
+ import os
24
+ import re
25
+ import math
26
+ import shutil
27
+ import struct
28
+ import asyncio
29
+ import socket
30
+ import gns3server
31
+ import subprocess
32
+ import time
33
+ import json
34
+ import shlex
35
+
36
+ from gns3server.utils import parse_version
37
+ from gns3server.utils.asyncio import subprocess_check_output, cancellable_wait_run_in_executor
38
+ from .qemu_error import QemuError
39
+ from .utils.qcow2 import Qcow2, Qcow2Error
40
+ from .utils.ziputils import pack_zip, unpack_zip
41
+ from ..adapters.ethernet_adapter import EthernetAdapter
42
+ from ..error import NodeError, ImageMissingError
43
+ from ..nios.nio_udp import NIOUDP
44
+ from ..nios.nio_tap import NIOTAP
45
+ from ..base_node import BaseNode
46
+ from ...utils.asyncio import monitor_process
47
+ from ...utils.images import md5sum
48
+ from ...utils import macaddress_to_int, int_to_macaddress, is_ipv6_enabled
49
+ from ...utils.hostname import is_rfc1123_hostname_valid
50
+
51
+ from gns3server.schemas.compute.qemu_nodes import Qemu, QemuPlatform
52
+
53
+ import logging
54
+
55
+ log = logging.getLogger(__name__)
56
+
57
+ # forbidden additional options
58
+ FORBIDDEN_OPTIONS = {"-blockdev", "-drive", "-hda", "-hdb", "-hdc", "-hdd",
59
+ "-fsdev", "-virtfs", "-nic", "-netdev"}
60
+ FORBIDDEN_OPTIONS |= {"-" + opt for opt in FORBIDDEN_OPTIONS
61
+ if opt.startswith("-") and not opt.startswith("--")}
62
+
63
+
64
+ class QemuVM(BaseNode):
65
+ module_name = "qemu"
66
+
67
+ """
68
+ QEMU VM implementation.
69
+
70
+ :param name: Qemu VM name
71
+ :param node_id: Node identifier
72
+ :param project: Project instance
73
+ :param manager: Manager instance
74
+ :param console: TCP console port
75
+ :param console_type: Console type
76
+ :param qemu_path: path to the QEMU binary
77
+ :param platform: Platform to emulate
78
+ """
79
+
80
+ def __init__(
81
+ self,
82
+ name,
83
+ node_id,
84
+ project,
85
+ manager,
86
+ linked_clone=True,
87
+ qemu_path=None,
88
+ console=None,
89
+ console_type="telnet",
90
+ aux=None,
91
+ aux_type="none",
92
+ platform=None,
93
+ ):
94
+
95
+ if not is_rfc1123_hostname_valid(name):
96
+ raise QemuError(f"'{name}' is an invalid name to create a Qemu node")
97
+
98
+ super().__init__(
99
+ name,
100
+ node_id,
101
+ project,
102
+ manager,
103
+ console=console,
104
+ console_type=console_type,
105
+ linked_clone=linked_clone,
106
+ aux=aux,
107
+ aux_type=aux_type,
108
+ wrap_console=True,
109
+ wrap_aux=True,
110
+ )
111
+
112
+ self._host = manager.config.settings.Server.host
113
+ self._monitor_host = manager.config.settings.Qemu.monitor_host
114
+ self._process = None
115
+ self._cpulimit_process = None
116
+ self._swtpm_process = None
117
+ self._monitor = None
118
+ self._stdout_file = ""
119
+ self._qemu_img_stdout_file = ""
120
+ self._execute_lock = asyncio.Lock()
121
+ self._local_udp_tunnels = {}
122
+ self._guest_cid = None
123
+ self._command_line_changed = False
124
+ self._qemu_version = None
125
+
126
+ # QEMU VM settings
127
+ if qemu_path:
128
+ try:
129
+ self.qemu_path = qemu_path
130
+ except QemuError:
131
+ # If the binary is not found for topologies 1.4 and later
132
+ # search via the platform otherwise use the binary name
133
+ log.warning(f"Could not find the QEMU binary {qemu_path} on this system, "
134
+ f"trying to find one using platform {platform}")
135
+ if platform:
136
+ self.platform = platform
137
+ else:
138
+ self.qemu_path = os.path.basename(qemu_path)
139
+ else:
140
+ self.platform = platform
141
+
142
+ self._hda_disk_image = ""
143
+ self._hdb_disk_image = ""
144
+ self._hdc_disk_image = ""
145
+ self._hdd_disk_image = ""
146
+ self._hda_disk_interface = "none"
147
+ self._hdb_disk_interface = "none"
148
+ self._hdc_disk_interface = "none"
149
+ self._hdd_disk_interface = "none"
150
+ self._cdrom_image = ""
151
+ self._bios_image = ""
152
+ self._boot_priority = "c"
153
+ self._mac_address = ""
154
+ self._options = ""
155
+ self._ram = 256
156
+ self._cpus = 1
157
+ self._maxcpus = 1
158
+ self._ethernet_adapters = []
159
+ self._adapter_type = "e1000"
160
+ self._initrd = ""
161
+ self._kernel_image = ""
162
+ self._kernel_command_line = ""
163
+ self._replicate_network_connection_state = True
164
+ self._tpm = False
165
+ self._uefi = False
166
+ self._create_config_disk = False
167
+ self._on_close = "power_off"
168
+ self._cpu_throttling = 0 # means no CPU throttling
169
+ self._process_priority = "low"
170
+
171
+ self.mac_address = "" # this will generate a MAC address
172
+ self.adapters = 1 # creates 1 adapter by default
173
+
174
+ # config disk
175
+ self.config_disk_name = self.manager.config_disk
176
+ self.config_disk_image = ""
177
+ if self.config_disk_name:
178
+ if not shutil.which("mcopy"):
179
+ log.warning("Config disk: 'mtools' are not installed.")
180
+ self.config_disk_name = ""
181
+ else:
182
+ try:
183
+ self.config_disk_image = self.manager.get_abs_image_path(self.config_disk_name)
184
+ except (NodeError, ImageMissingError):
185
+ log.warning(f"Config disk: image '{self.config_disk_name}' missing")
186
+ self.config_disk_name = ""
187
+
188
+ log.info(f'QEMU VM "{self._name}" [{self._id}] has been created')
189
+
190
+ @BaseNode.name.setter
191
+ def name(self, new_name):
192
+ """
193
+ Sets the name of this Qemu VM.
194
+
195
+ :param new_name: name
196
+ """
197
+
198
+ if not is_rfc1123_hostname_valid(new_name):
199
+ raise QemuError(f"'{new_name}' is an invalid name to rename Qemu node '{self._name}'")
200
+ super(QemuVM, QemuVM).name.__set__(self, new_name)
201
+
202
+ @property
203
+ def guest_cid(self):
204
+ """
205
+ Returns the CID (console ID) which is an unique identifier between 3 and 65535
206
+
207
+ :returns: integer between 3 and 65535
208
+ """
209
+
210
+ return self._guest_cid
211
+
212
+ @guest_cid.setter
213
+ def guest_cid(self, guest_cid):
214
+ """
215
+ Set the CID (console ID) which is an unique identifier between 3 and 65535
216
+
217
+ :returns: integer between 3 and 65535
218
+ """
219
+
220
+ self._guest_cid = guest_cid
221
+
222
+ @property
223
+ def monitor(self):
224
+ """
225
+ Returns the TCP monitor port.
226
+
227
+ :returns: monitor port (integer)
228
+ """
229
+
230
+ return self._monitor
231
+
232
+ @property
233
+ def qemu_path(self):
234
+ """
235
+ Returns the QEMU binary path for this QEMU VM.
236
+
237
+ :returns: QEMU path
238
+ """
239
+
240
+ return self._qemu_path
241
+
242
+ @qemu_path.setter
243
+ def qemu_path(self, qemu_path):
244
+ """
245
+ Sets the QEMU binary path this QEMU VM.
246
+
247
+ :param qemu_path: QEMU path
248
+ """
249
+
250
+ if qemu_path and os.pathsep not in qemu_path:
251
+ new_qemu_path = shutil.which(qemu_path, path=os.pathsep.join(self._manager.paths_list()))
252
+ if new_qemu_path is None:
253
+ raise QemuError(f"QEMU binary '{qemu_path}' cannot be found on the system")
254
+ qemu_path = new_qemu_path
255
+
256
+ self._check_qemu_path(qemu_path)
257
+ self._qemu_path = qemu_path
258
+ self._platform = os.path.basename(qemu_path)
259
+ if self._platform == "qemu-kvm":
260
+ self._platform = "x86_64"
261
+ else:
262
+ qemu_bin = os.path.basename(qemu_path)
263
+ qemu_bin = re.sub(r"(w)?\.(exe|EXE)$", "", qemu_bin)
264
+ # Old version of GNS3 provide a binary named qemu.exe
265
+ if qemu_bin == "qemu":
266
+ self._platform = "i386"
267
+ else:
268
+ self._platform = re.sub(r'^qemu-system-(\w+).*$', r'\1', qemu_bin, re.IGNORECASE)
269
+
270
+ try:
271
+ QemuPlatform(self._platform.split(".")[0])
272
+ except ValueError:
273
+ raise QemuError(f"Platform {self._platform} is unknown")
274
+ log.info(
275
+ 'QEMU VM "{name}" [{id}] has set the QEMU path to {qemu_path}'.format(
276
+ name=self._name, id=self._id, qemu_path=qemu_path
277
+ )
278
+ )
279
+
280
+ def _check_qemu_path(self, qemu_path):
281
+
282
+ if qemu_path is None:
283
+ raise QemuError("QEMU binary path is not set")
284
+ if not os.path.exists(qemu_path):
285
+ raise QemuError(f"QEMU binary '{qemu_path}' is not accessible")
286
+ if not os.access(qemu_path, os.X_OK):
287
+ raise QemuError(f"QEMU binary '{qemu_path}' is not executable")
288
+
289
+ @property
290
+ def platform(self):
291
+ """
292
+ Return the current platform
293
+ """
294
+ return self._platform
295
+
296
+ @platform.setter
297
+ def platform(self, platform):
298
+
299
+ self._platform = platform
300
+ log.info(f"QEMU VM '{self._name}' [{self._id}] has set the platform {platform}")
301
+ self.qemu_path = f"qemu-system-{platform}"
302
+
303
+ def _disk_setter(self, variable, value):
304
+ """
305
+ Use by disk image setter for checking and apply modifications
306
+
307
+ :param variable: Variable name in the class
308
+ :param value: New disk value
309
+ """
310
+
311
+ value = self.manager.get_abs_image_path(value, self.working_dir)
312
+ if not self.linked_clone:
313
+ for node in self.manager.nodes:
314
+ if node != self and getattr(node, variable) == value:
315
+ raise QemuError(
316
+ f"Sorry a node without the linked base setting enabled can only be used once on your server. {value} is already used by {node.name} in project {node.project.name}"
317
+ )
318
+ setattr(self, "_" + variable, value)
319
+ log.info(
320
+ 'QEMU VM "{name}" [{id}] has set the QEMU {variable} path to {disk_image}'.format(
321
+ name=self._name, variable=variable, id=self._id, disk_image=value
322
+ )
323
+ )
324
+
325
+ @property
326
+ def hda_disk_image(self):
327
+ """
328
+ Returns the hda disk image path for this QEMU VM.
329
+
330
+ :returns: QEMU hda disk image path
331
+ """
332
+
333
+ return self._hda_disk_image
334
+
335
+ @hda_disk_image.setter
336
+ def hda_disk_image(self, hda_disk_image):
337
+ """
338
+ Sets the hda disk image for this QEMU VM.
339
+
340
+ :param hda_disk_image: QEMU hda disk image path
341
+ """
342
+
343
+ self._disk_setter("hda_disk_image", hda_disk_image)
344
+
345
+ @property
346
+ def hdb_disk_image(self):
347
+ """
348
+ Returns the hdb disk image path for this QEMU VM.
349
+
350
+ :returns: QEMU hdb disk image path
351
+ """
352
+
353
+ return self._hdb_disk_image
354
+
355
+ @hdb_disk_image.setter
356
+ def hdb_disk_image(self, hdb_disk_image):
357
+ """
358
+ Sets the hdb disk image for this QEMU VM.
359
+
360
+ :param hdb_disk_image: QEMU hdb disk image path
361
+ """
362
+
363
+ self._disk_setter("hdb_disk_image", hdb_disk_image)
364
+
365
+ @property
366
+ def hdc_disk_image(self):
367
+ """
368
+ Returns the hdc disk image path for this QEMU VM.
369
+
370
+ :returns: QEMU hdc disk image path
371
+ """
372
+
373
+ return self._hdc_disk_image
374
+
375
+ @hdc_disk_image.setter
376
+ def hdc_disk_image(self, hdc_disk_image):
377
+ """
378
+ Sets the hdc disk image for this QEMU VM.
379
+
380
+ :param hdc_disk_image: QEMU hdc disk image path
381
+ """
382
+
383
+ self._disk_setter("hdc_disk_image", hdc_disk_image)
384
+
385
+ @property
386
+ def hdd_disk_image(self):
387
+ """
388
+ Returns the hdd disk image path for this QEMU VM.
389
+
390
+ :returns: QEMU hdd disk image path
391
+ """
392
+
393
+ return self._hdd_disk_image
394
+
395
+ @hdd_disk_image.setter
396
+ def hdd_disk_image(self, hdd_disk_image):
397
+ """
398
+ Sets the hdd disk image for this QEMU VM.
399
+
400
+ :param hdd_disk_image: QEMU hdd disk image path
401
+ """
402
+
403
+ self._disk_setter("hdd_disk_image", hdd_disk_image)
404
+
405
+ @property
406
+ def hda_disk_interface(self):
407
+ """
408
+ Returns the hda disk interface this QEMU VM.
409
+
410
+ :returns: QEMU hda disk interface
411
+ """
412
+
413
+ return self._hda_disk_interface
414
+
415
+ @hda_disk_interface.setter
416
+ def hda_disk_interface(self, hda_disk_interface):
417
+ """
418
+ Sets the hda disk interface for this QEMU VM.
419
+
420
+ :param hda_disk_interface: QEMU hda disk interface
421
+ """
422
+
423
+ self._hda_disk_interface = hda_disk_interface
424
+ log.info(
425
+ 'QEMU VM "{name}" [{id}] has set the QEMU hda disk interface to {interface}'.format(
426
+ name=self._name, id=self._id, interface=self._hda_disk_interface
427
+ )
428
+ )
429
+
430
+ @property
431
+ def hdb_disk_interface(self):
432
+ """
433
+ Returns the hdb disk interface this QEMU VM.
434
+
435
+ :returns: QEMU hdb disk interface
436
+ """
437
+
438
+ return self._hdb_disk_interface
439
+
440
+ @hdb_disk_interface.setter
441
+ def hdb_disk_interface(self, hdb_disk_interface):
442
+ """
443
+ Sets the hda disk interface for this QEMU VM.
444
+
445
+ :param hdb_disk_interface: QEMU hdb disk interface
446
+ """
447
+
448
+ self._hdb_disk_interface = hdb_disk_interface
449
+ log.info(
450
+ 'QEMU VM "{name}" [{id}] has set the QEMU hdb disk interface to {interface}'.format(
451
+ name=self._name, id=self._id, interface=self._hdb_disk_interface
452
+ )
453
+ )
454
+
455
+ @property
456
+ def hdc_disk_interface(self):
457
+ """
458
+ Returns the hdc disk interface this QEMU VM.
459
+
460
+ :returns: QEMU hdc disk interface
461
+ """
462
+
463
+ return self._hdc_disk_interface
464
+
465
+ @hdc_disk_interface.setter
466
+ def hdc_disk_interface(self, hdc_disk_interface):
467
+ """
468
+ Sets the hdc disk interface for this QEMU VM.
469
+
470
+ :param hdc_disk_interface: QEMU hdc disk interface
471
+ """
472
+
473
+ self._hdc_disk_interface = hdc_disk_interface
474
+ log.info(
475
+ 'QEMU VM "{name}" [{id}] has set the QEMU hdc disk interface to {interface}'.format(
476
+ name=self._name, id=self._id, interface=self._hdc_disk_interface
477
+ )
478
+ )
479
+
480
+ @property
481
+ def hdd_disk_interface(self):
482
+ """
483
+ Returns the hda disk interface this QEMU VM.
484
+
485
+ :returns: QEMU hda disk interface
486
+ """
487
+
488
+ return self._hdd_disk_interface
489
+
490
+ @hdd_disk_interface.setter
491
+ def hdd_disk_interface(self, hdd_disk_interface):
492
+ """
493
+ Sets the hdd disk interface for this QEMU VM.
494
+
495
+ :param hdd_disk_interface: QEMU hdd disk interface
496
+ """
497
+
498
+ self._hdd_disk_interface = hdd_disk_interface
499
+ log.info(
500
+ 'QEMU VM "{name}" [{id}] has set the QEMU hdd disk interface to {interface}'.format(
501
+ name=self._name, id=self._id, interface=self._hdd_disk_interface
502
+ )
503
+ )
504
+
505
+ @property
506
+ def cdrom_image(self):
507
+ """
508
+ Returns the cdrom image path for this QEMU VM.
509
+
510
+ :returns: QEMU cdrom image path
511
+ """
512
+
513
+ return self._cdrom_image
514
+
515
+ @cdrom_image.setter
516
+ def cdrom_image(self, cdrom_image):
517
+ """
518
+ Sets the cdrom image for this QEMU VM.
519
+
520
+ :param cdrom_image: QEMU cdrom image path
521
+ """
522
+
523
+ if cdrom_image:
524
+ self._cdrom_image = self.manager.get_abs_image_path(cdrom_image, self.working_dir)
525
+
526
+ log.info(
527
+ 'QEMU VM "{name}" [{id}] has set the QEMU cdrom image path to {cdrom_image}'.format(
528
+ name=self._name, id=self._id, cdrom_image=self._cdrom_image
529
+ )
530
+ )
531
+ else:
532
+ self._cdrom_image = ""
533
+
534
+ async def update_property(self, name, value):
535
+ """
536
+ Update Qemu VM properties.
537
+ """
538
+
539
+ setattr(self, name, value)
540
+ if name == "cdrom_image":
541
+ # let the guest know about the new cdrom image
542
+ await self._update_cdrom_image()
543
+ self._command_line_changed = True
544
+
545
+ async def _update_cdrom_image(self):
546
+ """
547
+ Update the cdrom image path for the Qemu guest OS
548
+ """
549
+
550
+ if self.is_running():
551
+ if self._cdrom_image:
552
+ self._cdrom_option() # this will check the cdrom image is accessible
553
+ await self._control_vm("eject -f ide1-cd0")
554
+ await self._control_vm(f"change ide1-cd0 {self._cdrom_image}")
555
+ log.info(
556
+ 'QEMU VM "{name}" [{id}] has changed the cdrom image path to {cdrom_image}'.format(
557
+ name=self._name, id=self._id, cdrom_image=self._cdrom_image
558
+ )
559
+ )
560
+ else:
561
+ await self._control_vm("eject -f ide1-cd0")
562
+ log.info(f'QEMU VM "{self._name}" [{self._id}] has ejected the cdrom image')
563
+
564
+ @property
565
+ def bios_image(self):
566
+ """
567
+ Returns the bios image path for this QEMU VM.
568
+
569
+ :returns: QEMU bios image path
570
+ """
571
+
572
+ return self._bios_image
573
+
574
+ @bios_image.setter
575
+ def bios_image(self, bios_image):
576
+ """
577
+ Sets the bios image for this QEMU VM.
578
+
579
+ :param bios_image: QEMU bios image path
580
+ """
581
+
582
+ self._bios_image = self.manager.get_abs_image_path(bios_image, self.working_dir)
583
+ log.info(
584
+ 'QEMU VM "{name}" [{id}] has set the QEMU bios image path to {bios_image}'.format(
585
+ name=self._name, id=self._id, bios_image=self._bios_image
586
+ )
587
+ )
588
+
589
+ @property
590
+ def boot_priority(self):
591
+ """
592
+ Returns the boot priority for this QEMU VM.
593
+
594
+ :returns: QEMU boot priority
595
+ """
596
+
597
+ return self._boot_priority
598
+
599
+ @boot_priority.setter
600
+ def boot_priority(self, boot_priority):
601
+ """
602
+ Sets the boot priority for this QEMU VM.
603
+
604
+ :param boot_priority: QEMU boot priority
605
+ """
606
+
607
+ self._boot_priority = boot_priority
608
+ log.info(
609
+ 'QEMU VM "{name}" [{id}] has set the boot priority to {boot_priority}'.format(
610
+ name=self._name, id=self._id, boot_priority=self._boot_priority
611
+ )
612
+ )
613
+
614
+ @property
615
+ def ethernet_adapters(self):
616
+ """
617
+ Return the list of ethernet adapters of the node
618
+ """
619
+ return self._ethernet_adapters
620
+
621
+ @property
622
+ def adapters(self):
623
+ """
624
+ Returns the number of Ethernet adapters for this QEMU VM.
625
+
626
+ :returns: number of adapters
627
+ """
628
+
629
+ return len(self._ethernet_adapters)
630
+
631
+ @adapters.setter
632
+ def adapters(self, adapters):
633
+ """
634
+ Sets the number of Ethernet adapters for this QEMU VM.
635
+
636
+ :param adapters: number of adapters
637
+ """
638
+
639
+ self._ethernet_adapters.clear()
640
+ for adapter_number in range(0, adapters):
641
+ self._ethernet_adapters.append(EthernetAdapter())
642
+
643
+ log.info(
644
+ 'QEMU VM "{name}" [{id}]: number of Ethernet adapters changed to {adapters}'.format(
645
+ name=self._name, id=self._id, adapters=adapters
646
+ )
647
+ )
648
+
649
+ @property
650
+ def adapter_type(self):
651
+ """
652
+ Returns the adapter type for this QEMU VM.
653
+
654
+ :returns: adapter type (string)
655
+ """
656
+
657
+ return self._adapter_type
658
+
659
+ @adapter_type.setter
660
+ def adapter_type(self, adapter_type):
661
+ """
662
+ Sets the adapter type for this QEMU VM.
663
+
664
+ :param adapter_type: adapter type (string)
665
+ """
666
+
667
+ self._adapter_type = adapter_type
668
+
669
+ log.info(
670
+ 'QEMU VM "{name}" [{id}]: adapter type changed to {adapter_type}'.format(
671
+ name=self._name, id=self._id, adapter_type=adapter_type
672
+ )
673
+ )
674
+
675
+ @property
676
+ def mac_address(self):
677
+ """
678
+ Returns the MAC address for this QEMU VM.
679
+
680
+ :returns: adapter type (string)
681
+ """
682
+
683
+ return self._mac_address
684
+
685
+ @mac_address.setter
686
+ def mac_address(self, mac_address):
687
+ """
688
+ Sets the MAC address for this QEMU VM.
689
+
690
+ :param mac_address: MAC address
691
+ """
692
+
693
+ if not mac_address:
694
+ # use the node UUID to generate a random MAC address
695
+ self._mac_address = f"0c:{self.id[2:4]}:{self.id[4:6]}:{self.id[6:8]}:00:00"
696
+ else:
697
+ self._mac_address = mac_address
698
+
699
+ log.info(
700
+ 'QEMU VM "{name}" [{id}]: MAC address changed to {mac_addr}'.format(
701
+ name=self._name, id=self._id, mac_addr=self._mac_address
702
+ )
703
+ )
704
+
705
+ @property
706
+ def replicate_network_connection_state(self):
707
+ """
708
+ Returns whether the network connection state for links is replicated in QEMU.
709
+
710
+ :returns: boolean
711
+ """
712
+
713
+ return self._replicate_network_connection_state
714
+
715
+ @replicate_network_connection_state.setter
716
+ def replicate_network_connection_state(self, replicate_network_connection_state):
717
+ """
718
+ Sets whether the network connection state for links is replicated in QEMU
719
+
720
+ :param replicate_network_connection_state: boolean
721
+ """
722
+
723
+ if replicate_network_connection_state:
724
+ log.info(f'QEMU VM "{self._name}" [{self._id}] has enabled network connection state replication')
725
+ else:
726
+ log.info(f'QEMU VM "{self._name}" [{self._id}] has disabled network connection state replication')
727
+ self._replicate_network_connection_state = replicate_network_connection_state
728
+
729
+ @property
730
+ def create_config_disk(self):
731
+ """
732
+ Returns whether a config disk is automatically created on HDD disk interface (secondary slave)
733
+
734
+ :returns: boolean
735
+ """
736
+
737
+ return self._create_config_disk
738
+
739
+ @create_config_disk.setter
740
+ def create_config_disk(self, create_config_disk):
741
+ """
742
+ Sets whether a config disk is automatically created on HDD disk interface (secondary slave)
743
+
744
+ :param create_config_disk: boolean
745
+ """
746
+
747
+ if create_config_disk:
748
+ log.info(f'QEMU VM "{self._name}" [{self._id}] has enabled the config disk creation feature')
749
+ else:
750
+ log.info(f'QEMU VM "{self._name}" [{self._id}] has disabled the config disk creation feature')
751
+ self._create_config_disk = create_config_disk
752
+
753
+ @property
754
+ def on_close(self):
755
+ """
756
+ Returns the action to execute when the VM is stopped/closed
757
+
758
+ :returns: string
759
+ """
760
+
761
+ return self._on_close
762
+
763
+ @on_close.setter
764
+ def on_close(self, on_close):
765
+ """
766
+ Sets the action to execute when the VM is stopped/closed
767
+
768
+ :param on_close: string
769
+ """
770
+
771
+ log.info(f'QEMU VM "{self._name}" [{self._id}] set the close action to "{on_close}"')
772
+ self._on_close = on_close
773
+
774
+ @property
775
+ def cpu_throttling(self):
776
+ """
777
+ Returns the percentage of CPU allowed.
778
+
779
+ :returns: integer
780
+ """
781
+
782
+ return self._cpu_throttling
783
+
784
+ @cpu_throttling.setter
785
+ def cpu_throttling(self, cpu_throttling):
786
+ """
787
+ Sets the percentage of CPU allowed.
788
+
789
+ :param cpu_throttling: integer
790
+ """
791
+
792
+ log.info(
793
+ 'QEMU VM "{name}" [{id}] has set the percentage of CPU allowed to {cpu}'.format(
794
+ name=self._name, id=self._id, cpu=cpu_throttling
795
+ )
796
+ )
797
+ self._cpu_throttling = cpu_throttling
798
+ self._stop_cpulimit()
799
+ if cpu_throttling:
800
+ self._set_cpu_throttling()
801
+
802
+ @property
803
+ def process_priority(self):
804
+ """
805
+ Returns the process priority.
806
+
807
+ :returns: string
808
+ """
809
+
810
+ return self._process_priority
811
+
812
+ @process_priority.setter
813
+ def process_priority(self, process_priority):
814
+ """
815
+ Sets the process priority.
816
+
817
+ :param process_priority: string
818
+ """
819
+
820
+ log.info(
821
+ 'QEMU VM "{name}" [{id}] has set the process priority to {priority}'.format(
822
+ name=self._name, id=self._id, priority=process_priority
823
+ )
824
+ )
825
+ self._process_priority = process_priority
826
+
827
+ @property
828
+ def ram(self):
829
+ """
830
+ Returns the RAM amount for this QEMU VM.
831
+
832
+ :returns: RAM amount in MB
833
+ """
834
+
835
+ return self._ram
836
+
837
+ @ram.setter
838
+ def ram(self, ram):
839
+ """
840
+ Sets the amount of RAM for this QEMU VM.
841
+
842
+ :param ram: RAM amount in MB
843
+ """
844
+
845
+ log.info(f'QEMU VM "{self._name}" [{self._id}] has set the RAM to {ram}')
846
+ self._ram = ram
847
+
848
+ @property
849
+ def cpus(self):
850
+ """
851
+ Returns the number of vCPUs this QEMU VM.
852
+
853
+ :returns: number of vCPUs.
854
+ """
855
+
856
+ return self._cpus
857
+
858
+ @cpus.setter
859
+ def cpus(self, cpus):
860
+ """
861
+ Sets the number of vCPUs this QEMU VM.
862
+
863
+ :param cpus: number of vCPUs.
864
+ """
865
+
866
+ log.info(f'QEMU VM "{self._name}" [{self._id}] has set the number of vCPUs to {cpus}')
867
+ self._cpus = cpus
868
+
869
+ @property
870
+ def maxcpus(self):
871
+ """
872
+ Returns the maximum number of hotpluggable vCPUs for this QEMU VM.
873
+
874
+ :returns: maximum number of hotpluggable vCPUs.
875
+ """
876
+
877
+ return self._maxcpus
878
+
879
+ @maxcpus.setter
880
+ def maxcpus(self, maxcpus):
881
+ """
882
+ Sets the maximum number of hotpluggable vCPUs for this QEMU VM.
883
+
884
+ :param maxcpus: maximum number of hotpluggable vCPUs
885
+ """
886
+
887
+ log.info(f'QEMU VM "{self._name}" [{self._id}] has set maximum number of hotpluggable vCPUs to {maxcpus}')
888
+ self._maxcpus = maxcpus
889
+
890
+ @property
891
+ def tpm(self):
892
+ """
893
+ Returns whether TPM is activated for this QEMU VM.
894
+
895
+ :returns: boolean
896
+ """
897
+
898
+ return self._tpm
899
+
900
+ @tpm.setter
901
+ def tpm(self, tpm):
902
+ """
903
+ Sets whether TPM is activated for this QEMU VM.
904
+
905
+ :param tpm: boolean
906
+ """
907
+
908
+ if tpm:
909
+ log.info(f'QEMU VM "{self._name}" [{self._id}] has enabled the Trusted Platform Module (TPM)')
910
+ else:
911
+ log.info(f'QEMU VM "{self._name}" [{self._id}] has disabled the Trusted Platform Module (TPM)')
912
+ self._tpm = tpm
913
+
914
+ @property
915
+ def uefi(self):
916
+ """
917
+ Returns whether UEFI boot mode is activated for this QEMU VM.
918
+
919
+ :returns: boolean
920
+ """
921
+
922
+ return self._uefi
923
+
924
+ @uefi.setter
925
+ def uefi(self, uefi):
926
+ """
927
+ Sets whether UEFI boot mode is activated for this QEMU VM.
928
+
929
+ :param uefi: boolean
930
+ """
931
+
932
+ if uefi:
933
+ log.info(f'QEMU VM "{self._name}" [{self._id}] has enabled the UEFI boot mode')
934
+ else:
935
+ log.info(f'QEMU VM "{self._name}" [{self._id}] has disabled the UEFI boot mode')
936
+ self._uefi = uefi
937
+
938
+ @property
939
+ def options(self):
940
+ """
941
+ Returns the options for this QEMU VM.
942
+
943
+ :returns: QEMU options
944
+ """
945
+
946
+ return self._options
947
+
948
+ @options.setter
949
+ def options(self, options):
950
+ """
951
+ Sets the options for this QEMU VM.
952
+
953
+ :param options: QEMU options
954
+ """
955
+
956
+ log.info(
957
+ 'QEMU VM "{name}" [{id}] has set the QEMU options to {options}'.format(
958
+ name=self._name, id=self._id, options=options
959
+ )
960
+ )
961
+
962
+ # "-no-kvm" and "-no-hax' are deprecated since Qemu v5.2
963
+ if "-no-kvm" in options:
964
+ options = options.replace("-no-kvm", "-machine accel=tcg")
965
+ if "-no-hax" in options:
966
+ options = options.replace("-no-hax", "-machine accel=tcg")
967
+
968
+ if "-enable-kvm" in options:
969
+ if not sys.platform.startswith("linux"):
970
+ # KVM can only be enabled on Linux
971
+ options = options.replace("-enable-kvm", "")
972
+ else:
973
+ options = options.replace("-enable-kvm", "-machine accel=kvm")
974
+
975
+ if "-enable-hax" in options:
976
+ if not sys.platform.startswith("darwin"):
977
+ # HAXM is only available on macOS
978
+ options = options.replace("-enable-hax", "")
979
+ else:
980
+ options = options.replace("-enable-hax", "-machine accel=hax")
981
+
982
+ self._options = options.strip()
983
+
984
+ @property
985
+ def initrd(self):
986
+ """
987
+ Returns the initrd path for this QEMU VM.
988
+
989
+ :returns: QEMU initrd path
990
+ """
991
+
992
+ return self._initrd
993
+
994
+ @initrd.setter
995
+ def initrd(self, initrd):
996
+ """
997
+ Sets the initrd path for this QEMU VM.
998
+
999
+ :param initrd: QEMU initrd path
1000
+ """
1001
+
1002
+ initrd = self.manager.get_abs_image_path(initrd, self.working_dir)
1003
+
1004
+ log.info(
1005
+ 'QEMU VM "{name}" [{id}] has set the QEMU initrd path to {initrd}'.format(
1006
+ name=self._name, id=self._id, initrd=initrd
1007
+ )
1008
+ )
1009
+ if "asa" in initrd and self._initrd != initrd:
1010
+ self.project.emit(
1011
+ "log.warning",
1012
+ {
1013
+ "message": "Warning ASA 8 is not supported by GNS3 and Cisco, please use ASAv instead. Depending of your hardware and OS this could not work or you could be limited to one instance. If ASA 8 is not booting their is no GNS3 solution, you must to upgrade to ASAv."
1014
+ },
1015
+ )
1016
+ self._initrd = initrd
1017
+
1018
+ @property
1019
+ def kernel_image(self):
1020
+ """
1021
+ Returns the kernel image path for this QEMU VM.
1022
+
1023
+ :returns: QEMU kernel image path
1024
+ """
1025
+
1026
+ return self._kernel_image
1027
+
1028
+ @kernel_image.setter
1029
+ def kernel_image(self, kernel_image):
1030
+ """
1031
+ Sets the kernel image path for this QEMU VM.
1032
+
1033
+ :param kernel_image: QEMU kernel image path
1034
+ """
1035
+
1036
+ kernel_image = self.manager.get_abs_image_path(kernel_image, self.working_dir)
1037
+ log.info(
1038
+ 'QEMU VM "{name}" [{id}] has set the QEMU kernel image path to {kernel_image}'.format(
1039
+ name=self._name, id=self._id, kernel_image=kernel_image
1040
+ )
1041
+ )
1042
+ self._kernel_image = kernel_image
1043
+
1044
+ @property
1045
+ def kernel_command_line(self):
1046
+ """
1047
+ Returns the kernel command line for this QEMU VM.
1048
+
1049
+ :returns: QEMU kernel command line
1050
+ """
1051
+
1052
+ return self._kernel_command_line
1053
+
1054
+ @kernel_command_line.setter
1055
+ def kernel_command_line(self, kernel_command_line):
1056
+ """
1057
+ Sets the kernel command line for this QEMU VM.
1058
+
1059
+ :param kernel_command_line: QEMU kernel command line
1060
+ """
1061
+
1062
+ log.info(
1063
+ 'QEMU VM "{name}" [{id}] has set the QEMU kernel command line to {kernel_command_line}'.format(
1064
+ name=self._name, id=self._id, kernel_command_line=kernel_command_line
1065
+ )
1066
+ )
1067
+ self._kernel_command_line = kernel_command_line
1068
+
1069
+ async def _set_process_priority(self):
1070
+ """
1071
+ Changes the process priority
1072
+ """
1073
+
1074
+ if self._process_priority == "normal":
1075
+ return
1076
+
1077
+ if self._process_priority == "realtime":
1078
+ priority = -20
1079
+ elif self._process_priority == "very high":
1080
+ priority = -15
1081
+ elif self._process_priority == "high":
1082
+ priority = -5
1083
+ elif self._process_priority == "low":
1084
+ priority = 5
1085
+ elif self._process_priority == "very low":
1086
+ priority = 19
1087
+ else:
1088
+ priority = 0
1089
+ try:
1090
+ process = await asyncio.create_subprocess_exec(
1091
+ "renice", "-n", str(priority), "-p", str(self._process.pid)
1092
+ )
1093
+ await process.wait()
1094
+ except (OSError, subprocess.SubprocessError) as e:
1095
+ log.error(f'Could not change process priority for QEMU VM "{self._name}": {e}')
1096
+
1097
+ def _stop_cpulimit(self):
1098
+ """
1099
+ Stops the cpulimit process.
1100
+ """
1101
+
1102
+ if self._cpulimit_process and self._cpulimit_process.returncode is None:
1103
+ self._cpulimit_process.terminate()
1104
+ self._cpulimit_process = None
1105
+
1106
+ def _set_cpu_throttling(self):
1107
+ """
1108
+ Limits the CPU usage for current QEMU process.
1109
+ """
1110
+
1111
+ if not self.is_running():
1112
+ return
1113
+
1114
+ try:
1115
+ if sys.platform.startswith("win") and hasattr(sys, "frozen"):
1116
+ cpulimit_exec = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "cpulimit", "cpulimit.exe")
1117
+ else:
1118
+ cpulimit_exec = "cpulimit"
1119
+
1120
+ command = [cpulimit_exec, "--lazy", "--pid={}".format(self._process.pid), "--limit={}".format(self._cpu_throttling)]
1121
+ self._cpulimit_process = subprocess.Popen(command, cwd=self.working_dir)
1122
+ log.info(f"CPU throttled to {self._cpu_throttling}%")
1123
+ except FileNotFoundError:
1124
+ raise QemuError("cpulimit could not be found, please install it or deactivate CPU throttling")
1125
+ except (OSError, subprocess.SubprocessError) as e:
1126
+ raise QemuError(f"Could not throttle CPU: {e}")
1127
+
1128
+ async def create(self):
1129
+ """
1130
+ Creates QEMU VM and sets proper MD5 hashes
1131
+ """
1132
+
1133
+ # In case user upload image manually we don't have md5 sums.
1134
+ # We need generate hashes at this point, otherwise they will be generated
1135
+ # at asdict but not on separate thread.
1136
+ await cancellable_wait_run_in_executor(md5sum, self._hda_disk_image, self.working_dir)
1137
+ await cancellable_wait_run_in_executor(md5sum, self._hdb_disk_image, self.working_dir)
1138
+ await cancellable_wait_run_in_executor(md5sum, self._hdc_disk_image, self.working_dir)
1139
+ await cancellable_wait_run_in_executor(md5sum, self._hdd_disk_image, self.working_dir)
1140
+
1141
+ super().create()
1142
+
1143
+ async def start(self):
1144
+ """
1145
+ Starts this QEMU VM.
1146
+ """
1147
+
1148
+ async with self._execute_lock:
1149
+ if self.is_running():
1150
+ # resume the VM if it is paused
1151
+ await self.resume()
1152
+ return
1153
+
1154
+ if self._manager.config.settings.Qemu.enable_monitor:
1155
+ try:
1156
+ info = socket.getaddrinfo(
1157
+ self._monitor_host, 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE
1158
+ )
1159
+ if not info:
1160
+ raise QemuError(f"getaddrinfo returns an empty list on {self._monitor_host}")
1161
+ for res in info:
1162
+ af, socktype, proto, _, sa = res
1163
+ # let the OS find an unused port for the Qemu monitor
1164
+ with socket.socket(af, socktype, proto) as sock:
1165
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1166
+ sock.bind(sa)
1167
+ self._monitor = sock.getsockname()[1]
1168
+ except OSError as e:
1169
+ raise QemuError(f"Could not find free port for the Qemu monitor: {e}")
1170
+
1171
+ # check if there is enough RAM to run
1172
+ self.check_available_ram(self.ram)
1173
+
1174
+ # start swtpm (TPM emulator) first if TPM is enabled
1175
+ if self._tpm:
1176
+ await self._start_swtpm()
1177
+
1178
+ command = await self._build_command()
1179
+ command_string = " ".join(shlex.quote(s) for s in command)
1180
+ try:
1181
+ log.info(f"Starting QEMU with: {command_string}")
1182
+ self._stdout_file = os.path.join(self.working_dir, "qemu.log")
1183
+ log.info(f"logging to {self._stdout_file}")
1184
+ with open(self._stdout_file, "w", encoding="utf-8") as fd:
1185
+ fd.write(f"Start QEMU with {command_string}\n\nExecution log:\n")
1186
+ self.command_line = " ".join(command)
1187
+ self._process = await asyncio.create_subprocess_exec(
1188
+ *command, stdout=fd, stderr=subprocess.STDOUT, cwd=self.working_dir
1189
+ )
1190
+ log.info(f'QEMU VM "{self._name}" started PID={self._process.pid}')
1191
+ self._command_line_changed = False
1192
+ self.status = "started"
1193
+ monitor_process(self._process, self._termination_callback)
1194
+ except (OSError, subprocess.SubprocessError, UnicodeEncodeError) as e:
1195
+ stdout = self.read_stdout()
1196
+ log.error(f"Could not start QEMU {self.qemu_path}: {e}\n{stdout}")
1197
+ raise QemuError(f"Could not start QEMU {self.qemu_path}: {e}\n{stdout}")
1198
+
1199
+ await self._set_process_priority()
1200
+ if self._cpu_throttling:
1201
+ self._set_cpu_throttling()
1202
+ if "-enable-kvm" in command_string or "-enable-hax" in command_string:
1203
+ self._hw_virtualization = True
1204
+
1205
+ await self._start_ubridge()
1206
+ set_link_commands = []
1207
+ for adapter_number, adapter in enumerate(self._ethernet_adapters):
1208
+ nio = adapter.get_nio(0)
1209
+ if nio:
1210
+ await self.add_ubridge_udp_connection(
1211
+ f"QEMU-{self._id}-{adapter_number}", self._local_udp_tunnels[adapter_number][1], nio
1212
+ )
1213
+ if nio.suspend and self._replicate_network_connection_state:
1214
+ set_link_commands.append(f"set_link gns3-{adapter_number} off")
1215
+ elif self._replicate_network_connection_state:
1216
+ set_link_commands.append(f"set_link gns3-{adapter_number} off")
1217
+
1218
+ if "-loadvm" not in command_string and self._replicate_network_connection_state:
1219
+ # only set the link statuses if not restoring a previous VM state
1220
+ await self._control_vm_commands(set_link_commands)
1221
+
1222
+ try:
1223
+ if self.is_running():
1224
+ await self.start_wrap_console()
1225
+ except OSError as e:
1226
+ raise QemuError(f"Could not start Telnet QEMU console {e}\n")
1227
+
1228
+ async def _termination_callback(self, returncode):
1229
+ """
1230
+ Called when the process has stopped.
1231
+
1232
+ :param returncode: Process returncode
1233
+ """
1234
+
1235
+ if self.started:
1236
+ log.info("QEMU process has stopped, return code: %d", returncode)
1237
+ await self.stop()
1238
+ if returncode != 0:
1239
+ self.project.emit(
1240
+ "log.error",
1241
+ {"message": f"QEMU process has stopped, return code: {returncode}\n{self.read_stdout()}"},
1242
+ )
1243
+
1244
+ async def stop(self):
1245
+ """
1246
+ Stops this QEMU VM.
1247
+ """
1248
+
1249
+ await self._stop_ubridge()
1250
+ async with self._execute_lock:
1251
+ # stop the QEMU process
1252
+ self._hw_virtualization = False
1253
+ if self.is_running():
1254
+ log.info(f'Stopping QEMU VM "{self._name}" PID={self._process.pid}')
1255
+ try:
1256
+
1257
+ if self.on_close == "save_vm_state":
1258
+ await self._control_vm("stop")
1259
+ await self._control_vm("savevm GNS3_SAVED_STATE")
1260
+ wait_for_savevm = 120
1261
+ while wait_for_savevm:
1262
+ await asyncio.sleep(1)
1263
+ status = await self._saved_state_option()
1264
+ wait_for_savevm -= 1
1265
+ if status != []:
1266
+ break
1267
+
1268
+ if self.on_close == "shutdown_signal":
1269
+ await self._control_vm("system_powerdown")
1270
+ await gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=120)
1271
+ else:
1272
+ self._process.terminate()
1273
+ await gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=3)
1274
+ except ProcessLookupError:
1275
+ pass
1276
+ except asyncio.TimeoutError:
1277
+ if self._process:
1278
+ try:
1279
+ self._process.kill()
1280
+ except ProcessLookupError:
1281
+ pass
1282
+ if self._process.returncode is None:
1283
+ log.warning(f'QEMU VM "{self._name}" PID={self._process.pid} is still running')
1284
+ self._process = None
1285
+ self._stop_cpulimit()
1286
+ self._stop_swtpm()
1287
+ if self.on_close != "save_vm_state":
1288
+ await self._clear_save_vm_stated()
1289
+ await self._export_config()
1290
+ await super().stop()
1291
+
1292
+ async def _open_qemu_monitor_connection_vm(self, timeout=10):
1293
+ """
1294
+ Opens a connection to the QEMU monitor.
1295
+
1296
+ :param timeout: timeout to connect to the monitor TCP server
1297
+ :returns: The reader returned is a StreamReader instance; the writer is a StreamWriter instance
1298
+ """
1299
+
1300
+ begin = time.time()
1301
+ connection_success = False
1302
+ last_exception = None
1303
+ reader = writer = None
1304
+ while time.time() - begin < timeout:
1305
+ await asyncio.sleep(0.01)
1306
+ try:
1307
+ log.debug(f"Connecting to Qemu monitor on {self._monitor_host}:{self._monitor}")
1308
+ reader, writer = await asyncio.open_connection(self._monitor_host, self._monitor)
1309
+ except (asyncio.TimeoutError, OSError) as e:
1310
+ last_exception = e
1311
+ continue
1312
+ connection_success = True
1313
+ break
1314
+
1315
+ if not connection_success:
1316
+ log.warning(
1317
+ "Could not connect to QEMU monitor on {}:{}: {}".format(
1318
+ self._monitor_host, self._monitor, last_exception
1319
+ )
1320
+ )
1321
+ else:
1322
+ log.info(
1323
+ f"Connected to QEMU monitor on {self._monitor_host}:{self._monitor} after {time.time() - begin:.4f} seconds"
1324
+ )
1325
+ return reader, writer
1326
+
1327
+ async def _control_vm(self, command, expected=None):
1328
+ """
1329
+ Executes a command with QEMU monitor when this VM is running.
1330
+
1331
+ :param command: QEMU monitor command (e.g. info status, stop etc.)
1332
+ :param expected: An array of expected strings
1333
+
1334
+ :returns: result of the command (matched object or None)
1335
+ """
1336
+
1337
+ result = None
1338
+ if self.is_running() and self._monitor:
1339
+ log.info(f"Execute QEMU monitor command: {command}")
1340
+ reader, writer = await self._open_qemu_monitor_connection_vm()
1341
+ if reader is None and writer is None:
1342
+ return result
1343
+
1344
+ try:
1345
+ cmd_byte = command.encode("ascii")
1346
+ writer.write(cmd_byte + b"\n")
1347
+ if not expected:
1348
+ while True:
1349
+ line = await asyncio.wait_for(reader.readline(), timeout=3) # echo of the command
1350
+ if not line or cmd_byte in line:
1351
+ break
1352
+ except asyncio.TimeoutError:
1353
+ log.warning(f"Missing echo of command '{command}'")
1354
+ except OSError as e:
1355
+ log.warning(f"Could not write to QEMU monitor: {e}")
1356
+ writer.close()
1357
+ return result
1358
+ if expected:
1359
+ try:
1360
+ while result is None:
1361
+ line = await asyncio.wait_for(reader.readline(), timeout=3)
1362
+ if not line:
1363
+ break
1364
+ for expect in expected:
1365
+ if expect in line:
1366
+ result = line.decode("utf-8").strip()
1367
+ break
1368
+ except asyncio.TimeoutError:
1369
+ log.warning(f"Timeout while waiting for result of command '{command}'")
1370
+ except (ConnectionError, EOFError) as e:
1371
+ log.warning(f"Could not read from QEMU monitor: {e}")
1372
+ writer.close()
1373
+ return result
1374
+
1375
+ async def _control_vm_commands(self, commands):
1376
+ """
1377
+ Executes commands with QEMU monitor when this VM is running.
1378
+
1379
+ :param commands: a list of QEMU monitor commands (e.g. info status, stop etc.)
1380
+ """
1381
+
1382
+ if self.is_running() and self._monitor:
1383
+
1384
+ reader, writer = await self._open_qemu_monitor_connection_vm()
1385
+ if reader is None and writer is None:
1386
+ return
1387
+
1388
+ for command in commands:
1389
+ log.info(f"Execute QEMU monitor command: {command}")
1390
+ try:
1391
+ cmd_byte = command.encode("ascii")
1392
+ writer.write(cmd_byte + b"\n")
1393
+ while True:
1394
+ line = await asyncio.wait_for(reader.readline(), timeout=3) # echo of the command
1395
+ if not line or cmd_byte in line:
1396
+ break
1397
+ except asyncio.TimeoutError:
1398
+ log.warning(f"Missing echo of command '{command}'")
1399
+ except OSError as e:
1400
+ log.warning(f"Could not write to QEMU monitor: {e}")
1401
+ writer.close()
1402
+
1403
+ async def close(self):
1404
+ """
1405
+ Closes this QEMU VM.
1406
+ """
1407
+
1408
+ if not (await super().close()):
1409
+ return False
1410
+
1411
+ # FIXME: Don't wait for ACPI shutdown when closing the project, should we?
1412
+ if self.on_close == "shutdown_signal":
1413
+ self.on_close = "power_off"
1414
+ await self.stop()
1415
+
1416
+ for adapter in self._ethernet_adapters:
1417
+ if adapter is not None:
1418
+ for nio in adapter.ports.values():
1419
+ if nio and isinstance(nio, NIOUDP):
1420
+ self.manager.port_manager.release_udp_port(nio.lport, self._project)
1421
+
1422
+ for udp_tunnel in self._local_udp_tunnels.values():
1423
+ self.manager.port_manager.release_udp_port(udp_tunnel[0].lport, self._project)
1424
+ self.manager.port_manager.release_udp_port(udp_tunnel[1].lport, self._project)
1425
+ self._local_udp_tunnels = {}
1426
+
1427
+ async def _get_vm_status(self):
1428
+ """
1429
+ Returns this VM suspend status.
1430
+
1431
+ Status are extracted from:
1432
+ https://github.com/qemu/qemu/blob/master/qapi-schema.json#L152
1433
+
1434
+ :returns: status (string)
1435
+ """
1436
+
1437
+ result = await self._control_vm(
1438
+ "info status",
1439
+ [
1440
+ b"debug",
1441
+ b"inmigrate",
1442
+ b"internal-error",
1443
+ b"io-error",
1444
+ b"paused",
1445
+ b"postmigrate",
1446
+ b"prelaunch",
1447
+ b"finish-migrate",
1448
+ b"restore-vm",
1449
+ b"running",
1450
+ b"save-vm",
1451
+ b"shutdown",
1452
+ b"suspended",
1453
+ b"watchdog",
1454
+ b"guest-panicked",
1455
+ ],
1456
+ )
1457
+ if result is None:
1458
+ return result
1459
+ status = result.rsplit(" ", 1)[1]
1460
+ if status == "running" or status == "prelaunch":
1461
+ self.status = "started"
1462
+ elif status == "suspended":
1463
+ self.status = "suspended"
1464
+ elif status == "shutdown":
1465
+ self.status = "stopped"
1466
+ return status
1467
+
1468
+ async def suspend(self):
1469
+ """
1470
+ Suspends this QEMU VM.
1471
+ """
1472
+
1473
+ if self.is_running():
1474
+ vm_status = await self._get_vm_status()
1475
+ if vm_status is None:
1476
+ raise QemuError("Suspending a QEMU VM is not supported")
1477
+ elif vm_status == "running" or vm_status == "prelaunch":
1478
+ await self._control_vm("stop")
1479
+ self.status = "suspended"
1480
+ log.debug("QEMU VM has been suspended")
1481
+ else:
1482
+ log.info(f"QEMU VM is not running to be suspended, current status is {vm_status}")
1483
+
1484
+ async def reload(self):
1485
+ """
1486
+ Reloads this QEMU VM.
1487
+ """
1488
+
1489
+ if self._command_line_changed:
1490
+ await self.stop()
1491
+ await self.start()
1492
+ else:
1493
+ await self._control_vm("system_reset")
1494
+ log.debug("QEMU VM has been reset")
1495
+
1496
+ async def resume(self):
1497
+ """
1498
+ Resumes this QEMU VM.
1499
+ """
1500
+
1501
+ vm_status = await self._get_vm_status()
1502
+ if vm_status is None:
1503
+ raise QemuError("Resuming a QEMU VM is not supported")
1504
+ elif vm_status == "paused":
1505
+ await self._control_vm("cont")
1506
+ self.status = "started"
1507
+ log.debug("QEMU VM has been resumed")
1508
+ else:
1509
+ log.info(f"QEMU VM is not paused to be resumed, current status is {vm_status}")
1510
+
1511
+ async def adapter_add_nio_binding(self, adapter_number, nio):
1512
+ """
1513
+ Adds an adapter NIO binding.
1514
+
1515
+ :param adapter_number: adapter number
1516
+ :param nio: NIO instance to add to the adapter
1517
+ """
1518
+
1519
+ try:
1520
+ adapter = self._ethernet_adapters[adapter_number]
1521
+ except IndexError:
1522
+ raise QemuError(
1523
+ 'Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(
1524
+ name=self._name, adapter_number=adapter_number
1525
+ )
1526
+ )
1527
+
1528
+ if self.is_running():
1529
+ try:
1530
+ await self.add_ubridge_udp_connection(
1531
+ f"QEMU-{self._id}-{adapter_number}", self._local_udp_tunnels[adapter_number][1], nio
1532
+ )
1533
+ if self._replicate_network_connection_state:
1534
+ await self._control_vm(f"set_link gns3-{adapter_number} on")
1535
+ except (IndexError, KeyError):
1536
+ raise QemuError(
1537
+ 'Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(
1538
+ name=self._name, adapter_number=adapter_number
1539
+ )
1540
+ )
1541
+
1542
+ adapter.add_nio(0, nio)
1543
+ log.info(
1544
+ 'QEMU VM "{name}" [{id}]: {nio} added to adapter {adapter_number}'.format(
1545
+ name=self._name, id=self._id, nio=nio, adapter_number=adapter_number
1546
+ )
1547
+ )
1548
+
1549
+ async def adapter_update_nio_binding(self, adapter_number, nio):
1550
+ """
1551
+ Update an adapter NIO binding.
1552
+
1553
+ :param adapter_number: adapter number
1554
+ :param nio: NIO instance to update the adapter
1555
+ """
1556
+
1557
+ if self.is_running():
1558
+ try:
1559
+ await self.update_ubridge_udp_connection(
1560
+ f"QEMU-{self._id}-{adapter_number}", self._local_udp_tunnels[adapter_number][1], nio
1561
+ )
1562
+ if self._replicate_network_connection_state:
1563
+ if nio.suspend:
1564
+ await self._control_vm(f"set_link gns3-{adapter_number} off")
1565
+ else:
1566
+ await self._control_vm(f"set_link gns3-{adapter_number} on")
1567
+ except IndexError:
1568
+ raise QemuError(
1569
+ 'Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(
1570
+ name=self._name, adapter_number=adapter_number
1571
+ )
1572
+ )
1573
+
1574
+ async def adapter_remove_nio_binding(self, adapter_number):
1575
+ """
1576
+ Removes an adapter NIO binding.
1577
+
1578
+ :param adapter_number: adapter number
1579
+
1580
+ :returns: NIO instance
1581
+ """
1582
+
1583
+ try:
1584
+ adapter = self._ethernet_adapters[adapter_number]
1585
+ except IndexError:
1586
+ raise QemuError(
1587
+ 'Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(
1588
+ name=self._name, adapter_number=adapter_number
1589
+ )
1590
+ )
1591
+
1592
+ await self.stop_capture(adapter_number)
1593
+ if self.is_running():
1594
+ if self._replicate_network_connection_state:
1595
+ await self._control_vm(f"set_link gns3-{adapter_number} off")
1596
+ await self._ubridge_send("bridge delete {name}".format(name=f"QEMU-{self._id}-{adapter_number}"))
1597
+
1598
+ nio = adapter.get_nio(0)
1599
+ if isinstance(nio, NIOUDP):
1600
+ self.manager.port_manager.release_udp_port(nio.lport, self._project)
1601
+ adapter.remove_nio(0)
1602
+
1603
+ log.info(
1604
+ 'QEMU VM "{name}" [{id}]: {nio} removed from adapter {adapter_number}'.format(
1605
+ name=self._name, id=self._id, nio=nio, adapter_number=adapter_number
1606
+ )
1607
+ )
1608
+ return nio
1609
+
1610
+ def get_nio(self, adapter_number):
1611
+ """
1612
+ Gets an adapter NIO binding.
1613
+
1614
+ :param adapter_number: adapter number
1615
+
1616
+ :returns: NIO instance
1617
+ """
1618
+
1619
+ try:
1620
+ adapter = self._ethernet_adapters[adapter_number]
1621
+ except IndexError:
1622
+ raise QemuError(
1623
+ 'Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(
1624
+ name=self._name, adapter_number=adapter_number
1625
+ )
1626
+ )
1627
+
1628
+ nio = adapter.get_nio(0)
1629
+
1630
+ if not nio:
1631
+ raise QemuError(f"Adapter {adapter_number} is not connected")
1632
+
1633
+ return nio
1634
+
1635
+ async def start_capture(self, adapter_number, output_file):
1636
+ """
1637
+ Starts a packet capture.
1638
+
1639
+ :param adapter_number: adapter number
1640
+ :param output_file: PCAP destination file for the capture
1641
+ """
1642
+
1643
+ nio = self.get_nio(adapter_number)
1644
+ if nio.capturing:
1645
+ raise QemuError(f"Packet capture is already activated on adapter {adapter_number}")
1646
+
1647
+ nio.start_packet_capture(output_file)
1648
+ if self.ubridge:
1649
+ await self._ubridge_send(
1650
+ 'bridge start_capture {name} "{output_file}"'.format(
1651
+ name=f"QEMU-{self._id}-{adapter_number}", output_file=output_file
1652
+ )
1653
+ )
1654
+
1655
+ log.info(
1656
+ "QEMU VM '{name}' [{id}]: starting packet capture on adapter {adapter_number}".format(
1657
+ name=self.name, id=self.id, adapter_number=adapter_number
1658
+ )
1659
+ )
1660
+
1661
+ async def stop_capture(self, adapter_number):
1662
+ """
1663
+ Stops a packet capture.
1664
+
1665
+ :param adapter_number: adapter number
1666
+ """
1667
+
1668
+ nio = self.get_nio(adapter_number)
1669
+ if not nio.capturing:
1670
+ return
1671
+
1672
+ nio.stop_packet_capture()
1673
+ if self.ubridge:
1674
+ await self._ubridge_send("bridge stop_capture {name}".format(name=f"QEMU-{self._id}-{adapter_number}"))
1675
+
1676
+ log.info(
1677
+ "QEMU VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(
1678
+ name=self.name, id=self.id, adapter_number=adapter_number
1679
+ )
1680
+ )
1681
+
1682
+ async def create_disk_image(self, disk_name, options):
1683
+ """
1684
+ Create a Qemu disk
1685
+
1686
+ :param disk_name: disk name
1687
+ :param options: disk creation options
1688
+ """
1689
+
1690
+ try:
1691
+ qemu_img_path = self._get_qemu_img()
1692
+ img_format = options.pop("format")
1693
+ img_size = options.pop("size")
1694
+ disk_path = os.path.join(self.working_dir, disk_name)
1695
+
1696
+ try:
1697
+ if os.path.exists(disk_path):
1698
+ raise QemuError(f"Could not create disk image '{disk_name}', file already exists")
1699
+ except UnicodeEncodeError:
1700
+ raise QemuError(
1701
+ f"Could not create disk image '{disk_name}', "
1702
+ "Disk image name contains characters not supported by the filesystem"
1703
+ )
1704
+
1705
+ command = [qemu_img_path, "create", "-f", img_format]
1706
+ for option in sorted(options.keys()):
1707
+ command.extend(["-o", f"{option}={options[option]}"])
1708
+ command.append(disk_path)
1709
+ command.append(f"{img_size}M")
1710
+ retcode = await self._qemu_img_exec(command)
1711
+ if retcode:
1712
+ stdout = self.read_qemu_img_stdout()
1713
+ raise QemuError(f"Could not create '{disk_name}' disk image: qemu-img returned with {retcode}\n{stdout}")
1714
+ else:
1715
+ log.info(f"QEMU VM '{self.name}' [{self.id}]: Qemu disk image'{disk_name}' created")
1716
+ except (OSError, subprocess.SubprocessError) as e:
1717
+ stdout = self.read_qemu_img_stdout()
1718
+ raise QemuError(f"Could not create '{disk_name}' disk image: {e}\n{stdout}")
1719
+
1720
+ async def resize_disk_image(self, disk_name, extend):
1721
+ """
1722
+ Resize a Qemu disk
1723
+
1724
+ :param disk_name: disk name
1725
+ :param extend: new size
1726
+ """
1727
+
1728
+ try:
1729
+ qemu_img_path = self._get_qemu_img()
1730
+ disk_path = os.path.join(self.working_dir, disk_name)
1731
+ if not os.path.exists(disk_path):
1732
+ raise QemuError(f"Qemu disk image '{disk_name}' does not exist")
1733
+
1734
+ command = [qemu_img_path, "resize", disk_path, f"+{extend}M"]
1735
+ retcode = await self._qemu_img_exec(command)
1736
+ if retcode:
1737
+ stdout = self.read_qemu_img_stdout()
1738
+ raise QemuError(f"Could not update '{disk_name}' disk image: qemu-img returned with {retcode}\n{stdout}")
1739
+ else:
1740
+ log.info(f"QEMU VM '{self.name}' [{self.id}]: Qemu disk image '{disk_name}' extended by {extend} MB")
1741
+ except (OSError, subprocess.SubprocessError) as e:
1742
+ stdout = self.read_qemu_img_stdout()
1743
+ raise QemuError(f"Could not update '{disk_name}' disk image: {e}\n{stdout}")
1744
+
1745
+ def delete_disk_image(self, disk_name):
1746
+ """
1747
+ Delete a Qemu disk
1748
+
1749
+ :param disk_name: disk name
1750
+ """
1751
+
1752
+ disk_path = os.path.join(self.working_dir, disk_name)
1753
+ if not os.path.exists(disk_path):
1754
+ raise QemuError(f"Qemu disk image '{disk_name}' does not exist")
1755
+
1756
+ try:
1757
+ os.remove(disk_path)
1758
+ except OSError as e:
1759
+ raise QemuError(f"Could not delete '{disk_name}' disk image: {e}")
1760
+
1761
+ @property
1762
+ def started(self):
1763
+ """
1764
+ Returns either this QEMU VM has been started or not.
1765
+
1766
+ :returns: boolean
1767
+ """
1768
+
1769
+ return self.status == "started"
1770
+
1771
+ def read_stdout(self):
1772
+ """
1773
+ Reads the standard output of the QEMU process.
1774
+ Only use when the process has been stopped or has crashed.
1775
+ """
1776
+
1777
+ output = ""
1778
+ if self._stdout_file:
1779
+ try:
1780
+ with open(self._stdout_file, "rb") as file:
1781
+ output = file.read().decode("utf-8", errors="replace")
1782
+ except OSError as e:
1783
+ log.warning(f"Could not read {self._stdout_file}: {e}")
1784
+ return output
1785
+
1786
+ def read_qemu_img_stdout(self):
1787
+ """
1788
+ Reads the standard output of the QEMU-IMG process.
1789
+ """
1790
+
1791
+ output = ""
1792
+ if self._qemu_img_stdout_file:
1793
+ try:
1794
+ with open(self._qemu_img_stdout_file, "rb") as file:
1795
+ output = file.read().decode("utf-8", errors="replace")
1796
+ except OSError as e:
1797
+ log.warning(f"Could not read {self._qemu_img_stdout_file}: {e}")
1798
+ return output
1799
+
1800
+ def is_running(self):
1801
+ """
1802
+ Checks if the QEMU process is running
1803
+
1804
+ :returns: True or False
1805
+ """
1806
+
1807
+ if self._process:
1808
+ if self._process.returncode is None:
1809
+ return True
1810
+ else:
1811
+ self._process = None
1812
+ return False
1813
+
1814
+ async def reset_console(self):
1815
+ """
1816
+ Reset console
1817
+ """
1818
+
1819
+ if self.is_running():
1820
+ await self.reset_wrap_console()
1821
+
1822
+ def command(self):
1823
+ """
1824
+ Returns the QEMU command line.
1825
+
1826
+ :returns: QEMU command line (string)
1827
+ """
1828
+
1829
+ return " ".join(self._build_command())
1830
+
1831
+ @BaseNode.console_type.setter
1832
+ def console_type(self, new_console_type):
1833
+ """
1834
+ Sets the console type for this QEMU VM.
1835
+
1836
+ :param new_console_type: console type (string)
1837
+ """
1838
+
1839
+ if self.is_running() and self.console_type != new_console_type:
1840
+ raise QemuError(f'"{self._name}" must be stopped to change the console type to {new_console_type}')
1841
+
1842
+ super(QemuVM, QemuVM).console_type.__set__(self, new_console_type)
1843
+
1844
+ def _serial_options(self, internal_console_port, external_console_port):
1845
+
1846
+ if external_console_port:
1847
+ return ["-serial", f"telnet:127.0.0.1:{internal_console_port},server,nowait"]
1848
+ else:
1849
+ return []
1850
+
1851
+ def _vnc_options(self, port):
1852
+
1853
+ if port:
1854
+ vnc_port = port - 5900 # subtract by 5900 to get the display number
1855
+ return ["-vnc", f"{self._manager.port_manager.console_host}:{vnc_port}"]
1856
+ else:
1857
+ return []
1858
+
1859
+ def _spice_options(self, port):
1860
+
1861
+ if port:
1862
+ console_host = self._manager.port_manager.console_host
1863
+ if console_host == "0.0.0.0":
1864
+ try:
1865
+ if is_ipv6_enabled():
1866
+ # to fix an issue with Qemu when IPv4 is not enabled
1867
+ # see https://github.com/GNS3/gns3-gui/issues/2352
1868
+ # FIXME: consider making this more global (not just for Qemu + SPICE)
1869
+ console_host = "::"
1870
+ except OSError as e:
1871
+ raise QemuError("Could not check if IPv6 is enabled: {}".format(e))
1872
+ return ["-spice",
1873
+ f"addr={console_host},port={port},disable-ticketing",
1874
+ "-vga", "qxl"]
1875
+ else:
1876
+ return []
1877
+
1878
+ def _spice_with_agent_options(self, port):
1879
+
1880
+ spice_options = self._spice_options(port)
1881
+ if spice_options:
1882
+ # agent options (mouse/screen)
1883
+ agent_options = [
1884
+ "-device",
1885
+ "virtio-serial",
1886
+ "-chardev",
1887
+ "spicevmc,id=vdagent,debug=0,name=vdagent",
1888
+ "-device",
1889
+ "virtserialport,chardev=vdagent,name=com.redhat.spice.0",
1890
+ ]
1891
+ spice_options.extend(agent_options)
1892
+ # folder sharing options
1893
+ folder_sharing_options = [
1894
+ "-chardev",
1895
+ "spiceport,name=org.spice-space.webdav.0,id=charchannel0",
1896
+ "-device",
1897
+ "virtserialport,chardev=charchannel0,id=channel0,name=org.spice-space.webdav.0",
1898
+ ]
1899
+ spice_options.extend(folder_sharing_options)
1900
+ return spice_options
1901
+
1902
+ def _console_options(self):
1903
+
1904
+ if self._console_type == "telnet" and self._wrap_console:
1905
+ return self._serial_options(self._internal_console_port, self.console)
1906
+ elif self._console_type == "vnc":
1907
+ return self._vnc_options(self.console)
1908
+ elif self._console_type == "spice":
1909
+ return self._spice_options(self.console)
1910
+ elif self._console_type == "spice+agent":
1911
+ return self._spice_with_agent_options(self.console)
1912
+ elif self._console_type != "none":
1913
+ raise QemuError(f"Console type {self._console_type} is unknown")
1914
+
1915
+ def _aux_options(self):
1916
+
1917
+ if self._aux_type != "none" and self._aux_type == self._console_type:
1918
+ raise QemuError(f"Auxiliary console type {self._aux_type} cannot be the same as console type")
1919
+
1920
+ if self._aux_type == "telnet" and self._wrap_aux:
1921
+ return self._serial_options(self._internal_aux_port, self.aux)
1922
+ elif self._aux_type == "vnc":
1923
+ return self._vnc_options(self.aux)
1924
+ elif self._aux_type == "spice":
1925
+ return self._spice_options(self.aux)
1926
+ elif self._aux_type == "spice+agent":
1927
+ return self._spice_with_agent_options(self.aux)
1928
+ elif self._aux_type != "none":
1929
+ raise QemuError(f"Auxiliary console type {self._aux_type} is unknown")
1930
+ return []
1931
+
1932
+ def _monitor_options(self):
1933
+
1934
+ if self._monitor:
1935
+ return ["-monitor", f"tcp:{self._monitor_host}:{self._monitor},server,nowait"]
1936
+ else:
1937
+ return []
1938
+
1939
+ def _get_qemu_img(self):
1940
+ """
1941
+ Search the qemu-img binary in the same binary of the qemu binary
1942
+ to avoid version incompatibility.
1943
+
1944
+ :returns: qemu-img path or raise an error
1945
+ """
1946
+
1947
+ qemu_path_dir = os.path.dirname(self.qemu_path)
1948
+ qemu_image_path = shutil.which("qemu-img", path=qemu_path_dir)
1949
+ if qemu_image_path:
1950
+ return qemu_image_path
1951
+ raise QemuError(f"Could not find qemu-img in {qemu_path_dir}")
1952
+
1953
+ async def _qemu_img_exec(self, command):
1954
+
1955
+ self._qemu_img_stdout_file = os.path.join(self.working_dir, "qemu-img.log")
1956
+ log.info(f"logging to {self._qemu_img_stdout_file}")
1957
+ command_string = " ".join(shlex.quote(s) for s in command)
1958
+ log.info(f"Executing qemu-img with: {command_string}")
1959
+ with open(self._qemu_img_stdout_file, "w", encoding="utf-8") as fd:
1960
+ process = await asyncio.create_subprocess_exec(
1961
+ *command, stdout=fd, stderr=subprocess.STDOUT, cwd=self.working_dir
1962
+ )
1963
+ retcode = await process.wait()
1964
+ if retcode != 0:
1965
+ log.info(f"{self._get_qemu_img()} returned with {retcode}")
1966
+ return retcode
1967
+
1968
+ async def _find_disk_file_format(self, disk):
1969
+
1970
+ qemu_img_path = self._get_qemu_img()
1971
+ try:
1972
+ output = await subprocess_check_output(qemu_img_path, "info", "--output=json", disk)
1973
+ except subprocess.SubprocessError as e:
1974
+ raise QemuError(f"Error received while checking Qemu disk format: {e}")
1975
+ if output:
1976
+ try:
1977
+ json_data = json.loads(output)
1978
+ except ValueError as e:
1979
+ raise QemuError(f"Invalid JSON data returned by qemu-img: {e}")
1980
+ return json_data.get("format")
1981
+
1982
+ async def _create_linked_clone(self, disk_name, disk_image, disk):
1983
+
1984
+ try:
1985
+ qemu_img_path = self._get_qemu_img()
1986
+ backing_file_format = await self._find_disk_file_format(disk_image)
1987
+ if not backing_file_format:
1988
+ raise QemuError(f"Could not detect format for disk image: {disk_image}")
1989
+ backing_options, base_qcow2 = Qcow2.backing_options(disk_image)
1990
+ if base_qcow2 and base_qcow2.crypt_method:
1991
+ # Workaround for https://gitlab.com/qemu-project/qemu/-/issues/441
1992
+ # (we have to pass -u and the size). Also embed secret name.
1993
+ command = [qemu_img_path, "create", "-b", backing_options,
1994
+ "-F", backing_file_format, "-f", "qcow2", "-u", disk, str(base_qcow2.size)]
1995
+ else:
1996
+ command = [qemu_img_path, "create", "-o", "backing_file={}".format(disk_image),
1997
+ "-F", backing_file_format, "-f", "qcow2", disk]
1998
+
1999
+ retcode = await self._qemu_img_exec(command)
2000
+ if retcode:
2001
+ stdout = self.read_qemu_img_stdout()
2002
+ raise QemuError(
2003
+ "Could not create '{}' disk image: qemu-img returned with {}\n{}".format(disk_name, retcode, stdout)
2004
+ )
2005
+ except (OSError, subprocess.SubprocessError) as e:
2006
+ stdout = self.read_qemu_img_stdout()
2007
+ raise QemuError(f"Could not create '{disk_name}' disk image: {e}\n{stdout}")
2008
+
2009
+ async def _mcopy(self, image, *args):
2010
+ try:
2011
+ # read offset of first partition from MBR
2012
+ with open(image, "rb") as img_file:
2013
+ mbr = img_file.read(512)
2014
+ part_type, offset, signature = struct.unpack("<450xB3xL52xH", mbr)
2015
+ if signature != 0xAA55:
2016
+ raise OSError(f"mcopy failure: {image}: invalid MBR")
2017
+ if part_type not in (1, 4, 6, 11, 12, 14):
2018
+ raise OSError("mcopy failure: {}: invalid partition type {:02X}".format(image, part_type))
2019
+ part_image = image + f"@@{offset}S"
2020
+
2021
+ process = await asyncio.create_subprocess_exec(
2022
+ "mcopy",
2023
+ "-i",
2024
+ part_image,
2025
+ *args,
2026
+ stdin=subprocess.DEVNULL,
2027
+ stdout=subprocess.PIPE,
2028
+ stderr=subprocess.STDOUT,
2029
+ cwd=self.working_dir,
2030
+ )
2031
+ (stdout, _) = await process.communicate()
2032
+ retcode = process.returncode
2033
+ except (OSError, subprocess.SubprocessError) as e:
2034
+ raise OSError(f"mcopy failure: {e}")
2035
+ if retcode != 0:
2036
+ stdout = stdout.decode("utf-8").rstrip()
2037
+ if stdout:
2038
+ raise OSError(f"mcopy failure: {stdout}")
2039
+ else:
2040
+ raise OSError(f"mcopy failure: return code {retcode}")
2041
+
2042
+ async def _export_config(self):
2043
+ disk_name = getattr(self, "config_disk_name")
2044
+ if not disk_name:
2045
+ return
2046
+ disk = os.path.join(self.working_dir, disk_name)
2047
+ if not os.path.exists(disk):
2048
+ return
2049
+ config_dir = os.path.join(self.working_dir, "configs")
2050
+ zip_file = os.path.join(self.working_dir, "config.zip")
2051
+ try:
2052
+ os.mkdir(config_dir)
2053
+ await self._mcopy(disk, "-s", "-m", "-n", "--", "::/", config_dir)
2054
+ if os.path.exists(zip_file):
2055
+ os.remove(zip_file)
2056
+ pack_zip(zip_file, config_dir)
2057
+ except OSError as e:
2058
+ log.warning(f"Can't export config: {e}")
2059
+ self.project.emit("log.warning", {"message": f"{self._name}: Can't export config: {e}"})
2060
+ shutil.rmtree(config_dir, ignore_errors=True)
2061
+
2062
+ async def _import_config(self):
2063
+ disk_name = getattr(self, "config_disk_name")
2064
+ zip_file = os.path.join(self.working_dir, "config.zip")
2065
+ if not disk_name or not os.path.exists(zip_file):
2066
+ return
2067
+ config_dir = os.path.join(self.working_dir, "configs")
2068
+ disk = os.path.join(self.working_dir, disk_name)
2069
+ disk_tmp = disk + ".tmp"
2070
+ try:
2071
+ os.mkdir(config_dir)
2072
+ shutil.copyfile(getattr(self, "config_disk_image"), disk_tmp)
2073
+ unpack_zip(zip_file, config_dir)
2074
+ config_files = [os.path.join(config_dir, fname) for fname in os.listdir(config_dir)]
2075
+ if config_files:
2076
+ await self._mcopy(disk_tmp, "-s", "-m", "-o", "--", *config_files, "::/")
2077
+ os.replace(disk_tmp, disk)
2078
+ except OSError as e:
2079
+ log.warning(f"Can't import config: {e}")
2080
+ self.project.emit("log.warning", {"message": f"{self._name}: Can't import config: {e}"})
2081
+ if os.path.exists(disk_tmp):
2082
+ os.remove(disk_tmp)
2083
+ os.remove(zip_file)
2084
+ shutil.rmtree(config_dir, ignore_errors=True)
2085
+
2086
+ async def _disk_interface_options(self, disk, disk_index, interface, format=None):
2087
+ options = []
2088
+ extra_drive_options = ""
2089
+ if format:
2090
+ extra_drive_options += f",format={format}"
2091
+
2092
+ # From Qemu man page: if the filename contains comma, you must double it
2093
+ # (for instance, "file=my,,file" to use file "my,file").
2094
+ disk = disk.replace(",", ",,")
2095
+
2096
+ if interface == "sata":
2097
+ # special case, sata controller doesn't exist in Qemu
2098
+ options.extend(["-device", f"ahci,id=ahci{disk_index}"])
2099
+ options.extend(
2100
+ [
2101
+ "-drive",
2102
+ f"file={disk},if=none,id=drive{disk_index},index={disk_index},media=disk{extra_drive_options}",
2103
+ ]
2104
+ )
2105
+ if self._qemu_version and parse_version(self._qemu_version) >= parse_version("4.2.0"):
2106
+ # The ‘ide-drive’ device is deprecated since version 4.2.0
2107
+ # https://qemu.readthedocs.io/en/latest/system/deprecated.html#ide-drive-since-4-2
2108
+ options.extend(
2109
+ ["-device", f"ide-hd,drive=drive{disk_index},bus=ahci{disk_index}.0,id=drive{disk_index}"]
2110
+ )
2111
+ else:
2112
+ options.extend(
2113
+ ["-device", f"ide-drive,drive=drive{disk_index},bus=ahci{disk_index}.0,id=drive{disk_index}"]
2114
+ )
2115
+ elif interface == "nvme":
2116
+ options.extend(
2117
+ [
2118
+ "-drive",
2119
+ f"file={disk},if=none,id=drive{disk_index},index={disk_index},media=disk{extra_drive_options}",
2120
+ ]
2121
+ )
2122
+ options.extend(["-device", f"nvme,drive=drive{disk_index},serial={disk_index}"])
2123
+ elif interface == "scsi":
2124
+ options.extend(["-device", f"virtio-scsi-pci,id=scsi{disk_index}"])
2125
+ options.extend(
2126
+ [
2127
+ "-drive",
2128
+ f"file={disk},if=none,id=drive{disk_index},index={disk_index},media=disk{extra_drive_options}",
2129
+ ]
2130
+ )
2131
+ options.extend(["-device", f"scsi-hd,drive=drive{disk_index}"])
2132
+ # elif interface == "sd":
2133
+ # options.extend(["-drive", 'file={},id=drive{},index={}{}'.format(disk, disk_index, disk_index, extra_drive_options)])
2134
+ # options.extend(["-device", 'sd-card,drive=drive{},id=drive{}'.format(disk_index, disk_index, disk_index)])
2135
+ else:
2136
+ options.extend(
2137
+ [
2138
+ "-drive",
2139
+ f"file={disk},if={interface},index={disk_index},media=disk,id=drive{disk_index}{extra_drive_options}",
2140
+ ]
2141
+ )
2142
+ return options
2143
+
2144
+ async def _disk_options(self):
2145
+ options = []
2146
+ qemu_img_path = self._get_qemu_img()
2147
+
2148
+ drives = ["a", "b", "c", "d"]
2149
+
2150
+ for disk_index, drive in enumerate(drives):
2151
+ # prioritize config disk over normal disks
2152
+ if drive == "d" and self._create_config_disk:
2153
+ continue
2154
+
2155
+ disk_image = getattr(self, f"_hd{drive}_disk_image")
2156
+ if not disk_image:
2157
+ continue
2158
+
2159
+ interface = getattr(self, f"hd{drive}_disk_interface")
2160
+ disk_name = f"hd{drive}"
2161
+ if not os.path.isfile(disk_image) or not os.path.exists(disk_image):
2162
+ if os.path.islink(disk_image):
2163
+ raise QemuError(
2164
+ f"'{disk_name}' disk image linked to "
2165
+ f"'{os.path.realpath(disk_image)}' is not accessible"
2166
+ )
2167
+ else:
2168
+ raise QemuError(f"'{disk_image}' is not accessible")
2169
+ else:
2170
+ try:
2171
+ # check for corrupt disk image
2172
+ retcode = await self._qemu_img_exec([qemu_img_path, "check", disk_image])
2173
+ # ignore retcode == 1, one reason is that the image is encrypted and
2174
+ # there is no encrypt.key-secret available
2175
+ if retcode == 3:
2176
+ # image has leaked clusters, but is not corrupted, let's try to fix it
2177
+ log.warning(f"Disk image '{disk_image}' has leaked clusters")
2178
+ if await self._qemu_img_exec([qemu_img_path, "check", "-r", "leaks", f"{disk_image}"]) == 3:
2179
+ self.project.emit(
2180
+ "log.warning",
2181
+ {"message": f"Disk image '{disk_image}' has leaked clusters and could not be fixed"}
2182
+ )
2183
+ elif retcode == 2:
2184
+ # image is corrupted, let's try to fix it
2185
+ log.warning(f"Disk image '{disk_image}' is corrupted")
2186
+ if await self._qemu_img_exec([qemu_img_path, "check", "-r", "all", f"{disk_image}"]) == 2:
2187
+ self.project.emit(
2188
+ "log.warning",
2189
+ {"message": f"Disk image '{disk_image}' is corrupted and could not be fixed"}
2190
+ )
2191
+ except (OSError, subprocess.SubprocessError) as e:
2192
+ stdout = self.read_qemu_img_stdout()
2193
+ raise QemuError(f"Could not check '{disk_name}' disk image: {e}\n{stdout}")
2194
+
2195
+ if self.linked_clone and os.path.dirname(disk_image) != self.working_dir:
2196
+
2197
+ #cloned_disk_image = os.path.splitext(os.path.basename(disk_image))
2198
+ disk = os.path.join(self.working_dir, f"{disk_name}_disk.qcow2")
2199
+ if not os.path.exists(disk):
2200
+ # create the disk
2201
+ await self._create_linked_clone(disk_name, disk_image, disk)
2202
+ else:
2203
+ backing_file_format = await self._find_disk_file_format(disk_image)
2204
+ if not backing_file_format:
2205
+ raise QemuError(f"Could not detect format for disk image '{disk_image}'")
2206
+ # Rebase the image. This is in case the base image moved to a different directory,
2207
+ # which will be the case if we imported a portable project. This uses
2208
+ # get_abs_image_path(hdX_disk_image) and ignores the old base path embedded
2209
+ # in the qcow2 file itself.
2210
+ try:
2211
+ qcow2 = Qcow2(disk)
2212
+ await qcow2.rebase(qemu_img_path, disk_image, backing_file_format)
2213
+ except (Qcow2Error, OSError) as e:
2214
+ raise QemuError(f"Could not use qcow2 disk image '{disk_image}' for {disk_name} {e}")
2215
+
2216
+ else:
2217
+ disk = disk_image
2218
+
2219
+ options.extend(await self._disk_interface_options(disk, disk_index, interface))
2220
+
2221
+ # config disk
2222
+ disk_image = getattr(self, "config_disk_image")
2223
+ if disk_image and self._create_config_disk:
2224
+ disk_name = getattr(self, "config_disk_name")
2225
+ disk = os.path.join(self.working_dir, disk_name)
2226
+ if self.hdd_disk_interface == "none":
2227
+ # use the HDA interface type if none has been configured for HDD
2228
+ self.hdd_disk_interface = getattr(self, "hda_disk_interface", "none")
2229
+ await self._import_config()
2230
+ disk_exists = os.path.exists(disk)
2231
+ if not disk_exists:
2232
+ try:
2233
+ shutil.copyfile(disk_image, disk)
2234
+ disk_exists = True
2235
+ except OSError as e:
2236
+ log.warning(f"Could not create '{disk_name}' disk image: {e}")
2237
+ if disk_exists:
2238
+ options.extend(await self._disk_interface_options(disk, 3, self.hdd_disk_interface, "raw"))
2239
+
2240
+ return options
2241
+
2242
+ async def resize_disk(self, drive_name, extend):
2243
+
2244
+ if self.is_running():
2245
+ raise QemuError(f"Cannot resize {drive_name} while the VM is running")
2246
+
2247
+ if self.linked_clone:
2248
+ disk_image_path = os.path.join(self.working_dir, f"{drive_name}_disk.qcow2")
2249
+ if not os.path.exists(disk_image_path):
2250
+ disk_image = getattr(self, f"_{drive_name}_disk_image")
2251
+ await self._create_linked_clone(drive_name, disk_image, disk_image_path)
2252
+ else:
2253
+ disk_image_path = getattr(self, f"{drive_name}_disk_image")
2254
+
2255
+ if not os.path.exists(disk_image_path):
2256
+ raise QemuError(f"Disk path '{disk_image_path}' does not exist")
2257
+ qemu_img_path = self._get_qemu_img()
2258
+ await self.manager.resize_disk(qemu_img_path, disk_image_path, extend)
2259
+
2260
+ def _cdrom_option(self):
2261
+
2262
+ options = []
2263
+ if self._cdrom_image:
2264
+ if not os.path.isfile(self._cdrom_image) or not os.path.exists(self._cdrom_image):
2265
+ if os.path.islink(self._cdrom_image):
2266
+ raise QemuError(
2267
+ f"cdrom image '{self._cdrom_image}' linked to '{os.path.realpath(self._cdrom_image)}' is not accessible"
2268
+ )
2269
+ else:
2270
+ raise QemuError(f"cdrom image '{self._cdrom_image}' is not accessible")
2271
+ if self._hdc_disk_image:
2272
+ raise QemuError("You cannot use a disk image on hdc disk and a CDROM image at the same time")
2273
+ options.extend(["-cdrom", self._cdrom_image.replace(",", ",,")])
2274
+ return options
2275
+
2276
+ def _bios_option(self):
2277
+
2278
+ options = []
2279
+ if self._bios_image:
2280
+ if self._uefi:
2281
+ raise QemuError("Cannot use a bios image and the UEFI boot mode at the same time")
2282
+ if not os.path.isfile(self._bios_image) or not os.path.exists(self._bios_image):
2283
+ if os.path.islink(self._bios_image):
2284
+ raise QemuError(
2285
+ f"bios image '{self._bios_image}' linked to '{os.path.realpath(self._bios_image)}' is not accessible"
2286
+ )
2287
+ else:
2288
+ raise QemuError(f"bios image '{self._bios_image}' is not accessible")
2289
+ options.extend(["-bios", self._bios_image.replace(",", ",,")])
2290
+ elif self._uefi:
2291
+ # get the OVMF firmware from the images directory
2292
+ ovmf_firmware_path = self.manager.get_abs_image_path("OVMF_CODE.fd")
2293
+ log.info("Configuring UEFI boot mode using OVMF file: '{}'".format(ovmf_firmware_path))
2294
+ options.extend(["-drive", "if=pflash,format=raw,readonly,file={}".format(ovmf_firmware_path)])
2295
+
2296
+ # the node should have its own copy of OVMF_VARS.fd (the UEFI variables store)
2297
+ ovmf_vars_node_path = os.path.join(self.working_dir, "OVMF_VARS.fd")
2298
+ if not os.path.exists(ovmf_vars_node_path):
2299
+ try:
2300
+ shutil.copyfile(self.manager.get_abs_image_path("OVMF_VARS.fd"), ovmf_vars_node_path)
2301
+ except OSError as e:
2302
+ raise QemuError("Cannot copy OVMF_VARS.fd file to the node working directory: {}".format(e))
2303
+ options.extend(["-drive", "if=pflash,format=raw,file={}".format(ovmf_vars_node_path)])
2304
+ return options
2305
+
2306
+ def _linux_boot_options(self):
2307
+
2308
+ options = []
2309
+ if self._initrd:
2310
+ if not os.path.isfile(self._initrd) or not os.path.exists(self._initrd):
2311
+ if os.path.islink(self._initrd):
2312
+ raise QemuError(
2313
+ f"initrd file '{self._initrd}' linked to '{os.path.realpath(self._initrd)}' is not accessible"
2314
+ )
2315
+ else:
2316
+ raise QemuError(f"initrd file '{self._initrd}' is not accessible")
2317
+ options.extend(["-initrd", self._initrd.replace(",", ",,")])
2318
+ if self._kernel_image:
2319
+ if not os.path.isfile(self._kernel_image) or not os.path.exists(self._kernel_image):
2320
+ if os.path.islink(self._kernel_image):
2321
+ raise QemuError(
2322
+ f"kernel image '{self._kernel_image}' linked to '{os.path.realpath(self._kernel_image)}' is not accessible"
2323
+ )
2324
+ else:
2325
+ raise QemuError(f"kernel image '{self._kernel_image}' is not accessible")
2326
+ options.extend(["-kernel", self._kernel_image.replace(",", ",,")])
2327
+ if self._kernel_command_line:
2328
+ options.extend(["-append", self._kernel_command_line])
2329
+ return options
2330
+
2331
+ async def _start_swtpm(self):
2332
+ """
2333
+ Start swtpm (TPM emulator)
2334
+ """
2335
+
2336
+ if sys.platform.startswith("win"):
2337
+ raise QemuError("swtpm (TPM emulator) is not supported on Windows")
2338
+ tpm_dir = os.path.join(self.working_dir, "tpm")
2339
+ os.makedirs(tpm_dir, exist_ok=True)
2340
+ tpm_sock = os.path.join(self.temporary_directory, "swtpm.sock")
2341
+ swtpm = shutil.which("swtpm")
2342
+ if not swtpm:
2343
+ raise QemuError("Could not find swtpm (TPM emulator)")
2344
+ swtpm_version = await self.manager.get_swtpm_version(swtpm)
2345
+ if swtpm_version and parse_version(swtpm_version) < parse_version("0.8.0"):
2346
+ # swtpm >= version 0.8.0 is required
2347
+ raise QemuError("swtpm version 0.8.0 or above must be installed (detected version is {})".format(swtpm_version))
2348
+ try:
2349
+ command = [
2350
+ swtpm,
2351
+ "socket",
2352
+ "--tpm2",
2353
+ '--tpmstate', "dir={}".format(tpm_dir),
2354
+ "--ctrl",
2355
+ "type=unixio,path={},terminate".format(tpm_sock)
2356
+ ]
2357
+ command_string = " ".join(shlex.quote(s) for s in command)
2358
+ log.info("Starting swtpm (TPM emulator) with: {}".format(command_string))
2359
+ self._swtpm_process = subprocess.Popen(command, cwd=self.working_dir)
2360
+ log.info("swtpm (TPM emulator) has started")
2361
+ except (OSError, subprocess.SubprocessError) as e:
2362
+ raise QemuError("Could not start swtpm (TPM emulator): {}".format(e))
2363
+
2364
+ def _stop_swtpm(self):
2365
+ """
2366
+ Stop swtpm (TPM emulator)
2367
+ """
2368
+
2369
+ if self._swtpm_process and self._swtpm_process.returncode is None:
2370
+ self._swtpm_process.terminate()
2371
+ self._swtpm_process = None
2372
+
2373
+ def _tpm_options(self):
2374
+ """
2375
+ Return the TPM options for Qemu.
2376
+ """
2377
+
2378
+ tpm_sock = os.path.join(self.temporary_directory, "swtpm.sock")
2379
+ if not os.path.exists(tpm_sock):
2380
+ raise QemuError("swtpm socket file '{}' does not exist".format(tpm_sock))
2381
+ options = [
2382
+ "-chardev",
2383
+ "socket,id=chrtpm,path={}".format(tpm_sock),
2384
+ "-tpmdev",
2385
+ "emulator,id=tpm0,chardev=chrtpm",
2386
+ "-device",
2387
+ "tpm-tis,tpmdev=tpm0"
2388
+ ]
2389
+ return options
2390
+
2391
+ async def _network_options(self):
2392
+
2393
+ network_options = []
2394
+ network_options.extend(
2395
+ ["-net", "none"]
2396
+ ) # we do not want any user networking back-end if no adapter is connected.
2397
+
2398
+ # Each 32 PCI device we need to add a PCI bridge with max 9 bridges
2399
+ pci_devices = 4 + len(self._ethernet_adapters) # 4 PCI devices are use by default by qemu
2400
+ pci_bridges = math.floor(pci_devices / 32)
2401
+ pci_bridges_created = 0
2402
+ if pci_bridges >= 1:
2403
+ if self._qemu_version and parse_version(self._qemu_version) < parse_version("2.4.0"):
2404
+ raise QemuError(
2405
+ "Qemu version 2.4 or later is required to run this VM with a large number of network adapters"
2406
+ )
2407
+
2408
+ pci_device_id = 4 + pci_bridges # Bridge consume PCI ports
2409
+ for adapter_number, adapter in enumerate(self._ethernet_adapters):
2410
+ mac = int_to_macaddress(macaddress_to_int(self._mac_address) + adapter_number)
2411
+
2412
+ # use a local UDP tunnel to connect to uBridge instead
2413
+ if adapter_number not in self._local_udp_tunnels:
2414
+ self._local_udp_tunnels[adapter_number] = self._create_local_udp_tunnel()
2415
+ nio = self._local_udp_tunnels[adapter_number][0]
2416
+
2417
+ custom_adapter = self._get_custom_adapter_settings(adapter_number)
2418
+ adapter_type = custom_adapter.get("adapter_type", self._adapter_type)
2419
+ custom_mac_address = custom_adapter.get("mac_address")
2420
+ if custom_mac_address:
2421
+ mac = int_to_macaddress(macaddress_to_int(custom_mac_address))
2422
+
2423
+ device_string = f"{adapter_type},mac={mac}"
2424
+ if adapter_type == "virtio-net-pci":
2425
+ device_string = "{},speed=10000,duplex=full".format(device_string)
2426
+ bridge_id = math.floor(pci_device_id / 32)
2427
+ if bridge_id > 0:
2428
+ if pci_bridges_created < bridge_id:
2429
+ network_options.extend(["-device", f"i82801b11-bridge,id=dmi_pci_bridge{bridge_id}"])
2430
+ network_options.extend(
2431
+ [
2432
+ "-device",
2433
+ "pci-bridge,id=pci-bridge{bridge_id},bus=dmi_pci_bridge{bridge_id},chassis_nr=0x1,addr=0x{bridge_id},shpc=off".format(
2434
+ bridge_id=bridge_id
2435
+ ),
2436
+ ]
2437
+ )
2438
+ pci_bridges_created += 1
2439
+ addr = pci_device_id % 32
2440
+ device_string = f"{device_string},bus=pci-bridge{bridge_id},addr=0x{addr:02x}"
2441
+ pci_device_id += 1
2442
+ if nio:
2443
+ network_options.extend(["-device", f"{device_string},netdev=gns3-{adapter_number}"])
2444
+ if isinstance(nio, NIOUDP):
2445
+ network_options.extend(
2446
+ [
2447
+ "-netdev",
2448
+ "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(
2449
+ adapter_number, nio.rhost, nio.rport, "127.0.0.1", nio.lport
2450
+ ),
2451
+ ]
2452
+ )
2453
+ elif isinstance(nio, NIOTAP):
2454
+ network_options.extend(
2455
+ ["-netdev", f"tap,id=gns3-{adapter_number},ifname={nio.tap_device},script=no,downscript=no"]
2456
+ )
2457
+ else:
2458
+ network_options.extend(["-device", device_string])
2459
+
2460
+ return network_options
2461
+
2462
+ async def _disable_graphics(self):
2463
+ """
2464
+ Disable graphics depending of the QEMU version
2465
+ """
2466
+
2467
+ if any(opt in self._options for opt in ["-display", "-nographic", "-curses", "-sdl" "-spice", "-vnc"]):
2468
+ return []
2469
+ if self._qemu_version and parse_version(self._qemu_version) >= parse_version("3.0"):
2470
+ return ["-display", "none"]
2471
+ else:
2472
+ return ["-nographic"]
2473
+
2474
+ async def _run_with_hardware_acceleration(self, qemu_path, options):
2475
+ """
2476
+ Check if we can run Qemu with hardware acceleration
2477
+
2478
+ :param qemu_path: Path to qemu
2479
+ :param options: String of qemu user options
2480
+ :returns: Boolean True if we need to enable hardware acceleration
2481
+ """
2482
+
2483
+ enable_hardware_accel = self.manager.config.settings.Qemu.enable_hardware_acceleration
2484
+ require_hardware_accel = self.manager.config.settings.Qemu.require_hardware_acceleration
2485
+ if enable_hardware_accel and "-machine accel=tcg" not in options:
2486
+ # Turn OFF hardware acceleration for non x86 architectures
2487
+ supported_binaries = ["qemu-system-x86_64", "qemu-system-i386", "qemu-kvm"]
2488
+ if os.path.basename(qemu_path) not in supported_binaries:
2489
+ if require_hardware_accel:
2490
+ raise QemuError(
2491
+ "Hardware acceleration can only be used with the following Qemu executables: {}".format(
2492
+ ", ".join(supported_binaries)
2493
+ )
2494
+ )
2495
+ else:
2496
+ return False
2497
+
2498
+ if sys.platform.startswith("linux") and not os.path.exists("/dev/kvm"):
2499
+ if require_hardware_accel:
2500
+ raise QemuError(
2501
+ "KVM acceleration cannot be used (/dev/kvm doesn't exist). It is possible to turn off KVM support in the gns3_server.conf by adding enable_hardware_acceleration = false to the [Qemu] section."
2502
+ )
2503
+ else:
2504
+ return False
2505
+ elif sys.platform.startswith("darwin"):
2506
+ process = await asyncio.create_subprocess_shell("kextstat | grep com.intel.kext.intelhaxm")
2507
+ await process.wait()
2508
+ if process.returncode != 0:
2509
+ if require_hardware_accel:
2510
+ raise QemuError(
2511
+ "HAXM acceleration support is not installed on this host (com.intel.kext.intelhaxm extension not loaded)"
2512
+ )
2513
+ else:
2514
+ return False
2515
+ return True
2516
+ return False
2517
+
2518
+ async def _clear_save_vm_stated(self, snapshot_name="GNS3_SAVED_STATE"):
2519
+
2520
+ drives = ["a", "b", "c", "d"]
2521
+ qemu_img_path = self._get_qemu_img()
2522
+ for disk_index, drive in enumerate(drives):
2523
+ disk_image = getattr(self, f"_hd{drive}_disk_image")
2524
+ if not disk_image:
2525
+ continue
2526
+ try:
2527
+ if self.linked_clone:
2528
+ disk = os.path.join(self.working_dir, f"hd{drive}_disk.qcow2")
2529
+ else:
2530
+ disk = disk_image
2531
+ if not os.path.exists(disk):
2532
+ continue
2533
+ output = await subprocess_check_output(qemu_img_path, "info", "--output=json", disk)
2534
+ if output:
2535
+ try:
2536
+ json_data = json.loads(output)
2537
+ except ValueError as e:
2538
+ raise QemuError(
2539
+ f"Invalid JSON data returned by qemu-img while looking for the Qemu VM saved state snapshot: {e}"
2540
+ )
2541
+ if "snapshots" in json_data:
2542
+ for snapshot in json_data["snapshots"]:
2543
+ if snapshot["name"] == snapshot_name:
2544
+ # delete the snapshot
2545
+ command = [qemu_img_path, "snapshot", "-d", snapshot_name, disk]
2546
+ retcode = await self._qemu_img_exec(command)
2547
+ if retcode:
2548
+ stdout = self.read_qemu_img_stdout()
2549
+ log.warning(f"Could not delete saved VM state from disk {disk}: {stdout}")
2550
+ else:
2551
+ log.info(f"Deleted saved VM state from disk {disk}")
2552
+ except subprocess.SubprocessError as e:
2553
+ raise QemuError(f"Error while looking for the Qemu VM saved state snapshot: {e}")
2554
+
2555
+ async def _saved_state_option(self, snapshot_name="GNS3_SAVED_STATE"):
2556
+
2557
+ drives = ["a", "b", "c", "d"]
2558
+ qemu_img_path = self._get_qemu_img()
2559
+ for disk_index, drive in enumerate(drives):
2560
+ disk_image = getattr(self, f"_hd{drive}_disk_image")
2561
+ if not disk_image:
2562
+ continue
2563
+ try:
2564
+ if self.linked_clone:
2565
+ disk = os.path.join(self.working_dir, f"hd{drive}_disk.qcow2")
2566
+ else:
2567
+ disk = disk_image
2568
+ if not os.path.exists(disk):
2569
+ continue
2570
+ output = await subprocess_check_output(qemu_img_path, "info", "--output=json", disk)
2571
+ if output:
2572
+ try:
2573
+ json_data = json.loads(output)
2574
+ except ValueError as e:
2575
+ raise QemuError(
2576
+ f"Invalid JSON data returned by qemu-img while looking for the Qemu VM saved state snapshot: {e}"
2577
+ )
2578
+ if "snapshots" in json_data:
2579
+ for snapshot in json_data["snapshots"]:
2580
+ if snapshot["name"] == snapshot_name:
2581
+ log.info(
2582
+ 'QEMU VM "{name}" [{id}] VM saved state detected (snapshot name: {snapshot})'.format(
2583
+ name=self._name, id=self.id, snapshot=snapshot_name
2584
+ )
2585
+ )
2586
+ return ["-loadvm", snapshot_name.replace(",", ",,")]
2587
+
2588
+ except subprocess.SubprocessError as e:
2589
+ raise QemuError(f"Error while looking for the Qemu VM saved state snapshot: {e}")
2590
+ return []
2591
+
2592
+ async def _build_command(self):
2593
+ """
2594
+ Command to start the QEMU process.
2595
+ (to be passed to subprocess.Popen())
2596
+ """
2597
+
2598
+ self._qemu_version = await self.manager.get_qemu_version(self.qemu_path)
2599
+ vm_name = self._name.replace(",", ",,")
2600
+ project_path = self.project.path.replace(",", ",,")
2601
+ additional_options = self._options.strip()
2602
+ additional_options = additional_options.replace("%vm-name%", '"' + vm_name.replace('"', '\\"') + '"')
2603
+ additional_options = additional_options.replace("%vm-id%", self._id)
2604
+ additional_options = additional_options.replace("%project-id%", self.project.id)
2605
+ additional_options = additional_options.replace("%project-path%", '"' + project_path.replace('"', '\\"') + '"')
2606
+ additional_options = additional_options.replace("%guest-cid%", str(self._guest_cid))
2607
+ if self._console_type != "none" and self._console:
2608
+ additional_options = additional_options.replace("%console-port%", str(self._console))
2609
+ command = [self.qemu_path]
2610
+ command.extend(["-name", vm_name])
2611
+ command.extend(["-m", f"{self._ram}M"])
2612
+ # set the maximum number of the hotpluggable CPUs to match the number of CPUs to avoid issues.
2613
+ maxcpus = self._maxcpus
2614
+ if self._cpus > maxcpus:
2615
+ maxcpus = self._cpus
2616
+ command.extend(["-smp", f"cpus={self._cpus},maxcpus={maxcpus},sockets=1"])
2617
+ if await self._run_with_hardware_acceleration(self.qemu_path, self._options):
2618
+ if sys.platform.startswith("linux"):
2619
+ command.extend(["-enable-kvm"])
2620
+ # Issue on some combo Intel CPU + KVM + Qemu 2.4.0
2621
+ # https://github.com/GNS3/gns3-server/issues/685
2622
+ if self._qemu_version and parse_version(self._qemu_version) >= parse_version("2.4.0") and self.platform == "x86_64":
2623
+ command.extend(["-machine", "smm=off"])
2624
+ elif sys.platform.startswith("darwin"):
2625
+ command.extend(["-enable-hax"])
2626
+ command.extend(["-boot", f"order={self._boot_priority}"])
2627
+ command.extend(self._bios_option())
2628
+ command.extend(self._cdrom_option())
2629
+ command.extend(await self._disk_options())
2630
+ command.extend(self._linux_boot_options())
2631
+ if "-uuid" not in additional_options:
2632
+ command.extend(["-uuid", self._id])
2633
+ command.extend(self._console_options())
2634
+ command.extend(self._aux_options())
2635
+ command.extend(self._monitor_options())
2636
+ command.extend(await self._network_options())
2637
+ if self.on_close != "save_vm_state":
2638
+ await self._clear_save_vm_stated()
2639
+ else:
2640
+ command.extend(await self._saved_state_option())
2641
+ if self._console_type == "telnet":
2642
+ command.extend(await self._disable_graphics())
2643
+ if self._tpm:
2644
+ command.extend(self._tpm_options())
2645
+ if additional_options:
2646
+ try:
2647
+ additional_opt_list = shlex.split(additional_options)
2648
+ except ValueError as e:
2649
+ raise QemuError(f"Invalid additional options: {additional_options} error {e}")
2650
+ allow_unsafe_options = self.manager.config.settings.Qemu.allow_unsafe_options
2651
+ if allow_unsafe_options is False:
2652
+ for opt in additional_opt_list:
2653
+ if opt in FORBIDDEN_OPTIONS:
2654
+ raise QemuError("Forbidden additional option: {}".format(opt))
2655
+ command.extend(additional_opt_list)
2656
+
2657
+ # avoiding mouse offset (see https://github.com/GNS3/gns3-server/issues/2335)
2658
+ if self._console_type == "vnc":
2659
+ command.extend(['-machine', 'usb=on', '-device', 'usb-tablet'])
2660
+ return command
2661
+
2662
+ def asdict(self):
2663
+ answer = {"project_id": self.project.id, "node_id": self.id, "node_directory": self.working_path}
2664
+ # Qemu has a long list of options. The JSON schema is the single source of information
2665
+ for field in Qemu.model_json_schema()["properties"]:
2666
+ if field not in answer:
2667
+ try:
2668
+ answer[field] = getattr(self, field)
2669
+ except AttributeError:
2670
+ pass
2671
+
2672
+ for drive in ["a", "b", "c", "d"]:
2673
+ disk_image = getattr(self, f"_hd{drive}_disk_image")
2674
+ if not disk_image:
2675
+ continue
2676
+ answer[f"hd{drive}_disk_image"] = self.manager.get_relative_image_path(disk_image, self.working_dir)
2677
+ answer[f"hd{drive}_disk_image_md5sum"] = md5sum(disk_image, self.working_dir)
2678
+
2679
+ local_disk = os.path.join(self.working_dir, f"hd{drive}_disk.qcow2")
2680
+ if os.path.exists(local_disk):
2681
+ try:
2682
+ qcow2 = Qcow2(local_disk)
2683
+ if qcow2.backing_file:
2684
+ answer[f"hd{drive}_disk_image_backed"] = os.path.basename(local_disk)
2685
+ except (Qcow2Error, OSError) as e:
2686
+ log.error(f"Could not read qcow2 disk image '{local_disk}': {e}")
2687
+ continue
2688
+
2689
+ answer["cdrom_image"] = self.manager.get_relative_image_path(self._cdrom_image, self.working_dir)
2690
+ answer["cdrom_image_md5sum"] = md5sum(self._cdrom_image, self.working_dir)
2691
+ answer["bios_image"] = self.manager.get_relative_image_path(self._bios_image, self.working_dir)
2692
+ answer["bios_image_md5sum"] = md5sum(self._bios_image, self.working_dir)
2693
+ answer["initrd"] = self.manager.get_relative_image_path(self._initrd, self.working_dir)
2694
+ answer["initrd_md5sum"] = md5sum(self._initrd, self.working_dir)
2695
+ answer["kernel_image"] = self.manager.get_relative_image_path(self._kernel_image, self.working_dir)
2696
+ answer["kernel_image_md5sum"] = md5sum(self._kernel_image, self.working_dir)
2697
+ return answer