mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-24 09:18:08 +00:00
Support for console type "none".
This commit is contained in:
parent
54661c50b2
commit
e52775fa3a
@ -82,6 +82,8 @@ class BaseNode:
|
|||||||
if console_type == "vnc":
|
if console_type == "vnc":
|
||||||
# VNC is a special case and the range must be 5900-6000
|
# VNC is a special case and the range must be 5900-6000
|
||||||
self._console = self._manager.port_manager.reserve_tcp_port(self._console, self._project, port_range_start=5900, port_range_end=6000)
|
self._console = self._manager.port_manager.reserve_tcp_port(self._console, self._project, port_range_start=5900, port_range_end=6000)
|
||||||
|
elif console_type == "none":
|
||||||
|
self._console = None
|
||||||
else:
|
else:
|
||||||
self._console = self._manager.port_manager.reserve_tcp_port(self._console, self._project)
|
self._console = self._manager.port_manager.reserve_tcp_port(self._console, self._project)
|
||||||
|
|
||||||
@ -291,9 +293,7 @@ class BaseNode:
|
|||||||
Stop the node process.
|
Stop the node process.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self._wrapper_telnet_server:
|
yield from self.stop_wrap_console()
|
||||||
self._wrapper_telnet_server.close()
|
|
||||||
yield from self._wrapper_telnet_server.wait_closed()
|
|
||||||
self.status = "stopped"
|
self.status = "stopped"
|
||||||
|
|
||||||
def suspend(self):
|
def suspend(self):
|
||||||
@ -353,6 +353,16 @@ class BaseNode:
|
|||||||
server = AsyncioTelnetServer(reader=reader, writer=writer, binary=True, echo=True)
|
server = AsyncioTelnetServer(reader=reader, writer=writer, binary=True, echo=True)
|
||||||
self._wrapper_telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
self._wrapper_telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def stop_wrap_console(self):
|
||||||
|
"""
|
||||||
|
Stops the telnet proxy.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._wrapper_telnet_server:
|
||||||
|
self._wrapper_telnet_server.close()
|
||||||
|
yield from self._wrapper_telnet_server.wait_closed()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def allocate_aux(self):
|
def allocate_aux(self):
|
||||||
"""
|
"""
|
||||||
@ -418,7 +428,7 @@ class BaseNode:
|
|||||||
:params console: Console port (integer) or None to free the port
|
:params console: Console port (integer) or None to free the port
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if console == self._console:
|
if console == self._console or self._console_type == "none":
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._console_type == "vnc" and console is not None and console < 5900:
|
if self._console_type == "vnc" and console is not None and console < 5900:
|
||||||
@ -470,10 +480,11 @@ class BaseNode:
|
|||||||
self._console = self._manager.port_manager.get_free_tcp_port(self._project)
|
self._console = self._manager.port_manager.get_free_tcp_port(self._project)
|
||||||
|
|
||||||
self._console_type = console_type
|
self._console_type = console_type
|
||||||
log.info("{module}: '{name}' [{id}]: console type set to {console_type}".format(module=self.manager.module_name,
|
log.info("{module}: '{name}' [{id}]: console type set to {console_type} (console port is {console})".format(module=self.manager.module_name,
|
||||||
name=self.name,
|
name=self.name,
|
||||||
id=self.id,
|
id=self.id,
|
||||||
console_type=console_type))
|
console_type=console_type,
|
||||||
|
console=self.console))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ubridge(self):
|
def ubridge(self):
|
||||||
|
@ -46,9 +46,9 @@ class C1700(Router):
|
|||||||
1710 is not supported.
|
1710 is not supported.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, aux=None, chassis="1720"):
|
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, console_type="telnet", aux=None, chassis="1720"):
|
||||||
|
|
||||||
super().__init__(name, node_id, project, manager, dynamips_id, console, aux, platform="c1700")
|
super().__init__(name, node_id, project, manager, dynamips_id, console, console_type, 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
|
||||||
|
@ -61,9 +61,9 @@ class C2600(Router):
|
|||||||
"2650XM": C2600_MB_1FE,
|
"2650XM": C2600_MB_1FE,
|
||||||
"2651XM": C2600_MB_2FE}
|
"2651XM": C2600_MB_2FE}
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, aux=None, chassis="2610"):
|
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, console_type="telnet", aux=None, chassis="2610"):
|
||||||
|
|
||||||
super().__init__(name, node_id, project, manager, dynamips_id, console, aux, platform="c2600")
|
super().__init__(name, node_id, project, manager, dynamips_id, console, console_type, 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
|
||||||
|
@ -43,9 +43,9 @@ class C2691(Router):
|
|||||||
:param aux: auxiliary console port
|
:param aux: auxiliary console port
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, aux=None, chassis=None):
|
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, console_type="telnet", aux=None, chassis=None):
|
||||||
|
|
||||||
super().__init__(name, node_id, project, manager, dynamips_id, console, aux, platform="c2691")
|
super().__init__(name, node_id, project, manager, dynamips_id, console, console_type, 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
|
||||||
|
@ -44,9 +44,9 @@ class C3600(Router):
|
|||||||
3620, 3640 or 3660 (default = 3640).
|
3620, 3640 or 3660 (default = 3640).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, aux=None, chassis="3640"):
|
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, console_type="telnet", aux=None, chassis="3640"):
|
||||||
|
|
||||||
super().__init__(name, node_id, project, manager, dynamips_id, console, aux, platform="c3600")
|
super().__init__(name, node_id, project, manager, dynamips_id, console, console_type, 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
|
||||||
|
@ -43,9 +43,9 @@ class C3725(Router):
|
|||||||
:param aux: auxiliary console port
|
:param aux: auxiliary console port
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, aux=None, chassis=None):
|
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, console_type="telnet", aux=None, chassis=None):
|
||||||
|
|
||||||
super().__init__(name, node_id, project, manager, dynamips_id, console, aux, platform="c3725")
|
super().__init__(name, node_id, project, manager, dynamips_id, console, console_type, 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
|
||||||
|
@ -43,9 +43,9 @@ class C3745(Router):
|
|||||||
:param aux: auxiliary console port
|
:param aux: auxiliary console port
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, aux=None, chassis=None):
|
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, console_type="telnet", aux=None, chassis=None):
|
||||||
|
|
||||||
super().__init__(name, node_id, project, manager, dynamips_id, console, aux, platform="c3745")
|
super().__init__(name, node_id, project, manager, dynamips_id, console, console_type, 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
|
||||||
|
@ -46,9 +46,9 @@ class C7200(Router):
|
|||||||
:param npe: Default NPE
|
:param npe: Default NPE
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, aux=None, npe="npe-400", chassis=None):
|
def __init__(self, name, node_id, project, manager, dynamips_id, console=None, console_type="telnet", aux=None, npe="npe-400", chassis=None):
|
||||||
|
|
||||||
super().__init__(name, node_id, project, manager, dynamips_id, console, aux, platform="c7200")
|
super().__init__(name, node_id, project, manager, dynamips_id, console, console_type, 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
|
||||||
|
@ -80,14 +80,21 @@ class EthernetSwitch(Device):
|
|||||||
:param hypervisor: Dynamips hypervisor instance
|
:param hypervisor: Dynamips hypervisor instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, ports=None, hypervisor=None):
|
def __init__(self, name, node_id, project, manager, console=None, console_type="telnet", ports=None, hypervisor=None):
|
||||||
|
|
||||||
super().__init__(name, node_id, project, manager, hypervisor)
|
super().__init__(name, node_id, project, manager, hypervisor)
|
||||||
self._nios = {}
|
self._nios = {}
|
||||||
self._mappings = {}
|
self._mappings = {}
|
||||||
self._telnet_console = None
|
self._telnet_console = None
|
||||||
self._telnet_shell = None
|
self._telnet_shell = None
|
||||||
self._console = self._manager.port_manager.get_free_tcp_port(self._project)
|
self._console = console
|
||||||
|
self._console_type = console_type
|
||||||
|
|
||||||
|
if self._console is not None:
|
||||||
|
self._console = self._manager.port_manager.reserve_tcp_port(self._console, self._project)
|
||||||
|
else:
|
||||||
|
self._console = self._manager.port_manager.get_free_tcp_port(self._project)
|
||||||
|
|
||||||
if ports is None:
|
if ports is None:
|
||||||
# create 8 ports by default
|
# create 8 ports by default
|
||||||
self._ports = []
|
self._ports = []
|
||||||
@ -103,7 +110,7 @@ class EthernetSwitch(Device):
|
|||||||
|
|
||||||
ethernet_switch_info = {"name": self.name,
|
ethernet_switch_info = {"name": self.name,
|
||||||
"console": self.console,
|
"console": self.console,
|
||||||
"console_type": "telnet",
|
"console_type": self.console_type,
|
||||||
"node_id": self.id,
|
"node_id": self.id,
|
||||||
"project_id": self.project.id,
|
"project_id": self.project.id,
|
||||||
"ports_mapping": self._ports,
|
"ports_mapping": self._ports,
|
||||||
@ -115,8 +122,16 @@ class EthernetSwitch(Device):
|
|||||||
return self._console
|
return self._console
|
||||||
|
|
||||||
@console.setter
|
@console.setter
|
||||||
def console(self, val):
|
def console(self, console):
|
||||||
self._console = val
|
self._console = console
|
||||||
|
|
||||||
|
@property
|
||||||
|
def console_type(self):
|
||||||
|
return self._console_type
|
||||||
|
|
||||||
|
@console_type.setter
|
||||||
|
def console_type(self, console_type):
|
||||||
|
self._console_type = console_type
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ports_mapping(self):
|
def ports_mapping(self):
|
||||||
@ -214,6 +229,7 @@ class EthernetSwitch(Device):
|
|||||||
"""
|
"""
|
||||||
Deletes this Ethernet switch.
|
Deletes this Ethernet switch.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
yield from self._telnet.close()
|
yield from self._telnet.close()
|
||||||
self._telnet_server.close()
|
self._telnet_server.close()
|
||||||
|
|
||||||
|
@ -62,9 +62,9 @@ class Router(BaseNode):
|
|||||||
2: "running",
|
2: "running",
|
||||||
3: "suspended"}
|
3: "suspended"}
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, dynamips_id=None, console=None, aux=None, platform="c7200", hypervisor=None, ghost_flag=False):
|
def __init__(self, name, node_id, project, manager, dynamips_id=None, console=None, console_type="telnet", aux=None, platform="c7200", hypervisor=None, ghost_flag=False):
|
||||||
|
|
||||||
super().__init__(name, node_id, project, manager, console=console, aux=aux, allocate_aux=aux)
|
super().__init__(name, node_id, project, manager, console=console, console_type=console_type, aux=aux, allocate_aux=aux)
|
||||||
|
|
||||||
self._working_directory = os.path.join(self.project.module_working_directory(self.manager.module_name.lower()), self.id)
|
self._working_directory = os.path.join(self.project.module_working_directory(self.manager.module_name.lower()), self.id)
|
||||||
try:
|
try:
|
||||||
@ -162,7 +162,7 @@ class Router(BaseNode):
|
|||||||
"auto_delete_disks": self._auto_delete_disks,
|
"auto_delete_disks": self._auto_delete_disks,
|
||||||
"status": self.status,
|
"status": self.status,
|
||||||
"console": self.console,
|
"console": self.console,
|
||||||
"console_type": "telnet",
|
"console_type": self.console_type,
|
||||||
"aux": self.aux,
|
"aux": self.aux,
|
||||||
"mac_addr": self._mac_addr,
|
"mac_addr": self._mac_addr,
|
||||||
"system_id": self._system_id}
|
"system_id": self._system_id}
|
||||||
@ -186,6 +186,7 @@ class Router(BaseNode):
|
|||||||
else:
|
else:
|
||||||
router_info["wic" + str(wic_slot_number)] = None
|
router_info["wic" + str(wic_slot_number)] = None
|
||||||
|
|
||||||
|
|
||||||
return router_info
|
return router_info
|
||||||
|
|
||||||
def _memory_changed(self, path):
|
def _memory_changed(self, path):
|
||||||
@ -223,7 +224,8 @@ class Router(BaseNode):
|
|||||||
platform=self._platform,
|
platform=self._platform,
|
||||||
id=self._id))
|
id=self._id))
|
||||||
|
|
||||||
yield from self._hypervisor.send('vm set_con_tcp_port "{name}" {console}'.format(name=self._name, console=self._console))
|
if self._console:
|
||||||
|
yield from self._hypervisor.send('vm set_con_tcp_port "{name}" {console}'.format(name=self._name, console=self._console))
|
||||||
|
|
||||||
if self.aux is not None:
|
if self.aux is not None:
|
||||||
yield from self._hypervisor.send('vm set_aux_tcp_port "{name}" {aux}'.format(name=self._name, aux=self.aux))
|
yield from self._hypervisor.send('vm set_aux_tcp_port "{name}" {aux}'.format(name=self._name, aux=self.aux))
|
||||||
@ -965,6 +967,26 @@ class Router(BaseNode):
|
|||||||
self.console = console
|
self.console = console
|
||||||
yield from self._hypervisor.send('vm set_con_tcp_port "{name}" {console}'.format(name=self._name, console=self.console))
|
yield from self._hypervisor.send('vm set_con_tcp_port "{name}" {console}'.format(name=self._name, console=self.console))
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def set_console_type(self, console_type):
|
||||||
|
"""
|
||||||
|
Sets the console type.
|
||||||
|
|
||||||
|
:param console_type: console type
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.console_type != console_type:
|
||||||
|
status = yield from self.get_status()
|
||||||
|
if status == "running":
|
||||||
|
raise DynamipsError('"{name}" must be stopped to change the console type to {console_type}'.format(name=self._name,
|
||||||
|
console_type=console_type))
|
||||||
|
|
||||||
|
|
||||||
|
self.console_type = console_type
|
||||||
|
|
||||||
|
if self._console and console_type == "telnet":
|
||||||
|
yield from self._hypervisor.send('vm set_con_tcp_port "{name}" {console}'.format(name=self._name, console=self._console))
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def set_aux(self, aux):
|
def set_aux(self, aux):
|
||||||
"""
|
"""
|
||||||
|
@ -63,11 +63,12 @@ class IOUVM(BaseNode):
|
|||||||
:param project: Project instance
|
:param project: Project instance
|
||||||
:param manager: Manager instance
|
:param manager: Manager instance
|
||||||
:param console: TCP console port
|
:param console: TCP console port
|
||||||
|
:param console_type: console type
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, application_id=None, path=None, console=None):
|
def __init__(self, name, node_id, project, manager, application_id=None, path=None, console=None, console_type="telnet"):
|
||||||
|
|
||||||
super().__init__(name, node_id, project, manager, console=console)
|
super().__init__(name, node_id, project, manager, console=console, console_type=console_type)
|
||||||
|
|
||||||
self._iou_process = None
|
self._iou_process = None
|
||||||
self._telnet_server = None
|
self._telnet_server = None
|
||||||
@ -219,7 +220,7 @@ class IOUVM(BaseNode):
|
|||||||
"node_id": self.id,
|
"node_id": self.id,
|
||||||
"node_directory": self.working_path,
|
"node_directory": self.working_path,
|
||||||
"console": self._console,
|
"console": self._console,
|
||||||
"console_type": "telnet",
|
"console_type": self._console_type,
|
||||||
"status": self.status,
|
"status": self.status,
|
||||||
"project_id": self.project.id,
|
"project_id": self.project.id,
|
||||||
"path": self.path,
|
"path": self.path,
|
||||||
@ -540,8 +541,9 @@ class IOUVM(BaseNode):
|
|||||||
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))
|
||||||
|
|
||||||
server = AsyncioTelnetServer(reader=self._iou_process.stdout, writer=self._iou_process.stdin, binary=True, echo=True)
|
if self.console and self.console_type == "telnet":
|
||||||
self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
server = AsyncioTelnetServer(reader=self._iou_process.stdout, writer=self._iou_process.stdin, binary=True, echo=True)
|
||||||
|
self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||||
|
|
||||||
# configure networking support
|
# configure networking support
|
||||||
yield from self._networking()
|
yield from self._networking()
|
||||||
@ -684,6 +686,19 @@ class IOUVM(BaseNode):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@BaseNode.console_type.setter
|
||||||
|
def console_type(self, new_console_type):
|
||||||
|
"""
|
||||||
|
Sets the console type for this IOU VM.
|
||||||
|
|
||||||
|
:param new_console_type: console type (string)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.is_running() and self.console_type != new_console_type:
|
||||||
|
raise IOUError('"{name}" must be stopped to change the console type to {new_console_type}'.format(name=self._name, new_console_type=new_console_type))
|
||||||
|
|
||||||
|
super(IOUVM, IOUVM).console_type.__set__(self, new_console_type)
|
||||||
|
|
||||||
def _create_netmap_config(self):
|
def _create_netmap_config(self):
|
||||||
"""
|
"""
|
||||||
Creates the NETMAP file.
|
Creates the NETMAP file.
|
||||||
|
@ -61,9 +61,9 @@ class QemuVM(BaseNode):
|
|||||||
:param project: Project instance
|
:param project: Project instance
|
||||||
:param manager: Manager instance
|
:param manager: Manager instance
|
||||||
:param console: TCP console port
|
:param console: TCP console port
|
||||||
|
:param console_type: Console type
|
||||||
:param qemu_path: path to the QEMU binary
|
:param qemu_path: path to the QEMU binary
|
||||||
:param platform: Platform to emulate
|
:param platform: Platform to emulate
|
||||||
:param console: TCP console port
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, linked_clone=True, qemu_path=None, console=None, console_type="telnet", platform=None):
|
def __init__(self, name, node_id, project, manager, linked_clone=True, qemu_path=None, console=None, console_type="telnet", platform=None):
|
||||||
@ -1412,6 +1412,19 @@ class QemuVM(BaseNode):
|
|||||||
|
|
||||||
return " ".join(self._build_command())
|
return " ".join(self._build_command())
|
||||||
|
|
||||||
|
@BaseNode.console_type.setter
|
||||||
|
def console_type(self, new_console_type):
|
||||||
|
"""
|
||||||
|
Sets the console type for this QEMU VM.
|
||||||
|
|
||||||
|
:param new_console_type: console type (string)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.is_running() and self.console_type != new_console_type:
|
||||||
|
raise QemuError('"{name}" must be stopped to change the console type to {new_console_type}'.format(name=self._name, new_console_type=new_console_type))
|
||||||
|
|
||||||
|
super(QemuVM, QemuVM).console_type.__set__(self, new_console_type)
|
||||||
|
|
||||||
def _serial_options(self):
|
def _serial_options(self):
|
||||||
|
|
||||||
if self._console:
|
if self._console:
|
||||||
|
@ -53,9 +53,9 @@ class VirtualBoxVM(BaseNode):
|
|||||||
VirtualBox VM implementation.
|
VirtualBox VM implementation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, vmname, linked_clone=False, console=None, adapters=0):
|
def __init__(self, name, node_id, project, manager, vmname, linked_clone=False, console=None, console_type="telnet", adapters=0):
|
||||||
|
|
||||||
super().__init__(name, node_id, project, manager, console=console, linked_clone=linked_clone, console_type="telnet")
|
super().__init__(name, node_id, project, manager, console=console, linked_clone=linked_clone, console_type=console_type)
|
||||||
|
|
||||||
self._maximum_adapters = 8
|
self._maximum_adapters = 8
|
||||||
self._system_properties = {}
|
self._system_properties = {}
|
||||||
@ -940,12 +940,14 @@ class VirtualBoxVM(BaseNode):
|
|||||||
"""
|
"""
|
||||||
Starts remote console support for this VM.
|
Starts remote console support for this VM.
|
||||||
"""
|
"""
|
||||||
self._remote_pipe = yield from asyncio_open_serial(self._get_pipe_name())
|
|
||||||
server = AsyncioTelnetServer(reader=self._remote_pipe,
|
if self.console and self.console_type == "telnet":
|
||||||
writer=self._remote_pipe,
|
self._remote_pipe = yield from asyncio_open_serial(self._get_pipe_name())
|
||||||
binary=True,
|
server = AsyncioTelnetServer(reader=self._remote_pipe,
|
||||||
echo=True)
|
writer=self._remote_pipe,
|
||||||
self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
binary=True,
|
||||||
|
echo=True)
|
||||||
|
self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def _stop_remote_console(self):
|
def _stop_remote_console(self):
|
||||||
@ -958,6 +960,19 @@ class VirtualBoxVM(BaseNode):
|
|||||||
self._remote_pipe.close()
|
self._remote_pipe.close()
|
||||||
self._telnet_server = None
|
self._telnet_server = None
|
||||||
|
|
||||||
|
@BaseNode.console_type.setter
|
||||||
|
def console_type(self, new_console_type):
|
||||||
|
"""
|
||||||
|
Sets the console type for this VirtualBox VM.
|
||||||
|
|
||||||
|
:param new_console_type: console type (string)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.is_running() and self.console_type != new_console_type:
|
||||||
|
raise VirtualBoxError('"{name}" must be stopped to change the console type to {new_console_type}'.format(name=self._name, new_console_type=new_console_type))
|
||||||
|
|
||||||
|
super(VirtualBoxVM, VirtualBoxVM).console_type.__set__(self, new_console_type)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def adapter_add_nio_binding(self, adapter_number, nio):
|
def adapter_add_nio_binding(self, adapter_number, nio):
|
||||||
"""
|
"""
|
||||||
|
@ -44,9 +44,9 @@ class VMwareVM(BaseNode):
|
|||||||
VMware VM implementation.
|
VMware VM implementation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, vmx_path, linked_clone=False, console=None):
|
def __init__(self, name, node_id, project, manager, vmx_path, linked_clone=False, console=None, console_type="telnet"):
|
||||||
|
|
||||||
super().__init__(name, node_id, project, manager, console=console, linked_clone=linked_clone)
|
super().__init__(name, node_id, project, manager, console=console, console_type=console_type, linked_clone=linked_clone)
|
||||||
|
|
||||||
self._vmx_pairs = OrderedDict()
|
self._vmx_pairs = OrderedDict()
|
||||||
self._telnet_server = None
|
self._telnet_server = None
|
||||||
@ -842,12 +842,11 @@ class VMwareVM(BaseNode):
|
|||||||
"""
|
"""
|
||||||
Starts remote console support for this VM.
|
Starts remote console support for this VM.
|
||||||
"""
|
"""
|
||||||
self._remote_pipe = yield from asyncio_open_serial(self._get_pipe_name())
|
|
||||||
server = AsyncioTelnetServer(reader=self._remote_pipe,
|
if self.console and self.console_type == "telnet":
|
||||||
writer=self._remote_pipe,
|
self._remote_pipe = yield from asyncio_open_serial(self._get_pipe_name())
|
||||||
binary=True,
|
server = AsyncioTelnetServer(reader=self._remote_pipe, writer=self._remote_pipe, binary=True, echo=True)
|
||||||
echo=True)
|
self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||||
self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def _stop_remote_console(self):
|
def _stop_remote_console(self):
|
||||||
@ -860,6 +859,19 @@ class VMwareVM(BaseNode):
|
|||||||
self._remote_pipe.close()
|
self._remote_pipe.close()
|
||||||
self._telnet_server = None
|
self._telnet_server = None
|
||||||
|
|
||||||
|
@BaseNode.console_type.setter
|
||||||
|
def console_type(self, new_console_type):
|
||||||
|
"""
|
||||||
|
Sets the console type for this VMware VM.
|
||||||
|
|
||||||
|
:param new_console_type: console type (string)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._started and self.console_type != new_console_type:
|
||||||
|
raise VMwareError('"{name}" must be stopped to change the console type to {new_console_type}'.format(name=self._name, new_console_type=new_console_type))
|
||||||
|
|
||||||
|
super(VMwareVM, VMwareVM).console_type.__set__(self, new_console_type)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def start_capture(self, adapter_number, output_file):
|
def start_capture(self, adapter_number, output_file):
|
||||||
"""
|
"""
|
||||||
|
@ -59,9 +59,9 @@ class VPCSVM(BaseNode):
|
|||||||
:param startup_script: content of the startup script file
|
:param startup_script: content of the startup script file
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, node_id, project, manager, console=None, startup_script=None):
|
def __init__(self, name, node_id, project, manager, console=None, console_type="telnet", startup_script=None):
|
||||||
|
|
||||||
super().__init__(name, node_id, project, manager, console=console, wrap_console=True)
|
super().__init__(name, node_id, project, manager, console=console, console_type=console_type, wrap_console=True)
|
||||||
self._process = None
|
self._process = None
|
||||||
self._vpcs_stdout_file = ""
|
self._vpcs_stdout_file = ""
|
||||||
self._vpcs_version = None
|
self._vpcs_version = None
|
||||||
@ -130,7 +130,7 @@ class VPCSVM(BaseNode):
|
|||||||
"node_directory": self.working_path,
|
"node_directory": self.working_path,
|
||||||
"status": self.status,
|
"status": self.status,
|
||||||
"console": self._console,
|
"console": self._console,
|
||||||
"console_type": "telnet",
|
"console_type": self._console_type,
|
||||||
"project_id": self.project.id,
|
"project_id": self.project.id,
|
||||||
"command_line": self.command_line}
|
"command_line": self.command_line}
|
||||||
|
|
||||||
@ -348,6 +348,19 @@ class VPCSVM(BaseNode):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@BaseNode.console_type.setter
|
||||||
|
def console_type(self, new_console_type):
|
||||||
|
"""
|
||||||
|
Sets the console type for this VPCS VM.
|
||||||
|
|
||||||
|
:param new_console_type: console type (string)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.is_running() and self.console_type != new_console_type:
|
||||||
|
raise VPCSError('"{name}" must be stopped to change the console type to {new_console_type}'.format(name=self._name, new_console_type=new_console_type))
|
||||||
|
|
||||||
|
super(VPCSVM, VPCSVM).console_type.__set__(self, new_console_type)
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def port_add_nio_binding(self, port_number, nio):
|
def port_add_nio_binding(self, port_number, nio):
|
||||||
"""
|
"""
|
||||||
|
@ -376,8 +376,8 @@ class Node:
|
|||||||
|
|
||||||
self._list_ports()
|
self._list_ports()
|
||||||
# We send notif only if object has changed
|
# We send notif only if object has changed
|
||||||
if old_json != self.__json__():
|
#if old_json != self.__json__():
|
||||||
self.project.controller.notification.emit("node.updated", self.__json__())
|
# self.project.controller.notification.emit("node.updated", self.__json__())
|
||||||
if update_compute:
|
if update_compute:
|
||||||
data = self._node_data(properties=compute_properties)
|
data = self._node_data(properties=compute_properties)
|
||||||
response = yield from self.put(None, data=data)
|
response = yield from self.put(None, data=data)
|
||||||
@ -389,6 +389,7 @@ class Node:
|
|||||||
"""
|
"""
|
||||||
Update the object with the remote node object
|
Update the object with the remote node object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for key, value in response.items():
|
for key, value in response.items():
|
||||||
if key == "console":
|
if key == "console":
|
||||||
self._console = value
|
self._console = value
|
||||||
|
@ -75,6 +75,7 @@ class DynamipsVMHandler:
|
|||||||
dynamips_id=request.json.get("dynamips_id"),
|
dynamips_id=request.json.get("dynamips_id"),
|
||||||
platform=platform,
|
platform=platform,
|
||||||
console=request.json.get("console"),
|
console=request.json.get("console"),
|
||||||
|
console_type=request.json.get("console_type", "telnet"),
|
||||||
aux=request.json.get("aux"),
|
aux=request.json.get("aux"),
|
||||||
chassis=request.json.pop("chassis", default_chassis),
|
chassis=request.json.pop("chassis", default_chassis),
|
||||||
node_type="dynamips")
|
node_type="dynamips")
|
||||||
|
@ -55,6 +55,8 @@ class EthernetSwitchHandler:
|
|||||||
node = yield from dynamips_manager.create_node(request.json.pop("name"),
|
node = yield from dynamips_manager.create_node(request.json.pop("name"),
|
||||||
request.match_info["project_id"],
|
request.match_info["project_id"],
|
||||||
request.json.get("node_id"),
|
request.json.get("node_id"),
|
||||||
|
console=request.json.get("console"),
|
||||||
|
console_type=request.json.get("console_type"),
|
||||||
node_type="ethernet_switch",
|
node_type="ethernet_switch",
|
||||||
ports=request.json.get("ports_mapping"))
|
ports=request.json.get("ports_mapping"))
|
||||||
|
|
||||||
@ -134,6 +136,8 @@ class EthernetSwitchHandler:
|
|||||||
if "ports_mapping" in request.json:
|
if "ports_mapping" in request.json:
|
||||||
node.ports_mapping = request.json["ports_mapping"]
|
node.ports_mapping = request.json["ports_mapping"]
|
||||||
yield from node.update_port_settings()
|
yield from node.update_port_settings()
|
||||||
|
if "console_type" in request.json:
|
||||||
|
node.console_type = request.json["console_type"]
|
||||||
|
|
||||||
# builtin_manager = Builtin.instance()
|
# builtin_manager = Builtin.instance()
|
||||||
# node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
# node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||||
|
@ -61,7 +61,8 @@ class IOUHandler:
|
|||||||
request.match_info["project_id"],
|
request.match_info["project_id"],
|
||||||
request.json.get("node_id"),
|
request.json.get("node_id"),
|
||||||
path=request.json.get("path"),
|
path=request.json.get("path"),
|
||||||
console=request.json.get("console"))
|
console=request.json.get("console"),
|
||||||
|
console_type=request.json.get("console_type", "telnet"))
|
||||||
|
|
||||||
for name, value in request.json.items():
|
for name, value in request.json.items():
|
||||||
if hasattr(vm, name) and getattr(vm, name) != value:
|
if hasattr(vm, name) and getattr(vm, name) != value:
|
||||||
|
@ -59,6 +59,7 @@ class VirtualBoxHandler:
|
|||||||
request.json.pop("vmname"),
|
request.json.pop("vmname"),
|
||||||
linked_clone=request.json.pop("linked_clone", False),
|
linked_clone=request.json.pop("linked_clone", False),
|
||||||
console=request.json.get("console", None),
|
console=request.json.get("console", None),
|
||||||
|
console_type=request.json.get("console_type", "telnet"),
|
||||||
adapters=request.json.get("adapters", 0))
|
adapters=request.json.get("adapters", 0))
|
||||||
|
|
||||||
if "ram" in request.json:
|
if "ram" in request.json:
|
||||||
|
@ -57,7 +57,8 @@ class VMwareHandler:
|
|||||||
request.json.get("node_id"),
|
request.json.get("node_id"),
|
||||||
request.json.pop("vmx_path"),
|
request.json.pop("vmx_path"),
|
||||||
linked_clone=request.json.pop("linked_clone"),
|
linked_clone=request.json.pop("linked_clone"),
|
||||||
console=request.json.get("console", None))
|
console=request.json.get("console", None),
|
||||||
|
console_type=request.json.get("console_type", "telnet"))
|
||||||
|
|
||||||
for name, value in request.json.items():
|
for name, value in request.json.items():
|
||||||
if name != "node_id":
|
if name != "node_id":
|
||||||
|
@ -54,6 +54,7 @@ class VPCSHandler:
|
|||||||
request.match_info["project_id"],
|
request.match_info["project_id"],
|
||||||
request.json.get("node_id"),
|
request.json.get("node_id"),
|
||||||
console=request.json.get("console"),
|
console=request.json.get("console"),
|
||||||
|
console_type=request.json.get("console_type", "telnet"),
|
||||||
startup_script=request.json.get("startup_script"))
|
startup_script=request.json.get("startup_script"))
|
||||||
response.set_status(201)
|
response.set_status(201)
|
||||||
response.json(vm)
|
response.json(vm)
|
||||||
@ -98,6 +99,7 @@ class VPCSHandler:
|
|||||||
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||||
vm.name = request.json.get("name", vm.name)
|
vm.name = request.json.get("name", vm.name)
|
||||||
vm.console = request.json.get("console", vm.console)
|
vm.console = request.json.get("console", vm.console)
|
||||||
|
vm.console_type = request.json.get("console_type", vm.console_type)
|
||||||
vm.updated()
|
vm.updated()
|
||||||
response.json(vm)
|
response.json(vm)
|
||||||
|
|
||||||
|
@ -121,13 +121,13 @@ VM_CREATE_SCHEMA = {
|
|||||||
},
|
},
|
||||||
"console": {
|
"console": {
|
||||||
"description": "Console TCP port",
|
"description": "Console TCP port",
|
||||||
"type": "integer",
|
"type": ["integer", "null"],
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 65535
|
"maximum": 65535
|
||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet"]
|
"enum": ["telnet", "none"]
|
||||||
},
|
},
|
||||||
"aux": {
|
"aux": {
|
||||||
"description": "Auxiliary console TCP port",
|
"description": "Auxiliary console TCP port",
|
||||||
@ -339,13 +339,13 @@ VM_UPDATE_SCHEMA = {
|
|||||||
},
|
},
|
||||||
"console": {
|
"console": {
|
||||||
"description": "Console TCP port",
|
"description": "Console TCP port",
|
||||||
"type": "integer",
|
"type": ["integer", "null"],
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 65535
|
"maximum": 65535
|
||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet"]
|
"enum": ["telnet", "none"]
|
||||||
},
|
},
|
||||||
"aux": {
|
"aux": {
|
||||||
"description": "Auxiliary console TCP port",
|
"description": "Auxiliary console TCP port",
|
||||||
@ -579,13 +579,13 @@ VM_OBJECT_SCHEMA = {
|
|||||||
},
|
},
|
||||||
"console": {
|
"console": {
|
||||||
"description": "Console TCP port",
|
"description": "Console TCP port",
|
||||||
"type": "integer",
|
"type": ["integer", "null"],
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 65535
|
"maximum": 65535
|
||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet"]
|
"enum": ["telnet", "none"]
|
||||||
},
|
},
|
||||||
"aux": {
|
"aux": {
|
||||||
"description": "Auxiliary console TCP port",
|
"description": "Auxiliary console TCP port",
|
||||||
|
@ -62,11 +62,11 @@ ETHERNET_SWITCH_CREATE_SCHEMA = {
|
|||||||
"description": "Console TCP port",
|
"description": "Console TCP port",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 65535,
|
"maximum": 65535,
|
||||||
"type": "integer"
|
"type": ["integer", "null"]
|
||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet"]
|
"enum": ["telnet", "none"]
|
||||||
},
|
},
|
||||||
"node_id": {
|
"node_id": {
|
||||||
"description": "Node UUID",
|
"description": "Node UUID",
|
||||||
@ -163,11 +163,11 @@ ETHERNET_SWITCH_OBJECT_SCHEMA = {
|
|||||||
"description": "Console TCP port",
|
"description": "Console TCP port",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 65535,
|
"maximum": 65535,
|
||||||
"type": "integer"
|
"type": ["integer", "null"]
|
||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet"]
|
"enum": ["telnet", "none"]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"additionalProperties": False,
|
"additionalProperties": False,
|
||||||
|
@ -44,7 +44,7 @@ IOU_CREATE_SCHEMA = {
|
|||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet", None]
|
"enum": ["telnet", "none", None]
|
||||||
},
|
},
|
||||||
"path": {
|
"path": {
|
||||||
"description": "Path of iou binary",
|
"description": "Path of iou binary",
|
||||||
@ -138,11 +138,11 @@ IOU_OBJECT_SCHEMA = {
|
|||||||
"description": "Console TCP port",
|
"description": "Console TCP port",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 65535,
|
"maximum": 65535,
|
||||||
"type": "integer"
|
"type": ["integer", "null"]
|
||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet"]
|
"enum": ["telnet", "none"]
|
||||||
},
|
},
|
||||||
"project_id": {
|
"project_id": {
|
||||||
"description": "Project UUID",
|
"description": "Project UUID",
|
||||||
|
@ -64,11 +64,11 @@ VBOX_CREATE_SCHEMA = {
|
|||||||
"description": "Console TCP port",
|
"description": "Console TCP port",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 65535,
|
"maximum": 65535,
|
||||||
"type": "integer"
|
"type": ["integer", "null"]
|
||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet"]
|
"enum": ["telnet", "none"]
|
||||||
},
|
},
|
||||||
"ram": {
|
"ram": {
|
||||||
"description": "Amount of RAM",
|
"description": "Amount of RAM",
|
||||||
@ -154,11 +154,11 @@ VBOX_OBJECT_SCHEMA = {
|
|||||||
"description": "Console TCP port",
|
"description": "Console TCP port",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 65535,
|
"maximum": 65535,
|
||||||
"type": "integer"
|
"type": ["integer", "null"]
|
||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet"]
|
"enum": ["telnet", "none"]
|
||||||
},
|
},
|
||||||
"ram": {
|
"ram": {
|
||||||
"description": "Amount of RAM",
|
"description": "Amount of RAM",
|
||||||
|
@ -46,11 +46,11 @@ VMWARE_CREATE_SCHEMA = {
|
|||||||
"description": "Console TCP port",
|
"description": "Console TCP port",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 65535,
|
"maximum": 65535,
|
||||||
"type": "integer"
|
"type": ["integer", "null"]
|
||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet"]
|
"enum": ["telnet", "none"]
|
||||||
},
|
},
|
||||||
"headless": {
|
"headless": {
|
||||||
"description": "Headless mode",
|
"description": "Headless mode",
|
||||||
@ -145,11 +145,11 @@ VMWARE_OBJECT_SCHEMA = {
|
|||||||
"description": "Console TCP port",
|
"description": "Console TCP port",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 65535,
|
"maximum": 65535,
|
||||||
"type": "integer"
|
"type": ["integer", "null"]
|
||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet"]
|
"enum": ["telnet", "none"]
|
||||||
},
|
},
|
||||||
"linked_clone": {
|
"linked_clone": {
|
||||||
"description": "Whether the VM is a linked clone or not",
|
"description": "Whether the VM is a linked clone or not",
|
||||||
|
@ -44,7 +44,7 @@ VPCS_CREATE_SCHEMA = {
|
|||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet"]
|
"enum": ["telnet", "none"]
|
||||||
},
|
},
|
||||||
"startup_script": {
|
"startup_script": {
|
||||||
"description": "Content of the VPCS startup script",
|
"description": "Content of the VPCS startup script",
|
||||||
@ -73,7 +73,7 @@ VPCS_UPDATE_SCHEMA = {
|
|||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet"]
|
"enum": ["telnet", "none"]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"additionalProperties": False,
|
"additionalProperties": False,
|
||||||
@ -108,11 +108,11 @@ VPCS_OBJECT_SCHEMA = {
|
|||||||
"description": "Console TCP port",
|
"description": "Console TCP port",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 65535,
|
"maximum": 65535,
|
||||||
"type": "integer"
|
"type": ["integer", "null"]
|
||||||
},
|
},
|
||||||
"console_type": {
|
"console_type": {
|
||||||
"description": "Console type",
|
"description": "Console type",
|
||||||
"enum": ["telnet"]
|
"enum": ["telnet", "none"]
|
||||||
},
|
},
|
||||||
"project_id": {
|
"project_id": {
|
||||||
"description": "Project UUID",
|
"description": "Project UUID",
|
||||||
|
Loading…
Reference in New Issue
Block a user