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

Suspend the GNS3 VM

Fix #656
This commit is contained in:
Julien Duponchelle 2016-09-08 15:32:35 +02:00
parent cfe834afc4
commit 1412462229
No known key found for this signature in database
GPG Key ID: CE8B29639E07F5E8
8 changed files with 114 additions and 42 deletions

View File

@ -88,7 +88,7 @@ class Controller:
# We don't care if a compute is down at this step # We don't care if a compute is down at this step
except aiohttp.errors.ClientOSError: except aiohttp.errors.ClientOSError:
pass pass
yield from self.gns3vm.auto_stop_vm() yield from self.gns3vm.exit_vm()
self._computes = {} self._computes = {}
self._projects = {} self._projects = {}
@ -204,10 +204,16 @@ class Controller:
for compute in self._computes.values(): for compute in self._computes.values():
if compute.host == vm_settings.get("remote_vm_host") and compute.port == vm_settings.get("remote_vm_port"): if compute.host == vm_settings.get("remote_vm_host") and compute.port == vm_settings.get("remote_vm_port"):
vmname = compute.name vmname = compute.name
if vm_settings.get("auto_stop", True):
when_exit = "stop"
else:
when_exit = "keep"
self.gns3vm.settings = { self.gns3vm.settings = {
"engine": engine, "engine": engine,
"enable": vm_settings.get("auto_start", False), "enable": vm_settings.get("auto_start", False),
"auto_stop": vm_settings.get("auto_stop", True), "when_exit": when_exit,
"headless": vm_settings.get("headless", False), "headless": vm_settings.get("headless", False),
"vmname": vmname "vmname": vmname
} }

View File

@ -40,7 +40,7 @@ class GNS3VM:
self._engines = {} self._engines = {}
self._settings = { self._settings = {
"vmname": None, "vmname": None,
"auto_stop": True, "when_exit": "stop",
"headless": False, "headless": False,
"enable": False, "enable": False,
"engine": "vmware" "engine": "vmware"
@ -54,7 +54,7 @@ class GNS3VM:
vmware_informations = { vmware_informations = {
"engine_id": "vmware", "engine_id": "vmware",
"description": "VMware is the recommended choice for best performances.", "description": "VMware is the recommended choice for best performances.",
"support_auto_stop": True, "support_when_exit": True,
"support_headless": True "support_headless": True
} }
if sys.platform.startswith("darwin"): if sys.platform.startswith("darwin"):
@ -66,7 +66,7 @@ class GNS3VM:
"engine_id": "virtualbox", "engine_id": "virtualbox",
"name": "VirtualBox", "name": "VirtualBox",
"description": "VirtualBox doesn't support nested virtualization, this means running Qemu based VM could be very slow.", "description": "VirtualBox doesn't support nested virtualization, this means running Qemu based VM could be very slow.",
"support_auto_stop": True, "support_when_exit": True,
"support_headless": True "support_headless": True
} }
@ -74,7 +74,7 @@ class GNS3VM:
"engine_id": "remote", "engine_id": "remote",
"name": "Remote", "name": "Remote",
"description": "Use a remote GNS3 server as the GNS3 VM.", "description": "Use a remote GNS3 server as the GNS3 VM.",
"support_auto_stop": False, "support_when_exit": False,
"support_headless": False "support_headless": False
} }
@ -149,11 +149,11 @@ class GNS3VM:
return self._settings.get("enable", False) return self._settings.get("enable", False)
@property @property
def auto_stop(self): def when_exit(self):
""" """
The GNSVM should auto stop What should be done when exit
""" """
return self._settings["auto_stop"] return self._settings["when_exit"]
@property @property
def settings(self): def settings(self):
@ -224,10 +224,13 @@ class GNS3VM:
force=True) force=True)
@asyncio.coroutine @asyncio.coroutine
def auto_stop_vm(self): def exit_vm(self):
if self.enable and self.auto_stop: if self.enable:
try: try:
if self._settings["when_exit"] == "stop":
yield from self._stop() yield from self._stop()
elif self._settings["when_exit"] == "suspend":
yield from self._suspend()
except GNS3VMError as e: except GNS3VMError as e:
log.warn(str(e)) log.warn(str(e))
@ -250,6 +253,18 @@ class GNS3VM:
password=self.password, password=self.password,
force=True) force=True)
@locked_coroutine
def _suspend(self):
"""
Suspend the GNS3 VM
"""
engine = self._current_engine()
if "vm" in self._controller.computes:
yield from self._controller.delete_compute("vm")
if engine.running:
log.info("Suspend the GNS3 VM")
yield from engine.suspend()
@locked_coroutine @locked_coroutine
def _stop(self): def _stop(self):
""" """

