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

Some spring cleaning.

This commit is contained in:
grossmj 2015-04-08 11:17:34 -06:00
parent 1c4202187a
commit 7d7972afb3
62 changed files with 459 additions and 363 deletions

View File

@ -25,7 +25,8 @@ class EthernetAdapter(Adapter):
"""
def __init__(self, interfaces=1):
Adapter.__init__(self, interfaces=interfaces)
super().__init__(interfaces)
def __str__(self):

View File

@ -25,7 +25,8 @@ class SerialAdapter(Adapter):
"""
def __init__(self, interfaces=1):
Adapter.__init__(self, interfaces=interfaces)
super().__init__(interfaces)
def __str__(self):

View File

@ -40,8 +40,8 @@ from .nios.nio_generic_ethernet import NIOGenericEthernet
class BaseManager:
"""
Base class for all Manager.
Responsible of management of a VM pool
Base class for all Manager classes.
Responsible of management of a VM pool of the same type.
"""
_convert_lock = None

View File

@ -54,19 +54,17 @@ class BaseVM:
else:
self._console = self._manager.port_manager.get_free_tcp_port(self._project)
log.debug("{module}: {name} [{id}] initialized. Console port {console}".format(
module=self.manager.module_name,
name=self.name,
id=self.id,
console=self._console
))
log.debug("{module}: {name} [{id}] initialized. Console port {console}".format(module=self.manager.module_name,
name=self.name,
id=self.id,
console=self._console))
def __del__(self):
self.close()
if self._temporary_directory is not None:
if os.path.exists(self._temporary_directory):
shutil.rmtree(self._temporary_directory)
shutil.rmtree(self._temporary_directory, ignore_errors=True)
@property
def project(self):
@ -195,7 +193,7 @@ class BaseVM:
@console.setter
def console(self, console):
"""
Change console port
Changes the console port
:params console: Console port (integer)
"""
@ -205,8 +203,7 @@ class BaseVM:
if self._console:
self._manager.port_manager.release_tcp_port(self._console, self._project)
self._console = self._manager.port_manager.reserve_tcp_port(console, self._project)
log.info("{module}: '{name}' [{id}]: console port set to {port}".format(
module=self.manager.module_name,
name=self.name,
id=self.id,
port=console))
log.info("{module}: '{name}' [{id}]: console port set to {port}".format(module=self.manager.module_name,
name=self.name,
id=self.id,
port=console))

View File

@ -25,7 +25,8 @@ class C1700_MB_1FE(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=1, wics=2)
super().__init__(interfaces=1, wics=2)
def __str__(self):

View File

@ -26,7 +26,8 @@ class C1700_MB_WIC1(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=0, wics=2)
super().__init__(interfaces=0, wics=2)
def __str__(self):

View File

@ -25,7 +25,8 @@ class C2600_MB_1E(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=1, wics=3)
super().__init__(interfaces=1, wics=3)
def __str__(self):

View File

@ -25,7 +25,8 @@ class C2600_MB_1FE(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=1, wics=3)
super().__init__(interfaces=1, wics=3)
self._interfaces = 1
def __str__(self):

View File

@ -25,7 +25,8 @@ class C2600_MB_2E(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=2, wics=3)
super().__init__(interfaces=2, wics=3)
def __str__(self):

View File

@ -25,7 +25,8 @@ class C2600_MB_2FE(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=2, wics=3)
super().__init__(interfaces=2, wics=3)
def __str__(self):

View File

@ -25,7 +25,8 @@ class C7200_IO_2FE(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=2)
super().__init__(interfaces=2)
def __str__(self):

View File

@ -25,7 +25,8 @@ class C7200_IO_FE(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=1)
super().__init__(interfaces=1)
def __str__(self):

View File

@ -25,7 +25,8 @@ class C7200_IO_GE_E(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=1)
super().__init__(interfaces=1)
def __str__(self):

View File

@ -21,7 +21,8 @@ from .adapter import Adapter
class GT96100_FE(Adapter):
def __init__(self):
Adapter.__init__(self, interfaces=2, wics=3)
super().__init__(interfaces=2, wics=3)
def __str__(self):

View File

@ -25,7 +25,8 @@ class Leopard_2FE(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=2)
super().__init__(interfaces=2)
self._interfaces = 2
def __str__(self):

View File

@ -25,7 +25,8 @@ class NM_16ESW(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=16)
super().__init__(interfaces=16)
def __str__(self):

View File

@ -25,7 +25,8 @@ class NM_1E(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=1)
super().__init__(interfaces=1)
def __str__(self):

View File

@ -25,7 +25,8 @@ class NM_1FE_TX(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=1)
super().__init__(interfaces=1)
def __str__(self):

View File

@ -25,7 +25,8 @@ class NM_4E(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=4)
super().__init__(interfaces=4)
def __str__(self):

View File

@ -25,7 +25,8 @@ class NM_4T(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=4)
super().__init__(interfaces=4)
def __str__(self):

View File

@ -25,7 +25,8 @@ class PA_2FE_TX(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=2)
super().__init__(interfaces=2)
def __str__(self):

View File

@ -25,7 +25,8 @@ class PA_4E(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=4)
super().__init__(interfaces=4)
def __str__(self):

View File

@ -25,7 +25,8 @@ class PA_4T(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=4)
super().__init__(interfaces=4)
def __str__(self):

View File

@ -25,7 +25,8 @@ class PA_8E(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=8)
super().__init__(interfaces=8)
def __str__(self):

View File

@ -25,7 +25,8 @@ class PA_8T(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=8)
super().__init__(interfaces=8)
def __str__(self):

View File

@ -25,7 +25,8 @@ class PA_A1(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=1)
super().__init__(interfaces=1)
def __str__(self):

View File

@ -25,7 +25,8 @@ class PA_FE_TX(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=1)
super().__init__(interfaces=1)
def __str__(self):

View File

@ -25,7 +25,8 @@ class PA_GE(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=1)
super().__init__(interfaces=1)
def __str__(self):

View File

@ -25,7 +25,8 @@ class PA_POS_OC3(Adapter):
"""
def __init__(self):
Adapter.__init__(self, interfaces=1)
super().__init__(interfaces=1)
def __str__(self):

View File

@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Custom exceptions for Dynamips module.
Custom exceptions for the Dynamips module.
"""
from ..vm_error import VMError

View File

@ -47,7 +47,7 @@ class Hypervisor(DynamipsHypervisor):
def __init__(self, path, working_dir, host, port, console_host):
DynamipsHypervisor.__init__(self, working_dir, host, port)
super().__init__(working_dir, host, port)
# create an unique ID
self._id = Hypervisor._instance_count

View File

@ -42,7 +42,7 @@ class NIOFIFO(NIO):
nio_id = NIOFIFO._instance_count
NIOFIFO._instance_count += 1
name = 'nio_fifo' + str(nio_id)
NIO.__init__(name, self, hypervisor)
super().__init__(name, hypervisor)
@classmethod
def reset(cls):

View File

@ -44,7 +44,7 @@ class NIOGenericEthernet(NIO):
NIOGenericEthernet._instance_count += 1
name = 'nio_gen_eth' + str(nio_id)
self._ethernet_device = ethernet_device
NIO.__init__(self, name, hypervisor)
super().__init__(name, hypervisor)
@classmethod
def reset(cls):

View File

@ -44,7 +44,7 @@ class NIOLinuxEthernet(NIO):
NIOLinuxEthernet._instance_count += 1
name = 'nio_linux_eth' + str(nio_id)
self._ethernet_device = ethernet_device
NIO.__init__(self, name, hypervisor)
super().__init__(name, hypervisor)
@classmethod
def reset(cls):

View File

@ -47,7 +47,7 @@ class NIOMcast(NIO):
self._group = group
self._port = port
self._ttl = 1 # default TTL
NIO.__init__(self, name, hypervisor)
super().__init__(name, hypervisor)
@classmethod
def reset(cls):

View File

@ -42,7 +42,7 @@ class NIONull(NIO):
nio_id = NIONull._instance_count
NIONull._instance_count += 1
name = 'nio_null' + str(nio_id)
NIO.__init__(self, name, hypervisor)
super().__init__(name, hypervisor)
@classmethod
def reset(cls):

View File

@ -44,7 +44,7 @@ class NIOTAP(NIO):
NIOTAP._instance_count += 1
name = 'nio_tap' + str(nio_id)
self._tap_device = tap_device
NIO.__init__(self, name, hypervisor)
super().__init__(name, hypervisor)
@classmethod
def reset(cls):

