1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-12-01 04:38:12 +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): def __init__(self, interfaces=1):
Adapter.__init__(self, interfaces=interfaces)
super().__init__(interfaces)
def __str__(self): def __str__(self):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # 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 from ..vm_error import VMError

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -46,7 +46,7 @@ class NIOVDE(NIO):
name = 'nio_vde' + str(nio_id) name = 'nio_vde' + str(nio_id)
self._control_file = control_file self._control_file = control_file
self._local_file = local_file self._local_file = local_file
NIO.__init__(self, name, hypervisor) super().__init__(name, hypervisor)
@classmethod @classmethod
def reset(cls): 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"): 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) # Set default values for this platform (must be the same as Dynamips)
self._ram = 64 self._ram = 64

View File

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

View File

@ -43,7 +43,7 @@ class EthernetHub(Bridge):
def __init__(self, name, device_id, project, manager, hypervisor=None): 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 = {} self._mappings = {}
def __json__(self): def __json__(self):

View File

@ -28,25 +28,37 @@ from .iou_vm import IOUVM
class IOU(BaseManager): class IOU(BaseManager):
_VM_CLASS = IOUVM _VM_CLASS = IOUVM
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self._free_application_ids = list(range(1, 512)) self._free_application_ids = list(range(1, 512))
self._used_application_ids = {} self._used_application_ids = {}
@asyncio.coroutine @asyncio.coroutine
def create_vm(self, *args, **kwargs): def create_vm(self, *args, **kwargs):
"""
Creates a new IOU VM.
:returns: IOUVM instance
"""
vm = yield from super().create_vm(*args, **kwargs) vm = yield from super().create_vm(*args, **kwargs)
try: try:
self._used_application_ids[vm.id] = self._free_application_ids.pop(0) self._used_application_ids[vm.id] = self._free_application_ids.pop(0)
except IndexError: 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 return vm
@asyncio.coroutine @asyncio.coroutine
def close_vm(self, vm_id, *args, **kwargs): def close_vm(self, vm_id, *args, **kwargs):
"""
Closes an IOU VM.
:returns: IOUVM instance
"""
vm = self.get_vm(vm_id) vm = self.get_vm(vm_id)
if vm_id in self._used_application_ids: if vm_id in self._used_application_ids:
@ -58,10 +70,11 @@ class IOU(BaseManager):
def get_application_id(self, vm_id): 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 :param vm_id: IOU VM identifier
:returns: IOU MAC id
:returns: IOU application identifier
""" """
return self._used_application_ids.get(vm_id, 1) 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/>. # 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 from ..vm_error import VMError

View File

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

View File

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

View File

@ -261,7 +261,7 @@ class PortManager:
:param force_remove: Force port removal even on Darwnin :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: if sys.platform.startswith("darwin") and force_remove is False:
return return

View File

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

View File

@ -32,14 +32,15 @@ from .qemu_vm import QemuVM
class Qemu(BaseManager): class Qemu(BaseManager):
_VM_CLASS = QemuVM _VM_CLASS = QemuVM
@staticmethod @staticmethod
def binary_list(): 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 = [] qemus = []
@ -82,7 +83,8 @@ class Qemu(BaseManager):
def _get_qemu_version(qemu_path): def _get_qemu_version(qemu_path):
""" """
Gets the Qemu version. Gets the Qemu version.
:param qemu_path: path to Qemu
:param qemu_path: path to Qemu executable.
""" """
if sys.platform.startswith("win"): if sys.platform.startswith("win"):

View File

@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # 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 from ..vm_error import VMError

View File

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

View File

@ -28,26 +28,38 @@ from .vpcs_vm import VPCSVM
class VPCS(BaseManager): class VPCS(BaseManager):
_VM_CLASS = VPCSVM _VM_CLASS = VPCSVM
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self._free_mac_ids = {} self._free_mac_ids = {}
self._used_mac_ids = {} self._used_mac_ids = {}
@asyncio.coroutine @asyncio.coroutine
def create_vm(self, *args, **kwargs): def create_vm(self, *args, **kwargs):
"""
Creates a new VPCS VM.
:returns: VPCSVM instance
"""
vm = yield from super().create_vm(*args, **kwargs) vm = yield from super().create_vm(*args, **kwargs)
self._free_mac_ids.setdefault(vm.project.id, list(range(0, 255))) self._free_mac_ids.setdefault(vm.project.id, list(range(0, 255)))
try: try:
self._used_mac_ids[vm.id] = self._free_mac_ids[vm.project.id].pop(0) self._used_mac_ids[vm.id] = self._free_mac_ids[vm.project.id].pop(0)
except IndexError: 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 return vm
@asyncio.coroutine @asyncio.coroutine
def close_vm(self, vm_id, *args, **kwargs): def close_vm(self, vm_id, *args, **kwargs):
"""
Closes a VPCS VM.
:returns: VPCSVM instance
"""
vm = self.get_vm(vm_id) vm = self.get_vm(vm_id)
i = self._used_mac_ids[vm_id] i = self._used_mac_ids[vm_id]
@ -59,10 +71,11 @@ class VPCS(BaseManager):
def get_mac_id(self, vm_id): 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 :param vm_id: VPCS VM identifier
:returns: VPCS MAC id
:returns: VPCS MAC identifier
""" """
return self._used_mac_ids.get(vm_id, 1) 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/>. # 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 from ..vm_error import VMError

View File

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

View File

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