1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-24 09:18:08 +00:00

Save state feature for VirtualBox and VMware. New "On close" setting to

select the action to execute when closing/stopping a Qemu/VirtualBox/VMware VM.
This commit is contained in:
grossmj 2018-03-30 21:18:44 +07:00
parent 3d1ee4da3f
commit ea0009db6c
9 changed files with 100 additions and 112 deletions

View File

@ -23,6 +23,7 @@ A minimal version:
The revision is the version of file format: The revision is the version of file format:
* 9: GNS3 2.2
* 8: GNS3 2.1 * 8: GNS3 2.1
* 7: GNS3 2.0 * 7: GNS3 2.0
* 6: GNS3 2.0 < beta 3 * 6: GNS3 2.0 < beta 3

View File

@ -116,8 +116,7 @@ class QemuVM(BaseNode):
self._kernel_image = "" self._kernel_image = ""
self._kernel_command_line = "" self._kernel_command_line = ""
self._legacy_networking = False self._legacy_networking = False
self._acpi_shutdown = False self._on_close = "power_off"
self._save_vm_state = False
self._cpu_throttling = 0 # means no CPU throttling self._cpu_throttling = 0 # means no CPU throttling
self._process_priority = "low" self._process_priority = "low"
@ -573,52 +572,25 @@ class QemuVM(BaseNode):
self._legacy_networking = legacy_networking self._legacy_networking = legacy_networking
@property @property
def acpi_shutdown(self): def on_close(self):
""" """
Returns either this QEMU VM can be ACPI shutdown. Returns the action to execute when the VM is stopped/closed
:returns: boolean :returns: string
""" """
return self._acpi_shutdown return self._on_close
@acpi_shutdown.setter @on_close.setter
def acpi_shutdown(self, acpi_shutdown): def on_close(self, on_close):
""" """
Sets either this QEMU VM can be ACPI shutdown. Sets the action to execute when the VM is stopped/closed
:param acpi_shutdown: boolean :param on_close: string
""" """
if acpi_shutdown: log.info('QEMU VM "{name}" [{id}] set the close action to "{action}"'.format(name=self._name, id=self._id, action=on_close))
log.info('QEMU VM "{name}" [{id}] has enabled ACPI shutdown'.format(name=self._name, id=self._id)) self._on_close = on_close
else:
log.info('QEMU VM "{name}" [{id}] has disabled ACPI shutdown'.format(name=self._name, id=self._id))
self._acpi_shutdown = acpi_shutdown
@property
def save_vm_state(self):
"""
Returns either this QEMU VM state can be saved.
:returns: boolean
"""
return self._save_vm_state
@save_vm_state.setter
def save_vm_state(self, save_vm_state):
"""
Sets either this QEMU VM state can be saved.
:param save_vm_state: boolean
"""
if save_vm_state:
log.info('QEMU VM "{name}" [{id}] has enabled the save VM state option'.format(name=self._name, id=self._id))
else:
log.info('QEMU VM "{name}" [{id}] has disabled the save VM state option'.format(name=self._name, id=self._id))
self._save_vm_state = save_vm_state
@property @property
def cpu_throttling(self): def cpu_throttling(self):
@ -1030,7 +1002,7 @@ class QemuVM(BaseNode):
log.info('Stopping QEMU VM "{}" PID={}'.format(self._name, self._process.pid)) log.info('Stopping QEMU VM "{}" PID={}'.format(self._name, self._process.pid))
try: try:
if self.save_vm_state: if self.on_close == "save_vm_state":
yield from self._control_vm("stop") yield from self._control_vm("stop")
yield from self._control_vm("savevm GNS3_SAVED_STATE") yield from self._control_vm("savevm GNS3_SAVED_STATE")
wait_for_savevm = 120 wait_for_savevm = 120
@ -1041,7 +1013,7 @@ class QemuVM(BaseNode):
if status != []: if status != []:
break break
if self.acpi_shutdown and not self.save_vm_state: if self.on_close == "shutdown_signal":
yield from self._control_vm("system_powerdown") yield from self._control_vm("system_powerdown")
yield from gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=30) yield from gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=30)
else: else:
@ -1059,7 +1031,7 @@ class QemuVM(BaseNode):
log.warning('QEMU VM "{}" PID={} is still running'.format(self._name, self._process.pid)) log.warning('QEMU VM "{}" PID={} is still running'.format(self._name, self._process.pid))
self._process = None self._process = None
self._stop_cpulimit() self._stop_cpulimit()
if not self.save_vm_state: if self.on_close != "save_vm_state":
yield from self._clear_save_vm_stated() yield from self._clear_save_vm_stated()
yield from super().stop() yield from super().stop()
@ -1164,7 +1136,7 @@ class QemuVM(BaseNode):
if not (yield from super().close()): if not (yield from super().close()):
return False return False
self.acpi_shutdown = False self.on_close = "power_off"
yield from self.stop() yield from self.stop()
for adapter in self._ethernet_adapters: for adapter in self._ethernet_adapters:
@ -1951,7 +1923,7 @@ class QemuVM(BaseNode):
command.extend(self._monitor_options()) command.extend(self._monitor_options())
command.extend((yield from self._network_options())) command.extend((yield from self._network_options()))
command.extend(self._graphic()) command.extend(self._graphic())
if not self.save_vm_state: if self.on_close != "save_vm_state":
yield from self._clear_save_vm_stated() yield from self._clear_save_vm_stated()
else: else:
command.extend((yield from self._saved_state_option())) command.extend((yield from self._saved_state_option()))