View File

@ -48,7 +48,7 @@ class NIOUDP(NIO):
self._lport = lport
self._rhost = rhost
self._rport = rport
NIO.__init__(self, name, hypervisor)
super().__init__(name, hypervisor)
@classmethod
def reset(cls):

View File

@ -46,7 +46,7 @@ class NIOUNIX(NIO):
name = 'nio_unix' + str(nio_id)
self._local_file = local_file
self._remote_file = remote_file
NIO.__init__(self, name, hypervisor)
super().__init__(name, hypervisor)
@classmethod
def reset(cls):

View File

@ -46,7 +46,7 @@ class NIOVDE(NIO):
name = 'nio_vde' + str(nio_id)
self._control_file = control_file
self._local_file = local_file
NIO.__init__(self, name, hypervisor)
super().__init__(name, hypervisor)
@classmethod
def reset(cls):

View File

@ -47,7 +47,8 @@ class C1700(Router):
"""
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, chassis="1720"):
Router.__init__(self, name, vm_id, project, manager, dynamips_id, console, aux, platform="c1700")
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c1700")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 64

View File

@ -62,7 +62,8 @@ class C2600(Router):
"2651XM": C2600_MB_2FE}
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, chassis="2610"):
Router.__init__(self, name, vm_id, project, manager, dynamips_id, console, aux, platform="c2600")
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c2600")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 64

View File

@ -44,7 +44,8 @@ class C2691(Router):
"""
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, chassis=None):
Router.__init__(self, name, vm_id, project, manager, dynamips_id, console, aux, platform="c2691")
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c2691")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 128

View File

@ -45,7 +45,8 @@ class C3600(Router):
"""
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, chassis="3640"):
Router.__init__(self, name, vm_id, project, manager, dynamips_id, console, aux, platform="c3600")
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c3600")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 128

View File

@ -44,7 +44,8 @@ class C3725(Router):
"""
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, chassis=None):
Router.__init__(self, name, vm_id, project, manager, dynamips_id, console, aux, platform="c3725")
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c3725")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 128

View File

@ -44,7 +44,8 @@ class C3745(Router):
"""
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, chassis=None):
Router.__init__(self, name, vm_id, project, manager, dynamips_id, console, aux, platform="c3745")
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c3745")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 128

View File

@ -47,7 +47,8 @@ class C7200(Router):
"""
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, npe="npe-400", chassis=None):
Router.__init__(self, name, vm_id, project, manager, dynamips_id, console, aux, platform="c7200")
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c7200")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 256

View File

@ -43,7 +43,7 @@ class EthernetHub(Bridge):
def __init__(self, name, device_id, project, manager, hypervisor=None):
Bridge.__init__(self, name, device_id, project, manager, hypervisor)
super().__init__(name, device_id, project, manager, hypervisor)
self._mappings = {}
def __json__(self):

View File

@ -28,25 +28,37 @@ from .iou_vm import IOUVM
class IOU(BaseManager):
_VM_CLASS = IOUVM
def __init__(self):
super().__init__()
self._free_application_ids = list(range(1, 512))
self._used_application_ids = {}
@asyncio.coroutine
def create_vm(self, *args, **kwargs):
"""
Creates a new IOU VM.
:returns: IOUVM instance
"""
vm = yield from super().create_vm(*args, **kwargs)
try:
self._used_application_ids[vm.id] = self._free_application_ids.pop(0)
except IndexError:
raise IOUError("No mac address available")
raise IOUError("Cannot create a new IOU VM (limit of 512 VMs reached on this host)")
return vm
@asyncio.coroutine
def close_vm(self, vm_id, *args, **kwargs):
"""
Closes an IOU VM.
:returns: IOUVM instance
"""
vm = self.get_vm(vm_id)
if vm_id in self._used_application_ids:
@ -58,10 +70,11 @@ class IOU(BaseManager):
def get_application_id(self, vm_id):
"""
Get an unique IOU mac id
Get an unique application identifier for IOU.
:param vm_id: ID of the IOU VM
:returns: IOU MAC id
:param vm_id: IOU VM identifier
:returns: IOU application identifier
"""
return self._used_application_ids.get(vm_id, 1)

View File

