mirror of
https://github.com/GNS3/gns3-server.git
synced 2024-12-19 04:47:54 +00:00
Some spring cleaning.
This commit is contained in:
parent
1c4202187a
commit
7d7972afb3
@ -25,7 +25,8 @@ class EthernetAdapter(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self, interfaces=1):
|
||||
Adapter.__init__(self, interfaces=interfaces)
|
||||
|
||||
super().__init__(interfaces)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class SerialAdapter(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self, interfaces=1):
|
||||
Adapter.__init__(self, interfaces=interfaces)
|
||||
|
||||
super().__init__(interfaces)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class C7200_IO_2FE(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=2)
|
||||
|
||||
super().__init__(interfaces=2)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class C7200_IO_FE(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=1)
|
||||
|
||||
super().__init__(interfaces=1)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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):
|
||||
|
@ -25,7 +25,8 @@ class NM_16ESW(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=16)
|
||||
|
||||
super().__init__(interfaces=16)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class NM_1E(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=1)
|
||||
|
||||
super().__init__(interfaces=1)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class NM_1FE_TX(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=1)
|
||||
|
||||
super().__init__(interfaces=1)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class NM_4E(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=4)
|
||||
|
||||
super().__init__(interfaces=4)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class NM_4T(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=4)
|
||||
|
||||
super().__init__(interfaces=4)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class PA_2FE_TX(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=2)
|
||||
|
||||
super().__init__(interfaces=2)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class PA_4E(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=4)
|
||||
|
||||
super().__init__(interfaces=4)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class PA_4T(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=4)
|
||||
|
||||
super().__init__(interfaces=4)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class PA_8E(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=8)
|
||||
|
||||
super().__init__(interfaces=8)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class PA_8T(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=8)
|
||||
|
||||
super().__init__(interfaces=8)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class PA_A1(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=1)
|
||||
|
||||
super().__init__(interfaces=1)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class PA_FE_TX(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=1)
|
||||
|
||||
super().__init__(interfaces=1)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class PA_GE(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=1)
|
||||
|
||||
super().__init__(interfaces=1)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -25,7 +25,8 @@ class PA_POS_OC3(Adapter):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
Adapter.__init__(self, interfaces=1)
|
||||
|
||||
super().__init__(interfaces=1)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -32,7 +32,7 @@ class NIOGenericEthernet(NIO):
|
||||
|
||||
def __init__(self, ethernet_device):
|
||||
|
||||
NIO.__init__(self)
|
||||
super().__init__()
|
||||
self._ethernet_device = ethernet_device
|
||||
|
||||
@property
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"):
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
|
||||
import asyncio
|
||||
import shutil
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
|
Loading…
Reference in New Issue
Block a user