View File

@ -66,7 +66,7 @@ class VirtualBoxVM(BaseNode):
self._adapters = adapters self._adapters = adapters
self._ethernet_adapters = {} self._ethernet_adapters = {}
self._headless = False self._headless = False
self._acpi_shutdown = False self._on_close = "power_off"
self._vmname = vmname self._vmname = vmname
self._use_any_adapter = False self._use_any_adapter = False
self._ram = 0 self._ram = 0
@ -81,7 +81,7 @@ class VirtualBoxVM(BaseNode):
"project_id": self.project.id, "project_id": self.project.id,
"vmname": self.vmname, "vmname": self.vmname,
"headless": self.headless, "headless": self.headless,
"acpi_shutdown": self.acpi_shutdown, "on_close": self.on_close,
"adapters": self._adapters, "adapters": self._adapters,
"adapter_type": self.adapter_type, "adapter_type": self.adapter_type,
"ram": self.ram, "ram": self.ram,
@ -307,7 +307,12 @@ class VirtualBoxVM(BaseNode):
yield from self._stop_remote_console() yield from self._stop_remote_console()
vm_state = yield from self._get_vm_state() vm_state = yield from self._get_vm_state()
if vm_state == "running" or vm_state == "paused" or vm_state == "stuck": if vm_state == "running" or vm_state == "paused" or vm_state == "stuck":
if self.acpi_shutdown:
if self.on_close == "save_vm_state":
result = yield from self._control_vm("savestate")
self.status = "stopped"
log.debug("Stop result: {}".format(result))
elif self.on_close == "shutdown_signal":
# use ACPI to shutdown the VM # use ACPI to shutdown the VM
result = yield from self._control_vm("acpipowerbutton") result = yield from self._control_vm("acpipowerbutton")
trial = 0 trial = 0
@ -509,7 +514,7 @@ class VirtualBoxVM(BaseNode):
self.manager.port_manager.release_udp_port(udp_tunnel[1].lport, self._project) self.manager.port_manager.release_udp_port(udp_tunnel[1].lport, self._project)
self._local_udp_tunnels = {} self._local_udp_tunnels = {}
self.acpi_shutdown = False self.on_close = "power_off"
yield from self.stop() yield from self.stop()
if self.linked_clone: if self.linked_clone:
@ -564,28 +569,25 @@ class VirtualBoxVM(BaseNode):
self._headless = headless self._headless = headless
@property @property
def acpi_shutdown(self): def on_close(self):
""" """
Returns either the VM will use ACPI shutdown Returns the action to execute when the VM is stopped/closed
:returns: boolean :returns: string
""" """
return self._acpi_shutdown return self._on_close
@acpi_shutdown.setter @on_close.setter
def acpi_shutdown(self, acpi_shutdown): def on_close(self, on_close):
""" """
Sets either the VM will use ACPI shutdown Sets the action to execute when the VM is stopped/closed
:param acpi_shutdown: boolean :param on_close: string
""" """
if acpi_shutdown: log.info('VirtualBox VM "{name}" [{id}] set the close action to "{action}"'.format(name=self._name, id=self._id, action=on_close))
log.info("VirtualBox VM '{name}' [{id}] has enabled the ACPI shutdown mode".format(name=self.name, id=self.id)) self._on_close = on_close
else:
log.info("VirtualBox VM '{name}' [{id}] has disabled the ACPI shutdown mode".format(name=self.name, id=self.id))
self._acpi_shutdown = acpi_shutdown
@property @property
def ram(self): def ram(self):

View File