@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Custom exceptions for IOU module.
Custom exceptions for the IOU module.
"""
from ..vm_error import VMError

View File

@ -17,7 +17,7 @@
"""
IOU VM management (creates command line, processes, files etc.) in
order to run an IOU instance.
order to run an IOU VM.
"""
import os
@ -54,20 +54,20 @@ class IOUVM(BaseVM):
module_name = 'iou'
"""
IOU vm implementation.
IOU VM implementation.
:param name: name of this IOU vm
:param vm_id: IOU instance identifier
:param name: IOU VM name
:param vm_id: IOU VM identifier
:param project: Project instance
:param manager: parent VM Manager
:param manager: Manager instance
:param console: TCP console port
:params ethernet_adapters: Number of ethernet adapters
:params serial_adapters: Number of serial adapters
:params ram: Ram MB
:params nvram: Nvram KB
:params l1_keepalives: Always up ethernet interface:
:params initial_config: Content of the initial configuration file
:params iourc_content: Content of the iourc file if no licence is installed on server
:params ethernet_adapters: number of ethernet adapters
:params serial_adapters: number of serial adapters
:params ram: amount of RAM in MB
:params nvram: amount of NVRAM in KB
:params l1_keepalives: always keep the Ethernet interfaces up
:params initial_config: content of the initial configuration file
:params iourc_content: content of the iourc file if no licence is installed on the machine
"""
def __init__(self, name, vm_id, project, manager,
@ -108,6 +108,9 @@ class IOUVM(BaseVM):
@asyncio.coroutine
def close(self):
"""
Closes this IOU VM.
"""
log.debug('IOU "{name}" [{id}] is closing'.format(name=self._name, id=self._id))
@ -126,16 +129,20 @@ class IOUVM(BaseVM):
@property
def path(self):
"""Path of the iou binary"""
"""
Path of the IOU executable.
:returns: path to the IOU image executable
"""
return self._path
@path.setter
def path(self, path):
"""
Path of the iou binary
Path of the IOU executable.
:params path: Path to the binary
:param path: path to the IOU image executable
"""
if not os.path.isabs(path):
@ -171,6 +178,7 @@ class IOUVM(BaseVM):
def use_default_iou_values(self):
"""
Returns if this device uses the default IOU image values.
:returns: boolean
"""
@ -180,28 +188,30 @@ class IOUVM(BaseVM):
def use_default_iou_values(self, state):
"""
Sets if this device uses the default IOU image values.
:param state: boolean
"""
self._use_default_iou_values = state
if state:
log.info("IOU {name} [id={id}]: uses the default IOU image values".format(name=self._name, id=self._id))
log.info('IOU "{name}" [{id}]: uses the default IOU image values'.format(name=self._name, id=self._id))
else:
log.info("IOU {name} [id={id}]: does not use the default IOU image values".format(name=self._name, id=self._id))
log.info('IOU "{name}" [{id}]: does not use the default IOU image values'.format(name=self._name, id=self._id))
def _check_requirements(self):
"""
Check if IOUYAP is available
Checks if IOUYAP executable is available.
"""
path = self.iouyap_path
if not path:
raise IOUError("No path to a IOU executable has been set")
raise IOUError("No path to iouyap program has been set")
if not os.path.isfile(path):
raise IOUError("IOU program '{}' is not accessible".format(path))
raise IOUError("iouyap program '{}' is not accessible".format(path))
if not os.access(path, os.X_OK):
raise IOUError("IOU program '{}' is not executable".format(path))
raise IOUError("iouyap program '{}' is not executable".format(path))
def __json__(self):
@ -243,7 +253,7 @@ class IOUVM(BaseVM):
@property
def iourc_path(self):
"""
Returns the IOURC path.
Returns the IOURC file path.
:returns: path to IOURC
"""
@ -267,8 +277,9 @@ class IOUVM(BaseVM):
@property
def ram(self):
"""
Returns the amount of RAM allocated to this IOU instance.
:returns: amount of RAM in Mbytes (integer)
Returns the amount of RAM allocated to this IOU VM.
:returns: amount of RAM in MBytes (integer)
"""
return self._ram
@ -277,16 +288,17 @@ class IOUVM(BaseVM):
def ram(self, ram):
"""
Sets amount of RAM allocated to this IOU instance.
:param ram: amount of RAM in Mbytes (integer)
:param ram: amount of RAM in MBytes (integer)
"""
if self._ram == ram:
return
log.info("IOU {name} [id={id}]: RAM updated from {old_ram}MB to {new_ram}MB".format(name=self._name,
id=self._id,
old_ram=self._ram,
new_ram=ram))
log.info('IOU "{name}" [{id}]: RAM updated from {old_ram}MB to {new_ram}MB'.format(name=self._name,
id=self._id,
old_ram=self._ram,
new_ram=ram))
self._ram = ram
@ -294,7 +306,8 @@ class IOUVM(BaseVM):
def nvram(self):
"""
Returns the mount of NVRAM allocated to this IOU instance.
:returns: amount of NVRAM in Kbytes (integer)
:returns: amount of NVRAM in KBytes (integer)
"""
return self._nvram
@ -303,22 +316,23 @@ class IOUVM(BaseVM):
def nvram(self, nvram):
"""
Sets amount of NVRAM allocated to this IOU instance.
:param nvram: amount of NVRAM in Kbytes (integer)
:param nvram: amount of NVRAM in KBytes (integer)
"""
if self._nvram == nvram:
return
log.info("IOU {name} [id={id}]: NVRAM updated from {old_nvram}KB to {new_nvram}KB".format(name=self._name,
id=self._id,
old_nvram=self._nvram,
new_nvram=nvram))
log.info('IOU "{name}" [{id}]: NVRAM updated from {old_nvram}KB to {new_nvram}KB'.format(name=self._name,
id=self._id,
old_nvram=self._nvram,
new_nvram=nvram))
self._nvram = nvram
@BaseVM.name.setter
def name(self, new_name):
"""
Sets the name of this IOU vm.
Sets the name of this IOU VM.
:param new_name: name
"""
@ -332,10 +346,12 @@ class IOUVM(BaseVM):
@property
def application_id(self):
return self._manager.get_application_id(self.id)
@property
def iourc_content(self):
try:
with open(os.path.join(self.temporary_directory, "iourc")) as f:
return f.read()
@ -344,13 +360,14 @@ class IOUVM(BaseVM):
@iourc_content.setter
def iourc_content(self, value):
if value is not None:
path = os.path.join(self.temporary_directory, "iourc")
try:
with open(path, "w+") as f:
f.write(value)
except OSError as e:
raise IOUError("Could not write iourc file {}: {}".format(path, e))
raise IOUError("Could not write the iourc file {}: {}".format(path, e))
@asyncio.coroutine
def _library_check(self):
@ -468,11 +485,11 @@ class IOUVM(BaseVM):
log.info("IOU instance {} started PID={}".format(self._id, self._iou_process.pid))
self._started = True
except FileNotFoundError as e:
raise IOUError("could not start IOU: {}: 32-bit binary support is probably not installed".format(e))
raise IOUError("Could not start IOU: {}: 32-bit binary support is probably not installed".format(e))
except (OSError, subprocess.SubprocessError) as e:
iou_stdout = self.read_iou_stdout()
log.error("could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout))
raise IOUError("could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout))
log.error("Could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout))
raise IOUError("Could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout))
# start console support
self._start_ioucon()
@ -481,7 +498,7 @@ class IOUVM(BaseVM):
def _rename_nvram_file(self):
"""
Before start the VM rename the nvram file to the correct application id
Before starting the VM, rename the nvram and vlan.dat files with the correct IOU application identifier.
"""
destination = os.path.join(self.working_dir, "nvram_{:05d}".format(self.application_id))
@ -494,7 +511,7 @@ class IOUVM(BaseVM):
@asyncio.coroutine
def _start_iouyap(self):
"""
Starts iouyap (handles connections to and from this IOU device).
Starts iouyap (handles connections to and from this IOU VM).
"""
try:
@ -512,7 +529,7 @@ class IOUVM(BaseVM):
log.info("iouyap started PID={}".format(self._iouyap_process.pid))
except (OSError, subprocess.SubprocessError) as e:
iouyap_stdout = self.read_iouyap_stdout()
log.error("could not start iouyap: {}\n{}".format(e, iouyap_stdout))
log.error("Could not start iouyap: {}\n{}".format(e, iouyap_stdout))
raise IOUError("Could not start iouyap: {}\n{}".format(e, iouyap_stdout))
def _update_iouyap_config(self):
@ -614,29 +631,33 @@ class IOUVM(BaseVM):
self._started = False
def _terminate_process_iouyap(self):
"""Terminate the process if running"""
"""
Terminate the IOUYAP process if running.
"""
log.info("Stopping IOUYAP instance {} PID={}".format(self.name, self._iouyap_process.pid))
log.info('Stopping IOUYAP process for IOU VM "{}" PID={}'.format(self.name, self._iouyap_process.pid))
try:
self._iouyap_process.terminate()
# Sometime the process can already be dead when we garbage collect
# Sometime the process may already be dead when we garbage collect
except ProcessLookupError:
pass
def _terminate_process_iou(self):
"""Terminate the process if running"""
"""
Terminate the IOU process if running
"""
log.info("Stopping IOU instance {} PID={}".format(self.name, self._iou_process.pid))
log.info('Stopping IOU process for IOU VM "{}" PID={}'.format(self.name, self._iou_process.pid))
try:
self._iou_process.terminate()
# Sometime the process can already be dead when we garbage collect
# Sometime the process may already be dead when we garbage collect
except ProcessLookupError:
pass
@asyncio.coroutine
def reload(self):
"""
Reload the IOU process. (Stop / Start)
Reloads the IOU process (stop & start).
"""
yield from self.stop()
@ -688,6 +709,7 @@ class IOUVM(BaseVM):
"""
Command to start the IOU process.
(to be passed to subprocess.Popen())
IOU command line:
Usage: <image> [options] <application id>
<image>: unix-js-m | unix-is-m | unix-i-m | ...
@ -777,7 +799,8 @@ class IOUVM(BaseVM):
@property
def ethernet_adapters(self):
"""
Returns the number of Ethernet adapters for this IOU instance.
Returns the number of Ethernet adapters for this IOU VM.
:returns: number of adapters
"""
@ -786,7 +809,8 @@ class IOUVM(BaseVM):
@ethernet_adapters.setter
def ethernet_adapters(self, ethernet_adapters):
"""
Sets the number of Ethernet adapters for this IOU instance.
Sets the number of Ethernet adapters for this IOU VM.
:param ethernet_adapters: number of adapters
"""
@ -794,16 +818,17 @@ class IOUVM(BaseVM):
for _ in range(0, ethernet_adapters):
self._ethernet_adapters.append(EthernetAdapter(interfaces=4))
log.info("IOU {name} [id={id}]: number of Ethernet adapters changed to {adapters}".format(name=self._name,
id=self._id,
adapters=len(self._ethernet_adapters)))
log.info('IOU "{name}" [{id}]: number of Ethernet adapters changed to {adapters}'.format(name=self._name,
id=self._id,
adapters=len(self._ethernet_adapters)))
self._adapters = self._ethernet_adapters + self._serial_adapters
@property
def serial_adapters(self):
"""
Returns the number of Serial adapters for this IOU instance.
Returns the number of Serial adapters for this IOU VM.
:returns: number of adapters
"""
@ -812,7 +837,8 @@ class IOUVM(BaseVM):
@serial_adapters.setter
def serial_adapters(self, serial_adapters):
"""
Sets the number of Serial adapters for this IOU instance.
Sets the number of Serial adapters for this IOU VM.
:param serial_adapters: number of adapters
"""
@ -820,67 +846,69 @@ class IOUVM(BaseVM):
for _ in range(0, serial_adapters):
self._serial_adapters.append(SerialAdapter(interfaces=4))
log.info("IOU {name} [id={id}]: number of Serial adapters changed to {adapters}".format(name=self._name,
id=self._id,
adapters=len(self._serial_adapters)))
log.info('IOU "{name}" [{id}]: number of Serial adapters changed to {adapters}'.format(name=self._name,
id=self._id,
adapters=len(self._serial_adapters)))
self._adapters = self._ethernet_adapters + self._serial_adapters
def adapter_add_nio_binding(self, adapter_number, port_number, nio):
"""
Adds a adapter NIO binding.
:param adapter_number: adapter ID
:param port_number: port ID
:param adapter_number: adapter number
:param port_number: port number
:param nio: NIO instance to add to the adapter/port
"""
try:
adapter = self._adapters[adapter_number]
except IndexError:
raise IOUError("Adapter {adapter_number} doesn't exist on IOU {name}".format(name=self._name,
adapter_number=adapter_number))
raise IOUError('Adapter {adapter_number} does not exist for IOU "{name}"'.format(name=self._name,
adapter_number=adapter_number))
if not adapter.port_exists(port_number):
raise IOUError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=adapter,
port_number=port_number))
raise IOUError("Port {port_number} does not exist in adapter {adapter}".format(adapter=adapter,
port_number=port_number))
adapter.add_nio(port_number, nio)
log.info("IOU {name} [id={id}]: {nio} added to {adapter_number}/{port_number}".format(name=self._name,
id=self._id,
nio=nio,
adapter_number=adapter_number,
port_number=port_number))
log.info('IOU "{name}" [{id}]: {nio} added to {adapter_number}/{port_number}'.format(name=self._name,
id=self._id,
nio=nio,
adapter_number=adapter_number,
port_number=port_number))
if self.is_iouyap_running():
self._update_iouyap_config()
os.kill(self._iouyap_process.pid, signal.SIGHUP)
def adapter_remove_nio_binding(self, adapter_number, port_number):
"""
Removes a adapter NIO binding.
:param adapter_number: adapter ID
:param port_number: port ID
Removes an adapter NIO binding.
:param adapter_number: adapter number
:param port_number: port number
:returns: NIO instance
"""
try:
adapter = self._adapters[adapter_number]
except IndexError:
raise IOUError("Adapter {adapter_number} doesn't exist on IOU {name}".format(name=self._name,
adapter_number=adapter_number))
raise IOUError('Adapter {adapter_number} does not exist on IOU "{name}"'.format(name=self._name,
adapter_number=adapter_number))
if not adapter.port_exists(port_number):
raise IOUError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=adapter,
port_number=port_number))
raise IOUError("Port {port_number} does not exist in adapter {adapter}".format(adapter=adapter,
port_number=port_number))
nio = adapter.get_nio(port_number)
if isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
adapter.remove_nio(port_number)
log.info("IOU {name} [id={id}]: {nio} removed from {adapter_number}/{port_number}".format(name=self._name,
id=self._id,
nio=nio,
adapter_number=adapter_number,
port_number=port_number))
log.info('IOU "{name}" [{id}]: {nio} removed from {adapter_number}/{port_number}'.format(name=self._name,
id=self._id,
nio=nio,
adapter_number=adapter_number,
port_number=port_number))
if self.is_iouyap_running():
self._update_iouyap_config()
os.kill(self._iouyap_process.pid, signal.SIGHUP)
@ -891,6 +919,7 @@ class IOUVM(BaseVM):
def l1_keepalives(self):
"""
Returns either layer 1 keepalive messages option is enabled or disabled.
:returns: boolean
"""
@ -900,19 +929,21 @@ class IOUVM(BaseVM):
def l1_keepalives(self, state):
"""
Enables or disables layer 1 keepalive messages.
:param state: boolean
"""
self._l1_keepalives = state
if state:
log.info("IOU {name} [id={id}]: has activated layer 1 keepalive messages".format(name=self._name, id=self._id))
log.info('IOU "{name}" [{id}]: has activated layer 1 keepalive messages'.format(name=self._name, id=self._id))
else:
log.info("IOU {name} [id={id}]: has deactivated layer 1 keepalive messages".format(name=self._name, id=self._id))
log.info('IOU "{name}" [{id}]: has deactivated layer 1 keepalive messages'.format(name=self._name, id=self._id))
@asyncio.coroutine
def _enable_l1_keepalives(self, command):
"""
Enables L1 keepalive messages if supported.
:param command: command line
"""
@ -930,7 +961,9 @@ class IOUVM(BaseVM):
@property
def initial_config(self):
"""Return the content of the current initial-config file"""
"""
Returns the content of the current initial-config file.
"""
config_file = self.initial_config_file
if config_file is None:
@ -947,7 +980,7 @@ class IOUVM(BaseVM):
"""
Update the initial config
:param initial_config: The content of the initial configuration file
:param initial_config: content of the initial configuration file
"""
try:
@ -964,7 +997,7 @@ class IOUVM(BaseVM):
@property
def initial_config_file(self):
"""
Returns the initial config file for this IOU instance.
Returns the initial config file for this IOU VM.
:returns: path to config file. None if the file doesn't exist
"""
@ -979,7 +1012,7 @@ class IOUVM(BaseVM):
def relative_initial_config_file(self):
"""
Returns the initial config file relative to the project directory.
It's compatible with pre 1.3 topologies.
It's compatible with pre 1.3 projects.
:returns: path to config file. None if the file doesn't exist
"""
@ -994,9 +1027,9 @@ class IOUVM(BaseVM):
def start_capture(self, adapter_number, port_number, output_file, data_link_type="DLT_EN10MB"):
"""
Starts a packet capture.
:param adapter_number: adapter ID
:param port_number: port ID
:param port: allocated port
:param adapter_number: adapter number
:param port_number: port number
:param output_file: PCAP destination file for the capture
:param data_link_type: PCAP data link type (DLT_*), default is DLT_EN10MB
"""
@ -1004,35 +1037,28 @@ class IOUVM(BaseVM):
try:
adapter = self._adapters[adapter_number]
except IndexError:
raise IOUError("Adapter {adapter_number} doesn't exist on IOU {name}".format(name=self._name,
adapter_number=adapter_number))
raise IOUError('Adapter {adapter_number} does not exist on IOU "{name}"'.format(name=self._name,
adapter_number=adapter_number))
if not adapter.port_exists(port_number):
raise IOUError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=adapter,
port_number=port_number))
raise IOUError("Port {port_number} does not exist in adapter {adapter}".format(adapter=adapter,
port_number=port_number))
nio = adapter.get_nio(port_number)
if not nio:
raise IOUError("NIO {port_number} doesn't exist in adapter {adapter}".format(adapter=adapter,
port_number=port_number))
raise IOUError("NIO {port_number} does not exist in adapter {adapter}".format(adapter=adapter,
port_number=port_number))
if nio.capturing:
raise IOUError("Packet capture is already activated on {adapter_number}/{port_number}".format(adapter_number=adapter_number,
port_number=port_number))
try:
os.makedirs(os.path.dirname(output_file))
except FileExistsError:
pass
except OSError as e:
raise IOUError("Could not create captures directory {}".format(e))
nio.startPacketCapture(output_file, data_link_type)
log.info("IOU {name} [id={id}]: starting packet capture on {adapter_number}/{port_number}".format(name=self._name,
id=self._id,
adapter_number=adapter_number,
port_number=port_number))
log.info('IOU "{name}" [{id}]: starting packet capture on {adapter_number}/{port_number}'.format(name=self._name,
id=self._id,
adapter_number=adapter_number,
port_number=port_number))
if self.is_iouyap_running():
self._update_iouyap_config()
@ -1042,26 +1068,27 @@ class IOUVM(BaseVM):
def stop_capture(self, adapter_number, port_number):
"""
Stops a packet capture.
:param adapter_number: adapter ID
:param port_number: port ID
:param adapter_number: adapter number
:param port_number: port number
"""
try:
adapter = self._adapters[adapter_number]
except IndexError:
raise IOUError("Adapter {adapter_number} doesn't exist on IOU {name}".format(name=self._name,
adapter_number=adapter_number))
raise IOUError('Adapter {adapter_number} does not exist on IOU "{name}"'.format(name=self._name,
adapter_number=adapter_number))
if not adapter.port_exists(port_number):
raise IOUError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=adapter,
port_number=port_number))
raise IOUError("Port {port_number} does not exist in adapter {adapter}".format(adapter=adapter,
port_number=port_number))
nio = adapter.get_nio(port_number)
nio.stopPacketCapture()
log.info("IOU {name} [id={id}]: stopping packet capture on {adapter_number}/{port_number}".format(name=self._name,
id=self._id,
adapter_number=adapter_number,
port_number=port_number))
log.info('IOU "{name}" [{id}]: stopping packet capture on {adapter_number}/{port_number}'.format(name=self._name,
id=self._id,
adapter_number=adapter_number,
port_number=port_number))
if self.is_iouyap_running():
self._update_iouyap_config()
os.kill(self._iouyap_process.pid, signal.SIGHUP)