View File

@ -28,7 +28,6 @@ class BaseGNS3VM:
self._controller = controller self._controller = controller
self._vmname = None self._vmname = None
self._auto_stop = False
self._ip_address = None self._ip_address = None
self._port = 3080 self._port = 3080
self._headless = False self._headless = False
@ -245,26 +244,6 @@ class BaseGNS3VM:
self._ram = new_ram self._ram = new_ram
@property
def auto_stop(self):
"""
Returns whether the VM should automatically be stopped when GNS3 quit
:returns: boolean
"""
return self._auto_start
@auto_stop.setter
def auto_stop(self, new_auto_stop):
"""
Set whether the VM should automatically be stopped when GNS3 quit
:param new_auto_stop: boolean
"""
self._auto_stop = new_auto_stop
@property @property
def engine(self): def engine(self):
""" """
@ -291,6 +270,14 @@ class BaseGNS3VM:
raise NotImplementedError raise NotImplementedError
@asyncio.coroutine
def suspend(self):
"""
Suspend the GNS3 VM.
"""
raise NotImplementedError
@asyncio.coroutine @asyncio.coroutine
def stop(self, force=False): def stop(self, force=False):
""" """

View File

@ -63,6 +63,13 @@ class RemoteGNS3VM(BaseGNS3VM):
return return
raise GNS3VMError("Can't start the GNS3 VM remote VM {} not found".format(self.vmname)) raise GNS3VMError("Can't start the GNS3 VM remote VM {} not found".format(self.vmname))
@asyncio.coroutine
def suspend(self):
"""
Suspend do nothing for remote server
"""
self.running = False
@asyncio.coroutine @asyncio.coroutine
def stop(self): def stop(self):
""" """

View File

@ -180,7 +180,9 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
if self._headless: if self._headless:
args.extend(["--type", "headless"]) args.extend(["--type", "headless"])
yield from self._execute("startvm", args) yield from self._execute("startvm", args)
elif vm_state == "paused":
args = [self._vmname, "resume"]
yield from self._execute("controlvm", args)
ip_address = "127.0.0.1" ip_address = "127.0.0.1"
try: try:
# get a random port on localhost # get a random port on localhost
@ -221,7 +223,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
try: try:
resp = None resp = None
resp = yield from session.get('http://127.0.0.1:{}/v2/compute/network/interfaces'.format(api_port)) resp = yield from session.get('http://127.0.0.1:{}/v2/compute/network/interfaces'.format(api_port))
except OSError: except (OSError, aiohttp.errors.ClientHttpProcessingError):
pass pass
if resp: if resp:
@ -242,6 +244,16 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
yield from asyncio.sleep(1) yield from asyncio.sleep(1)
raise GNS3VMError("Could not get the GNS3 VM ip make sure the VM receive an IP from VirtualBox") raise GNS3VMError("Could not get the GNS3 VM ip make sure the VM receive an IP from VirtualBox")
@asyncio.coroutine
def suspend(self):
"""
Suspend the GNS3 VM.
"""
yield from self._execute("controlvm", [self._vmname, "savestate"], timeout=3)
log.info("GNS3 VM has been suspend")
self.running = False
@asyncio.coroutine @asyncio.coroutine
def stop(self): def stop(self):
""" """
@ -249,7 +261,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
""" """
yield from self._execute("controlvm", [self._vmname, "acpipowerbutton"], timeout=3) yield from self._execute("controlvm", [self._vmname, "acpipowerbutton"], timeout=3)
log.info("GNS3 VM hsd been stopped") log.info("GNS3 VM has been stopped")
self.running = False self.running = False
@asyncio.coroutine @asyncio.coroutine