@ -58,7 +58,7 @@ class VMwareVM(BaseNode):
# VMware VM settings # VMware VM settings
self._headless = False self._headless = False
self._vmx_path = vmx_path self._vmx_path = vmx_path
self._acpi_shutdown = False self._on_close = "power_off"
self._adapters = 0 self._adapters = 0
self._ethernet_adapters = {} self._ethernet_adapters = {}
self._adapter_type = "e1000" self._adapter_type = "e1000"
@ -80,7 +80,7 @@ class VMwareVM(BaseNode):
"project_id": self.project.id, "project_id": self.project.id,
"vmx_path": self.vmx_path, "vmx_path": self.vmx_path,
"headless": self.headless, "headless": self.headless,
"acpi_shutdown": self.acpi_shutdown, "on_close": self.on_close,
"adapters": self._adapters, "adapters": self._adapters,
"adapter_type": self.adapter_type, "adapter_type": self.adapter_type,
"use_any_adapter": self.use_any_adapter, "use_any_adapter": self.use_any_adapter,
@ -482,7 +482,9 @@ class VMwareVM(BaseNode):
try: try:
if (yield from self.is_running()): if (yield from self.is_running()):
if self.acpi_shutdown: if self.on_close == "save_vm_state":
yield from self._control_vm("suspend")
elif self.on_close == "shutdown_signal":
# use ACPI to shutdown the VM # use ACPI to shutdown the VM
yield from self._control_vm("stop", "soft") yield from self._control_vm("stop", "soft")
else: else:
@ -563,7 +565,7 @@ class VMwareVM(BaseNode):
if nio and isinstance(nio, NIOUDP): if nio and isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project) self.manager.port_manager.release_udp_port(nio.lport, self._project)
try: try:
self.acpi_shutdown = False self.on_close = "power_off"
yield from self.stop() yield from self.stop()
except VMwareError: except VMwareError:
pass pass
@ -596,28 +598,25 @@ class VMwareVM(BaseNode):
self._headless = headless self._headless = headless
@property @property
def acpi_shutdown(self): def on_close(self):
""" """
Returns either the VM will use ACPI shutdown Returns the action to execute when the VM is stopped/closed
:returns: boolean :returns: string
""" """
return self._acpi_shutdown return self._on_close
@acpi_shutdown.setter @on_close.setter
def acpi_shutdown(self, acpi_shutdown): def on_close(self, on_close):
""" """
Sets either the VM will use ACPI shutdown Sets the action to execute when the VM is stopped/closed
:param acpi_shutdown: boolean :param on_close: string
""" """
if acpi_shutdown: log.info('VMware VM "{name}" [{id}] set the close action to "{action}"'.format(name=self._name, id=self._id, action=on_close))
log.info("VMware VM '{name}' [{id}] has enabled the ACPI shutdown mode".format(name=self.name, id=self.id)) self._on_close = on_close
else:
log.info("VMware VM '{name}' [{id}] has disabled the ACPI shutdown mode".format(name=self.name, id=self.id))
self._acpi_shutdown = acpi_shutdown
@property @property
def vmx_path(self): def vmx_path(self):

View File

@ -123,7 +123,7 @@ class Controller:
for vm in vms: for vm in vms:
# remove deprecated properties # remove deprecated properties
for prop in vm.copy(): for prop in vm.copy():
if prop in ["enable_remote_console", "use_ubridge"]: if prop in ["enable_remote_console", "use_ubridge", "acpi_shutdown"]:
del vm[prop] del vm[prop]
# remove deprecated default_symbol and hover_symbol # remove deprecated default_symbol and hover_symbol

View File

@ -37,7 +37,7 @@ import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
GNS3_FILE_FORMAT_REVISION = 8 GNS3_FILE_FORMAT_REVISION = 9
def _check_topology_schema(topo): def _check_topology_schema(topo):
@ -151,6 +151,10 @@ def load_topology(path):
if topo["revision"] < 8: if topo["revision"] < 8:
topo = _convert_2_0_0(topo, path) topo = _convert_2_0_0(topo, path)
# Version before GNS3 2.1
if topo["revision"] < 9:
topo = _convert_2_1_0(topo, path)
try: try:
_check_topology_schema(topo) _check_topology_schema(topo)
except aiohttp.web.HTTPConflict as e: except aiohttp.web.HTTPConflict as e:
@ -166,6 +170,25 @@ def load_topology(path):
return topo return topo
def _convert_2_1_0(topo, topo_path):
"""
Convert topologies from GNS3 2.1.x to 2.2
Changes:
* Removed acpi_shutdown option from Qemu, VMware and VirtualBox
"""
topo["revision"] = 9
for node in topo.get("topology", {}).get("nodes", []):
if "properties" in node:
if node["node_type"] in ("qemu", "vmware", "virtualbox"):
if "acpi_shutdown" in node["properties"]:
del node["properties"]["acpi_shutdown"]
if "save_vm_state" in node["properties"]:
del node["properties"]["save_vm_state"]
return topo
def _convert_2_0_0(topo, topo_path): def _convert_2_0_0(topo, topo_path):
""" """
Convert topologies from GNS3 2.0.0 to 2.1 Convert topologies from GNS3 2.0.0 to 2.1

