diff --git a/gns3server/modules/virtualbox/__init__.py b/gns3server/modules/virtualbox/__init__.py index 999fdaed..cfd8a6aa 100644 --- a/gns3server/modules/virtualbox/__init__.py +++ b/gns3server/modules/virtualbox/__init__.py @@ -417,7 +417,10 @@ class VirtualBox(IModule): try: vbox_instance.start() except VirtualBoxError as e: - self.send_custom_error(str(e)) + if self._vboxwrapper: + self.send_custom_error("{}: {}".format(e, self._vboxwrapper.read_stderr())) + else: + self.send_custom_error(str(e)) return self.send_response(True) diff --git a/gns3server/modules/virtualbox/schemas.py b/gns3server/modules/virtualbox/schemas.py index b86839a0..bc72cb9a 100644 --- a/gns3server/modules/virtualbox/schemas.py +++ b/gns3server/modules/virtualbox/schemas.py @@ -82,9 +82,15 @@ VBOX_UPDATE_SCHEMA = { "adapters": { "description": "number of adapters", "type": "integer", - "minimum": 0, + "minimum": 1, "maximum": 36, # maximum given by the ICH9 chipset in VirtualBox }, + "adapter_start_index": { + "description": "adapter index from which to start using adapters", + "type": "integer", + "minimum": 0, + "maximum": 35, # maximum given by the ICH9 chipset in VirtualBox + }, "adapter_type": { "description": "VirtualBox adapter type", "type": "string", @@ -96,6 +102,10 @@ VBOX_UPDATE_SCHEMA = { "maximum": 65535, "type": "integer" }, + "enable_console": { + "description": "enable the console", + "type": "boolean" + }, "headless": { "description": "headless mode", "type": "boolean" diff --git a/gns3server/modules/virtualbox/virtualbox_controller.py b/gns3server/modules/virtualbox/virtualbox_controller.py index 72cd8a96..9ed37953 100644 --- a/gns3server/modules/virtualbox/virtualbox_controller.py +++ b/gns3server/modules/virtualbox/virtualbox_controller.py @@ -54,6 +54,7 @@ class VirtualBoxController(object): self._console = 0 self._adapters = [] self._headless = False + self._enable_console = True self._adapter_type = "Automatic" try: @@ -101,6 +102,16 @@ class VirtualBoxController(object): self._headless = headless + @property + def enable_console(self): + + return self._enable_console + + @enable_console.setter + def enable_console(self, enable_console): + + self._enable_console = enable_console + @property def adapters(self): @@ -123,13 +134,17 @@ class VirtualBoxController(object): def start(self): + if len(self._adapters) > self._maximum_adapters: + raise VirtualBoxError("Number of adapters above the maximum supported of {}".format(self._maximum_adapters)) + if self._machine.state == self._vboxmanager.constants.MachineState_Paused: self.resume() return self._get_session() self._set_network_options() - self._set_console_options() + if self._enable_console: + self._set_console_options() progress = self._launch_vm_process() log.info("VM is starting with {}% completed".format(progress.percent)) @@ -145,25 +160,26 @@ class VirtualBoxController(object): except Exception: pass - # starts the Telnet to pipe thread - pipe_name = self._get_pipe_name() - if sys.platform.startswith('win'): - try: - self._serial_pipe = open(pipe_name, "a+b") - except OSError as e: - raise VirtualBoxError("Could not open the pipe {}: {}".format(pipe_name, e)) - self._serial_pipe_thread = PipeProxy(self._vmname, msvcrt.get_osfhandle(self._serial_pipe.fileno()), self._host, self._console) - #self._serial_pipe_thread.setDaemon(True) - self._serial_pipe_thread.start() - else: - try: - self._serial_pipe = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - self._serial_pipe.connect(pipe_name) - except OSError as e: - raise VirtualBoxError("Could not connect to the pipe {}: {}".format(pipe_name, e)) - self._serial_pipe_thread = PipeProxy(self._vmname, self._serial_pipe, self._host, self._console) - #self._serial_pipe_thread.setDaemon(True) - self._serial_pipe_thread.start() + if self._enable_console: + # starts the Telnet to pipe thread + pipe_name = self._get_pipe_name() + if sys.platform.startswith('win'): + try: + self._serial_pipe = open(pipe_name, "a+b") + except OSError as e: + raise VirtualBoxError("Could not open the pipe {}: {}".format(pipe_name, e)) + self._serial_pipe_thread = PipeProxy(self._vmname, msvcrt.get_osfhandle(self._serial_pipe.fileno()), self._host, self._console) + #self._serial_pipe_thread.setDaemon(True) + self._serial_pipe_thread.start() + else: + try: + self._serial_pipe = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self._serial_pipe.connect(pipe_name) + except OSError as e: + raise VirtualBoxError("Could not connect to the pipe {}: {}".format(pipe_name, e)) + self._serial_pipe_thread = PipeProxy(self._vmname, self._serial_pipe, self._host, self._console) + #self._serial_pipe_thread.setDaemon(True) + self._serial_pipe_thread.start() def stop(self): @@ -196,8 +212,14 @@ class VirtualBoxController(object): log.info("VM is stopping with {}% completed".format(self.vmname, progress.percent)) self._lock_machine() + for adapter_id in range(0, len(self._adapters)): + if self._adapters[adapter_id] is None: + continue self._disable_adapter(adapter_id, disable=True) + if self._enable_console: + serial_port = self._session.machine.getSerialPort(0) + serial_port.enabled = False self._session.machine.saveSettings() self._unlock_machine() except Exception as e: @@ -252,11 +274,17 @@ class VirtualBoxController(object): #raise VirtualBoxError("VirtualBox error: {}".format(e)) for adapter_id in range(0, len(self._adapters)): + try: # VirtualBox starts counting from 0 adapter = self._session.machine.getNetworkAdapter(adapter_id) - vbox_adapter_type = adapter.adapterType + if self._adapters[adapter_id] is None: + # force enable to avoid any discrepancy in the interface numbering inside the VM + # e.g. Ethernet2 in GNS3 becoming eth0 inside the VM when using a start index of 2. + adapter.enabled = True + continue + vbox_adapter_type = adapter.adapterType if self._adapter_type == "PCnet-PCI II (Am79C970A)": vbox_adapter_type = self._vboxmanager.constants.NetworkAdapterType_Am79C970A if self._adapter_type == "PCNet-FAST III (Am79C973)": @@ -310,9 +338,9 @@ class VirtualBoxController(object): except Exception as e: raise VirtualBoxError("VirtualBox error: {}".format(e)) - #for adapter_id in range(len(self._ethernet_adapters), self._maximum_adapters): - # log.debug("disabling remaining adapter {}".format(adapter_id)) - # self._disable_adapter(adapter_id) + for adapter_id in range(len(self._adapters), self._maximum_adapters): + log.debug("disabling remaining adapter {}".format(adapter_id)) + self._disable_adapter(adapter_id) try: self._session.machine.saveSettings() @@ -526,4 +554,4 @@ class VirtualBoxController(object): log.warn("cannot unlock the machine for {}, retrying {}: {}".format(self._vmname, retry + 1, e)) time.sleep(1) last_exception = e - continue \ No newline at end of file + continue diff --git a/gns3server/modules/virtualbox/virtualbox_vm.py b/gns3server/modules/virtualbox/virtualbox_vm.py index d05ea631..dee20427 100644 --- a/gns3server/modules/virtualbox/virtualbox_vm.py +++ b/gns3server/modules/virtualbox/virtualbox_vm.py @@ -91,7 +91,9 @@ class VirtualBoxVM(object): self._console = console self._ethernet_adapters = [] self._headless = False + self._enable_console = True self._vmname = vmname + self._adapter_start_index = 0 self._adapter_type = "Automatic" working_dir_path = os.path.join(working_dir, "vbox", "vm-{}".format(self._id)) @@ -137,9 +139,11 @@ class VirtualBoxVM(object): vbox_defaults = {"name": self._name, "vmname": self._vmname, - "adapters": len(self._ethernet_adapters), + "adapters": self.adapters, + "adapter_start_index": self._adapter_start_index, "adapter_type": "Automatic", "console": self._console, + "enable_console": self._enable_console, "headless": self._headless} return vbox_defaults @@ -329,6 +333,38 @@ class VirtualBoxVM(object): log.info("VirtualBox VM {name} [id={id}] has disabled the headless mode".format(name=self._name, id=self._id)) self._headless = headless + @property + def enable_console(self): + """ + Returns either the console is enabled or not + + :returns: boolean + """ + + return self._enable_console + + @enable_console.setter + def enable_console(self, enable_console): + """ + Sets either the console is enabled or not + + :param enable_console: boolean + """ + + if enable_console: + if self._vboxwrapper: + self._vboxwrapper.send('vbox setattr "{}" enable_console True'.format(self._name)) + else: + self._vboxcontroller.enable_console = True + log.info("VirtualBox VM {name} [id={id}] has enabled the console".format(name=self._name, id=self._id)) + else: + if self._vboxwrapper: + self._vboxwrapper.send('vbox setattr "{}" enable_console False'.format(self._name)) + else: + self._vboxcontroller.enable_console = False + log.info("VirtualBox VM {name} [id={id}] has disabled the console".format(name=self._name, id=self._id)) + self._enable_console = enable_console + @property def vmname(self): """ @@ -374,17 +410,47 @@ class VirtualBoxVM(object): """ self._ethernet_adapters.clear() - for _ in range(0, adapters): + for adapter_id in range(0, self._adapter_start_index + adapters): + if adapter_id < self._adapter_start_index: + self._ethernet_adapters.append(None) + continue self._ethernet_adapters.append(EthernetAdapter()) if self._vboxwrapper: - self._vboxwrapper.send('vbox setattr "{}" nics {}'.format(self._name, len(self._ethernet_adapters))) + self._vboxwrapper.send('vbox setattr "{}" nics {}'.format(self._name, adapters)) else: self._vboxcontroller.adapters = self._ethernet_adapters log.info("VirtualBox VM {name} [id={id}]: number of Ethernet adapters changed to {adapters}".format(name=self._name, id=self._id, - adapters=len(self._ethernet_adapters))) + adapters=adapters)) + + @property + def adapter_start_index(self): + """ + Returns the adapter start index for this VirtualBox VM instance. + + :returns: index + """ + + return self._adapter_start_index + + @adapter_start_index.setter + def adapter_start_index(self, adapter_start_index): + """ + Sets the adapter start index for this VirtualBox VM instance. + + :param adapter_start_index: index + """ + + if self._vboxwrapper: + self._vboxwrapper.send('vbox setattr "{}" nic_start_index {}'.format(self._name, adapter_start_index)) + + self._adapter_start_index = adapter_start_index + self.adapters = self.adapters # this forces to recreate the adapter list with the correct index + log.info("VirtualBox VM {name} [id={id}]: adapter start index changed to {index}".format(name=self._name, + id=self._id, + index=adapter_start_index)) @property def adapter_type(self):