View File

@ -126,6 +126,21 @@ class VMwareGNS3VM(BaseGNS3VM):
log.info("GNS3 VM IP address set to {}".format(guest_ip_address)) log.info("GNS3 VM IP address set to {}".format(guest_ip_address))
self.running = True self.running = True
@asyncio.coroutine
def suspend(self):
"""
Suspend the GNS3 VM.
"""
if self._vmx_path is None:
raise GNS3VMError("No VMX path configured, can't suspend the VM")
try:
yield from self._execute("suspend", [self._vmx_path])
except GNS3VMError as e:
log.warning("Error when suspending the VM: {}".format(str(e)))
log.info("GNS3 VM has been suspended")
self.running = False
@asyncio.coroutine @asyncio.coroutine
def stop(self): def stop(self):
""" """

View File

@ -29,9 +29,9 @@ GNS3VM_SETTINGS_SCHEMA = {
"type": "string", "type": "string",
"description": "The name of the VM" "description": "The name of the VM"
}, },
"auto_stop": { "when_exit": {
"type": "boolean", "description": "What to do with the VM when GNS3 exit",
"description": "The VM auto stop with GNS3" "enum": ["stop", "suspend", "keep"]
}, },
"headless": { "headless": {
"type": "boolean", "type": "boolean",

View File

@ -149,7 +149,7 @@ def test_import_gns3vm_1_x(controller, controller_config_path, async_run):
assert controller.gns3vm.settings["engine"] == "vmware" assert controller.gns3vm.settings["engine"] == "vmware"
assert controller.gns3vm.settings["enable"] assert controller.gns3vm.settings["enable"]
assert controller.gns3vm.settings["headless"] assert controller.gns3vm.settings["headless"]
assert controller.gns3vm.settings["auto_stop"] is False assert controller.gns3vm.settings["when_exit"] == "keep"
assert controller.gns3vm.settings["vmname"] == "GNS3 VM" assert controller.gns3vm.settings["vmname"] == "GNS3 VM"
@ -408,12 +408,12 @@ def test_stop(controller, async_run):
def test_stop_vm(controller, async_run): def test_stop_vm(controller, async_run):
""" """
Start the controller with a GNS3 VM running Stop GNS3 VM if configured
""" """
controller.gns3vm.settings = { controller.gns3vm.settings = {
"enable": True, "enable": True,
"engine": "vmware", "engine": "vmware",
"auto_stop": True "when_exit": "stop"
} }
controller.gns3vm._current_engine().running = True controller.gns3vm._current_engine().running = True
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.stop") as mock: with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.stop") as mock:
@ -421,6 +421,36 @@ def test_stop_vm(controller, async_run):
assert mock.called assert mock.called
def test_suspend_vm(controller, async_run):
"""
Suspend GNS3 VM if configured
"""
controller.gns3vm.settings = {
"enable": True,
"engine": "vmware",
"when_exit": "suspend"
}
controller.gns3vm._current_engine().running = True
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.suspend") as mock:
async_run(controller.stop())
assert mock.called
def test_keep_vm(controller, async_run):
"""
Keep GNS3 VM if configured
"""
controller.gns3vm.settings = {
"enable": True,
"engine": "vmware",
"when_exit": "keep"
}
controller.gns3vm._current_engine().running = True
with asyncio_patch("gns3server.controller.gns3vm.vmware_gns3_vm.VMwareGNS3VM.suspend") as mock:
async_run(controller.stop())
assert not mock.called
def test_get_free_project_name(controller, async_run): def test_get_free_project_name(controller, async_run):
async_run(controller.add_project(project_id=str(uuid.uuid4()), name="Test")) async_run(controller.add_project(project_id=str(uuid.uuid4()), name="Test"))