View File

@ -32,7 +32,7 @@ class NIOGenericEthernet(NIO):
def __init__(self, ethernet_device):
NIO.__init__(self)
super().__init__()
self._ethernet_device = ethernet_device
@property

View File

@ -261,7 +261,7 @@ class PortManager:
:param force_remove: Force port removal even on Darwnin
"""
# A bug with dynamips on darwin doesn't correctly free the port we free it only when changing project
# A bug with Dynamips on Darwin which doesn't correctly free UDP ports, they are freed only when changing the project
if sys.platform.startswith("darwin") and force_remove is False:
return

View File

@ -35,10 +35,10 @@ class Project:
A project contains a list of VM.
In theory VM are isolated project/project.
:param project_id: Force project identifier (None by default auto generate an UUID)
:param path: Path of the project. (None use the standard directory)
:param location: Parent path of the project. (None should create a tmp directory)
:param temporary: Boolean the project is a temporary project (destroy when closed)
:param project_id: force project identifier (None by default auto generate an UUID)
:param path: path of the project. (None use the standard directory)
:param location: parent path of the project. (None should create a tmp directory)
:param temporary: boolean to tell if the project is a temporary project (destroy when closed)
"""
def __init__(self, name=None, project_id=None, path=None, location=None, temporary=False):
@ -231,7 +231,7 @@ class Project:
def module_working_directory(self, module_name):
"""
Return a working directory for the module
Returns a working directory for the module
If the directory doesn't exist, the directory is created.
:param module_name: name for the module
@ -247,18 +247,20 @@ class Project:
def module_working_path(self, module_name):
"""
Return the working direcotory for the module. If you want
Returns the working directory for the module. If you want
to be sure to have the directory on disk take a look on:
module_working_directory
"""
return os.path.join(self._path, "project-files", module_name)
def vm_working_directory(self, vm):
"""
Return a working directory for a specific VM.
Returns a working directory for a specific VM.
If the directory doesn't exist, the directory is created.
:param vm: VM instance
:returns: VM working directory
"""
@ -271,7 +273,7 @@ class Project:
def capture_working_directory(self):
"""
Return a working directory where to store packet capture files.
Returns a working directory where to store packet capture files.
:returns: path to the directory
"""
@ -293,7 +295,7 @@ class Project:
def add_vm(self, vm):
"""
Add a VM to the project.
Adds a VM to the project.
In theory this should be called by the VM manager.
:param vm: VM instance
@ -303,7 +305,7 @@ class Project:
def remove_vm(self, vm):
"""
Remove a VM from the project.
Removes a VM from the project.
In theory this should be called by the VM manager.
:param vm: VM instance
@ -314,7 +316,9 @@ class Project:
@asyncio.coroutine
def close(self):
"""Close the project, but keep information on disk"""
"""
Closes the project, but keep information on disk
"""
for module in self.modules():
yield from module.instance().project_closing(self)
@ -325,7 +329,7 @@ class Project:
@asyncio.coroutine
def _close_and_clean(self, cleanup):
"""
Close the project, and cleanup the disk if cleanup is True
Closes the project, and cleanup the disk if cleanup is True
:param cleanup: If True drop the project directory
"""
@ -365,7 +369,9 @@ class Project:
@asyncio.coroutine
def commit(self):
"""Write project changes on disk"""
"""
Writes project changes on disk
"""
while self._vms_to_destroy:
vm = self._vms_to_destroy.pop()
@ -376,7 +382,9 @@ class Project:
@asyncio.coroutine
def delete(self):
"""Remove project from disk"""
"""
Removes project from disk
"""
for module in self.modules():
yield from module.instance().project_closing(self)
@ -386,7 +394,9 @@ class Project:
@classmethod
def clean_project_directory(cls):
"""At startup drop old temporary project. After a crash for example"""
"""
At startup drop old temporary project. After a crash for example
"""
config = Config.instance().get_section_config("Server")
directory = config.get("project_directory", cls._get_default_project_directory())
@ -398,7 +408,9 @@ class Project:
shutil.rmtree(path)
def modules(self):
"""Return VM modules loaded"""
"""
Returns all loaded VM modules.
"""
# We import it at the last time to avoid circular dependencies
from ..modules import MODULES

View File

@ -32,14 +32,15 @@ from .qemu_vm import QemuVM
class Qemu(BaseManager):
_VM_CLASS = QemuVM
@staticmethod
def binary_list():
"""
Gets QEMU binaries list available on the matchine
Gets QEMU binaries list available on the host.
:returns: Array of dictionnary {"path": Qemu binaries path, "version": Version of Qemu}
:returns: Array of dictionary {"path": Qemu binary path, "version": version of Qemu}
"""
qemus = []
@ -82,7 +83,8 @@ class Qemu(BaseManager):
def _get_qemu_version(qemu_path):
"""
Gets the Qemu version.
:param qemu_path: path to Qemu
:param qemu_path: path to Qemu executable.
"""
if sys.platform.startswith("win"):

View File

@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Custom exceptions for Qemu module.
Custom exceptions for the Qemu module.
"""
from ..vm_error import VMError