View File

@ -184,13 +184,9 @@ QEMU_CREATE_SCHEMA = {
"description": "Use QEMU legagy networking commands (-net syntax)", "description": "Use QEMU legagy networking commands (-net syntax)",
"type": ["boolean", "null"], "type": ["boolean", "null"],
}, },
"acpi_shutdown": { "on_close": {
"description": "ACPI shutdown support", "description": "Action to execute on the VM is closed",
"type": ["boolean", "null"], "enum": ["power_off", "shutdown_signal", "save_vm_state"],
},
"save_vm_state": {
"description": "Save VM state support",
"type": ["boolean", "null"],
}, },
"cpu_throttling": { "cpu_throttling": {
"description": "Percentage of CPU allowed for QEMU", "description": "Percentage of CPU allowed for QEMU",
@ -373,13 +369,9 @@ QEMU_UPDATE_SCHEMA = {
"description": "Use QEMU legagy networking commands (-net syntax)", "description": "Use QEMU legagy networking commands (-net syntax)",
"type": ["boolean", "null"], "type": ["boolean", "null"],
}, },
"acpi_shutdown": { "on_close": {
"description": "ACPI shutdown support", "description": "Action to execute on the VM is closed",
"type": ["boolean", "null"], "enum": ["power_off", "shutdown_signal", "save_vm_state"],
},
"save_vm_state": {
"description": "Save VM state support",
"type": ["boolean", "null"],
}, },
"cpu_throttling": { "cpu_throttling": {
"description": "Percentage of CPU allowed for QEMU", "description": "Percentage of CPU allowed for QEMU",
@ -575,9 +567,9 @@ QEMU_OBJECT_SCHEMA = {
"description": "Use QEMU legagy networking commands (-net syntax)", "description": "Use QEMU legagy networking commands (-net syntax)",
"type": "boolean", "type": "boolean",
}, },
"acpi_shutdown": { "on_close": {
"description": "ACPI shutdown support", "description": "Action to execute on the VM is closed",
"type": "boolean", "enum": ["power_off", "shutdown_signal", "save_vm_state"],
}, },
"save_vm_state": { "save_vm_state": {
"description": "Save VM state support", "description": "Save VM state support",
@ -644,8 +636,7 @@ QEMU_OBJECT_SCHEMA = {
"kernel_image_md5sum", "kernel_image_md5sum",
"kernel_command_line", "kernel_command_line",
"legacy_networking", "legacy_networking",
"acpi_shutdown", "on_close",
"save_vm_state",
"cpu_throttling", "cpu_throttling",
"process_priority", "process_priority",
"options", "options",

View File

@ -80,10 +80,10 @@ VBOX_CREATE_SCHEMA = {
"description": "Headless mode", "description": "Headless mode",
"type": "boolean" "type": "boolean"
}, },
"acpi_shutdown": { "on_close": {
"description": "ACPI shutdown", "description": "Action to execute on the VM is closed",
"type": "boolean" "enum": ["power_off", "shutdown_signal", "save_vm_state"],
} },
}, },
"additionalProperties": False, "additionalProperties": False,
"required": ["name", "vmname"], "required": ["name", "vmname"],
@ -131,9 +131,9 @@ VBOX_OBJECT_SCHEMA = {
"description": "Headless mode", "description": "Headless mode",
"type": "boolean" "type": "boolean"
}, },
"acpi_shutdown": { "on_close": {
"description": "ACPI shutdown", "description": "Action to execute on the VM is closed",
"type": "boolean" "enum": ["power_off", "shutdown_signal", "save_vm_state"],
}, },
"adapters": { "adapters": {
"description": "Number of adapters", "description": "Number of adapters",

View File

@ -56,9 +56,9 @@ VMWARE_CREATE_SCHEMA = {
"description": "Headless mode", "description": "Headless mode",
"type": "boolean" "type": "boolean"
}, },
"acpi_shutdown": { "on_close": {
"description": "ACPI shutdown", "description": "Action to execute on the VM is closed",
"type": "boolean" "enum": ["power_off", "shutdown_signal", "save_vm_state"],
}, },
"adapters": { "adapters": {
"description": "Number of adapters", "description": "Number of adapters",
@ -122,9 +122,9 @@ VMWARE_OBJECT_SCHEMA = {
"description": "Headless mode", "description": "Headless mode",
"type": "boolean" "type": "boolean"
}, },
"acpi_shutdown": { "on_close": {
"description": "ACPI shutdown", "description": "Action to execute on the VM is closed",
"type": "boolean" "enum": ["power_off", "shutdown_signal", "save_vm_state"],
}, },
"adapters": { "adapters": {
"description": "Number of adapters", "description": "Number of adapters",