mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-28 03:08:14 +00:00
Fix VBoxManage fails if VM has specific special characters in name. Fixes #2739
This commit is contained in:
parent
e00bde51da
commit
dd211bce52
@ -20,6 +20,7 @@ VirtualBox server module.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
import asyncio
|
import asyncio
|
||||||
@ -177,14 +178,17 @@ class VirtualBox(BaseManager):
|
|||||||
for line in result:
|
for line in result:
|
||||||
if len(line) == 0 or line[0] != '"' or line[-1:] != "}":
|
if len(line) == 0 or line[0] != '"' or line[-1:] != "}":
|
||||||
continue # Broken output (perhaps a carriage return in VM name)
|
continue # Broken output (perhaps a carriage return in VM name)
|
||||||
vmname, _ = line.rsplit(' ', 1)
|
match = re.search(r"\"(.*)\"\ {(.*)}", line)
|
||||||
vmname = vmname.strip('"')
|
if not match:
|
||||||
|
continue
|
||||||
|
vmname = match.group(1)
|
||||||
|
uuid = match.group(2)
|
||||||
if vmname == "<inaccessible>":
|
if vmname == "<inaccessible>":
|
||||||
continue # ignore inaccessible VMs
|
continue # ignore inaccessible VMs
|
||||||
extra_data = await self.execute("getextradata", [vmname, "GNS3/Clone"])
|
extra_data = await self.execute("getextradata", [uuid, "GNS3/Clone"])
|
||||||
if allow_clone or len(extra_data) == 0 or not extra_data[0].strip() == "Value: yes":
|
if allow_clone or len(extra_data) == 0 or not extra_data[0].strip() == "Value: yes":
|
||||||
# get the amount of RAM
|
# get the amount of RAM
|
||||||
info_results = await self.execute("showvminfo", [vmname, "--machinereadable"])
|
info_results = await self.execute("showvminfo", [uuid, "--machinereadable"])
|
||||||
ram = 0
|
ram = 0
|
||||||
for info in info_results:
|
for info in info_results:
|
||||||
try:
|
try:
|
||||||
|
@ -57,6 +57,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
|
|
||||||
super().__init__(name, node_id, project, manager, console=console, linked_clone=linked_clone, console_type=console_type)
|
super().__init__(name, node_id, project, manager, console=console, linked_clone=linked_clone, console_type=console_type)
|
||||||
|
|
||||||
|
self._uuid = None # UUID in VirtualBox
|
||||||
self._maximum_adapters = 8
|
self._maximum_adapters = 8
|
||||||
self._system_properties = {}
|
self._system_properties = {}
|
||||||
self._telnet_server = None
|
self._telnet_server = None
|
||||||
@ -116,7 +117,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
:returns: state (string)
|
:returns: state (string)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
results = await self.manager.execute("showvminfo", [self._vmname, "--machinereadable"])
|
results = await self.manager.execute("showvminfo", [self._uuid, "--machinereadable"])
|
||||||
for info in results:
|
for info in results:
|
||||||
if '=' in info:
|
if '=' in info:
|
||||||
name, value = info.split('=', 1)
|
name, value = info.split('=', 1)
|
||||||
@ -134,7 +135,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
args = shlex.split(params)
|
args = shlex.split(params)
|
||||||
result = await self.manager.execute("controlvm", [self._vmname] + args)
|
result = await self.manager.execute("controlvm", [self._uuid] + args)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def _modify_vm(self, params):
|
async def _modify_vm(self, params):
|
||||||
@ -145,7 +146,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
args = shlex.split(params)
|
args = shlex.split(params)
|
||||||
await self.manager.execute("modifyvm", [self._vmname] + args)
|
await self.manager.execute("modifyvm", [self._uuid] + args)
|
||||||
|
|
||||||
async def _check_duplicate_linked_clone(self):
|
async def _check_duplicate_linked_clone(self):
|
||||||
"""
|
"""
|
||||||
@ -174,6 +175,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
async def create(self):
|
async def create(self):
|
||||||
|
|
||||||
if not self.linked_clone:
|
if not self.linked_clone:
|
||||||
await self._check_duplicate_linked_clone()
|
await self._check_duplicate_linked_clone()
|
||||||
|
|
||||||
@ -184,21 +186,29 @@ class VirtualBoxVM(BaseNode):
|
|||||||
raise VirtualBoxError("The VirtualBox API version is lower than 4.3")
|
raise VirtualBoxError("The VirtualBox API version is lower than 4.3")
|
||||||
log.info("VirtualBox VM '{name}' [{id}] created".format(name=self.name, id=self.id))
|
log.info("VirtualBox VM '{name}' [{id}] created".format(name=self.name, id=self.id))
|
||||||
|
|
||||||
|
vm_info = await self._get_vm_info()
|
||||||
|
if "memory" in vm_info:
|
||||||
|
self._ram = int(vm_info["memory"])
|
||||||
|
if "UUID" in vm_info:
|
||||||
|
self._uuid = vm_info["UUID"]
|
||||||
|
if not self._uuid:
|
||||||
|
raise VirtualBoxError("Could not find any UUID for VM '{}'".format(self._vmname))
|
||||||
|
|
||||||
if self.linked_clone:
|
if self.linked_clone:
|
||||||
if self.id and os.path.isdir(os.path.join(self.working_dir, self._vmname)):
|
if self.id and os.path.isdir(os.path.join(self.working_dir, self._vmname)):
|
||||||
self._patch_vm_uuid()
|
self._patch_vm_uuid()
|
||||||
await self.manager.execute("registervm", [self._linked_vbox_file()])
|
await self.manager.execute("registervm", [self._linked_vbox_file()])
|
||||||
await self._reattach_linked_hdds()
|
await self._reattach_linked_hdds()
|
||||||
|
vm_info = await self._get_vm_info()
|
||||||
|
self._uuid = vm_info.get("UUID")
|
||||||
|
if not self._uuid:
|
||||||
|
raise VirtualBoxError("Could not find any UUID for VM '{}'".format(self._vmname))
|
||||||
else:
|
else:
|
||||||
await self._create_linked_clone()
|
await self._create_linked_clone()
|
||||||
|
|
||||||
if self._adapters:
|
if self._adapters:
|
||||||
await self.set_adapters(self._adapters)
|
await self.set_adapters(self._adapters)
|
||||||
|
|
||||||
vm_info = await self._get_vm_info()
|
|
||||||
if "memory" in vm_info:
|
|
||||||
self._ram = int(vm_info["memory"])
|
|
||||||
|
|
||||||
def _linked_vbox_file(self):
|
def _linked_vbox_file(self):
|
||||||
return os.path.join(self.working_dir, self._vmname, self._vmname + ".vbox")
|
return os.path.join(self.working_dir, self._vmname, self._vmname + ".vbox")
|
||||||
|
|
||||||
@ -266,7 +276,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
# check if there is enough RAM to run
|
# check if there is enough RAM to run
|
||||||
self.check_available_ram(self.ram)
|
self.check_available_ram(self.ram)
|
||||||
|
|
||||||
args = [self._vmname]
|
args = [self._uuid]
|
||||||
if self._headless:
|
if self._headless:
|
||||||
args.extend(["--type", "headless"])
|
args.extend(["--type", "headless"])
|
||||||
result = await self.manager.execute("startvm", args)
|
result = await self.manager.execute("startvm", args)
|
||||||
@ -275,9 +285,9 @@ class VirtualBoxVM(BaseNode):
|
|||||||
log.debug("Start result: {}".format(result))
|
log.debug("Start result: {}".format(result))
|
||||||
|
|
||||||
# add a guest property to let the VM know about the GNS3 name
|
# add a guest property to let the VM know about the GNS3 name
|
||||||
await self.manager.execute("guestproperty", ["set", self._vmname, "NameInGNS3", self.name])
|
await self.manager.execute("guestproperty", ["set", self._uuid, "NameInGNS3", self.name])
|
||||||
# add a guest property to let the VM know about the GNS3 project directory
|
# add a guest property to let the VM know about the GNS3 project directory
|
||||||
await self.manager.execute("guestproperty", ["set", self._vmname, "ProjectDirInGNS3", self.working_dir])
|
await self.manager.execute("guestproperty", ["set", self._uuid, "ProjectDirInGNS3", self.working_dir])
|
||||||
|
|
||||||
await self._start_ubridge()
|
await self._start_ubridge()
|
||||||
for adapter_number in range(0, self._adapters):
|
for adapter_number in range(0, self._adapters):
|
||||||
@ -739,7 +749,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
vm_info = {}
|
vm_info = {}
|
||||||
results = await self.manager.execute("showvminfo", [self._vmname, "--machinereadable"])
|
results = await self.manager.execute("showvminfo", ["--machinereadable", "--", self._vmname]) # "--" is to protect against vm names containing the "-" character
|
||||||
for info in results:
|
for info in results:
|
||||||
try:
|
try:
|
||||||
name, value = info.split('=', 1)
|
name, value = info.split('=', 1)
|
||||||
@ -775,7 +785,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
|
|
||||||
# set server mode with a pipe on the first serial port
|
# set server mode with a pipe on the first serial port
|
||||||
pipe_name = self._get_pipe_name()
|
pipe_name = self._get_pipe_name()
|
||||||
args = [self._vmname, "--uartmode1", "server", pipe_name]
|
args = [self._uuid, "--uartmode1", "server", pipe_name]
|
||||||
await self.manager.execute("modifyvm", args)
|
await self.manager.execute("modifyvm", args)
|
||||||
|
|
||||||
async def _storage_attach(self, params):
|
async def _storage_attach(self, params):
|
||||||
@ -786,7 +796,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
args = shlex.split(params)
|
args = shlex.split(params)
|
||||||
await self.manager.execute("storageattach", [self._vmname] + args)
|
await self.manager.execute("storageattach", [self._uuid] + args)
|
||||||
|
|
||||||
async def _get_nic_attachements(self, maximum_adapters):
|
async def _get_nic_attachements(self, maximum_adapters):
|
||||||
"""
|
"""
|
||||||
@ -850,7 +860,7 @@ class VirtualBoxVM(BaseNode):
|
|||||||
vbox_adapter_type = "82545EM"
|
vbox_adapter_type = "82545EM"
|
||||||
if adapter_type == "Paravirtualized Network (virtio-net)":
|
if adapter_type == "Paravirtualized Network (virtio-net)":
|
||||||
vbox_adapter_type = "virtio"
|
vbox_adapter_type = "virtio"
|
||||||
args = [self._vmname, "--nictype{}".format(adapter_number + 1), vbox_adapter_type]
|
args = [self._uuid, "--nictype{}".format(adapter_number + 1), vbox_adapter_type]
|
||||||
await self.manager.execute("modifyvm", args)
|
await self.manager.execute("modifyvm", args)
|
||||||
|
|
||||||
if isinstance(nio, NIOUDP):
|
if isinstance(nio, NIOUDP):
|
||||||
@ -888,10 +898,10 @@ class VirtualBoxVM(BaseNode):
|
|||||||
gns3_snapshot_exists = True
|
gns3_snapshot_exists = True
|
||||||
|
|
||||||
if not gns3_snapshot_exists:
|
if not gns3_snapshot_exists:
|
||||||
result = await self.manager.execute("snapshot", [self._vmname, "take", "GNS3 Linked Base for clones"])
|
result = await self.manager.execute("snapshot", [self._uuid, "take", "GNS3 Linked Base for clones"])
|
||||||
log.debug("GNS3 snapshot created: {}".format(result))
|
log.debug("GNS3 snapshot created: {}".format(result))
|
||||||
|
|
||||||
args = [self._vmname,
|
args = [self._uuid,
|
||||||
"--snapshot",
|
"--snapshot",
|
||||||
"GNS3 Linked Base for clones",
|
"GNS3 Linked Base for clones",
|
||||||
"--options",
|
"--options",
|
||||||
@ -906,12 +916,12 @@ class VirtualBoxVM(BaseNode):
|
|||||||
log.debug("VirtualBox VM: {} cloned".format(result))
|
log.debug("VirtualBox VM: {} cloned".format(result))
|
||||||
|
|
||||||
self._vmname = self._name
|
self._vmname = self._name
|
||||||
await self.manager.execute("setextradata", [self._vmname, "GNS3/Clone", "yes"])
|
await self.manager.execute("setextradata", [self._uuid, "GNS3/Clone", "yes"])
|
||||||
|
|
||||||
# We create a reset snapshot in order to simplify life of user who want to rollback their VM
|
# We create a reset snapshot in order to simplify life of user who want to rollback their VM
|
||||||
# Warning: Do not document this it's seem buggy we keep it because Raizo students use it.
|
# Warning: Do not document this it's seem buggy we keep it because Raizo students use it.
|
||||||
try:
|
try:
|
||||||
args = [self._vmname, "take", "reset"]
|
args = [self._uuid, "take", "reset"]
|
||||||
result = await self.manager.execute("snapshot", args)
|
result = await self.manager.execute("snapshot", args)
|
||||||
log.debug("Snapshot 'reset' created: {}".format(result))
|
log.debug("Snapshot 'reset' created: {}".format(result))
|
||||||
# It seem sometimes this failed due to internal race condition of Vbox
|
# It seem sometimes this failed due to internal race condition of Vbox
|
||||||
|
@ -74,7 +74,7 @@ def test_vboxmanage_path(manager, tmpdir):
|
|||||||
def test_list_vms(manager, loop):
|
def test_list_vms(manager, loop):
|
||||||
vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
||||||
'"Carriage',
|
'"Carriage',
|
||||||
'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c3f3}',
|
||||||
'',
|
'',
|
||||||
'"<inaccessible>" {42b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
'"<inaccessible>" {42b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
|
||||||
'"Linux Microcore 4.7.1" {ccd8c50b-c172-457d-99fa-dd69371ede0e}']
|
'"Linux Microcore 4.7.1" {ccd8c50b-c172-457d-99fa-dd69371ede0e}']
|
||||||
@ -83,9 +83,10 @@ def test_list_vms(manager, loop):
|
|||||||
if cmd == "list":
|
if cmd == "list":
|
||||||
return vm_list
|
return vm_list
|
||||||
else:
|
else:
|
||||||
if args[0] == "Windows 8.1":
|
print(args)
|
||||||
|
if args[0] == "27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1":
|
||||||
return ["memory=512"]
|
return ["memory=512"]
|
||||||
elif args[0] == "Linux Microcore 4.7.1":
|
elif args[0] == "ccd8c50b-c172-457d-99fa-dd69371ede0e":
|
||||||
return ["memory=256"]
|
return ["memory=256"]
|
||||||
assert False, "Unknow {} {}".format(cmd, args)
|
assert False, "Unknow {} {}".format(cmd, args)
|
||||||
|
|
||||||
|
@ -75,6 +75,7 @@ def test_rename_vmname(project, manager, async_run):
|
|||||||
def test_vm_valid_virtualbox_api_version(loop, project, manager):
|
def test_vm_valid_virtualbox_api_version(loop, project, manager):
|
||||||
with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.execute", return_value=["API version: 4_3"]):
|
with asyncio_patch("gns3server.compute.virtualbox.VirtualBox.execute", return_value=["API version: 4_3"]):
|
||||||
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False)
|
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False)
|
||||||
|
vm._uuid = "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||||
loop.run_until_complete(asyncio.ensure_future(vm.create()))
|
loop.run_until_complete(asyncio.ensure_future(vm.create()))
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user