View File

@ -16,7 +16,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
QEMU VM instance.
QEMU VM management (creates command line, processes, files etc.) in
order to run a QEMU VM.
"""
import sys
@ -44,26 +45,18 @@ class QemuVM(BaseVM):
"""
QEMU VM implementation.
:param name: name of this Qemu vm
:param vm_id: IOU instance identifier
:param name: Qemu VM name
:param vm_id: Qemu VM identifier
:param project: Project instance
:param manager: parent VM Manager
:param manager: Manager instance
:param console: TCP console port
:param qemu_path: path to the QEMU binary
:param qemu_id: QEMU VM instance ID
:param console: TCP console port
"""
def __init__(self,
name,
vm_id,
project,
manager,
qemu_path=None,
console=None):
def __init__(self, name, vm_id, project, manager, qemu_path=None, console=None):
super().__init__(name, vm_id, project, manager, console=console)
server_config = manager.config.get_section_config("Server")
self._host = server_config.get("host", "127.0.0.1")
self._monitor_host = server_config.get("monitor_host", "127.0.0.1")
@ -74,7 +67,7 @@ class QemuVM(BaseVM):
self._monitor = None
self._stdout_file = ""
# QEMU settings
# QEMU VM settings
self._qemu_path = qemu_path
self._hda_disk_image = ""
self._hdb_disk_image = ""
@ -92,8 +85,7 @@ class QemuVM(BaseVM):
self._process_priority = "low"
self.adapters = 1 # creates 1 adapter by default
log.info("QEMU VM {name} [id={id}] has been created".format(name=self._name,
id=self._id))
log.info('QEMU VM "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
@property
def monitor(self):
@ -134,9 +126,9 @@ class QemuVM(BaseVM):
raise QemuError("QEMU binary '{}' is not executable".format(qemu_path))
self._qemu_path = qemu_path
log.info("QEMU VM {name} [id={id}] has set the QEMU path to {qemu_path}".format(name=self._name,
id=self._id,
qemu_path=qemu_path))
log.info('QEMU VM "{name}" [{id}] has set the QEMU path to {qemu_path}'.format(name=self._name,
id=self._id,
qemu_path=qemu_path))
@property
def hda_disk_image(self):
@ -160,9 +152,9 @@ class QemuVM(BaseVM):
server_config = self.manager.config.get_section_config("Server")
hda_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hda_disk_image)
log.info("QEMU VM {name} [id={id}] has set the QEMU hda disk image path to {disk_image}".format(name=self._name,
id=self._id,
disk_image=hda_disk_image))
log.info('QEMU VM "{name}" [{id}] has set the QEMU hda disk image path to {disk_image}'.format(name=self._name,
id=self._id,
disk_image=hda_disk_image))
self._hda_disk_image = hda_disk_image
@property
@ -187,9 +179,9 @@ class QemuVM(BaseVM):
server_config = self.manager.config.get_section_config("Server")
hdb_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hdb_disk_image)
log.info("QEMU VM {name} [id={id}] has set the QEMU hdb disk image path to {disk_image}".format(name=self._name,
id=self._id,
disk_image=hdb_disk_image))
log.info('QEMU VM "{name}" [{id}] has set the QEMU hdb disk image path to {disk_image}'.format(name=self._name,
id=self._id,
disk_image=hdb_disk_image))
self._hdb_disk_image = hdb_disk_image
@property
@ -214,9 +206,9 @@ class QemuVM(BaseVM):
server_config = self.manager.config.get_section_config("Server")
hdc_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hdc_disk_image)
log.info("QEMU VM {name} [id={id}] has set the QEMU hdc disk image path to {disk_image}".format(name=self._name,
id=self._id,
disk_image=hdc_disk_image))
log.info('QEMU VM "{name}" [{id}] has set the QEMU hdc disk image path to {disk_image}'.format(name=self._name,
id=self._id,
disk_image=hdc_disk_image))
self._hdc_disk_image = hdc_disk_image
@property
@ -241,15 +233,15 @@ class QemuVM(BaseVM):
server_config = self.manager.config.get_section_config("Server")
hdd_disk_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", hdd_disk_image)
log.info("QEMU VM {name} [id={id}] has set the QEMU hdd disk image path to {disk_image}".format(name=self._name,
id=self._id,
disk_image=hdd_disk_image))
log.info('QEMU VM "{name}" [{id}] has set the QEMU hdd disk image path to {disk_image}'.format(name=self._name,
id=self._id,
disk_image=hdd_disk_image))
self._hdd_disk_image = hdd_disk_image
@property
def adapters(self):
"""
Returns the number of Ethernet adapters for this QEMU VM instance.
Returns the number of Ethernet adapters for this QEMU VM.
:returns: number of adapters
"""
@ -259,23 +251,23 @@ class QemuVM(BaseVM):
@adapters.setter
def adapters(self, adapters):
"""
Sets the number of Ethernet adapters for this QEMU VM instance.
Sets the number of Ethernet adapters for this QEMU VM.
:param adapters: number of adapters
"""
self._ethernet_adapters.clear()
for adapter_id in range(0, adapters):
for adapter_number in range(0, adapters):
self._ethernet_adapters.append(EthernetAdapter())
log.info("QEMU VM {name} [id={id}]: number of Ethernet adapters changed to {adapters}".format(name=self._name,
id=self._id,
adapters=adapters))
log.info('QEMU VM "{name}" [{id}]: number of Ethernet adapters changed to {adapters}'.format(name=self._name,
id=self._id,
adapters=adapters))
@property
def adapter_type(self):
"""
Returns the adapter type for this QEMU VM instance.
Returns the adapter type for this QEMU VM.
:returns: adapter type (string)
"""
@ -285,16 +277,16 @@ class QemuVM(BaseVM):
@adapter_type.setter
def adapter_type(self, adapter_type):
"""
Sets the adapter type for this QEMU VM instance.
Sets the adapter type for this QEMU VM.
:param adapter_type: adapter type (string)
"""
self._adapter_type = adapter_type
log.info("QEMU VM {name} [id={id}]: adapter type changed to {adapter_type}".format(name=self._name,
id=self._id,
adapter_type=adapter_type))
log.info('QEMU VM "{name}" [{id}]: adapter type changed to {adapter_type}'.format(name=self._name,
id=self._id,
adapter_type=adapter_type))
@property
def legacy_networking(self):
@ -315,9 +307,9 @@ class QemuVM(BaseVM):
"""
if legacy_networking:
log.info("QEMU VM {name} [id={id}] has enabled legacy networking".format(name=self._name, id=self._id))
log.info('QEMU VM "{name}" [{id}] has enabled legacy networking'.format(name=self._name, id=self._id))
else:
log.info("QEMU VM {name} [id={id}] has disabled legacy networking".format(name=self._name, id=self._id))
log.info('QEMU VM "{name}" [{id}] has disabled legacy networking'.format(name=self._name, id=self._id))
self._legacy_networking = legacy_networking
@property
@ -338,9 +330,9 @@ class QemuVM(BaseVM):
:param cpu_throttling: integer
"""
log.info("QEMU VM {name} [id={id}] has set the percentage of CPU allowed to {cpu}".format(name=self._name,
id=self._id,
cpu=cpu_throttling))
log.info('QEMU VM "{name}" [{id}] has set the percentage of CPU allowed to {cpu}'.format(name=self._name,
id=self._id,
cpu=cpu_throttling))
self._cpu_throttling = cpu_throttling
self._stop_cpulimit()
if cpu_throttling:
@ -364,9 +356,9 @@ class QemuVM(BaseVM):
:param process_priority: string
"""
log.info("QEMU VM {name} [id={id}] has set the process priority to {priority}".format(name=self._name,
id=self._id,
priority=process_priority))
log.info('QEMU VM "{name}" [{id}] has set the process priority to {priority}'.format(name=self._name,
id=self._id,
priority=process_priority))
self._process_priority = process_priority
@property
@ -387,9 +379,7 @@ class QemuVM(BaseVM):
:param ram: RAM amount in MB
"""
log.info("QEMU VM {name} [id={id}] has set the RAM to {ram}".format(name=self._name,
id=self._id,
ram=ram))
log.info('QEMU VM "{name}" [{id}] has set the RAM to {ram}'.format(name=self._name, id=self._id, ram=ram))
self._ram = ram
@property
@ -410,9 +400,9 @@ class QemuVM(BaseVM):
:param options: QEMU options
"""
log.info("QEMU VM {name} [id={id}] has set the QEMU options to {options}".format(name=self._name,
id=self._id,
options=options))
log.info('QEMU VM "{name}" [{id}] has set the QEMU options to {options}'.format(name=self._name,
id=self._id,
options=options))
self._options = options
@property
@ -437,9 +427,9 @@ class QemuVM(BaseVM):
server_config = self.manager.config.get_section_config("Server")
initrd = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", initrd)
log.info("QEMU VM {name} [id={id}] has set the QEMU initrd path to {initrd}".format(name=self._name,
id=self._id,
initrd=initrd))
log.info('QEMU VM "{name}" [{id}] has set the QEMU initrd path to {initrd}'.format(name=self._name,
id=self._id,
initrd=initrd))
self._initrd = initrd
@property
@ -464,9 +454,9 @@ class QemuVM(BaseVM):
server_config = self.manager.config.get_section_config("Server")
kernel_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", kernel_image)
log.info("QEMU VM {name} [id={id}] has set the QEMU kernel image path to {kernel_image}".format(name=self._name,
id=self._id,
kernel_image=kernel_image))
log.info('QEMU VM "{name}" [{id}] has set the QEMU kernel image path to {kernel_image}'.format(name=self._name,
id=self._id,
kernel_image=kernel_image))
self._kernel_image = kernel_image
@property
@ -487,9 +477,9 @@ class QemuVM(BaseVM):
:param kernel_command_line: QEMU kernel command line
"""
log.info("QEMU VM {name} [id={id}] has set the QEMU kernel command line to {kernel_command_line}".format(name=self._name,
id=self._id,
kernel_command_line=kernel_command_line))
log.info('QEMU VM "{name}" [{id}] has set the QEMU kernel command line to {kernel_command_line}'.format(name=self._name,
id=self._id,
kernel_command_line=kernel_command_line))
self._kernel_command_line = kernel_command_line
@asyncio.coroutine
@ -538,7 +528,7 @@ class QemuVM(BaseVM):
process = yield from asyncio.create_subprocess_exec('renice', '-n', str(priority), '-p', str(self._process.pid))
yield from process.wait()
except (OSError, subprocess.SubprocessError) as e:
log.error("could not change process priority for QEMU VM {}: {}".format(self._name, e))
log.error('Could not change process priority for QEMU VM "{}": {}'.format(self._name, e))
def _stop_cpulimit(self):
"""
@ -550,7 +540,7 @@ class QemuVM(BaseVM):
try:
self._process.wait(3)
except subprocess.TimeoutExpired:
log.error("could not kill cpulimit process {}".format(self._cpulimit_process.pid))
log.error("Could not kill cpulimit process {}".format(self._cpulimit_process.pid))
def _set_cpu_throttling(self):
"""
@ -579,7 +569,6 @@ class QemuVM(BaseVM):
"""
if self.is_running():
# resume the VM if it is paused
yield from self.resume()
return
@ -597,7 +586,7 @@ class QemuVM(BaseVM):
self._command = yield from self._build_command()
try:
log.info("starting QEMU: {}".format(self._command))
log.info("Starting QEMU: {}".format(self._command))
self._stdout_file = os.path.join(self.working_dir, "qemu.log")
log.info("logging to {}".format(self._stdout_file))
with open(self._stdout_file, "w") as fd:
@ -605,12 +594,12 @@ class QemuVM(BaseVM):
stdout=fd,
stderr=subprocess.STDOUT,
cwd=self.working_dir)
log.info("QEMU VM instance {} started PID={}".format(self._id, self._process.pid))
log.info('QEMU VM "{}" started PID={}'.format(self._name, self._process.pid))
self._started = True
except (OSError, subprocess.SubprocessError) as e:
stdout = self.read_stdout()
log.error("could not start QEMU {}: {}\n{}".format(self.qemu_path, e, stdout))
raise QemuError("could not start QEMU {}: {}\n{}".format(self.qemu_path, e, stdout))
log.error("Could not start QEMU {}: {}\n{}".format(self.qemu_path, e, stdout))
raise QemuError("Could not start QEMU {}: {}\n{}".format(self.qemu_path, e, stdout))
self._set_process_priority()
if self._cpu_throttling:
@ -624,15 +613,14 @@ class QemuVM(BaseVM):
# stop the QEMU process
if self.is_running():
log.info("stopping QEMU VM instance {} PID={}".format(self._id, self._process.pid))
log.info('Stopping QEMU VM "{}" PID={}'.format(self._name, self._process.pid))
try:
self._process.terminate()
self._process.wait()
except subprocess.TimeoutExpired:
self._process.kill()
if self._process.returncode is None:
log.warn("QEMU VM instance {} PID={} is still running".format(self._id,
self._process.pid))
log.warn('QEMU VM "{}" PID={} is still running'.format(self._name, self._process.pid))
self._process = None
self._started = False
self._stop_cpulimit()
@ -643,9 +631,9 @@ class QemuVM(BaseVM):
Executes a command with QEMU monitor when this VM is running.
:param command: QEMU monitor command (e.g. info status, stop etc.)
:params expected: An array with the string attended (Default None)
:param expected: An array of expected strings
:returns: result of the command (Match object or None)
:returns: result of the command (matched object or None)
"""
result = None
@ -680,6 +668,9 @@ class QemuVM(BaseVM):
@asyncio.coroutine
def close(self):
"""
Closes this QEMU VM.
"""
log.debug('QEMU VM "{name}" [{id}] is closing'.format(name=self._name, id=self._id))
yield from self.stop()
@ -690,7 +681,7 @@ class QemuVM(BaseVM):
@asyncio.coroutine
def _get_vm_status(self):
"""
Returns this VM suspend status (running|paused)
Returns this VM suspend status.
Status are extracted from:
https://github.com/qemu/qemu/blob/master/qapi-schema.json#L152
@ -749,27 +740,27 @@ class QemuVM(BaseVM):
log.info("QEMU VM is not paused to be resumed, current status is {}".format(vm_status))
@asyncio.coroutine
def adapter_add_nio_binding(self, adapter_id, nio):
def adapter_add_nio_binding(self, adapter_number, nio):
"""
Adds a port NIO binding.
:param adapter_id: adapter ID
:param adapter_number: adapter number
:param nio: NIO instance to add to the adapter
"""
try:
adapter = self._ethernet_adapters[adapter_id]
adapter = self._ethernet_adapters[adapter_number]
except IndexError:
raise QemuError("Adapter {adapter_id} doesn't exist on QEMU VM {name}".format(name=self._name,
adapter_id=adapter_id))
raise QemuError('Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(name=self._name,
adapter_number=adapter_number))
if self.is_running():
# dynamically configure an UDP tunnel on the QEMU VM adapter
if nio and isinstance(nio, NIOUDP):
if self._legacy_networking:
yield from self._control_vm("host_net_remove {} gns3-{}".format(adapter_id, adapter_id))
yield from self._control_vm("host_net_add udp vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_id,
adapter_id,
yield from self._control_vm("host_net_remove {} gns3-{}".format(adapter_number, adapter_number))
yield from self._control_vm("host_net_add udp vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_number,
adapter_number,
nio.lport,
nio.rport,
nio.rhost))
@ -778,48 +769,48 @@ class QemuVM(BaseVM):
# Apparently there is a bug in Qemu...
# netdev_add [user|tap|socket|hubport|netmap],id=str[,prop=value][,...] -- add host network device
# netdev_del id -- remove host network device
yield from self._control_vm("netdev_del gns3-{}".format(adapter_id))
yield from self._control_vm("netdev_add socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id,
yield from self._control_vm("netdev_del gns3-{}".format(adapter_number))
yield from self._control_vm("netdev_add socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_number,
nio.rhost,
nio.rport,
self._host,
nio.lport))
adapter.add_nio(0, nio)
log.info("QEMU VM {name} [id={id}]: {nio} added to adapter {adapter_id}".format(name=self._name,
id=self._id,
nio=nio,
adapter_id=adapter_id))
log.info('QEMU VM "{name}" [{id}]: {nio} added to adapter {adapter_number}'.format(name=self._name,
id=self._id,
nio=nio,
adapter_number=adapter_number))
@asyncio.coroutine
def adapter_remove_nio_binding(self, adapter_id):
def adapter_remove_nio_binding(self, adapter_number):
"""
Removes a port NIO binding.
:param adapter_id: adapter ID
:param adapter_number: adapter number
:returns: NIO instance
"""
try:
adapter = self._ethernet_adapters[adapter_id]
adapter = self._ethernet_adapters[adapter_number]
except IndexError:
raise QemuError("Adapter {adapter_id} doesn't exist on QEMU VM {name}".format(name=self._name,
adapter_id=adapter_id))
raise QemuError('Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(name=self._name,
adapter_number=adapter_number))
if self.is_running():
# dynamically disable the QEMU VM adapter
yield from self._control_vm("host_net_remove {} gns3-{}".format(adapter_id, adapter_id))
yield from self._control_vm("host_net_add user vlan={},name=gns3-{}".format(adapter_id, adapter_id))
yield from self._control_vm("host_net_remove {} gns3-{}".format(adapter_number, adapter_number))
yield from self._control_vm("host_net_add user vlan={},name=gns3-{}".format(adapter_number, adapter_number))
nio = adapter.get_nio(0)
if isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
adapter.remove_nio(0)
log.info("QEMU VM {name} [id={id}]: {nio} removed from adapter {adapter_id}".format(name=self._name,
id=self._id,
nio=nio,
adapter_id=adapter_id))
log.info('QEMU VM "{name}" [{id}]: {nio} removed from adapter {adapter_number}'.format(name=self._name,
id=self._id,
nio=nio,
adapter_number=adapter_number))
return nio
@property
@ -844,7 +835,7 @@ class QemuVM(BaseVM):
with open(self._stdout_file, errors="replace") as file:
output = file.read()
except OSError as e:
log.warn("could not read {}: {}".format(self._stdout_file, e))
log.warn("Could not read {}: {}".format(self._stdout_file, e))
return output
def is_running(self):
@ -1004,47 +995,48 @@ class QemuVM(BaseVM):
return options
def _get_random_mac(self, adapter_id):
def _get_random_mac(self, adapter_number):
# TODO: let users specify a base mac address
return "00:00:ab:%02x:%02x:%02d" % (random.randint(0x00, 0xff), random.randint(0x00, 0xff), adapter_id)
return "00:00:ab:%02x:%02x:%02d" % (random.randint(0x00, 0xff), random.randint(0x00, 0xff), adapter_number)
def _network_options(self):
network_options = []
adapter_id = 0
adapter_number = 0
for adapter in self._ethernet_adapters:
mac = self._get_random_mac(adapter_id)
mac = self._get_random_mac(adapter_number)
if self._legacy_networking:
network_options.extend(["-net", "nic,vlan={},macaddr={},model={}".format(adapter_id, mac, self._adapter_type)])
network_options.extend(["-net", "nic,vlan={},macaddr={},model={}".format(adapter_number, mac, self._adapter_type)])
else:
network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_id)])
network_options.extend(["-device", "{},mac={},netdev=gns3-{}".format(self._adapter_type, mac, adapter_number)])
nio = adapter.get_nio(0)
if nio and isinstance(nio, NIOUDP):
if self._legacy_networking:
network_options.extend(["-net", "udp,vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_id,
adapter_id,
network_options.extend(["-net", "udp,vlan={},name=gns3-{},sport={},dport={},daddr={}".format(adapter_number,
adapter_number,
nio.lport,
nio.rport,
nio.rhost)])
else:
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_id,
network_options.extend(["-netdev", "socket,id=gns3-{},udp={}:{},localaddr={}:{}".format(adapter_number,
nio.rhost,
nio.rport,
self._host,
nio.lport)])
else:
if self._legacy_networking:
network_options.extend(["-net", "user,vlan={},name=gns3-{}".format(adapter_id, adapter_id)])
network_options.extend(["-net", "user,vlan={},name=gns3-{}".format(adapter_number, adapter_number)])
else:
network_options.extend(["-netdev", "user,id=gns3-{}".format(adapter_id)])
adapter_id += 1
network_options.extend(["-netdev", "user,id=gns3-{}".format(adapter_number)])
adapter_number += 1
return network_options
def _graphic(self):
"""
Add the correct graphic options depending of the OS
Adds the correct graphic options depending of the OS
"""
if sys.platform.startswith("win"):
return []
if len(os.environ.get("DISPLAY", "")) > 0:
@ -1086,7 +1078,7 @@ class QemuVM(BaseVM):
"""
if disk_image:
# return the relative path if disks images are in the images_path directory
# return the relative path if the disk image is in the images_path directory
server_config = self.manager.config.get_section_config("Server")
relative_image = os.path.join(os.path.expanduser(server_config.get("images_path", "~/GNS3/images")), "QEMU", disk_image)
if os.path.exists(relative_image):

View File

@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Custom exceptions for VirtualBox module.
Custom exceptions for the VirtualBox module.
"""
from ..vm_error import VMError

View File

@ -28,26 +28,38 @@ from .vpcs_vm import VPCSVM
class VPCS(BaseManager):
_VM_CLASS = VPCSVM
def __init__(self):
super().__init__()
self._free_mac_ids = {}
self._used_mac_ids = {}
@asyncio.coroutine
def create_vm(self, *args, **kwargs):
"""
Creates a new VPCS VM.
:returns: VPCSVM instance
"""
vm = yield from super().create_vm(*args, **kwargs)
self._free_mac_ids.setdefault(vm.project.id, list(range(0, 255)))
try:
self._used_mac_ids[vm.id] = self._free_mac_ids[vm.project.id].pop(0)
except IndexError:
raise VPCSError("No mac address available")
raise VPCSError("Cannot create a new VPCS VM (limit of 255 VMs reached on this host)")
return vm
@asyncio.coroutine
def close_vm(self, vm_id, *args, **kwargs):
"""
Closes a VPCS VM.
:returns: VPCSVM instance
"""
vm = self.get_vm(vm_id)
i = self._used_mac_ids[vm_id]
@ -59,10 +71,11 @@ class VPCS(BaseManager):
def get_mac_id(self, vm_id):
"""
Get an unique VPCS mac id
Get an unique VPCS MAC id (offset)
:param vm_id: ID of the VPCS VM
:returns: VPCS MAC id
:param vm_id: VPCS VM identifier
:returns: VPCS MAC identifier
"""
return self._used_mac_ids.get(vm_id, 1)

View File

@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Custom exceptions for VPCS module.
Custom exceptions for the VPCS module.
"""
from ..vm_error import VMError

View File

@ -17,7 +17,7 @@
"""
VPCS VM management (creates command line, processes, files etc.) in
order to run an VPCS instance.
order to run a VPCS VM.
"""
import os
@ -45,20 +45,19 @@ class VPCSVM(BaseVM):
module_name = 'vpcs'
"""
VPCS vm implementation.
VPCS VM implementation.
:param name: The name of this VM
:param vm_id: VPCS instance identifier
:param name: VPCS VM name
:param vm_id: VPCS VM identifier
:param project: Project instance
:param manager: Parent VM Manager
:param manager: Manager instance
:param console: TCP console port
:param startup_script: Content of vpcs startup script file
:param startup_script: content of the startup script file
"""
def __init__(self, name, vm_id, project, manager, console=None, startup_script=None):
super().__init__(name, vm_id, project, manager, console=console)
self._command = []
self._process = None
self._vpcs_stdout_file = ""
@ -71,8 +70,11 @@ class VPCSVM(BaseVM):
@asyncio.coroutine
def close(self):
"""
Closes this VPCS VM.
"""
log.debug("VPCS {name} [{id}] is closing".format(name=self._name, id=self._id))
log.debug('VPCS "{name}" [{id}] is closing'.format(name=self._name, id=self._id))
if self._console:
self._manager.port_manager.release_tcp_port(self._console, self._project)
self._console = None
@ -87,8 +89,9 @@ class VPCSVM(BaseVM):
@asyncio.coroutine
def _check_requirements(self):
"""
Check if VPCS is available with the correct version
Check if VPCS is available with the correct version.
"""
path = self.vpcs_path
if not path:
raise VPCSError("No path to a VPCS executable has been set")
@ -140,7 +143,7 @@ class VPCSVM(BaseVM):
@BaseVM.name.setter
def name(self, new_name):
"""
Sets the name of this VPCS vm.
Sets the name of this VPCS VM.
:param new_name: name
"""
@ -154,7 +157,9 @@ class VPCSVM(BaseVM):
@property
def startup_script(self):
"""Return the content of the current startup script"""
"""
Returns the content of the current startup script
"""
script_file = self.script_file
if script_file is None:
@ -164,14 +169,14 @@ class VPCSVM(BaseVM):
with open(script_file) as f:
return f.read()
except OSError as e:
raise VPCSError("Can't read VPCS startup file '{}'".format(script_file))
raise VPCSError('Cannot read the startup script file "{}": {}'.format(script_file, e))
@startup_script.setter
def startup_script(self, startup_script):
"""
Update the startup script
Updates the startup script.
:param startup_script The content of the vpcs startup script
:param startup_script: content of the startup script
"""
try:
@ -183,7 +188,7 @@ class VPCSVM(BaseVM):
startup_script = startup_script.replace("%h", self._name)
f.write(startup_script)
except OSError as e:
raise VPCSError("Can't write VPCS startup file '{}'".format(self.script_file))
raise VPCSError('Cannot write the startup script file "{}": {}'.format(self.script_file, e))
@asyncio.coroutine
def _check_vpcs_version(self):
@ -209,7 +214,6 @@ class VPCSVM(BaseVM):
"""
yield from self._check_requirements()
if not self.is_running():
if not self._ethernet_adapter.get_nio(0):
raise VPCSError("This VPCS instance must be connected in order to start")
@ -256,14 +260,16 @@ class VPCSVM(BaseVM):
@asyncio.coroutine
def reload(self):
"""
Reload the VPCS process. (Stop / Start)
Reloads the VPCS process (stop & start).
"""
yield from self.stop()
yield from self.start()
def _terminate_process(self):
"""Terminate the process if running"""
"""
Terminate the process if running
"""
log.info("Stopping VPCS instance {} PID={}".format(self.name, self._process.pid))
if sys.platform.startswith("win32"):
@ -271,7 +277,7 @@ class VPCSVM(BaseVM):
else:
try:
self._process.terminate()
# Sometime the process can already be dead when we garbage collect
# Sometime the process may already be dead when we garbage collect
except ProcessLookupError:
pass
@ -280,13 +286,14 @@ class VPCSVM(BaseVM):
Reads the standard output of the VPCS process.
Only use when the process has been stopped or has crashed.
"""
output = ""
if self._vpcs_stdout_file:
try:
with open(self._vpcs_stdout_file, errors="replace") as file:
output = file.read()
except OSError as e:
log.warn("could not read {}: {}".format(self._vpcs_stdout_file, e))
log.warn("Could not read {}: {}".format(self._vpcs_stdout_file, e))
return output
def is_running(self):
@ -313,10 +320,10 @@ class VPCSVM(BaseVM):
port_number=port_number))
self._ethernet_adapter.add_nio(port_number, nio)
log.info("VPCS {name} [{id}]: {nio} added to port {port_number}".format(name=self._name,
id=self.id,
nio=nio,
port_number=port_number))
log.info('VPCS "{name}" [{id}]: {nio} added to port {port_number}'.format(name=self._name,
id=self.id,
nio=nio,
port_number=port_number))
return nio
def port_remove_nio_binding(self, port_number):
@ -337,10 +344,10 @@ class VPCSVM(BaseVM):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
self._ethernet_adapter.remove_nio(port_number)
log.info("VPCS {name} [{id}]: {nio} removed from port {port_number}".format(name=self._name,
id=self.id,
nio=nio,
port_number=port_number))
log.info('VPCS "{name}" [{id}]: {nio} removed from port {port_number}'.format(name=self._name,
id=self.id,
nio=nio,
port_number=port_number))
return nio
def _build_command(self):
@ -406,12 +413,12 @@ class VPCSVM(BaseVM):
@property
def script_file(self):
"""
Returns the script-file for this VPCS instance.
Returns the startup script file for this VPCS VM.
:returns: path to script-file
:returns: path to startup script file
"""
# If the default VPCS file exist we use it
# use the default VPCS file if it exists
path = os.path.join(self.working_dir, 'startup.vpc')
if os.path.exists(path):
return path

View File

@ -17,7 +17,6 @@
import asyncio
import shutil
@asyncio.coroutine