mirror of
https://github.com/GNS3/gns3-server.git
synced 2024-12-24 06:56:42 +00:00
Drop Python 3.4 and switch to async / await syntax for asyncio. Fixes #1425
This commit is contained in:
parent
8baa480b79
commit
76af98404a
@ -34,7 +34,6 @@ log = logging.getLogger(__name__)
|
||||
|
||||
from uuid import UUID, uuid4
|
||||
from gns3server.utils.interfaces import is_interface_up
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
from ..config import Config
|
||||
from ..utils.asyncio import wait_run_in_executor
|
||||
from ..utils import force_unix_path
|
||||
@ -130,15 +129,14 @@ class BaseManager:
|
||||
|
||||
return self._config
|
||||
|
||||
@asyncio.coroutine
|
||||
def unload(self):
|
||||
async def unload(self):
|
||||
|
||||
tasks = []
|
||||
for node_id in self._nodes.keys():
|
||||
tasks.append(asyncio_ensure_future(self.close_node(node_id)))
|
||||
tasks.append(asyncio.ensure_future(self.close_node(node_id)))
|
||||
|
||||
if tasks:
|
||||
done, _ = yield from asyncio.wait(tasks)
|
||||
done, _ = await asyncio.wait(tasks)
|
||||
for future in done:
|
||||
try:
|
||||
future.result()
|
||||
@ -179,8 +177,7 @@ class BaseManager:
|
||||
|
||||
return node
|
||||
|
||||
@asyncio.coroutine
|
||||
def convert_old_project(self, project, legacy_id, name):
|
||||
async def convert_old_project(self, project, legacy_id, name):
|
||||
"""
|
||||
Convert projects made before version 1.3
|
||||
|
||||
@ -199,7 +196,7 @@ class BaseManager:
|
||||
log.info("Converting old project...")
|
||||
try:
|
||||
log.info('Moving "{}" to "{}"'.format(legacy_project_files_path, new_project_files_path))
|
||||
yield from wait_run_in_executor(shutil.move, legacy_project_files_path, new_project_files_path)
|
||||
await wait_run_in_executor(shutil.move, legacy_project_files_path, new_project_files_path)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not move project files directory: {} to {} {}".format(legacy_project_files_path,
|
||||
new_project_files_path, e))
|
||||
@ -212,7 +209,7 @@ class BaseManager:
|
||||
log.info("Converting old remote project...")
|
||||
try:
|
||||
log.info('Moving "{}" to "{}"'.format(legacy_remote_project_path, new_remote_project_path))
|
||||
yield from wait_run_in_executor(shutil.move, legacy_remote_project_path, new_remote_project_path)
|
||||
await wait_run_in_executor(shutil.move, legacy_remote_project_path, new_remote_project_path)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not move directory: {} to {} {}".format(legacy_remote_project_path,
|
||||
new_remote_project_path, e))
|
||||
@ -226,15 +223,14 @@ class BaseManager:
|
||||
if os.path.exists(legacy_vm_working_path) and not os.path.exists(new_vm_working_path):
|
||||
try:
|
||||
log.info('Moving "{}" to "{}"'.format(legacy_vm_working_path, new_vm_working_path))
|
||||
yield from wait_run_in_executor(shutil.move, legacy_vm_working_path, new_vm_working_path)
|
||||
await wait_run_in_executor(shutil.move, legacy_vm_working_path, new_vm_working_path)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not move vm working directory: {} to {} {}".format(legacy_vm_working_path,
|
||||
new_vm_working_path, e))
|
||||
|
||||
return new_id
|
||||
|
||||
@asyncio.coroutine
|
||||
def create_node(self, name, project_id, node_id, *args, **kwargs):
|
||||
async def create_node(self, name, project_id, node_id, *args, **kwargs):
|
||||
"""
|
||||
Create a new node
|
||||
|
||||
@ -249,23 +245,22 @@ class BaseManager:
|
||||
project = ProjectManager.instance().get_project(project_id)
|
||||
if node_id and isinstance(node_id, int):
|
||||
# old project
|
||||
with (yield from BaseManager._convert_lock):
|
||||
node_id = yield from self.convert_old_project(project, node_id, name)
|
||||
async with BaseManager._convert_lock:
|
||||
node_id = await self.convert_old_project(project, node_id, name)
|
||||
|
||||
if not node_id:
|
||||
node_id = str(uuid4())
|
||||
|
||||
node = self._NODE_CLASS(name, node_id, project, self, *args, **kwargs)
|
||||
if asyncio.iscoroutinefunction(node.create):
|
||||
yield from node.create()
|
||||
await node.create()
|
||||
else:
|
||||
node.create()
|
||||
self._nodes[node.id] = node
|
||||
project.add_node(node)
|
||||
return node
|
||||
|
||||
@asyncio.coroutine
|
||||
def duplicate_node(self, source_node_id, destination_node_id):
|
||||
async def duplicate_node(self, source_node_id, destination_node_id):
|
||||
"""
|
||||
Duplicate a node
|
||||
|
||||
@ -296,8 +291,7 @@ class BaseManager:
|
||||
|
||||
return destination_node
|
||||
|
||||
@asyncio.coroutine
|
||||
def close_node(self, node_id):
|
||||
async def close_node(self, node_id):
|
||||
"""
|
||||
Close a node
|
||||
|
||||
@ -308,13 +302,12 @@ class BaseManager:
|
||||
|
||||
node = self.get_node(node_id)
|
||||
if asyncio.iscoroutinefunction(node.close):
|
||||
yield from node.close()
|
||||
await node.close()
|
||||
else:
|
||||
node.close()
|
||||
return node
|
||||
|
||||
@asyncio.coroutine
|
||||
def project_closing(self, project):
|
||||
async def project_closing(self, project):
|
||||
"""
|
||||
Called when a project is about to be closed.
|
||||
|
||||
@ -323,8 +316,7 @@ class BaseManager:
|
||||
|
||||
pass
|
||||
|
||||
@asyncio.coroutine
|
||||
def project_closed(self, project):
|
||||
async def project_closed(self, project):
|
||||
"""
|
||||
Called when a project is closed.
|
||||
|
||||
@ -335,8 +327,7 @@ class BaseManager:
|
||||
if node.id in self._nodes:
|
||||
del self._nodes[node.id]
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete_node(self, node_id):
|
||||
async def delete_node(self, node_id):
|
||||
"""
|
||||
Delete a node. The node working directory will be destroyed when a commit is received.
|
||||
|
||||
@ -347,11 +338,11 @@ class BaseManager:
|
||||
node = None
|
||||
try:
|
||||
node = self.get_node(node_id)
|
||||
yield from self.close_node(node_id)
|
||||
await self.close_node(node_id)
|
||||
finally:
|
||||
if node:
|
||||
node.project.emit("node.deleted", node)
|
||||
yield from node.project.remove_node(node)
|
||||
await node.project.remove_node(node)
|
||||
if node.id in self._nodes:
|
||||
del self._nodes[node.id]
|
||||
return node
|
||||
@ -526,8 +517,7 @@ class BaseManager:
|
||||
return relpath
|
||||
return path
|
||||
|
||||
@asyncio.coroutine
|
||||
def list_images(self):
|
||||
async def list_images(self):
|
||||
"""
|
||||
Return the list of available images for this node type
|
||||
|
||||
@ -548,8 +538,7 @@ class BaseManager:
|
||||
return default_images_directory(self._NODE_TYPE)
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def write_image(self, filename, stream):
|
||||
async def write_image(self, filename, stream):
|
||||
|
||||
directory = self.get_images_directory()
|
||||
path = os.path.abspath(os.path.join(directory, *os.path.split(filename)))
|
||||
@ -563,13 +552,13 @@ class BaseManager:
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(tmp_path, 'wb') as f:
|
||||
while True:
|
||||
packet = yield from stream.read(4096)
|
||||
packet = await stream.read(4096)
|
||||
if not packet:
|
||||
break
|
||||
f.write(packet)
|
||||
os.chmod(tmp_path, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC)
|
||||
shutil.move(tmp_path, path)
|
||||
yield from cancellable_wait_run_in_executor(md5sum, path)
|
||||
await cancellable_wait_run_in_executor(md5sum, path)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPConflict(text="Could not write image: {} because {}".format(filename, e))
|
||||
|
||||
|
@ -273,8 +273,7 @@ class BaseNode:
|
||||
name=self.name,
|
||||
id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Delete the node (including all its files).
|
||||
"""
|
||||
@ -285,7 +284,7 @@ class BaseNode:
|
||||
directory = self.project.node_working_directory(self)
|
||||
if os.path.exists(directory):
|
||||
try:
|
||||
yield from wait_run_in_executor(shutil.rmtree, directory, onerror=set_rw)
|
||||
await wait_run_in_executor(shutil.rmtree, directory, onerror=set_rw)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not delete the node working directory: {}".format(e))
|
||||
|
||||
@ -296,13 +295,12 @@ class BaseNode:
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stop the node process.
|
||||
"""
|
||||
|
||||
yield from self.stop_wrap_console()
|
||||
await self.stop_wrap_console()
|
||||
self.status = "stopped"
|
||||
|
||||
def suspend(self):
|
||||
@ -312,8 +310,7 @@ class BaseNode:
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Close the node process.
|
||||
"""
|
||||
@ -339,8 +336,7 @@ class BaseNode:
|
||||
self._closed = True
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_wrap_console(self):
|
||||
async def start_wrap_console(self):
|
||||
"""
|
||||
Start a telnet proxy for the console allowing multiple client
|
||||
connected at the same time
|
||||
@ -351,27 +347,26 @@ class BaseNode:
|
||||
remaining_trial = 60
|
||||
while True:
|
||||
try:
|
||||
(reader, writer) = yield from asyncio.open_connection(host="127.0.0.1", port=self._internal_console_port)
|
||||
(reader, writer) = await asyncio.open_connection(host="127.0.0.1", port=self._internal_console_port)
|
||||
break
|
||||
except (OSError, ConnectionRefusedError) as e:
|
||||
if remaining_trial <= 0:
|
||||
raise e
|
||||
yield from asyncio.sleep(0.1)
|
||||
await asyncio.sleep(0.1)
|
||||
remaining_trial -= 1
|
||||
yield from AsyncioTelnetServer.write_client_intro(writer, echo=True)
|
||||
await AsyncioTelnetServer.write_client_intro(writer, echo=True)
|
||||
server = AsyncioTelnetServer(reader=reader, writer=writer, binary=True, echo=True)
|
||||
# warning: this will raise OSError exception if there is a problem...
|
||||
self._wrapper_telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
self._wrapper_telnet_server = await asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_wrap_console(self):
|
||||
async 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()
|
||||
await self._wrapper_telnet_server.wait_closed()
|
||||
|
||||
@property
|
||||
def allocate_aux(self):
|
||||
@ -530,8 +525,7 @@ class BaseNode:
|
||||
path = shutil.which(path)
|
||||
return path
|
||||
|
||||
@asyncio.coroutine
|
||||
def _ubridge_send(self, command):
|
||||
async def _ubridge_send(self, command):
|
||||
"""
|
||||
Sends a command to uBridge hypervisor.
|
||||
|
||||
@ -539,17 +533,16 @@ class BaseNode:
|
||||
"""
|
||||
|
||||
if not self._ubridge_hypervisor or not self._ubridge_hypervisor.is_running():
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
if not self._ubridge_hypervisor or not self._ubridge_hypervisor.is_running():
|
||||
raise NodeError("Cannot send command '{}': uBridge is not running".format(command))
|
||||
try:
|
||||
yield from self._ubridge_hypervisor.send(command)
|
||||
await self._ubridge_hypervisor.send(command)
|
||||
except UbridgeError as e:
|
||||
raise UbridgeError("Error while sending command '{}': {}: {}".format(command, e, self._ubridge_hypervisor.read_stdout()))
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def _start_ubridge(self):
|
||||
async def _start_ubridge(self):
|
||||
"""
|
||||
Starts uBridge (handles connections to and from this node).
|
||||
"""
|
||||
@ -569,24 +562,22 @@ class BaseNode:
|
||||
if not self.ubridge:
|
||||
self._ubridge_hypervisor = Hypervisor(self._project, self.ubridge_path, self.working_dir, server_host)
|
||||
log.info("Starting new uBridge hypervisor {}:{}".format(self._ubridge_hypervisor.host, self._ubridge_hypervisor.port))
|
||||
yield from self._ubridge_hypervisor.start()
|
||||
await self._ubridge_hypervisor.start()
|
||||
if self._ubridge_hypervisor:
|
||||
log.info("Hypervisor {}:{} has successfully started".format(self._ubridge_hypervisor.host, self._ubridge_hypervisor.port))
|
||||
yield from self._ubridge_hypervisor.connect()
|
||||
await self._ubridge_hypervisor.connect()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _stop_ubridge(self):
|
||||
async def _stop_ubridge(self):
|
||||
"""
|
||||
Stops uBridge.
|
||||
"""
|
||||
|
||||
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
|
||||
log.info("Stopping uBridge hypervisor {}:{}".format(self._ubridge_hypervisor.host, self._ubridge_hypervisor.port))
|
||||
yield from self._ubridge_hypervisor.stop()
|
||||
await self._ubridge_hypervisor.stop()
|
||||
self._ubridge_hypervisor = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_ubridge_udp_connection(self, bridge_name, source_nio, destination_nio):
|
||||
async def add_ubridge_udp_connection(self, bridge_name, source_nio, destination_nio):
|
||||
"""
|
||||
Creates an UDP connection in uBridge.
|
||||
|
||||
@ -595,43 +586,41 @@ class BaseNode:
|
||||
:param destination_nio: destination NIO instance
|
||||
"""
|
||||
|
||||
yield from self._ubridge_send("bridge create {name}".format(name=bridge_name))
|
||||
await self._ubridge_send("bridge create {name}".format(name=bridge_name))
|
||||
|
||||
if not isinstance(destination_nio, NIOUDP):
|
||||
raise NodeError("Destination NIO is not UDP")
|
||||
|
||||
yield from self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
lport=source_nio.lport,
|
||||
rhost=source_nio.rhost,
|
||||
rport=source_nio.rport))
|
||||
|
||||
yield from self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
lport=destination_nio.lport,
|
||||
rhost=destination_nio.rhost,
|
||||
rport=destination_nio.rport))
|
||||
|
||||
if destination_nio.capturing:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{pcap_file}"'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge start_capture {name} "{pcap_file}"'.format(name=bridge_name,
|
||||
pcap_file=destination_nio.pcap_output_file))
|
||||
|
||||
yield from self._ubridge_send('bridge start {name}'.format(name=bridge_name))
|
||||
yield from self._ubridge_apply_filters(bridge_name, destination_nio.filters)
|
||||
await self._ubridge_send('bridge start {name}'.format(name=bridge_name))
|
||||
await self._ubridge_apply_filters(bridge_name, destination_nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_ubridge_udp_connection(self, bridge_name, source_nio, destination_nio):
|
||||
async def update_ubridge_udp_connection(self, bridge_name, source_nio, destination_nio):
|
||||
if destination_nio:
|
||||
yield from self._ubridge_apply_filters(bridge_name, destination_nio.filters)
|
||||
await self._ubridge_apply_filters(bridge_name, destination_nio.filters)
|
||||
|
||||
def ubridge_delete_bridge(self, name):
|
||||
async def ubridge_delete_bridge(self, name):
|
||||
"""
|
||||
:params name: Delete the bridge with this name
|
||||
"""
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name=name))
|
||||
await self._ubridge_send("bridge delete {name}".format(name=name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _ubridge_apply_filters(self, bridge_name, filters):
|
||||
async def _ubridge_apply_filters(self, bridge_name, filters):
|
||||
"""
|
||||
Apply packet filters
|
||||
|
||||
@ -639,11 +628,11 @@ class BaseNode:
|
||||
:param filters: Array of filter dictionary
|
||||
"""
|
||||
|
||||
yield from self._ubridge_send('bridge reset_packet_filters ' + bridge_name)
|
||||
await self._ubridge_send('bridge reset_packet_filters ' + bridge_name)
|
||||
for packet_filter in self._build_filter_list(filters):
|
||||
cmd = 'bridge add_packet_filter {} {}'.format(bridge_name, packet_filter)
|
||||
try:
|
||||
yield from self._ubridge_send(cmd)
|
||||
await self._ubridge_send(cmd)
|
||||
except UbridgeError as e:
|
||||
match = re.search("Cannot compile filter '(.*)': syntax error", str(e))
|
||||
if match:
|
||||
@ -675,8 +664,7 @@ class BaseNode:
|
||||
filter_value=" ".join([str(v) for v in values]))
|
||||
i += 1
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_ubridge_ethernet_connection(self, bridge_name, ethernet_interface, block_host_traffic=False):
|
||||
async def _add_ubridge_ethernet_connection(self, bridge_name, ethernet_interface, block_host_traffic=False):
|
||||
"""
|
||||
Creates a connection with an Ethernet interface in uBridge.
|
||||
|
||||
@ -687,7 +675,7 @@ class BaseNode:
|
||||
|
||||
if sys.platform.startswith("linux") and block_host_traffic is False:
|
||||
# on Linux we use RAW sockets by default excepting if host traffic must be blocked
|
||||
yield from self._ubridge_send('bridge add_nio_linux_raw {name} "{interface}"'.format(name=bridge_name, interface=ethernet_interface))
|
||||
await self._ubridge_send('bridge add_nio_linux_raw {name} "{interface}"'.format(name=bridge_name, interface=ethernet_interface))
|
||||
elif sys.platform.startswith("win"):
|
||||
# on Windows we use Winpcap/Npcap
|
||||
windows_interfaces = interfaces()
|
||||
@ -702,26 +690,26 @@ class BaseNode:
|
||||
npf_id = interface["id"]
|
||||
source_mac = interface["mac_address"]
|
||||
if npf_id:
|
||||
yield from self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name,
|
||||
interface=npf_id))
|
||||
else:
|
||||
raise NodeError("Could not find NPF id for interface {}".format(ethernet_interface))
|
||||
|
||||
if block_host_traffic:
|
||||
if source_mac:
|
||||
yield from self._ubridge_send('bridge set_pcap_filter {name} "not ether src {mac}"'.format(name=bridge_name, mac=source_mac))
|
||||
await self._ubridge_send('bridge set_pcap_filter {name} "not ether src {mac}"'.format(name=bridge_name, mac=source_mac))
|
||||
log.info('PCAP filter applied on "{interface}" for source MAC {mac}'.format(interface=ethernet_interface, mac=source_mac))
|
||||
else:
|
||||
log.warning("Could not block host network traffic on {} (no MAC address found)".format(ethernet_interface))
|
||||
else:
|
||||
# on other platforms we just rely on the pcap library
|
||||
yield from self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name, interface=ethernet_interface))
|
||||
await self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name, interface=ethernet_interface))
|
||||
source_mac = None
|
||||
for interface in interfaces():
|
||||
if interface["name"] == ethernet_interface:
|
||||
source_mac = interface["mac_address"]
|
||||
if source_mac:
|
||||
yield from self._ubridge_send('bridge set_pcap_filter {name} "not ether src {mac}"'.format(name=bridge_name, mac=source_mac))
|
||||
await self._ubridge_send('bridge set_pcap_filter {name} "not ether src {mac}"'.format(name=bridge_name, mac=source_mac))
|
||||
log.info('PCAP filter applied on "{interface}" for source MAC {mac}'.format(interface=ethernet_interface, mac=source_mac))
|
||||
|
||||
def _create_local_udp_tunnel(self):
|
||||
|
@ -207,58 +207,54 @@ class Cloud(BaseNode):
|
||||
|
||||
self._ports_mapping = ports
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Creates this cloud.
|
||||
"""
|
||||
|
||||
yield from self.start()
|
||||
await self.start()
|
||||
log.info('Cloud "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts this cloud.
|
||||
"""
|
||||
|
||||
if self.status != "started":
|
||||
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
|
||||
yield from self._stop_ubridge()
|
||||
yield from self._start_ubridge()
|
||||
await self._stop_ubridge()
|
||||
await self._start_ubridge()
|
||||
for port_number in self._nios:
|
||||
if self._nios[port_number]:
|
||||
try:
|
||||
yield from self._add_ubridge_connection(self._nios[port_number], port_number)
|
||||
await self._add_ubridge_connection(self._nios[port_number], port_number)
|
||||
except (UbridgeError, NodeError) as e:
|
||||
self.status = "stopped"
|
||||
raise e
|
||||
self.status = "started"
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this cloud.
|
||||
"""
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
for nio in self._nios.values():
|
||||
if nio and isinstance(nio, NIOUDP):
|
||||
self.manager.port_manager.release_udp_port(nio.lport, self._project)
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
log.info('Cloud "{name}" [{id}] has been closed'.format(name=self._name, id=self._id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _is_wifi_adapter_osx(self, adapter_name):
|
||||
async def _is_wifi_adapter_osx(self, adapter_name):
|
||||
"""
|
||||
Detects a Wifi adapter on Mac.
|
||||
"""
|
||||
|
||||
try:
|
||||
output = yield from gns3server.utils.asyncio.subprocess_check_output("networksetup", "-listallhardwareports")
|
||||
output = await gns3server.utils.asyncio.subprocess_check_output("networksetup", "-listallhardwareports")
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
log.warning("Could not execute networksetup: {}".format(e))
|
||||
return False
|
||||
@ -274,8 +270,7 @@ class Cloud(BaseNode):
|
||||
is_wifi = True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_ubridge_connection(self, nio, port_number):
|
||||
async def _add_ubridge_connection(self, nio, port_number):
|
||||
"""
|
||||
Creates a connection in uBridge.
|
||||
|
||||
@ -294,19 +289,19 @@ class Cloud(BaseNode):
|
||||
port_number=port_number))
|
||||
|
||||
bridge_name = "{}-{}".format(self._id, port_number)
|
||||
yield from self._ubridge_send("bridge create {name}".format(name=bridge_name))
|
||||
await self._ubridge_send("bridge create {name}".format(name=bridge_name))
|
||||
if not isinstance(nio, NIOUDP):
|
||||
raise NodeError("Source NIO is not UDP")
|
||||
yield from self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
lport=nio.lport,
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport))
|
||||
|
||||
yield from self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
await self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
if port_info["type"] in ("ethernet", "tap"):
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
yield from self._add_ubridge_ethernet_connection(bridge_name, port_info["interface"])
|
||||
await self._add_ubridge_ethernet_connection(bridge_name, port_info["interface"])
|
||||
|
||||
else:
|
||||
|
||||
@ -316,29 +311,28 @@ class Cloud(BaseNode):
|
||||
raise NodeError("Interface '{}' could not be found on this system".format(port_info["interface"]))
|
||||
|
||||
if sys.platform.startswith("linux"):
|
||||
yield from self._add_linux_ethernet(port_info, bridge_name)
|
||||
await self._add_linux_ethernet(port_info, bridge_name)
|
||||
elif sys.platform.startswith("darwin"):
|
||||
yield from self._add_osx_ethernet(port_info, bridge_name)
|
||||
await self._add_osx_ethernet(port_info, bridge_name)
|
||||
else:
|
||||
yield from self._add_windows_ethernet(port_info, bridge_name)
|
||||
await self._add_windows_ethernet(port_info, bridge_name)
|
||||
|
||||
elif port_info["type"] == "tap":
|
||||
yield from self._ubridge_send('bridge add_nio_tap {name} "{interface}"'.format(name=bridge_name, interface=port_info["interface"]))
|
||||
await self._ubridge_send('bridge add_nio_tap {name} "{interface}"'.format(name=bridge_name, interface=port_info["interface"]))
|
||||
|
||||
elif port_info["type"] == "udp":
|
||||
yield from self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
lport=port_info["lport"],
|
||||
rhost=port_info["rhost"],
|
||||
rport=port_info["rport"]))
|
||||
|
||||
if nio.capturing:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{pcap_file}"'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge start_capture {name} "{pcap_file}"'.format(name=bridge_name,
|
||||
pcap_file=nio.pcap_output_file))
|
||||
|
||||
yield from self._ubridge_send('bridge start {name}'.format(name=bridge_name))
|
||||
await self._ubridge_send('bridge start {name}'.format(name=bridge_name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_linux_ethernet(self, port_info, bridge_name):
|
||||
async def _add_linux_ethernet(self, port_info, bridge_name):
|
||||
"""
|
||||
Connects an Ethernet interface on Linux using raw sockets.
|
||||
|
||||
@ -356,41 +350,38 @@ class Cloud(BaseNode):
|
||||
break
|
||||
i += 1
|
||||
|
||||
yield from self._ubridge_send('bridge add_nio_tap "{name}" "{interface}"'.format(name=bridge_name, interface=tap))
|
||||
yield from self._ubridge_send('brctl addif "{interface}" "{tap}"'.format(tap=tap, interface=interface))
|
||||
await self._ubridge_send('bridge add_nio_tap "{name}" "{interface}"'.format(name=bridge_name, interface=tap))
|
||||
await self._ubridge_send('brctl addif "{interface}" "{tap}"'.format(tap=tap, interface=interface))
|
||||
else:
|
||||
yield from self._ubridge_send('bridge add_nio_linux_raw {name} "{interface}"'.format(name=bridge_name, interface=interface))
|
||||
await self._ubridge_send('bridge add_nio_linux_raw {name} "{interface}"'.format(name=bridge_name, interface=interface))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_osx_ethernet(self, port_info, bridge_name):
|
||||
async def _add_osx_ethernet(self, port_info, bridge_name):
|
||||
"""
|
||||
Connects an Ethernet interface on OSX using libpcap.
|
||||
"""
|
||||
|
||||
# Wireless adapters are not well supported by the libpcap on OSX
|
||||
if (yield from self._is_wifi_adapter_osx(port_info["interface"])):
|
||||
if (await self._is_wifi_adapter_osx(port_info["interface"])):
|
||||
raise NodeError("Connecting to a Wireless adapter is not supported on Mac OS")
|
||||
if port_info["interface"].startswith("vmnet"):
|
||||
# Use a special NIO to connect to VMware vmnet interfaces on OSX (libpcap doesn't support them)
|
||||
yield from self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=bridge_name,
|
||||
interface=port_info["interface"]))
|
||||
return
|
||||
if not gns3server.utils.interfaces.has_netmask(port_info["interface"]):
|
||||
raise NodeError("Interface {} has no netmask, interface down?".format(port_info["interface"]))
|
||||
yield from self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name, interface=port_info["interface"]))
|
||||
await self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name, interface=port_info["interface"]))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_windows_ethernet(self, port_info, bridge_name):
|
||||
async def _add_windows_ethernet(self, port_info, bridge_name):
|
||||
"""
|
||||
Connects an Ethernet interface on Windows.
|
||||
"""
|
||||
|
||||
if not gns3server.utils.interfaces.has_netmask(port_info["interface"]):
|
||||
raise NodeError("Interface {} has no netmask, interface down?".format(port_info["interface"]))
|
||||
yield from self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name, interface=port_info["interface"]))
|
||||
await self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name, interface=port_info["interface"]))
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on this cloud.
|
||||
|
||||
@ -406,23 +397,22 @@ class Cloud(BaseNode):
|
||||
nio=nio,
|
||||
port=port_number))
|
||||
try:
|
||||
yield from self.start()
|
||||
yield from self._add_ubridge_connection(nio, port_number)
|
||||
await self.start()
|
||||
await self._add_ubridge_connection(nio, port_number)
|
||||
self._nios[port_number] = nio
|
||||
except NodeError as e:
|
||||
self.project.emit("log.error", {"message": str(e)})
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
self.status = "stopped"
|
||||
self._nios[port_number] = nio
|
||||
# Cleanup stuff
|
||||
except UbridgeError as e:
|
||||
self.project.emit("log.error", {"message": str(e)})
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
self.status = "stopped"
|
||||
self._nios[port_number] = nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_nio(self, port_number, nio):
|
||||
async def update_nio(self, port_number, nio):
|
||||
"""
|
||||
Update an nio on this node
|
||||
|
||||
@ -432,10 +422,9 @@ class Cloud(BaseNode):
|
||||
|
||||
bridge_name = "{}-{}".format(self._id, port_number)
|
||||
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
|
||||
yield from self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
await self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _delete_ubridge_connection(self, port_number):
|
||||
async def _delete_ubridge_connection(self, port_number):
|
||||
"""
|
||||
Deletes a connection in uBridge.
|
||||
|
||||
@ -443,10 +432,9 @@ class Cloud(BaseNode):
|
||||
"""
|
||||
|
||||
bridge_name = "{}-{}".format(self._id, port_number)
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name=bridge_name))
|
||||
await self._ubridge_send("bridge delete {name}".format(name=bridge_name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of cloud.
|
||||
|
||||
@ -469,12 +457,11 @@ class Cloud(BaseNode):
|
||||
|
||||
del self._nios[port_number]
|
||||
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
|
||||
yield from self._delete_ubridge_connection(port_number)
|
||||
yield from self.start()
|
||||
await self._delete_ubridge_connection(port_number)
|
||||
await self.start()
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -496,14 +483,13 @@ class Cloud(BaseNode):
|
||||
raise NodeError("Packet capture is already activated on port {port_number}".format(port_number=port_number))
|
||||
nio.startPacketCapture(output_file)
|
||||
bridge_name = "{}-{}".format(self._id, port_number)
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name=bridge_name,
|
||||
output_file=output_file))
|
||||
log.info("Cloud '{name}' [{id}]: starting packet capture on port {port_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
port_number=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@ -520,7 +506,7 @@ class Cloud(BaseNode):
|
||||
nio = self._nios[port_number]
|
||||
nio.stopPacketCapture()
|
||||
bridge_name = "{}-{}".format(self._id, port_number)
|
||||
yield from self._ubridge_send("bridge stop_capture {name}".format(name=bridge_name))
|
||||
await self._ubridge_send("bridge stop_capture {name}".format(name=bridge_name))
|
||||
|
||||
log.info("Cloud'{name}' [{id}]: stopping packet capture on port {port_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
|
@ -44,8 +44,7 @@ class EthernetHub(BaseNode):
|
||||
"node_id": self.id,
|
||||
"project_id": self.project.id}
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Creates this hub.
|
||||
"""
|
||||
@ -53,16 +52,14 @@ class EthernetHub(BaseNode):
|
||||
super().create()
|
||||
log.info('Ethernet hub "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes this hub.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on this hub.
|
||||
|
||||
@ -72,8 +69,7 @@ class EthernetHub(BaseNode):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of this hub.
|
||||
|
||||
@ -84,8 +80,7 @@ class EthernetHub(BaseNode):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -96,8 +91,7 @@ class EthernetHub(BaseNode):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
|
@ -44,8 +44,7 @@ class EthernetSwitch(BaseNode):
|
||||
"node_id": self.id,
|
||||
"project_id": self.project.id}
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Creates this switch.
|
||||
"""
|
||||
@ -53,16 +52,14 @@ class EthernetSwitch(BaseNode):
|
||||
super().create()
|
||||
log.info('Ethernet switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes this switch.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on this switch.
|
||||
|
||||
@ -72,8 +69,7 @@ class EthernetSwitch(BaseNode):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of this switch.
|
||||
|
||||
@ -84,8 +80,7 @@ class EthernetSwitch(BaseNode):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -96,8 +91,7 @@ class EthernetSwitch(BaseNode):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
|
@ -54,14 +54,13 @@ class Docker(BaseManager):
|
||||
self._session = None
|
||||
self._api_version = DOCKER_MINIMUM_API_VERSION
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_connection(self):
|
||||
async def _check_connection(self):
|
||||
|
||||
if not self._connected:
|
||||
try:
|
||||
self._connected = True
|
||||
connector = self.connector()
|
||||
version = yield from self.query("GET", "version")
|
||||
version = await self.query("GET", "version")
|
||||
except (aiohttp.ClientOSError, FileNotFoundError):
|
||||
self._connected = False
|
||||
raise DockerError("Can't connect to docker daemon")
|
||||
@ -88,16 +87,14 @@ class Docker(BaseManager):
|
||||
raise DockerError("Can't connect to docker daemon")
|
||||
return self._connector
|
||||
|
||||
@asyncio.coroutine
|
||||
def unload(self):
|
||||
async def unload(self):
|
||||
|
||||
yield from super().unload()
|
||||
await super().unload()
|
||||
if self._connected:
|
||||
if self._connector and not self._connector.closed:
|
||||
self._connector.close()
|
||||
|
||||
@asyncio.coroutine
|
||||
def query(self, method, path, data={}, params={}):
|
||||
async def query(self, method, path, data={}, params={}):
|
||||
"""
|
||||
Makes a query to the Docker daemon and decode the request
|
||||
|
||||
@ -107,8 +104,8 @@ class Docker(BaseManager):
|
||||
:param params: Parameters added as a query arg
|
||||
"""
|
||||
|
||||
response = yield from self.http_query(method, path, data=data, params=params)
|
||||
body = yield from response.read()
|
||||
response = await self.http_query(method, path, data=data, params=params)
|
||||
body = await response.read()
|
||||
if body and len(body):
|
||||
if response.headers['CONTENT-TYPE'] == 'application/json':
|
||||
body = json.loads(body.decode("utf-8"))
|
||||
@ -117,8 +114,7 @@ class Docker(BaseManager):
|
||||
log.debug("Query Docker %s %s params=%s data=%s Response: %s", method, path, params, data, body)
|
||||
return body
|
||||
|
||||
@asyncio.coroutine
|
||||
def http_query(self, method, path, data={}, params={}, timeout=300):
|
||||
async def http_query(self, method, path, data={}, params={}, timeout=300):
|
||||
"""
|
||||
Makes a query to the docker daemon
|
||||
|
||||
@ -140,11 +136,11 @@ class Docker(BaseManager):
|
||||
url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path
|
||||
try:
|
||||
if path != "version": # version is use by check connection
|
||||
yield from self._check_connection()
|
||||
await self._check_connection()
|
||||
if self._session is None or self._session.closed:
|
||||
connector = self.connector()
|
||||
self._session = aiohttp.ClientSession(connector=connector)
|
||||
response = yield from self._session.request(
|
||||
response = await self._session.request(
|
||||
method,
|
||||
url,
|
||||
params=params,
|
||||
@ -157,7 +153,7 @@ class Docker(BaseManager):
|
||||
except (asyncio.TimeoutError):
|
||||
raise DockerError("Docker timeout " + method + " " + path)
|
||||
if response.status >= 300:
|
||||
body = yield from response.read()
|
||||
body = await response.read()
|
||||
try:
|
||||
body = json.loads(body.decode("utf-8"))["message"]
|
||||
except ValueError:
|
||||
@ -171,8 +167,7 @@ class Docker(BaseManager):
|
||||
raise DockerError("Docker has returned an error: {} {}".format(response.status, body))
|
||||
return response
|
||||
|
||||
@asyncio.coroutine
|
||||
def websocket_query(self, path, params={}):
|
||||
async def websocket_query(self, path, params={}):
|
||||
"""
|
||||
Opens a websocket connection
|
||||
|
||||
@ -182,14 +177,13 @@ class Docker(BaseManager):
|
||||
"""
|
||||
|
||||
url = "http://docker/v" + self._api_version + "/" + path
|
||||
connection = yield from self._session.ws_connect(url,
|
||||
connection = await self._session.ws_connect(url,
|
||||
origin="http://docker",
|
||||
autoping=True)
|
||||
return connection
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def pull_image(self, image, progress_callback=None):
|
||||
async def pull_image(self, image, progress_callback=None):
|
||||
"""
|
||||
Pulls an image from the Docker repository
|
||||
|
||||
@ -198,19 +192,19 @@ class Docker(BaseManager):
|
||||
"""
|
||||
|
||||
try:
|
||||
yield from self.query("GET", "images/{}/json".format(image))
|
||||
await self.query("GET", "images/{}/json".format(image))
|
||||
return # We already have the image skip the download
|
||||
except DockerHttp404Error:
|
||||
pass
|
||||
|
||||
if progress_callback:
|
||||
progress_callback("Pulling '{}' from docker hub".format(image))
|
||||
response = yield from self.http_query("POST", "images/create", params={"fromImage": image}, timeout=None)
|
||||
response = await self.http_query("POST", "images/create", params={"fromImage": image}, timeout=None)
|
||||
# The pull api will stream status via an HTTP JSON stream
|
||||
content = ""
|
||||
while True:
|
||||
try:
|
||||
chunk = yield from response.content.read(1024)
|
||||
chunk = await response.content.read(1024)
|
||||
except aiohttp.ServerDisconnectedError:
|
||||
log.error("Disconnected from server while pulling Docker image '{}' from docker hub".format(image))
|
||||
break
|
||||
@ -234,8 +228,7 @@ class Docker(BaseManager):
|
||||
if progress_callback:
|
||||
progress_callback("Success pulling image {}".format(image))
|
||||
|
||||
@asyncio.coroutine
|
||||
def list_images(self):
|
||||
async def list_images(self):
|
||||
"""
|
||||
Gets Docker image list.
|
||||
|
||||
@ -244,7 +237,7 @@ class Docker(BaseManager):
|
||||
"""
|
||||
|
||||
images = []
|
||||
for image in (yield from self.query("GET", "images/json", params={"all": 0})):
|
||||
for image in (await self.query("GET", "images/json", params={"all": 0})):
|
||||
if image['RepoTags']:
|
||||
for tag in image['RepoTags']:
|
||||
if tag != "<none>:<none>":
|
||||
|
@ -29,7 +29,6 @@ import os
|
||||
from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer
|
||||
from gns3server.utils.asyncio.raw_command_server import AsyncioRawCommandServer
|
||||
from gns3server.utils.asyncio import wait_for_file_creation
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
from gns3server.utils.asyncio import monitor_process
|
||||
from gns3server.utils.get_resource import get_resource
|
||||
|
||||
@ -195,8 +194,7 @@ class DockerVM(BaseNode):
|
||||
def extra_hosts(self, extra_hosts):
|
||||
self._extra_hosts = extra_hosts
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_container_state(self):
|
||||
async def _get_container_state(self):
|
||||
"""
|
||||
Returns the container state (e.g. running, paused etc.)
|
||||
|
||||
@ -205,7 +203,7 @@ class DockerVM(BaseNode):
|
||||
"""
|
||||
|
||||
try:
|
||||
result = yield from self.manager.query("GET", "containers/{}/json".format(self._cid))
|
||||
result = await self.manager.query("GET", "containers/{}/json".format(self._cid))
|
||||
except DockerError:
|
||||
return "exited"
|
||||
|
||||
@ -215,13 +213,12 @@ class DockerVM(BaseNode):
|
||||
return "running"
|
||||
return "exited"
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_image_information(self):
|
||||
async def _get_image_information(self):
|
||||
"""
|
||||
:returns: Dictionary information about the container image
|
||||
"""
|
||||
|
||||
result = yield from self.manager.query("GET", "images/{}/json".format(self._image))
|
||||
result = await self.manager.query("GET", "images/{}/json".format(self._image))
|
||||
return result
|
||||
|
||||
def _mount_binds(self, image_info):
|
||||
@ -288,18 +285,17 @@ class DockerVM(BaseNode):
|
||||
# iface eth{adapter} inet dhcp""".format(adapter=adapter))
|
||||
return path
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Creates the Docker container.
|
||||
"""
|
||||
|
||||
try:
|
||||
image_infos = yield from self._get_image_information()
|
||||
image_infos = await self._get_image_information()
|
||||
except DockerHttp404Error:
|
||||
log.info("Image '{}' is missing, pulling it from Docker hub...".format(self._image))
|
||||
yield from self.pull_image(self._image)
|
||||
image_infos = yield from self._get_image_information()
|
||||
await self.pull_image(self._image)
|
||||
image_infos = await self._get_image_information()
|
||||
|
||||
if image_infos is None:
|
||||
raise DockerError("Cannot get information for image '{}', please try again.".format(self._image))
|
||||
@ -359,7 +355,7 @@ class DockerVM(BaseNode):
|
||||
params["Env"].append(formatted)
|
||||
|
||||
if self._console_type == "vnc":
|
||||
yield from self._start_vnc()
|
||||
await self._start_vnc()
|
||||
params["Env"].append("QT_GRAPHICSSYSTEM=native") # To fix a Qt issue: https://github.com/GNS3/gns3-server/issues/556
|
||||
params["Env"].append("DISPLAY=:{}".format(self._display))
|
||||
params["HostConfig"]["Binds"].append("/tmp/.X11-unix/:/tmp/.X11-unix/")
|
||||
@ -369,7 +365,7 @@ class DockerVM(BaseNode):
|
||||
if extra_hosts:
|
||||
params["Env"].append("GNS3_EXTRA_HOSTS={}".format(extra_hosts))
|
||||
|
||||
result = yield from self.manager.query("POST", "containers/create", data=params)
|
||||
result = await self.manager.query("POST", "containers/create", data=params)
|
||||
self._cid = result['Id']
|
||||
log.info("Docker container '{name}' [{id}] created".format(name=self._name, id=self._id))
|
||||
return True
|
||||
@ -393,8 +389,7 @@ class DockerVM(BaseNode):
|
||||
raise DockerError("Can't apply `ExtraHosts`, wrong format: {}".format(extra_hosts))
|
||||
return "\n".join(["{}\t{}".format(h[1], h[0]) for h in hosts])
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self):
|
||||
async def update(self):
|
||||
"""
|
||||
Destroy an recreate the container with the new settings
|
||||
"""
|
||||
@ -402,66 +397,65 @@ class DockerVM(BaseNode):
|
||||
# We need to save the console and state and restore it
|
||||
console = self.console
|
||||
aux = self.aux
|
||||
state = yield from self._get_container_state()
|
||||
state = await self._get_container_state()
|
||||
|
||||
# reset the docker container, but don't release the NIO UDP ports
|
||||
yield from self.reset(False)
|
||||
yield from self.create()
|
||||
await self.reset(False)
|
||||
await self.create()
|
||||
self.console = console
|
||||
self.aux = aux
|
||||
if state == "running":
|
||||
yield from self.start()
|
||||
await self.start()
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts this Docker container.
|
||||
"""
|
||||
|
||||
try:
|
||||
state = yield from self._get_container_state()
|
||||
state = await self._get_container_state()
|
||||
except DockerHttp404Error:
|
||||
raise DockerError("Docker container '{name}' with ID {cid} does not exist or is not ready yet. Please try again in a few seconds.".format(name=self.name,
|
||||
cid=self._cid))
|
||||
if state == "paused":
|
||||
yield from self.unpause()
|
||||
await self.unpause()
|
||||
elif state == "running":
|
||||
return
|
||||
else:
|
||||
|
||||
if self._console_type == "vnc" and not self._x11vnc_process:
|
||||
# start the x11vnc process in case it had previously crashed
|
||||
self._x11vnc_process = yield from asyncio.create_subprocess_exec("x11vnc", "-forever", "-nopw", "-shared", "-geometry", self._console_resolution, "-display", "WAIT:{}".format(self._display), "-rfbport", str(self.console), "-rfbportv6", str(self.console), "-noncache", "-listen", self._manager.port_manager.console_host)
|
||||
self._x11vnc_process = await asyncio.create_subprocess_exec("x11vnc", "-forever", "-nopw", "-shared", "-geometry", self._console_resolution, "-display", "WAIT:{}".format(self._display), "-rfbport", str(self.console), "-rfbportv6", str(self.console), "-noncache", "-listen", self._manager.port_manager.console_host)
|
||||
|
||||
yield from self._clean_servers()
|
||||
await self._clean_servers()
|
||||
|
||||
yield from self.manager.query("POST", "containers/{}/start".format(self._cid))
|
||||
self._namespace = yield from self._get_namespace()
|
||||
await self.manager.query("POST", "containers/{}/start".format(self._cid))
|
||||
self._namespace = await self._get_namespace()
|
||||
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
|
||||
for adapter_number in range(0, self.adapters):
|
||||
nio = self._ethernet_adapters[adapter_number].get_nio(0)
|
||||
with (yield from self.manager.ubridge_lock):
|
||||
async with self.manager.ubridge_lock:
|
||||
try:
|
||||
yield from self._add_ubridge_connection(nio, adapter_number)
|
||||
await self._add_ubridge_connection(nio, adapter_number)
|
||||
except UbridgeNamespaceError:
|
||||
log.error("Container %s failed to start", self.name)
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
|
||||
# The container can crash soon after the start, this means we can not move the interface to the container namespace
|
||||
logdata = yield from self._get_log()
|
||||
logdata = await self._get_log()
|
||||
for line in logdata.split('\n'):
|
||||
log.error(line)
|
||||
raise DockerError(logdata)
|
||||
|
||||
if self.console_type == "telnet":
|
||||
yield from self._start_console()
|
||||
await self._start_console()
|
||||
elif self.console_type == "http" or self.console_type == "https":
|
||||
yield from self._start_http()
|
||||
await self._start_http()
|
||||
|
||||
if self.allocate_aux:
|
||||
yield from self._start_aux()
|
||||
await self._start_aux()
|
||||
|
||||
self.status = "started"
|
||||
log.info("Docker container '{name}' [{image}] started listen for {console_type} on {console}".format(name=self._name,
|
||||
@ -469,8 +463,7 @@ class DockerVM(BaseNode):
|
||||
console=self.console,
|
||||
console_type=self.console_type))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_aux(self):
|
||||
async def _start_aux(self):
|
||||
"""
|
||||
Start an auxiliary console
|
||||
"""
|
||||
@ -478,7 +471,7 @@ class DockerVM(BaseNode):
|
||||
# We can not use the API because docker doesn't expose a websocket api for exec
|
||||
# https://github.com/GNS3/gns3-gui/issues/1039
|
||||
try:
|
||||
process = yield from asyncio.subprocess.create_subprocess_exec(
|
||||
process = await asyncio.subprocess.create_subprocess_exec(
|
||||
"docker", "exec", "-i", self._cid, "/gns3/bin/busybox", "script", "-qfc", "while true; do TERM=vt100 /gns3/bin/busybox sh; done", "/dev/null",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.STDOUT,
|
||||
@ -487,29 +480,28 @@ class DockerVM(BaseNode):
|
||||
raise DockerError("Could not start auxiliary console process: {}".format(e))
|
||||
server = AsyncioTelnetServer(reader=process.stdout, writer=process.stdin, binary=True, echo=True)
|
||||
try:
|
||||
self._telnet_servers.append((yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.aux)))
|
||||
self._telnet_servers.append((await asyncio.start_server(server.run, self._manager.port_manager.console_host, self.aux)))
|
||||
except OSError as e:
|
||||
raise DockerError("Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.aux, e))
|
||||
log.debug("Docker container '%s' started listen for auxilary telnet on %d", self.name, self.aux)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _fix_permissions(self):
|
||||
async def _fix_permissions(self):
|
||||
"""
|
||||
Because docker run as root we need to fix permission and ownership to allow user to interact
|
||||
with it from their filesystem and do operation like file delete
|
||||
"""
|
||||
|
||||
state = yield from self._get_container_state()
|
||||
state = await self._get_container_state()
|
||||
if state == "stopped" or state == "exited":
|
||||
# We need to restart it to fix permissions
|
||||
yield from self.manager.query("POST", "containers/{}/start".format(self._cid))
|
||||
await self.manager.query("POST", "containers/{}/start".format(self._cid))
|
||||
|
||||
for volume in self._volumes:
|
||||
log.debug("Docker container '{name}' [{image}] fix ownership on {path}".format(
|
||||
name=self._name, image=self._image, path=volume))
|
||||
|
||||
try:
|
||||
process = yield from asyncio.subprocess.create_subprocess_exec(
|
||||
process = await asyncio.subprocess.create_subprocess_exec(
|
||||
"docker",
|
||||
"exec",
|
||||
self._cid,
|
||||
@ -526,10 +518,9 @@ class DockerVM(BaseNode):
|
||||
)
|
||||
except OSError as e:
|
||||
raise DockerError("Could not fix permissions for {}: {}".format(volume, e))
|
||||
yield from process.wait()
|
||||
await process.wait()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_vnc(self):
|
||||
async def _start_vnc(self):
|
||||
"""
|
||||
Starts a VNC server for this container
|
||||
"""
|
||||
@ -537,11 +528,11 @@ class DockerVM(BaseNode):
|
||||
self._display = self._get_free_display_port()
|
||||
if shutil.which("Xvfb") is None or shutil.which("x11vnc") is None:
|
||||
raise DockerError("Please install Xvfb and x11vnc before using VNC support")
|
||||
self._xvfb_process = yield from asyncio.create_subprocess_exec("Xvfb", "-nolisten", "tcp", ":{}".format(self._display), "-screen", "0", self._console_resolution + "x16")
|
||||
self._xvfb_process = await asyncio.create_subprocess_exec("Xvfb", "-nolisten", "tcp", ":{}".format(self._display), "-screen", "0", self._console_resolution + "x16")
|
||||
# We pass a port for TCPV6 due to a crash in X11VNC if not here: https://github.com/GNS3/gns3-server/issues/569
|
||||
self._x11vnc_process = yield from asyncio.create_subprocess_exec("x11vnc", "-forever", "-nopw", "-shared", "-geometry", self._console_resolution, "-display", "WAIT:{}".format(self._display), "-rfbport", str(self.console), "-rfbportv6", str(self.console), "-noncache", "-listen", self._manager.port_manager.console_host)
|
||||
self._x11vnc_process = await asyncio.create_subprocess_exec("x11vnc", "-forever", "-nopw", "-shared", "-geometry", self._console_resolution, "-display", "WAIT:{}".format(self._display), "-rfbport", str(self.console), "-rfbportv6", str(self.console), "-noncache", "-listen", self._manager.port_manager.console_host)
|
||||
x11_socket = os.path.join("/tmp/.X11-unix/", "X{}".format(self._display))
|
||||
yield from wait_for_file_creation(x11_socket)
|
||||
await wait_for_file_creation(x11_socket)
|
||||
|
||||
# sometimes the x11vnc process can crash
|
||||
monitor_process(self._x11vnc_process, self._x11vnc_callback)
|
||||
@ -557,8 +548,7 @@ class DockerVM(BaseNode):
|
||||
self.project.emit("log.error", {"message": "The x11vnc process has stopped with return code {} for node '{}'. Please restart this node.".format(returncode, self.name)})
|
||||
self._x11vnc_process = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_http(self):
|
||||
async def _start_http(self):
|
||||
"""
|
||||
Starts an HTTP tunnel to container localhost. It's not perfect
|
||||
but the only way we have to inject network packet is using nc.
|
||||
@ -577,10 +567,9 @@ class DockerVM(BaseNode):
|
||||
':{}'.format(self.console).encode(),
|
||||
)
|
||||
])
|
||||
self._telnet_servers.append((yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)))
|
||||
self._telnet_servers.append((await asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_console(self):
|
||||
async def _start_console(self):
|
||||
"""
|
||||
Starts streaming the console via telnet
|
||||
"""
|
||||
@ -593,8 +582,7 @@ class DockerVM(BaseNode):
|
||||
def write(self, data):
|
||||
self._data += data
|
||||
|
||||
@asyncio.coroutine
|
||||
def drain(self):
|
||||
async def drain(self):
|
||||
if not self.ws.closed:
|
||||
self.ws.send_bytes(self._data)
|
||||
self._data = b""
|
||||
@ -604,19 +592,18 @@ class DockerVM(BaseNode):
|
||||
|
||||
telnet = AsyncioTelnetServer(reader=output_stream, writer=input_stream, echo=True)
|
||||
try:
|
||||
self._telnet_servers.append((yield from asyncio.start_server(telnet.run, self._manager.port_manager.console_host, self.console)))
|
||||
self._telnet_servers.append((await asyncio.start_server(telnet.run, self._manager.port_manager.console_host, self.console)))
|
||||
except OSError as e:
|
||||
raise DockerError("Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e))
|
||||
|
||||
self._console_websocket = yield from self.manager.websocket_query("containers/{}/attach/ws?stream=1&stdin=1&stdout=1&stderr=1".format(self._cid))
|
||||
self._console_websocket = await self.manager.websocket_query("containers/{}/attach/ws?stream=1&stdin=1&stdout=1&stderr=1".format(self._cid))
|
||||
input_stream.ws = self._console_websocket
|
||||
|
||||
output_stream.feed_data(self.name.encode() + b" console is now available... Press RETURN to get started.\r\n")
|
||||
|
||||
asyncio_ensure_future(self._read_console_output(self._console_websocket, output_stream))
|
||||
asyncio.ensure_future(self._read_console_output(self._console_websocket, output_stream))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _read_console_output(self, ws, out):
|
||||
async def _read_console_output(self, ws, out):
|
||||
"""
|
||||
Reads Websocket and forward it to the telnet
|
||||
|
||||
@ -625,7 +612,7 @@ class DockerVM(BaseNode):
|
||||
"""
|
||||
|
||||
while True:
|
||||
msg = yield from ws.receive()
|
||||
msg = await ws.receive()
|
||||
if msg.tp == aiohttp.WSMsgType.text:
|
||||
out.feed_data(msg.data.encode())
|
||||
elif msg.tp == aiohttp.WSMsgType.BINARY:
|
||||
@ -636,10 +623,9 @@ class DockerVM(BaseNode):
|
||||
out.feed_eof()
|
||||
ws.close()
|
||||
break
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
|
||||
@asyncio.coroutine
|
||||
def is_running(self):
|
||||
async def is_running(self):
|
||||
"""
|
||||
Checks if the container is running.
|
||||
|
||||
@ -647,25 +633,23 @@ class DockerVM(BaseNode):
|
||||
:rtype: bool
|
||||
"""
|
||||
|
||||
state = yield from self._get_container_state()
|
||||
state = await self._get_container_state()
|
||||
if state == "running":
|
||||
return True
|
||||
if self.status == "started": # The container crashed we need to clean
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def restart(self):
|
||||
async def restart(self):
|
||||
"""
|
||||
Restart this Docker container.
|
||||
"""
|
||||
|
||||
yield from self.manager.query("POST", "containers/{}/restart".format(self._cid))
|
||||
await self.manager.query("POST", "containers/{}/restart".format(self._cid))
|
||||
log.info("Docker container '{name}' [{image}] restarted".format(
|
||||
name=self._name, image=self._image))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _clean_servers(self):
|
||||
async def _clean_servers(self):
|
||||
"""
|
||||
Clean the list of running console servers
|
||||
"""
|
||||
@ -673,34 +657,33 @@ class DockerVM(BaseNode):
|
||||
if len(self._telnet_servers) > 0:
|
||||
for telnet_server in self._telnet_servers:
|
||||
telnet_server.close()
|
||||
yield from telnet_server.wait_closed()
|
||||
await telnet_server.wait_closed()
|
||||
self._telnet_servers = []
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops this Docker container.
|
||||
"""
|
||||
|
||||
try:
|
||||
yield from self._clean_servers()
|
||||
yield from self._stop_ubridge()
|
||||
await self._clean_servers()
|
||||
await self._stop_ubridge()
|
||||
|
||||
try:
|
||||
state = yield from self._get_container_state()
|
||||
state = await self._get_container_state()
|
||||
except DockerHttp404Error:
|
||||
self.status = "stopped"
|
||||
return
|
||||
|
||||
if state == "paused":
|
||||
yield from self.unpause()
|
||||
await self.unpause()
|
||||
|
||||
yield from self._fix_permissions()
|
||||
state = yield from self._get_container_state()
|
||||
await self._fix_permissions()
|
||||
state = await self._get_container_state()
|
||||
if state != "stopped" or state != "exited":
|
||||
# t=5 number of seconds to wait before killing the container
|
||||
try:
|
||||
yield from self.manager.query("POST", "containers/{}/stop".format(self._cid), params={"t": 5})
|
||||
await self.manager.query("POST", "containers/{}/stop".format(self._cid), params={"t": 5})
|
||||
log.info("Docker container '{name}' [{image}] stopped".format(
|
||||
name=self._name, image=self._image))
|
||||
except DockerHttp304Error:
|
||||
@ -712,56 +695,52 @@ class DockerVM(BaseNode):
|
||||
return
|
||||
self.status = "stopped"
|
||||
|
||||
@asyncio.coroutine
|
||||
def pause(self):
|
||||
async def pause(self):
|
||||
"""
|
||||
Pauses this Docker container.
|
||||
"""
|
||||
|
||||
yield from self.manager.query("POST", "containers/{}/pause".format(self._cid))
|
||||
await self.manager.query("POST", "containers/{}/pause".format(self._cid))
|
||||
self.status = "suspended"
|
||||
log.info("Docker container '{name}' [{image}] paused".format(name=self._name, image=self._image))
|
||||
|
||||
@asyncio.coroutine
|
||||
def unpause(self):
|
||||
async def unpause(self):
|
||||
"""
|
||||
Unpauses this Docker container.
|
||||
"""
|
||||
|
||||
yield from self.manager.query("POST", "containers/{}/unpause".format(self._cid))
|
||||
await self.manager.query("POST", "containers/{}/unpause".format(self._cid))
|
||||
self.status = "started"
|
||||
log.info("Docker container '{name}' [{image}] unpaused".format(name=self._name, image=self._image))
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this Docker container.
|
||||
"""
|
||||
|
||||
self._closing = True
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
yield from self.reset()
|
||||
await self.reset()
|
||||
|
||||
@asyncio.coroutine
|
||||
def reset(self, release_nio_udp_ports=True):
|
||||
async def reset(self, release_nio_udp_ports=True):
|
||||
|
||||
try:
|
||||
state = yield from self._get_container_state()
|
||||
state = await self._get_container_state()
|
||||
if state == "paused" or state == "running":
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
|
||||
if self.console_type == "vnc":
|
||||
if self._x11vnc_process:
|
||||
try:
|
||||
self._x11vnc_process.terminate()
|
||||
yield from self._x11vnc_process.wait()
|
||||
await self._x11vnc_process.wait()
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
if self._xvfb_process:
|
||||
try:
|
||||
self._xvfb_process.terminate()
|
||||
yield from self._xvfb_process.wait()
|
||||
await self._xvfb_process.wait()
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
|
||||
@ -776,7 +755,7 @@ class DockerVM(BaseNode):
|
||||
# v – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false.
|
||||
# force - 1/True/true or 0/False/false, Kill then remove the container. Default false.
|
||||
try:
|
||||
yield from self.manager.query("DELETE", "containers/{}".format(self._cid), params={"force": 1, "v": 1})
|
||||
await self.manager.query("DELETE", "containers/{}".format(self._cid), params={"force": 1, "v": 1})
|
||||
except DockerError:
|
||||
pass
|
||||
log.info("Docker container '{name}' [{image}] removed".format(
|
||||
@ -793,8 +772,7 @@ class DockerVM(BaseNode):
|
||||
log.debug("Docker error when closing: {}".format(str(e)))
|
||||
return
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_ubridge_connection(self, nio, adapter_number):
|
||||
async def _add_ubridge_connection(self, nio, adapter_number):
|
||||
"""
|
||||
Creates a connection in uBridge.
|
||||
|
||||
@ -816,44 +794,41 @@ class DockerVM(BaseNode):
|
||||
raise DockerError("Adapter {adapter_number} couldn't allocate interface on Docker container '{name}'. Too many Docker interfaces already exists".format(name=self.name,
|
||||
adapter_number=adapter_number))
|
||||
bridge_name = 'bridge{}'.format(adapter_number)
|
||||
yield from self._ubridge_send('bridge create {}'.format(bridge_name))
|
||||
await self._ubridge_send('bridge create {}'.format(bridge_name))
|
||||
self._bridges.add(bridge_name)
|
||||
yield from self._ubridge_send('bridge add_nio_tap bridge{adapter_number} {hostif}'.format(adapter_number=adapter_number,
|
||||
await self._ubridge_send('bridge add_nio_tap bridge{adapter_number} {hostif}'.format(adapter_number=adapter_number,
|
||||
hostif=adapter.host_ifc))
|
||||
log.debug("Move container %s adapter %s to namespace %s", self.name, adapter.host_ifc, self._namespace)
|
||||
try:
|
||||
yield from self._ubridge_send('docker move_to_ns {ifc} {ns} eth{adapter}'.format(ifc=adapter.host_ifc,
|
||||
await self._ubridge_send('docker move_to_ns {ifc} {ns} eth{adapter}'.format(ifc=adapter.host_ifc,
|
||||
ns=self._namespace,
|
||||
adapter=adapter_number))
|
||||
except UbridgeError as e:
|
||||
raise UbridgeNamespaceError(e)
|
||||
|
||||
if nio:
|
||||
yield from self._connect_nio(adapter_number, nio)
|
||||
await self._connect_nio(adapter_number, nio)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_namespace(self):
|
||||
async def _get_namespace(self):
|
||||
|
||||
result = yield from self.manager.query("GET", "containers/{}/json".format(self._cid))
|
||||
result = await self.manager.query("GET", "containers/{}/json".format(self._cid))
|
||||
return int(result['State']['Pid'])
|
||||
|
||||
@asyncio.coroutine
|
||||
def _connect_nio(self, adapter_number, nio):
|
||||
async def _connect_nio(self, adapter_number, nio):
|
||||
|
||||
bridge_name = 'bridge{}'.format(adapter_number)
|
||||
yield from self._ubridge_send('bridge add_nio_udp {bridge_name} {lport} {rhost} {rport}'.format(bridge_name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_udp {bridge_name} {lport} {rhost} {rport}'.format(bridge_name=bridge_name,
|
||||
lport=nio.lport,
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport))
|
||||
|
||||
if nio.capturing:
|
||||
yield from self._ubridge_send('bridge start_capture {bridge_name} "{pcap_file}"'.format(bridge_name=bridge_name,
|
||||
await self._ubridge_send('bridge start_capture {bridge_name} "{pcap_file}"'.format(bridge_name=bridge_name,
|
||||
pcap_file=nio.pcap_output_file))
|
||||
yield from self._ubridge_send('bridge start {bridge_name}'.format(bridge_name=bridge_name))
|
||||
yield from self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
await self._ubridge_send('bridge start {bridge_name}'.format(bridge_name=bridge_name))
|
||||
await self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Adds an adapter NIO binding.
|
||||
|
||||
@ -868,7 +843,7 @@ class DockerVM(BaseNode):
|
||||
adapter_number=adapter_number))
|
||||
|
||||
if self.status == "started" and self.ubridge:
|
||||
yield from self._connect_nio(adapter_number, nio)
|
||||
await self._connect_nio(adapter_number, nio)
|
||||
|
||||
adapter.add_nio(0, nio)
|
||||
log.info("Docker container '{name}' [{id}]: {nio} added to adapter {adapter_number}".format(name=self.name,
|
||||
@ -876,8 +851,7 @@ class DockerVM(BaseNode):
|
||||
nio=nio,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Update a port NIO binding.
|
||||
|
||||
@ -888,10 +862,9 @@ class DockerVM(BaseNode):
|
||||
if self.ubridge:
|
||||
bridge_name = 'bridge{}'.format(adapter_number)
|
||||
if bridge_name in self._bridges:
|
||||
yield from self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
await self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_remove_nio_binding(self, adapter_number):
|
||||
async def adapter_remove_nio_binding(self, adapter_number):
|
||||
"""
|
||||
Removes an adapter NIO binding.
|
||||
|
||||
@ -909,8 +882,8 @@ class DockerVM(BaseNode):
|
||||
if self.ubridge:
|
||||
nio = adapter.get_nio(0)
|
||||
bridge_name = 'bridge{}'.format(adapter_number)
|
||||
yield from self._ubridge_send("bridge stop {}".format(bridge_name))
|
||||
yield from self._ubridge_send('bridge remove_nio_udp bridge{adapter} {lport} {rhost} {rport}'.format(adapter=adapter_number,
|
||||
await self._ubridge_send("bridge stop {}".format(bridge_name))
|
||||
await self._ubridge_send('bridge remove_nio_udp bridge{adapter} {lport} {rhost} {rport}'.format(adapter=adapter_number,
|
||||
lport=nio.lport,
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport))
|
||||
@ -952,18 +925,16 @@ class DockerVM(BaseNode):
|
||||
id=self._id,
|
||||
adapters=adapters))
|
||||
|
||||
@asyncio.coroutine
|
||||
def pull_image(self, image):
|
||||
async def pull_image(self, image):
|
||||
"""
|
||||
Pulls an image from Docker repository
|
||||
"""
|
||||
|
||||
def callback(msg):
|
||||
self.project.emit("log.info", {"message": msg})
|
||||
yield from self.manager.pull_image(image, progress_callback=callback)
|
||||
await self.manager.pull_image(image, progress_callback=callback)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_ubridge_capture(self, adapter_number, output_file):
|
||||
async def _start_ubridge_capture(self, adapter_number, output_file):
|
||||
"""
|
||||
Starts a packet capture in uBridge.
|
||||
|
||||
@ -974,10 +945,9 @@ class DockerVM(BaseNode):
|
||||
adapter = "bridge{}".format(adapter_number)
|
||||
if not self.ubridge:
|
||||
raise DockerError("Cannot start the packet capture: uBridge is not running")
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name=adapter, output_file=output_file))
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name=adapter, output_file=output_file))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _stop_ubridge_capture(self, adapter_number):
|
||||
async def _stop_ubridge_capture(self, adapter_number):
|
||||
"""
|
||||
Stops a packet capture in uBridge.
|
||||
|
||||
@ -987,10 +957,9 @@ class DockerVM(BaseNode):
|
||||
adapter = "bridge{}".format(adapter_number)
|
||||
if not self.ubridge:
|
||||
raise DockerError("Cannot stop the packet capture: uBridge is not running")
|
||||
yield from self._ubridge_send("bridge stop_capture {name}".format(name=adapter))
|
||||
await self._ubridge_send("bridge stop_capture {name}".format(name=adapter))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, adapter_number, output_file):
|
||||
async def start_capture(self, adapter_number, output_file):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -1015,13 +984,13 @@ class DockerVM(BaseNode):
|
||||
nio.startPacketCapture(output_file)
|
||||
|
||||
if self.status == "started" and self.ubridge:
|
||||
yield from self._start_ubridge_capture(adapter_number, output_file)
|
||||
await self._start_ubridge_capture(adapter_number, output_file)
|
||||
|
||||
log.info("Docker VM '{name}' [{id}]: starting packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
def stop_capture(self, adapter_number):
|
||||
async def stop_capture(self, adapter_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@ -1042,28 +1011,26 @@ class DockerVM(BaseNode):
|
||||
nio.stopPacketCapture()
|
||||
|
||||
if self.status == "started" and self.ubridge:
|
||||
yield from self._stop_ubridge_capture(adapter_number)
|
||||
await self._stop_ubridge_capture(adapter_number)
|
||||
|
||||
log.info("Docker VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_log(self):
|
||||
async def _get_log(self):
|
||||
"""
|
||||
Returns the log from the container
|
||||
|
||||
:returns: string
|
||||
"""
|
||||
|
||||
result = yield from self.manager.query("GET", "containers/{}/logs".format(self._cid), params={"stderr": 1, "stdout": 1})
|
||||
result = await self.manager.query("GET", "containers/{}/logs".format(self._cid), params={"stderr": 1, "stdout": 1})
|
||||
return result
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes the VM (including all its files).
|
||||
"""
|
||||
|
||||
yield from self.close()
|
||||
yield from super().delete()
|
||||
await self.close()
|
||||
await super().delete()
|
||||
|
@ -34,9 +34,8 @@ import re
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
from gns3server.utils.interfaces import interfaces, is_interface_up
|
||||
from gns3server.utils.asyncio import wait_run_in_executor, asyncio_ensure_future
|
||||
from gns3server.utils.asyncio import wait_run_in_executor
|
||||
from gns3server.utils import parse_version
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
from uuid import uuid4
|
||||
from ..base_manager import BaseManager
|
||||
from ..port_manager import PortManager
|
||||
@ -166,17 +165,16 @@ class Dynamips(BaseManager):
|
||||
if dynamips_id in self._dynamips_ids[project_id]:
|
||||
self._dynamips_ids[project_id].remove(dynamips_id)
|
||||
|
||||
@asyncio.coroutine
|
||||
def unload(self):
|
||||
async def unload(self):
|
||||
|
||||
yield from BaseManager.unload(self)
|
||||
await BaseManager.unload(self)
|
||||
|
||||
tasks = []
|
||||
for device in self._devices.values():
|
||||
tasks.append(asyncio_ensure_future(device.hypervisor.stop()))
|
||||
tasks.append(asyncio.ensure_future(device.hypervisor.stop()))
|
||||
|
||||
if tasks:
|
||||
done, _ = yield from asyncio.wait(tasks)
|
||||
done, _ = await asyncio.wait(tasks)
|
||||
for future in done:
|
||||
try:
|
||||
future.result()
|
||||
@ -184,37 +182,35 @@ class Dynamips(BaseManager):
|
||||
log.error("Could not stop device hypervisor {}".format(e), exc_info=1)
|
||||
continue
|
||||
|
||||
@asyncio.coroutine
|
||||
def project_closing(self, project):
|
||||
async def project_closing(self, project):
|
||||
"""
|
||||
Called when a project is about to be closed.
|
||||
|
||||
:param project: Project instance
|
||||
"""
|
||||
|
||||
yield from super().project_closing(project)
|
||||
await super().project_closing(project)
|
||||
# delete the Dynamips devices corresponding to the project
|
||||
tasks = []
|
||||
for device in self._devices.values():
|
||||
if device.project.id == project.id:
|
||||
tasks.append(asyncio_ensure_future(device.delete()))
|
||||
tasks.append(asyncio.ensure_future(device.delete()))
|
||||
|
||||
if tasks:
|
||||
done, _ = yield from asyncio.wait(tasks)
|
||||
done, _ = await asyncio.wait(tasks)
|
||||
for future in done:
|
||||
try:
|
||||
future.result()
|
||||
except (Exception, GeneratorExit) as e:
|
||||
log.error("Could not delete device {}".format(e), exc_info=1)
|
||||
|
||||
@asyncio.coroutine
|
||||
def project_closed(self, project):
|
||||
async def project_closed(self, project):
|
||||
"""
|
||||
Called when a project is closed.
|
||||
|
||||
:param project: Project instance
|
||||
"""
|
||||
yield from super().project_closed(project)
|
||||
await super().project_closed(project)
|
||||
# delete useless Dynamips files
|
||||
project_dir = project.module_working_path(self.module_name.lower())
|
||||
|
||||
@ -230,7 +226,7 @@ class Dynamips(BaseManager):
|
||||
log.debug("Deleting file {}".format(file))
|
||||
if file in self._ghost_files:
|
||||
self._ghost_files.remove(file)
|
||||
yield from wait_run_in_executor(os.remove, file)
|
||||
await wait_run_in_executor(os.remove, file)
|
||||
except OSError as e:
|
||||
log.warning("Could not delete file {}: {}".format(file, e))
|
||||
continue
|
||||
@ -267,8 +263,7 @@ class Dynamips(BaseManager):
|
||||
self._dynamips_path = dynamips_path
|
||||
return dynamips_path
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_new_hypervisor(self, working_dir=None):
|
||||
async def start_new_hypervisor(self, working_dir=None):
|
||||
"""
|
||||
Creates a new Dynamips process and start it.
|
||||
|
||||
@ -306,27 +301,25 @@ class Dynamips(BaseManager):
|
||||
hypervisor = Hypervisor(self._dynamips_path, working_dir, server_host, port, port_manager.console_host)
|
||||
|
||||
log.info("Creating new hypervisor {}:{} with working directory {}".format(hypervisor.host, hypervisor.port, working_dir))
|
||||
yield from hypervisor.start()
|
||||
await hypervisor.start()
|
||||
log.info("Hypervisor {}:{} has successfully started".format(hypervisor.host, hypervisor.port))
|
||||
yield from hypervisor.connect()
|
||||
await hypervisor.connect()
|
||||
if parse_version(hypervisor.version) < parse_version('0.2.11'):
|
||||
raise DynamipsError("Dynamips version must be >= 0.2.11, detected version is {}".format(hypervisor.version))
|
||||
|
||||
return hypervisor
|
||||
|
||||
@asyncio.coroutine
|
||||
def ghost_ios_support(self, vm):
|
||||
async def ghost_ios_support(self, vm):
|
||||
|
||||
ghost_ios_support = self.config.get_section_config("Dynamips").getboolean("ghost_ios_support", True)
|
||||
if ghost_ios_support:
|
||||
with (yield from Dynamips._ghost_ios_lock):
|
||||
async with Dynamips._ghost_ios_lock:
|
||||
try:
|
||||
yield from self._set_ghost_ios(vm)
|
||||
await self._set_ghost_ios(vm)
|
||||
except GeneratorExit:
|
||||
log.warning("Could not create ghost IOS image {} (GeneratorExit)".format(vm.name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def create_nio(self, node, nio_settings):
|
||||
async def create_nio(self, node, nio_settings):
|
||||
"""
|
||||
Creates a new NIO.
|
||||
|
||||
@ -394,11 +387,10 @@ class Dynamips(BaseManager):
|
||||
else:
|
||||
raise aiohttp.web.HTTPConflict(text="NIO of type {} is not supported".format(nio_settings["type"]))
|
||||
|
||||
yield from nio.create()
|
||||
await nio.create()
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def _set_ghost_ios(self, vm):
|
||||
async def _set_ghost_ios(self, vm):
|
||||
"""
|
||||
Manages Ghost IOS support.
|
||||
|
||||
@ -421,29 +413,28 @@ class Dynamips(BaseManager):
|
||||
ghost_id = str(uuid4())
|
||||
ghost = Router("ghost-" + ghost_file, ghost_id, vm.project, vm.manager, platform=vm.platform, hypervisor=vm.hypervisor, ghost_flag=True)
|
||||
try:
|
||||
yield from ghost.create()
|
||||
yield from ghost.set_image(vm.image)
|
||||
yield from ghost.set_ghost_status(1)
|
||||
yield from ghost.set_ghost_file(ghost_file_path)
|
||||
yield from ghost.set_ram(vm.ram)
|
||||
await ghost.create()
|
||||
await ghost.set_image(vm.image)
|
||||
await ghost.set_ghost_status(1)
|
||||
await ghost.set_ghost_file(ghost_file_path)
|
||||
await ghost.set_ram(vm.ram)
|
||||
try:
|
||||
yield from ghost.start()
|
||||
yield from ghost.stop()
|
||||
await ghost.start()
|
||||
await ghost.stop()
|
||||
self._ghost_files.add(ghost_file_path)
|
||||
except DynamipsError:
|
||||
raise
|
||||
finally:
|
||||
yield from ghost.clean_delete()
|
||||
await ghost.clean_delete()
|
||||
except DynamipsError as e:
|
||||
log.warning("Could not create ghost instance: {}".format(e))
|
||||
|
||||
if vm.ghost_file != ghost_file and os.path.isfile(ghost_file_path):
|
||||
# set the ghost file to the router
|
||||
yield from vm.set_ghost_status(2)
|
||||
yield from vm.set_ghost_file(ghost_file_path)
|
||||
await vm.set_ghost_status(2)
|
||||
await vm.set_ghost_file(ghost_file_path)
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_vm_settings(self, vm, settings):
|
||||
async def update_vm_settings(self, vm, settings):
|
||||
"""
|
||||
Updates the VM settings.
|
||||
|
||||
@ -455,23 +446,23 @@ class Dynamips(BaseManager):
|
||||
if hasattr(vm, name) and getattr(vm, name) != value:
|
||||
if hasattr(vm, "set_{}".format(name)):
|
||||
setter = getattr(vm, "set_{}".format(name))
|
||||
yield from setter(value)
|
||||
await setter(value)
|
||||
elif name.startswith("slot") and value in ADAPTER_MATRIX:
|
||||
slot_id = int(name[-1])
|
||||
adapter_name = value
|
||||
adapter = ADAPTER_MATRIX[adapter_name]()
|
||||
try:
|
||||
if vm.slots[slot_id] and not isinstance(vm.slots[slot_id], type(adapter)):
|
||||
yield from vm.slot_remove_binding(slot_id)
|
||||
await vm.slot_remove_binding(slot_id)
|
||||
if not isinstance(vm.slots[slot_id], type(adapter)):
|
||||
yield from vm.slot_add_binding(slot_id, adapter)
|
||||
await vm.slot_add_binding(slot_id, adapter)
|
||||
except IndexError:
|
||||
raise DynamipsError("Slot {} doesn't exist on this router".format(slot_id))
|
||||
elif name.startswith("slot") and (value is None or value is ""):
|
||||
slot_id = int(name[-1])
|
||||
try:
|
||||
if vm.slots[slot_id]:
|
||||
yield from vm.slot_remove_binding(slot_id)
|
||||
await vm.slot_remove_binding(slot_id)
|
||||
except IndexError:
|
||||
raise DynamipsError("Slot {} doesn't exist on this router".format(slot_id))
|
||||
elif name.startswith("wic") and value in WIC_MATRIX:
|
||||
@ -480,32 +471,31 @@ class Dynamips(BaseManager):
|
||||
wic = WIC_MATRIX[wic_name]()
|
||||
try:
|
||||
if vm.slots[0].wics[wic_slot_id] and not isinstance(vm.slots[0].wics[wic_slot_id], type(wic)):
|
||||
yield from vm.uninstall_wic(wic_slot_id)
|
||||
await vm.uninstall_wic(wic_slot_id)
|
||||
if not isinstance(vm.slots[0].wics[wic_slot_id], type(wic)):
|
||||
yield from vm.install_wic(wic_slot_id, wic)
|
||||
await vm.install_wic(wic_slot_id, wic)
|
||||
except IndexError:
|
||||
raise DynamipsError("WIC slot {} doesn't exist on this router".format(wic_slot_id))
|
||||
elif name.startswith("wic") and (value is None or value is ""):
|
||||
wic_slot_id = int(name[-1])
|
||||
try:
|
||||
if vm.slots[0].wics and vm.slots[0].wics[wic_slot_id]:
|
||||
yield from vm.uninstall_wic(wic_slot_id)
|
||||
await vm.uninstall_wic(wic_slot_id)
|
||||
except IndexError:
|
||||
raise DynamipsError("WIC slot {} doesn't exist on this router".format(wic_slot_id))
|
||||
|
||||
mmap_support = self.config.get_section_config("Dynamips").getboolean("mmap_support", True)
|
||||
if mmap_support is False:
|
||||
yield from vm.set_mmap(False)
|
||||
await vm.set_mmap(False)
|
||||
|
||||
sparse_memory_support = self.config.get_section_config("Dynamips").getboolean("sparse_memory_support", True)
|
||||
if sparse_memory_support is False:
|
||||
yield from vm.set_sparsemem(False)
|
||||
await vm.set_sparsemem(False)
|
||||
|
||||
# update the configs if needed
|
||||
yield from self.set_vm_configs(vm, settings)
|
||||
await self.set_vm_configs(vm, settings)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_vm_configs(self, vm, settings):
|
||||
async def set_vm_configs(self, vm, settings):
|
||||
"""
|
||||
Set VM configs from pushed content or existing config files.
|
||||
|
||||
@ -554,25 +544,24 @@ class Dynamips(BaseManager):
|
||||
|
||||
return os.path.join("configs", os.path.basename(path))
|
||||
|
||||
@asyncio.coroutine
|
||||
def auto_idlepc(self, vm):
|
||||
async def auto_idlepc(self, vm):
|
||||
"""
|
||||
Try to find the best possible idle-pc value.
|
||||
|
||||
:param vm: VM instance
|
||||
"""
|
||||
|
||||
yield from vm.set_idlepc("0x0")
|
||||
await vm.set_idlepc("0x0")
|
||||
was_auto_started = False
|
||||
old_priority = None
|
||||
try:
|
||||
status = yield from vm.get_status()
|
||||
status = await vm.get_status()
|
||||
if status != "running":
|
||||
yield from vm.start()
|
||||
await vm.start()
|
||||
was_auto_started = True
|
||||
yield from asyncio.sleep(20) # leave time to the router to boot
|
||||
await asyncio.sleep(20) # leave time to the router to boot
|
||||
validated_idlepc = None
|
||||
idlepcs = yield from vm.get_idle_pc_prop()
|
||||
idlepcs = await vm.get_idle_pc_prop()
|
||||
if not idlepcs:
|
||||
raise DynamipsError("No Idle-PC values found")
|
||||
|
||||
@ -582,14 +571,14 @@ class Dynamips(BaseManager):
|
||||
match = re.search(r"^0x[0-9a-f]{8}$", idlepc.split()[0])
|
||||
if not match:
|
||||
continue
|
||||
yield from vm.set_idlepc(idlepc.split()[0])
|
||||
await vm.set_idlepc(idlepc.split()[0])
|
||||
log.debug("Auto Idle-PC: trying idle-PC value {}".format(vm.idlepc))
|
||||
start_time = time.time()
|
||||
initial_cpu_usage = yield from vm.get_cpu_usage()
|
||||
initial_cpu_usage = await vm.get_cpu_usage()
|
||||
log.debug("Auto Idle-PC: initial CPU usage is {}%".format(initial_cpu_usage))
|
||||
yield from asyncio.sleep(3) # wait 3 seconds to probe the cpu again
|
||||
await asyncio.sleep(3) # wait 3 seconds to probe the cpu again
|
||||
elapsed_time = time.time() - start_time
|
||||
cpu_usage = yield from vm.get_cpu_usage()
|
||||
cpu_usage = await vm.get_cpu_usage()
|
||||
cpu_elapsed_usage = cpu_usage - initial_cpu_usage
|
||||
cpu_usage = abs(cpu_elapsed_usage * 100.0 / elapsed_time)
|
||||
if cpu_usage > 100:
|
||||
@ -609,11 +598,10 @@ class Dynamips(BaseManager):
|
||||
if old_priority is not None:
|
||||
vm.set_process_priority_windows(vm.hypervisor.process.pid, old_priority)
|
||||
if was_auto_started:
|
||||
yield from vm.stop()
|
||||
await vm.stop()
|
||||
return validated_idlepc
|
||||
|
||||
@asyncio.coroutine
|
||||
def duplicate_node(self, source_node_id, destination_node_id):
|
||||
async def duplicate_node(self, source_node_id, destination_node_id):
|
||||
"""
|
||||
Duplicate a node
|
||||
|
||||
@ -626,7 +614,7 @@ class Dynamips(BaseManager):
|
||||
|
||||
# Not a Dynamips router
|
||||
if not hasattr(source_node, "startup_config_path"):
|
||||
return (yield from super().duplicate_node(source_node_id, destination_node_id))
|
||||
return (await super().duplicate_node(source_node_id, destination_node_id))
|
||||
|
||||
try:
|
||||
with open(source_node.startup_config_path) as f:
|
||||
@ -638,13 +626,13 @@ class Dynamips(BaseManager):
|
||||
private_config = f.read()
|
||||
except OSError:
|
||||
private_config = None
|
||||
yield from self.set_vm_configs(destination_node, {
|
||||
await self.set_vm_configs(destination_node, {
|
||||
"startup_config_content": startup_config,
|
||||
"private_config_content": private_config
|
||||
})
|
||||
|
||||
# Force refresh of the name in configuration files
|
||||
new_name = destination_node.name
|
||||
yield from destination_node.set_name(source_node.name)
|
||||
yield from destination_node.set_name(new_name)
|
||||
await destination_node.set_name(source_node.name)
|
||||
await destination_node.set_name(new_name)
|
||||
return destination_node
|
||||
|
@ -59,8 +59,7 @@ class DynamipsHypervisor:
|
||||
self._writer = None
|
||||
self._io_lock = asyncio.Lock()
|
||||
|
||||
@asyncio.coroutine
|
||||
def connect(self, timeout=10):
|
||||
async def connect(self, timeout=10):
|
||||
"""
|
||||
Connects to the hypervisor.
|
||||
"""
|
||||
@ -78,9 +77,9 @@ class DynamipsHypervisor:
|
||||
connection_success = False
|
||||
last_exception = None
|
||||
while time.time() - begin < timeout:
|
||||
yield from asyncio.sleep(0.01)
|
||||
await asyncio.sleep(0.01)
|
||||
try:
|
||||
self._reader, self._writer = yield from asyncio.wait_for(asyncio.open_connection(host, self._port), timeout=1)
|
||||
self._reader, self._writer = await asyncio.wait_for(asyncio.open_connection(host, self._port), timeout=1)
|
||||
except (asyncio.TimeoutError, OSError) as e:
|
||||
last_exception = e
|
||||
continue
|
||||
@ -93,13 +92,13 @@ class DynamipsHypervisor:
|
||||
log.info("Connected to Dynamips hypervisor on {}:{} after {:.4f} seconds".format(host, self._port, time.time() - begin))
|
||||
|
||||
try:
|
||||
version = yield from self.send("hypervisor version")
|
||||
version = await self.send("hypervisor version")
|
||||
self._version = version[0].split("-", 1)[0]
|
||||
except IndexError:
|
||||
self._version = "Unknown"
|
||||
|
||||
# this forces to send the working dir to Dynamips
|
||||
yield from self.set_working_dir(self._working_dir)
|
||||
await self.set_working_dir(self._working_dir)
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
@ -111,45 +110,41 @@ class DynamipsHypervisor:
|
||||
|
||||
return self._version
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes the connection to this hypervisor (but leave it running).
|
||||
"""
|
||||
|
||||
yield from self.send("hypervisor close")
|
||||
await self.send("hypervisor close")
|
||||
self._writer.close()
|
||||
self._reader, self._writer = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops this hypervisor (will no longer run).
|
||||
"""
|
||||
|
||||
try:
|
||||
# try to properly stop the hypervisor
|
||||
yield from self.send("hypervisor stop")
|
||||
await self.send("hypervisor stop")
|
||||
except DynamipsError:
|
||||
pass
|
||||
try:
|
||||
if self._writer is not None:
|
||||
yield from self._writer.drain()
|
||||
await self._writer.drain()
|
||||
self._writer.close()
|
||||
except OSError as e:
|
||||
log.debug("Stopping hypervisor {}:{} {}".format(self._host, self._port, e))
|
||||
self._reader = self._writer = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def reset(self):
|
||||
async def reset(self):
|
||||
"""
|
||||
Resets this hypervisor (used to get an empty configuration).
|
||||
"""
|
||||
|
||||
yield from self.send("hypervisor reset")
|
||||
await self.send("hypervisor reset")
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_working_dir(self, working_dir):
|
||||
async def set_working_dir(self, working_dir):
|
||||
"""
|
||||
Sets the working directory for this hypervisor.
|
||||
|
||||
@ -157,7 +152,7 @@ class DynamipsHypervisor:
|
||||
"""
|
||||
|
||||
# encase working_dir in quotes to protect spaces in the path
|
||||
yield from self.send('hypervisor working_dir "{}"'.format(working_dir))
|
||||
await self.send('hypervisor working_dir "{}"'.format(working_dir))
|
||||
self._working_dir = working_dir
|
||||
log.debug("Working directory set to {}".format(self._working_dir))
|
||||
|
||||
@ -221,8 +216,7 @@ class DynamipsHypervisor:
|
||||
|
||||
self._host = host
|
||||
|
||||
@asyncio.coroutine
|
||||
def send(self, command):
|
||||
async def send(self, command):
|
||||
"""
|
||||
Sends commands to this hypervisor.
|
||||
|
||||
@ -244,7 +238,7 @@ class DynamipsHypervisor:
|
||||
# but still have more data. The only thing we know for sure is the last line
|
||||
# will begin with '100-' or a '2xx-' and end with '\r\n'
|
||||
|
||||
with (yield from self._io_lock):
|
||||
async with self._io_lock:
|
||||
if self._writer is None or self._reader is None:
|
||||
raise DynamipsError("Not connected")
|
||||
|
||||
@ -252,7 +246,7 @@ class DynamipsHypervisor:
|
||||
command = command.strip() + '\n'
|
||||
log.debug("sending {}".format(command))
|
||||
self._writer.write(command.encode())
|
||||
yield from self._writer.drain()
|
||||
await self._writer.drain()
|
||||
except OSError as e:
|
||||
raise DynamipsError("Could not send Dynamips command '{command}' to {host}:{port}: {error}, process running: {run}"
|
||||
.format(command=command.strip(), host=self._host, port=self._port, error=e, run=self.is_running()))
|
||||
@ -265,8 +259,8 @@ class DynamipsHypervisor:
|
||||
while True:
|
||||
try:
|
||||
try:
|
||||
# line = yield from self._reader.readline() # this can lead to ValueError: Line is too long
|
||||
chunk = yield from self._reader.read(1024) # match to Dynamips' buffer size
|
||||
# line = await self._reader.readline() # this can lead to ValueError: Line is too long
|
||||
chunk = await self._reader.read(1024) # match to Dynamips' buffer size
|
||||
except asyncio.CancelledError:
|
||||
# task has been canceled but continue to read
|
||||
# any remaining data sent by the hypervisor
|
||||
@ -283,7 +277,7 @@ class DynamipsHypervisor:
|
||||
.format(host=self._host, port=self._port, run=self.is_running()))
|
||||
else:
|
||||
retries += 1
|
||||
yield from asyncio.sleep(0.1)
|
||||
await asyncio.sleep(0.1)
|
||||
continue
|
||||
retries = 0
|
||||
buf += chunk.decode("utf-8", errors="ignore")
|
||||
|
@ -111,8 +111,7 @@ class Hypervisor(DynamipsHypervisor):
|
||||
|
||||
self._path = path
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts the Dynamips hypervisor process.
|
||||
"""
|
||||
@ -129,7 +128,7 @@ class Hypervisor(DynamipsHypervisor):
|
||||
self._stdout_file = os.path.join(self.working_dir, "dynamips_i{}_stdout.txt".format(self._id))
|
||||
log.info("Dynamips process logging to {}".format(self._stdout_file))
|
||||
with open(self._stdout_file, "w", encoding="utf-8") as fd:
|
||||
self._process = yield from asyncio.create_subprocess_exec(*self._command,
|
||||
self._process = await asyncio.create_subprocess_exec(*self._command,
|
||||
stdout=fd,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=self._working_dir,
|
||||
@ -140,20 +139,19 @@ class Hypervisor(DynamipsHypervisor):
|
||||
log.error("Could not start Dynamips: {}".format(e))
|
||||
raise DynamipsError("Could not start Dynamips: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the Dynamips hypervisor process.
|
||||
"""
|
||||
|
||||
if self.is_running():
|
||||
log.info("Stopping Dynamips process PID={}".format(self._process.pid))
|
||||
yield from DynamipsHypervisor.stop(self)
|
||||
await DynamipsHypervisor.stop(self)
|
||||
# give some time for the hypervisor to properly stop.
|
||||
# time to delete UNIX NIOs for instance.
|
||||
yield from asyncio.sleep(0.01)
|
||||
await asyncio.sleep(0.01)
|
||||
try:
|
||||
yield from wait_for_process_termination(self._process, timeout=3)
|
||||
await wait_for_process_termination(self._process, timeout=3)
|
||||
except asyncio.TimeoutError:
|
||||
if self._process.returncode is None:
|
||||
log.warning("Dynamips process {} is still running... killing it".format(self._process.pid))
|
||||
|
@ -48,53 +48,48 @@ class NIO:
|
||||
self._output_filter_options = None # no output filter options by default
|
||||
self._dynamips_direction = {"in": 0, "out": 1, "both": 2}
|
||||
|
||||
@asyncio.coroutine
|
||||
def list(self):
|
||||
async def list(self):
|
||||
"""
|
||||
Returns all NIOs.
|
||||
|
||||
:returns: NIO list
|
||||
"""
|
||||
|
||||
nio_list = yield from self._hypervisor.send("nio list")
|
||||
nio_list = await self._hypervisor.send("nio list")
|
||||
return nio_list
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes this NIO.
|
||||
"""
|
||||
|
||||
if self._input_filter or self._output_filter:
|
||||
yield from self.unbind_filter("both")
|
||||
yield from self._hypervisor.send("nio delete {}".format(self._name))
|
||||
await self.unbind_filter("both")
|
||||
await self._hypervisor.send("nio delete {}".format(self._name))
|
||||
log.info("NIO {name} has been deleted".format(name=self._name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def rename(self, new_name):
|
||||
async def rename(self, new_name):
|
||||
"""
|
||||
Renames this NIO
|
||||
|
||||
:param new_name: new NIO name
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send("nio rename {name} {new_name}".format(name=self._name, new_name=new_name))
|
||||
await self._hypervisor.send("nio rename {name} {new_name}".format(name=self._name, new_name=new_name))
|
||||
|
||||
log.info("NIO {name} renamed to {new_name}".format(name=self._name, new_name=new_name))
|
||||
self._name = new_name
|
||||
|
||||
@asyncio.coroutine
|
||||
def debug(self, debug):
|
||||
async def debug(self, debug):
|
||||
"""
|
||||
Enables/Disables debugging for this NIO.
|
||||
|
||||
:param debug: debug value (0 = disable, enable = 1)
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send("nio set_debug {name} {debug}".format(name=self._name, debug=debug))
|
||||
await self._hypervisor.send("nio set_debug {name} {debug}".format(name=self._name, debug=debug))
|
||||
|
||||
@asyncio.coroutine
|
||||
def bind_filter(self, direction, filter_name):
|
||||
async def bind_filter(self, direction, filter_name):
|
||||
"""
|
||||
Adds a packet filter to this NIO.
|
||||
Filter "freq_drop" drops packets.
|
||||
@ -108,7 +103,7 @@ class NIO:
|
||||
raise DynamipsError("Unknown direction {} to bind filter {}:".format(direction, filter_name))
|
||||
dynamips_direction = self._dynamips_direction[direction]
|
||||
|
||||
yield from self._hypervisor.send("nio bind_filter {name} {direction} {filter}".format(name=self._name,
|
||||
await self._hypervisor.send("nio bind_filter {name} {direction} {filter}".format(name=self._name,
|
||||
direction=dynamips_direction,
|
||||
filter=filter_name))
|
||||
|
||||
@ -120,8 +115,7 @@ class NIO:
|
||||
self._input_filter = filter_name
|
||||
self._output_filter = filter_name
|
||||
|
||||
@asyncio.coroutine
|
||||
def unbind_filter(self, direction):
|
||||
async def unbind_filter(self, direction):
|
||||
"""
|
||||
Removes packet filter for this NIO.
|
||||
|
||||
@ -132,7 +126,7 @@ class NIO:
|
||||
raise DynamipsError("Unknown direction {} to unbind filter:".format(direction))
|
||||
dynamips_direction = self._dynamips_direction[direction]
|
||||
|
||||
yield from self._hypervisor.send("nio unbind_filter {name} {direction}".format(name=self._name,
|
||||
await self._hypervisor.send("nio unbind_filter {name} {direction}".format(name=self._name,
|
||||
direction=dynamips_direction))
|
||||
|
||||
if direction == "in":
|
||||
@ -143,8 +137,7 @@ class NIO:
|
||||
self._input_filter = None
|
||||
self._output_filter = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def setup_filter(self, direction, options):
|
||||
async def setup_filter(self, direction, options):
|
||||
"""
|
||||
Setups a packet filter bound with this NIO.
|
||||
|
||||
@ -166,7 +159,7 @@ class NIO:
|
||||
raise DynamipsError("Unknown direction {} to setup filter:".format(direction))
|
||||
dynamips_direction = self._dynamips_direction[direction]
|
||||
|
||||
yield from self._hypervisor.send("nio setup_filter {name} {direction} {options}".format(name=self._name,
|
||||
await self._hypervisor.send("nio setup_filter {name} {direction} {options}".format(name=self._name,
|
||||
direction=dynamips_direction,
|
||||
options=options))
|
||||
|
||||
@ -198,24 +191,22 @@ class NIO:
|
||||
|
||||
return self._output_filter, self._output_filter_options
|
||||
|
||||
@asyncio.coroutine
|
||||
def get_stats(self):
|
||||
async def get_stats(self):
|
||||
"""
|
||||
Gets statistics for this NIO.
|
||||
|
||||
:returns: NIO statistics (string with packets in, packets out, bytes in, bytes out)
|
||||
"""
|
||||
|
||||
stats = yield from self._hypervisor.send("nio get_stats {}".format(self._name))
|
||||
stats = await self._hypervisor.send("nio get_stats {}".format(self._name))
|
||||
return stats[0]
|
||||
|
||||
@asyncio.coroutine
|
||||
def reset_stats(self):
|
||||
async def reset_stats(self):
|
||||
"""
|
||||
Resets statistics for this NIO.
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send("nio reset_stats {}".format(self._name))
|
||||
await self._hypervisor.send("nio reset_stats {}".format(self._name))
|
||||
|
||||
@property
|
||||
def bandwidth(self):
|
||||
@ -227,15 +218,14 @@ class NIO:
|
||||
|
||||
return self._bandwidth
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_bandwidth(self, bandwidth):
|
||||
async def set_bandwidth(self, bandwidth):
|
||||
"""
|
||||
Sets bandwidth constraint.
|
||||
|
||||
:param bandwidth: bandwidth integer value (in Kb/s)
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send("nio set_bandwidth {name} {bandwidth}".format(name=self._name, bandwidth=bandwidth))
|
||||
await self._hypervisor.send("nio set_bandwidth {name} {bandwidth}".format(name=self._name, bandwidth=bandwidth))
|
||||
self._bandwidth = bandwidth
|
||||
|
||||
@property
|
||||
|
@ -43,10 +43,9 @@ class NIOGenericEthernet(NIO):
|
||||
self._ethernet_device = ethernet_device
|
||||
super().__init__(name, hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self._hypervisor.send("nio create_gen_eth {name} {eth_device}".format(name=self._name,
|
||||
await self._hypervisor.send("nio create_gen_eth {name} {eth_device}".format(name=self._name,
|
||||
eth_device=self._ethernet_device))
|
||||
|
||||
log.info("NIO Generic Ethernet {name} created with device {device}".format(name=self._name,
|
||||
|
@ -42,10 +42,9 @@ class NIOLinuxEthernet(NIO):
|
||||
self._ethernet_device = ethernet_device
|
||||
super().__init__(name, hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self._hypervisor.send("nio create_linux_eth {name} {eth_device}".format(name=self._name,
|
||||
await self._hypervisor.send("nio create_linux_eth {name} {eth_device}".format(name=self._name,
|
||||
eth_device=self._ethernet_device))
|
||||
|
||||
log.info("NIO Linux Ethernet {name} created with device {device}".format(name=self._name,
|
||||
|
@ -41,10 +41,9 @@ class NIONull(NIO):
|
||||
name = 'null-{}'.format(uuid.uuid4())
|
||||
super().__init__(name, hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self._hypervisor.send("nio create_null {}".format(self._name))
|
||||
await self._hypervisor.send("nio create_null {}".format(self._name))
|
||||
log.info("NIO NULL {name} created.".format(name=self._name))
|
||||
|
||||
def __json__(self):
|
||||
|
@ -43,10 +43,9 @@ class NIOTAP(NIO):
|
||||
self._tap_device = tap_device
|
||||
super().__init__(name, hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self._hypervisor.send("nio create_tap {name} {tap}".format(name=self._name, tap=self._tap_device))
|
||||
await self._hypervisor.send("nio create_tap {name} {tap}".format(name=self._name, tap=self._tap_device))
|
||||
log.info("NIO TAP {name} created with device {device}".format(name=self._name, device=self._tap_device))
|
||||
|
||||
@property
|
||||
|
@ -53,13 +53,12 @@ class NIOUDP(NIO):
|
||||
self._node = node
|
||||
super().__init__(name, node.hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
if not self._hypervisor:
|
||||
return
|
||||
# Ubridge is not supported
|
||||
if not hasattr(self._node, "add_ubridge_udp_connection"):
|
||||
yield from self._hypervisor.send("nio create_udp {name} {lport} {rhost} {rport}".format(name=self._name,
|
||||
await self._hypervisor.send("nio create_udp {name} {lport} {rhost} {rport}".format(name=self._name,
|
||||
lport=self._lport,
|
||||
rhost=self._rhost,
|
||||
rport=self._rport))
|
||||
@ -67,7 +66,7 @@ class NIOUDP(NIO):
|
||||
self._local_tunnel_lport = self._node.manager.port_manager.get_free_udp_port(self._node.project)
|
||||
self._local_tunnel_rport = self._node.manager.port_manager.get_free_udp_port(self._node.project)
|
||||
self._bridge_name = 'DYNAMIPS-{}-{}'.format(self._local_tunnel_lport, self._local_tunnel_rport)
|
||||
yield from self._hypervisor.send("nio create_udp {name} {lport} {rhost} {rport}".format(name=self._name,
|
||||
await self._hypervisor.send("nio create_udp {name} {lport} {rhost} {rport}".format(name=self._name,
|
||||
lport=self._local_tunnel_lport,
|
||||
rhost='127.0.0.1',
|
||||
rport=self._local_tunnel_rport))
|
||||
@ -84,24 +83,22 @@ class NIOUDP(NIO):
|
||||
self._rhost,
|
||||
self._rport)
|
||||
self._destination_nio.filters = self._filters
|
||||
yield from self._node.add_ubridge_udp_connection(
|
||||
await self._node.add_ubridge_udp_connection(
|
||||
self._bridge_name,
|
||||
self._source_nio,
|
||||
self._destination_nio
|
||||
)
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self):
|
||||
async def update(self):
|
||||
self._destination_nio.filters = self._filters
|
||||
yield from self._node.update_ubridge_udp_connection(
|
||||
await self._node.update_ubridge_udp_connection(
|
||||
self._bridge_name,
|
||||
self._source_nio,
|
||||
self._destination_nio)
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
if self._local_tunnel_lport:
|
||||
yield from self._node.ubridge_delete_bridge(self._bridge_name)
|
||||
await self._node.ubridge_delete_bridge(self._bridge_name)
|
||||
self._node.manager.port_manager.release_udp_port(self._local_tunnel_lport, self ._node.project)
|
||||
if self._local_tunnel_rport:
|
||||
self._node.manager.port_manager.release_udp_port(self._local_tunnel_rport, self._node.project)
|
||||
|
@ -45,10 +45,9 @@ class NIOUNIX(NIO):
|
||||
self._remote_file = remote_file
|
||||
super().__init__(name, hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self._hypervisor.send("nio create_unix {name} {local} {remote}".format(name=self._name,
|
||||
await self._hypervisor.send("nio create_unix {name} {local} {remote}".format(name=self._name,
|
||||
local=self._local_file,
|
||||
remote=self._remote_file))
|
||||
|
||||
|
@ -45,10 +45,9 @@ class NIOVDE(NIO):
|
||||
self._local_file = local_file
|
||||
super().__init__(name, hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self._hypervisor.send("nio create_vde {name} {control} {local}".format(name=self._name,
|
||||
await self._hypervisor.send("nio create_vde {name} {control} {local}".format(name=self._name,
|
||||
control=self._control_file,
|
||||
local=self._local_file))
|
||||
|
||||
|
@ -64,26 +64,24 @@ class ATMSwitch(Device):
|
||||
"mappings": mappings,
|
||||
"status": "started"}
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
if self._hypervisor is None:
|
||||
module_workdir = self.project.module_working_directory(self.manager.module_name.lower())
|
||||
self._hypervisor = yield from self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
self._hypervisor = await self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
|
||||
yield from self._hypervisor.send('atmsw create "{}"'.format(self._name))
|
||||
await self._hypervisor.send('atmsw create "{}"'.format(self._name))
|
||||
log.info('ATM switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
self._hypervisor.devices.append(self)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_name(self, new_name):
|
||||
async def set_name(self, new_name):
|
||||
"""
|
||||
Renames this ATM switch.
|
||||
|
||||
:param new_name: New name for this switch
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('atm rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
|
||||
await self._hypervisor.send('atm rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
|
||||
log.info('ATM switch "{name}" [{id}]: renamed to "{new_name}"'.format(name=self._name,
|
||||
id=self._id,
|
||||
new_name=new_name))
|
||||
@ -119,31 +117,29 @@ class ATMSwitch(Device):
|
||||
|
||||
self._mappings = mappings
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
for nio in self._nios.values():
|
||||
if nio:
|
||||
yield from nio.close()
|
||||
await nio.close()
|
||||
|
||||
if self._hypervisor:
|
||||
try:
|
||||
yield from self._hypervisor.send('atmsw delete "{}"'.format(self._name))
|
||||
await self._hypervisor.send('atmsw delete "{}"'.format(self._name))
|
||||
log.info('ATM switch "{name}" [{id}] has been deleted'.format(name=self._name, id=self._id))
|
||||
except DynamipsError:
|
||||
log.debug("Could not properly delete ATM switch {}".format(self._name))
|
||||
if self._hypervisor and self in self._hypervisor.devices:
|
||||
self._hypervisor.devices.remove(self)
|
||||
if self._hypervisor and not self._hypervisor.devices:
|
||||
yield from self.hypervisor.stop()
|
||||
await self.hypervisor.stop()
|
||||
self._hypervisor = None
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes this ATM switch.
|
||||
"""
|
||||
yield from self.close()
|
||||
await self.close()
|
||||
|
||||
def has_port(self, port):
|
||||
"""
|
||||
@ -156,8 +152,7 @@ class ATMSwitch(Device):
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on ATM switch.
|
||||
|
||||
@ -174,10 +169,9 @@ class ATMSwitch(Device):
|
||||
port=port_number))
|
||||
|
||||
self._nios[port_number] = nio
|
||||
yield from self.set_mappings(self._mappings)
|
||||
await self.set_mappings(self._mappings)
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of this ATM switch.
|
||||
|
||||
@ -202,8 +196,8 @@ class ATMSwitch(Device):
|
||||
destination_port=destination_port,
|
||||
destination_vpi=destination_vpi,
|
||||
destination_vci=destination_vci))
|
||||
yield from self.unmap_pvc(source_port, source_vpi, source_vci, destination_port, destination_vpi, destination_vci)
|
||||
yield from self.unmap_pvc(destination_port, destination_vpi, destination_vci, source_port, source_vpi, source_vci)
|
||||
await self.unmap_pvc(source_port, source_vpi, source_vci, destination_port, destination_vpi, destination_vci)
|
||||
await self.unmap_pvc(destination_port, destination_vpi, destination_vci, source_port, source_vpi, source_vci)
|
||||
else:
|
||||
# remove the virtual paths mapped with this port/nio
|
||||
source_port, source_vpi = source
|
||||
@ -215,8 +209,8 @@ class ATMSwitch(Device):
|
||||
source_vpi=source_vpi,
|
||||
destination_port=destination_port,
|
||||
destination_vpi=destination_vpi))
|
||||
yield from self.unmap_vp(source_port, source_vpi, destination_port, destination_vpi)
|
||||
yield from self.unmap_vp(destination_port, destination_vpi, source_port, source_vpi)
|
||||
await self.unmap_vp(source_port, source_vpi, destination_port, destination_vpi)
|
||||
await self.unmap_vp(destination_port, destination_vpi, source_port, source_vpi)
|
||||
|
||||
nio = self._nios[port_number]
|
||||
if isinstance(nio, NIOUDP):
|
||||
@ -229,8 +223,7 @@ class ATMSwitch(Device):
|
||||
del self._nios[port_number]
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_mappings(self, mappings):
|
||||
async def set_mappings(self, mappings):
|
||||
"""
|
||||
Applies VC mappings
|
||||
|
||||
@ -258,8 +251,8 @@ class ATMSwitch(Device):
|
||||
destination_port=destination_port,
|
||||
destination_vpi=destination_vpi,
|
||||
destination_vci=destination_vci))
|
||||
yield from self.map_pvc(source_port, source_vpi, source_vci, destination_port, destination_vpi, destination_vci)
|
||||
yield from self.map_pvc(destination_port, destination_vpi, destination_vci, source_port, source_vpi, source_vci)
|
||||
await self.map_pvc(source_port, source_vpi, source_vci, destination_port, destination_vpi, destination_vci)
|
||||
await self.map_pvc(destination_port, destination_vpi, destination_vci, source_port, source_vpi, source_vci)
|
||||
else:
|
||||
# add the virtual paths
|
||||
source_port, source_vpi = map(int, source.split(':'))
|
||||
@ -272,11 +265,10 @@ class ATMSwitch(Device):
|
||||
source_vpi=source_vpi,
|
||||
destination_port=destination_port,
|
||||
destination_vpi=destination_vpi))
|
||||
yield from self.map_vp(source_port, source_vpi, destination_port, destination_vpi)
|
||||
yield from self.map_vp(destination_port, destination_vpi, source_port, source_vpi)
|
||||
await self.map_vp(source_port, source_vpi, destination_port, destination_vpi)
|
||||
await self.map_vp(destination_port, destination_vpi, source_port, source_vpi)
|
||||
|
||||
@asyncio.coroutine
|
||||
def map_vp(self, port1, vpi1, port2, vpi2):
|
||||
async def map_vp(self, port1, vpi1, port2, vpi2):
|
||||
"""
|
||||
Creates a new Virtual Path connection.
|
||||
|
||||
@ -295,7 +287,7 @@ class ATMSwitch(Device):
|
||||
nio1 = self._nios[port1]
|
||||
nio2 = self._nios[port2]
|
||||
|
||||
yield from self._hypervisor.send('atmsw create_vpc "{name}" {input_nio} {input_vpi} {output_nio} {output_vpi}'.format(name=self._name,
|
||||
await self._hypervisor.send('atmsw create_vpc "{name}" {input_nio} {input_vpi} {output_nio} {output_vpi}'.format(name=self._name,
|
||||
input_nio=nio1,
|
||||
input_vpi=vpi1,
|
||||
output_nio=nio2,
|
||||
@ -310,8 +302,7 @@ class ATMSwitch(Device):
|
||||
|
||||
self._active_mappings[(port1, vpi1)] = (port2, vpi2)
|
||||
|
||||
@asyncio.coroutine
|
||||
def unmap_vp(self, port1, vpi1, port2, vpi2):
|
||||
async def unmap_vp(self, port1, vpi1, port2, vpi2):
|
||||
"""
|
||||
Deletes a new Virtual Path connection.
|
||||
|
||||
@ -330,7 +321,7 @@ class ATMSwitch(Device):
|
||||
nio1 = self._nios[port1]
|
||||
nio2 = self._nios[port2]
|
||||
|
||||
yield from self._hypervisor.send('atmsw delete_vpc "{name}" {input_nio} {input_vpi} {output_nio} {output_vpi}'.format(name=self._name,
|
||||
await self._hypervisor.send('atmsw delete_vpc "{name}" {input_nio} {input_vpi} {output_nio} {output_vpi}'.format(name=self._name,
|
||||
input_nio=nio1,
|
||||
input_vpi=vpi1,
|
||||
output_nio=nio2,
|
||||
@ -345,8 +336,7 @@ class ATMSwitch(Device):
|
||||
|
||||
del self._active_mappings[(port1, vpi1)]
|
||||
|
||||
@asyncio.coroutine
|
||||
def map_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
|
||||
async def map_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
|
||||
"""
|
||||
Creates a new Virtual Channel connection (unidirectional).
|
||||
|
||||
@ -367,7 +357,7 @@ class ATMSwitch(Device):
|
||||
nio1 = self._nios[port1]
|
||||
nio2 = self._nios[port2]
|
||||
|
||||
yield from self._hypervisor.send('atmsw create_vcc "{name}" {input_nio} {input_vpi} {input_vci} {output_nio} {output_vpi} {output_vci}'.format(name=self._name,
|
||||
await self._hypervisor.send('atmsw create_vcc "{name}" {input_nio} {input_vpi} {input_vci} {output_nio} {output_vpi} {output_vci}'.format(name=self._name,
|
||||
input_nio=nio1,
|
||||
input_vpi=vpi1,
|
||||
input_vci=vci1,
|
||||
@ -386,8 +376,7 @@ class ATMSwitch(Device):
|
||||
|
||||
self._active_mappings[(port1, vpi1, vci1)] = (port2, vpi2, vci2)
|
||||
|
||||
@asyncio.coroutine
|
||||
def unmap_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
|
||||
async def unmap_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
|
||||
"""
|
||||
Deletes a new Virtual Channel connection (unidirectional).
|
||||
|
||||
@ -408,7 +397,7 @@ class ATMSwitch(Device):
|
||||
nio1 = self._nios[port1]
|
||||
nio2 = self._nios[port2]
|
||||
|
||||
yield from self._hypervisor.send('atmsw delete_vcc "{name}" {input_nio} {input_vpi} {input_vci} {output_nio} {output_vpi} {output_vci}'.format(name=self._name,
|
||||
await self._hypervisor.send('atmsw delete_vcc "{name}" {input_nio} {input_vpi} {input_vci} {output_nio} {output_vpi} {output_vci}'.format(name=self._name,
|
||||
input_nio=nio1,
|
||||
input_vpi=vpi1,
|
||||
input_vci=vci1,
|
||||
@ -426,8 +415,7 @@ class ATMSwitch(Device):
|
||||
vci2=vci2))
|
||||
del self._active_mappings[(port1, vpi1, vci1)]
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_ATM_RFC1483"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_ATM_RFC1483"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -448,15 +436,14 @@ class ATMSwitch(Device):
|
||||
if nio.input_filter[0] is not None and nio.output_filter[0] is not None:
|
||||
raise DynamipsError("Port {} has already a filter applied".format(port_number))
|
||||
|
||||
yield from nio.bind_filter("both", "capture")
|
||||
yield from nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
await nio.bind_filter("both", "capture")
|
||||
await nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
|
||||
log.info('ATM switch "{name}" [{id}]: starting packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@ -467,7 +454,7 @@ class ATMSwitch(Device):
|
||||
raise DynamipsError("Port {} is not allocated".format(port_number))
|
||||
|
||||
nio = self._nios[port_number]
|
||||
yield from nio.unbind_filter("both")
|
||||
await nio.unbind_filter("both")
|
||||
log.info('ATM switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
|
@ -41,25 +41,23 @@ class Bridge(Device):
|
||||
super().__init__(name, node_id, project, manager, hypervisor)
|
||||
self._nios = []
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
if self._hypervisor is None:
|
||||
module_workdir = self.project.module_working_directory(self.manager.module_name.lower())
|
||||
self._hypervisor = yield from self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
self._hypervisor = await self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
|
||||
yield from self._hypervisor.send('nio_bridge create "{}"'.format(self._name))
|
||||
await self._hypervisor.send('nio_bridge create "{}"'.format(self._name))
|
||||
self._hypervisor.devices.append(self)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_name(self, new_name):
|
||||
async def set_name(self, new_name):
|
||||
"""
|
||||
Renames this bridge.
|
||||
|
||||
:param new_name: New name for this bridge
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('nio_bridge rename "{name}" "{new_name}"'.format(name=self._name,
|
||||
await self._hypervisor.send('nio_bridge rename "{name}" "{new_name}"'.format(name=self._name,
|
||||
new_name=new_name))
|
||||
|
||||
self._name = new_name
|
||||
@ -74,8 +72,7 @@ class Bridge(Device):
|
||||
|
||||
return self._nios
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes this bridge.
|
||||
"""
|
||||
@ -83,28 +80,26 @@ class Bridge(Device):
|
||||
if self._hypervisor and self in self._hypervisor.devices:
|
||||
self._hypervisor.devices.remove(self)
|
||||
if self._hypervisor and not self._hypervisor.devices:
|
||||
yield from self._hypervisor.send('nio_bridge delete "{}"'.format(self._name))
|
||||
await self._hypervisor.send('nio_bridge delete "{}"'.format(self._name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio):
|
||||
async def add_nio(self, nio):
|
||||
"""
|
||||
Adds a NIO as new port on this bridge.
|
||||
|
||||
:param nio: NIO instance to add
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('nio_bridge add_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
await self._hypervisor.send('nio_bridge add_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
self._nios.append(nio)
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, nio):
|
||||
async def remove_nio(self, nio):
|
||||
"""
|
||||
Removes the specified NIO as member of this bridge.
|
||||
|
||||
:param nio: NIO instance to remove
|
||||
"""
|
||||
if self._hypervisor:
|
||||
yield from self._hypervisor.send('nio_bridge remove_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
await self._hypervisor.send('nio_bridge remove_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
self._nios.remove(nio)
|
||||
|
||||
@property
|
||||
|
@ -70,12 +70,11 @@ class C1700(Router):
|
||||
router_info.update(c1700_router_info)
|
||||
return router_info
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from Router.create(self)
|
||||
await Router.create(self)
|
||||
if self._chassis != "1720":
|
||||
yield from self.set_chassis(self._chassis)
|
||||
await self.set_chassis(self._chassis)
|
||||
self._setup_chassis()
|
||||
|
||||
def _setup_chassis(self):
|
||||
@ -103,8 +102,7 @@ class C1700(Router):
|
||||
|
||||
return self._chassis
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_chassis(self, chassis):
|
||||
async def set_chassis(self, chassis):
|
||||
"""
|
||||
Sets the chassis.
|
||||
|
||||
@ -112,7 +110,7 @@ class C1700(Router):
|
||||
1720, 1721, 1750, 1751 or 1760
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c1700 set_chassis "{name}" {chassis}'.format(name=self._name, chassis=chassis))
|
||||
await self._hypervisor.send('c1700 set_chassis "{name}" {chassis}'.format(name=self._name, chassis=chassis))
|
||||
|
||||
log.info('Router "{name}" [{id}]: chassis set to {chassis}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@ -131,15 +129,14 @@ class C1700(Router):
|
||||
|
||||
return self._iomem
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_iomem(self, iomem):
|
||||
async def set_iomem(self, iomem):
|
||||
"""
|
||||
Sets I/O memory size for this router.
|
||||
|
||||
:param iomem: I/O memory size
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c1700 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
await self._hypervisor.send('c1700 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
|
||||
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
|
||||
id=self._id,
|
||||
|
@ -85,12 +85,11 @@ class C2600(Router):
|
||||
router_info.update(c2600_router_info)
|
||||
return router_info
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from Router.create(self)
|
||||
await Router.create(self)
|
||||
if self._chassis != "2610":
|
||||
yield from self.set_chassis(self._chassis)
|
||||
await self.set_chassis(self._chassis)
|
||||
self._setup_chassis()
|
||||
|
||||
def _setup_chassis(self):
|
||||
@ -112,8 +111,7 @@ class C2600(Router):
|
||||
|
||||
return self._chassis
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_chassis(self, chassis):
|
||||
async def set_chassis(self, chassis):
|
||||
"""
|
||||
Sets the chassis.
|
||||
|
||||
@ -122,7 +120,7 @@ class C2600(Router):
|
||||
2620XM, 2621XM, 2650XM or 2651XM
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c2600 set_chassis "{name}" {chassis}'.format(name=self._name, chassis=chassis))
|
||||
await self._hypervisor.send('c2600 set_chassis "{name}" {chassis}'.format(name=self._name, chassis=chassis))
|
||||
|
||||
log.info('Router "{name}" [{id}]: chassis set to {chassis}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@ -140,15 +138,14 @@ class C2600(Router):
|
||||
|
||||
return self._iomem
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_iomem(self, iomem):
|
||||
async def set_iomem(self, iomem):
|
||||
"""
|
||||
Sets I/O memory size for this router.
|
||||
|
||||
:param iomem: I/O memory size
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c2600 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
await self._hypervisor.send('c2600 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
|
||||
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
|
||||
id=self._id,
|
||||
|
@ -79,15 +79,14 @@ class C2691(Router):
|
||||
|
||||
return self._iomem
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_iomem(self, iomem):
|
||||
async def set_iomem(self, iomem):
|
||||
"""
|
||||
Sets I/O memory size for this router.
|
||||
|
||||
:param iomem: I/O memory size
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c2691 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
await self._hypervisor.send('c2691 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
|
||||
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
|
||||
id=self._id,
|
||||
|
@ -66,12 +66,11 @@ class C3600(Router):
|
||||
router_info.update(c3600_router_info)
|
||||
return router_info
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from Router.create(self)
|
||||
await Router.create(self)
|
||||
if self._chassis != "3640":
|
||||
yield from self.set_chassis(self._chassis)
|
||||
await self.set_chassis(self._chassis)
|
||||
self._setup_chassis()
|
||||
|
||||
def _setup_chassis(self):
|
||||
@ -98,15 +97,14 @@ class C3600(Router):
|
||||
|
||||
return self._chassis
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_chassis(self, chassis):
|
||||
async def set_chassis(self, chassis):
|
||||
"""
|
||||
Sets the chassis.
|
||||
|
||||
:param: chassis string: 3620, 3640 or 3660
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c3600 set_chassis "{name}" {chassis}'.format(name=self._name, chassis=chassis))
|
||||
await self._hypervisor.send('c3600 set_chassis "{name}" {chassis}'.format(name=self._name, chassis=chassis))
|
||||
|
||||
log.info('Router "{name}" [{id}]: chassis set to {chassis}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@ -125,15 +123,14 @@ class C3600(Router):
|
||||
|
||||
return self._iomem
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_iomem(self, iomem):
|
||||
async def set_iomem(self, iomem):
|
||||
"""
|
||||
Set I/O memory size for this router.
|
||||
|
||||
:param iomem: I/O memory size
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c3600 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
await self._hypervisor.send('c3600 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
|
||||
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
|
||||
id=self._id,
|
||||
|
@ -79,15 +79,14 @@ class C3725(Router):
|
||||
|
||||
return self._iomem
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_iomem(self, iomem):
|
||||
async def set_iomem(self, iomem):
|
||||
"""
|
||||
Sets I/O memory size for this router.
|
||||
|
||||
:param iomem: I/O memory size
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c3725 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
await self._hypervisor.send('c3725 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
|
||||
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
|
||||
id=self._id,
|
||||
|
@ -79,15 +79,14 @@ class C3745(Router):
|
||||
|
||||
return self._iomem
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_iomem(self, iomem):
|
||||
async def set_iomem(self, iomem):
|
||||
"""
|
||||
Sets I/O memory size for this router.
|
||||
|
||||
:param iomem: I/O memory size
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c3745 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
await self._hypervisor.send('c3745 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
|
||||
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
|
||||
id=self._id,
|
||||
|
@ -86,19 +86,18 @@ class C7200(Router):
|
||||
router_info.update(c7200_router_info)
|
||||
return router_info
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from Router.create(self)
|
||||
await Router.create(self)
|
||||
|
||||
if self._npe != "npe-400":
|
||||
yield from self.set_npe(self._npe)
|
||||
await self.set_npe(self._npe)
|
||||
|
||||
# first slot is a mandatory Input/Output controller (based on NPE type)
|
||||
if self.npe == "npe-g2":
|
||||
yield from self.slot_add_binding(0, C7200_IO_GE_E())
|
||||
await self.slot_add_binding(0, C7200_IO_GE_E())
|
||||
else:
|
||||
yield from self.slot_add_binding(0, C7200_IO_FE())
|
||||
await self.slot_add_binding(0, C7200_IO_FE())
|
||||
|
||||
@property
|
||||
def npe(self):
|
||||
@ -110,8 +109,7 @@ class C7200(Router):
|
||||
|
||||
return self._npe
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_npe(self, npe):
|
||||
async def set_npe(self, npe):
|
||||
"""
|
||||
Sets the NPE model.
|
||||
|
||||
@ -120,10 +118,10 @@ class C7200(Router):
|
||||
npe-225, npe-300, npe-400 and npe-g2 (PowerPC c7200 only)
|
||||
"""
|
||||
|
||||
if (yield from self.is_running()):
|
||||
if (await self.is_running()):
|
||||
raise DynamipsError("Cannot change NPE on running router")
|
||||
|
||||
yield from self._hypervisor.send('c7200 set_npe "{name}" {npe}'.format(name=self._name, npe=npe))
|
||||
await self._hypervisor.send('c7200 set_npe "{name}" {npe}'.format(name=self._name, npe=npe))
|
||||
|
||||
log.info('Router "{name}" [{id}]: NPE updated from {old_npe} to {new_npe}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@ -141,15 +139,14 @@ class C7200(Router):
|
||||
|
||||
return self._midplane
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_midplane(self, midplane):
|
||||
async def set_midplane(self, midplane):
|
||||
"""
|
||||
Sets the midplane model.
|
||||
|
||||
:returns: midplane model string (e.g. "vxr" or "std")
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c7200 set_midplane "{name}" {midplane}'.format(name=self._name, midplane=midplane))
|
||||
await self._hypervisor.send('c7200 set_midplane "{name}" {midplane}'.format(name=self._name, midplane=midplane))
|
||||
|
||||
log.info('Router "{name}" [{id}]: midplane updated from {old_midplane} to {new_midplane}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@ -167,8 +164,7 @@ class C7200(Router):
|
||||
|
||||
return self._sensors
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_sensors(self, sensors):
|
||||
async def set_sensors(self, sensors):
|
||||
"""
|
||||
Sets the 4 sensors with temperature in degree Celcius.
|
||||
|
||||
@ -182,7 +178,7 @@ class C7200(Router):
|
||||
|
||||
sensor_id = 0
|
||||
for sensor in sensors:
|
||||
yield from self._hypervisor.send('c7200 set_temp_sensor "{name}" {sensor_id} {temp}'.format(name=self._name,
|
||||
await self._hypervisor.send('c7200 set_temp_sensor "{name}" {sensor_id} {temp}'.format(name=self._name,
|
||||
sensor_id=sensor_id,
|
||||
temp=sensor))
|
||||
|
||||
@ -205,8 +201,7 @@ class C7200(Router):
|
||||
|
||||
return self._power_supplies
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_power_supplies(self, power_supplies):
|
||||
async def set_power_supplies(self, power_supplies):
|
||||
"""
|
||||
Sets the 2 power supplies with 0 = off, 1 = on.
|
||||
|
||||
@ -216,7 +211,7 @@ class C7200(Router):
|
||||
|
||||
power_supply_id = 0
|
||||
for power_supply in power_supplies:
|
||||
yield from self._hypervisor.send('c7200 set_power_supply "{name}" {power_supply_id} {powered_on}'.format(name=self._name,
|
||||
await self._hypervisor.send('c7200 set_power_supply "{name}" {power_supply_id} {powered_on}'.format(name=self._name,
|
||||
power_supply_id=power_supply_id,
|
||||
powered_on=power_supply))
|
||||
|
||||
@ -228,8 +223,7 @@ class C7200(Router):
|
||||
|
||||
self._power_supplies = power_supplies
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts this router.
|
||||
At least the IOS image must be set before starting it.
|
||||
@ -237,8 +231,8 @@ class C7200(Router):
|
||||
|
||||
# trick: we must send sensors and power supplies info after starting the router
|
||||
# otherwise they are not taken into account (Dynamips bug?)
|
||||
yield from Router.start(self)
|
||||
await Router.start(self)
|
||||
if self._sensors != [22, 22, 22, 22]:
|
||||
yield from self.set_sensors(self._sensors)
|
||||
await self.set_sensors(self._sensors)
|
||||
if self._power_supplies != [1, 1]:
|
||||
yield from self.set_power_supplies(self._power_supplies)
|
||||
await self.set_power_supplies(self._power_supplies)
|
||||
|
@ -93,10 +93,9 @@ class EthernetHub(Bridge):
|
||||
|
||||
self._ports = ports
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from Bridge.create(self)
|
||||
await Bridge.create(self)
|
||||
log.info('Ethernet hub "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
|
||||
@property
|
||||
@ -109,32 +108,29 @@ class EthernetHub(Bridge):
|
||||
|
||||
return self._mappings
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
return (yield from self.close())
|
||||
async def delete(self):
|
||||
return (await self.close())
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Deletes this hub.
|
||||
"""
|
||||
|
||||
for nio in self._nios:
|
||||
if nio:
|
||||
yield from nio.close()
|
||||
await nio.close()
|
||||
|
||||
try:
|
||||
yield from Bridge.delete(self)
|
||||
await Bridge.delete(self)
|
||||
log.info('Ethernet hub "{name}" [{id}] has been deleted'.format(name=self._name, id=self._id))
|
||||
except DynamipsError:
|
||||
log.debug("Could not properly delete Ethernet hub {}".format(self._name))
|
||||
if self._hypervisor and not self._hypervisor.devices:
|
||||
yield from self.hypervisor.stop()
|
||||
await self.hypervisor.stop()
|
||||
self._hypervisor = None
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on this hub.
|
||||
|
||||
@ -148,7 +144,7 @@ class EthernetHub(Bridge):
|
||||
if port_number in self._mappings:
|
||||
raise DynamipsError("Port {} isn't free".format(port_number))
|
||||
|
||||
yield from Bridge.add_nio(self, nio)
|
||||
await Bridge.add_nio(self, nio)
|
||||
|
||||
log.info('Ethernet hub "{name}" [{id}]: NIO {nio} bound to port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@ -156,8 +152,7 @@ class EthernetHub(Bridge):
|
||||
port=port_number))
|
||||
self._mappings[port_number] = nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of this hub.
|
||||
|
||||
@ -172,7 +167,7 @@ class EthernetHub(Bridge):
|
||||
nio = self._mappings[port_number]
|
||||
if isinstance(nio, NIOUDP):
|
||||
self.manager.port_manager.release_udp_port(nio.lport, self._project)
|
||||
yield from Bridge.remove_nio(self, nio)
|
||||
await Bridge.remove_nio(self, nio)
|
||||
|
||||
log.info('Ethernet hub "{name}" [{id}]: NIO {nio} removed from port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@ -182,8 +177,7 @@ class EthernetHub(Bridge):
|
||||
del self._mappings[port_number]
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -204,15 +198,14 @@ class EthernetHub(Bridge):
|
||||
if nio.input_filter[0] is not None and nio.output_filter[0] is not None:
|
||||
raise DynamipsError("Port {} has already a filter applied".format(port_number))
|
||||
|
||||
yield from nio.bind_filter("both", "capture")
|
||||
yield from nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
await nio.bind_filter("both", "capture")
|
||||
await nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
|
||||
log.info('Ethernet hub "{name}" [{id}]: starting packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@ -223,7 +216,7 @@ class EthernetHub(Bridge):
|
||||
raise DynamipsError("Port {} is not allocated".format(port_number))
|
||||
|
||||
nio = self._mappings[port_number]
|
||||
yield from nio.unbind_filter("both")
|
||||
await nio.unbind_filter("both")
|
||||
log.info('Ethernet hub "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
|
@ -43,13 +43,12 @@ class EthernetSwitchConsole(EmbedShell):
|
||||
super().__init__(welcome_message="Welcome to GNS3 builtin Ethernet switch.\n\nType help for available commands\n")
|
||||
self._node = node
|
||||
|
||||
@asyncio.coroutine
|
||||
def mac(self):
|
||||
async def mac(self):
|
||||
"""
|
||||
Show MAC address table
|
||||
"""
|
||||
res = 'Port Mac VLAN\n'
|
||||
result = (yield from self._node._hypervisor.send('ethsw show_mac_addr_table {}'.format(self._node.name)))
|
||||
result = (await self._node._hypervisor.send('ethsw show_mac_addr_table {}'.format(self._node.name)))
|
||||
for line in result:
|
||||
mac, vlan, nio = line.replace(' ', ' ').split(' ')
|
||||
mac = mac.replace('.', '')
|
||||
@ -163,41 +162,38 @@ class EthernetSwitch(Device):
|
||||
|
||||
self._ports = ports
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_port_settings(self):
|
||||
async def update_port_settings(self):
|
||||
for port_settings in self._ports:
|
||||
port_number = port_settings["port_number"]
|
||||
if port_number in self._nios and self._nios[port_number] is not None:
|
||||
yield from self.set_port_settings(port_number, port_settings)
|
||||
await self.set_port_settings(port_number, port_settings)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
if self._hypervisor is None:
|
||||
module_workdir = self.project.module_working_directory(self.manager.module_name.lower())
|
||||
self._hypervisor = yield from self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
self._hypervisor = await self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
|
||||
yield from self._hypervisor.send('ethsw create "{}"'.format(self._name))
|
||||
await self._hypervisor.send('ethsw create "{}"'.format(self._name))
|
||||
log.info('Ethernet switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
|
||||
self._telnet_shell = EthernetSwitchConsole(self)
|
||||
self._telnet_shell.prompt = self._name + '> '
|
||||
self._telnet = create_telnet_shell(self._telnet_shell)
|
||||
try:
|
||||
self._telnet_server = (yield from asyncio.start_server(self._telnet.run, self._manager.port_manager.console_host, self.console))
|
||||
self._telnet_server = (await asyncio.start_server(self._telnet.run, self._manager.port_manager.console_host, self.console))
|
||||
except OSError as e:
|
||||
self.project.emit("log.warning", {"message": "Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e)})
|
||||
self._hypervisor.devices.append(self)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_name(self, new_name):
|
||||
async def set_name(self, new_name):
|
||||
"""
|
||||
Renames this Ethernet switch.
|
||||
|
||||
:param new_name: New name for this switch
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('ethsw rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
|
||||
await self._hypervisor.send('ethsw rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
|
||||
log.info('Ethernet switch "{name}" [{id}]: renamed to "{new_name}"'.format(name=self._name,
|
||||
id=self._id,
|
||||
new_name=new_name))
|
||||
@ -223,39 +219,36 @@ class EthernetSwitch(Device):
|
||||
|
||||
return self._mappings
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
return (yield from self.close())
|
||||
async def delete(self):
|
||||
return (await self.close())
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Deletes this Ethernet switch.
|
||||
"""
|
||||
|
||||
yield from self._telnet.close()
|
||||
await self._telnet.close()
|
||||
if self._telnet_server:
|
||||
self._telnet_server.close()
|
||||
|
||||
for nio in self._nios.values():
|
||||
if nio:
|
||||
yield from nio.close()
|
||||
await nio.close()
|
||||
self.manager.port_manager.release_tcp_port(self._console, self._project)
|
||||
if self._hypervisor:
|
||||
try:
|
||||
yield from self._hypervisor.send('ethsw delete "{}"'.format(self._name))
|
||||
await self._hypervisor.send('ethsw delete "{}"'.format(self._name))
|
||||
log.info('Ethernet switch "{name}" [{id}] has been deleted'.format(name=self._name, id=self._id))
|
||||
except DynamipsError:
|
||||
log.debug("Could not properly delete Ethernet switch {}".format(self._name))
|
||||
if self._hypervisor and self in self._hypervisor.devices:
|
||||
self._hypervisor.devices.remove(self)
|
||||
if self._hypervisor and not self._hypervisor.devices:
|
||||
yield from self.hypervisor.stop()
|
||||
await self.hypervisor.stop()
|
||||
self._hypervisor = None
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on Ethernet switch.
|
||||
|
||||
@ -266,7 +259,7 @@ class EthernetSwitch(Device):
|
||||
if port_number in self._nios:
|
||||
raise DynamipsError("Port {} isn't free".format(port_number))
|
||||
|
||||
yield from self._hypervisor.send('ethsw add_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
await self._hypervisor.send('ethsw add_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
|
||||
log.info('Ethernet switch "{name}" [{id}]: NIO {nio} bound to port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@ -275,11 +268,10 @@ class EthernetSwitch(Device):
|
||||
self._nios[port_number] = nio
|
||||
for port_settings in self._ports:
|
||||
if port_settings["port_number"] == port_number:
|
||||
yield from self.set_port_settings(port_number, port_settings)
|
||||
await self.set_port_settings(port_number, port_settings)
|
||||
break
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of this Ethernet switch.
|
||||
|
||||
@ -295,7 +287,7 @@ class EthernetSwitch(Device):
|
||||
if isinstance(nio, NIOUDP):
|
||||
self.manager.port_manager.release_udp_port(nio.lport, self._project)
|
||||
if self._hypervisor:
|
||||
yield from self._hypervisor.send('ethsw remove_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
await self._hypervisor.send('ethsw remove_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
|
||||
log.info('Ethernet switch "{name}" [{id}]: NIO {nio} removed from port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@ -308,8 +300,7 @@ class EthernetSwitch(Device):
|
||||
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_port_settings(self, port_number, settings):
|
||||
async def set_port_settings(self, port_number, settings):
|
||||
"""
|
||||
Applies port settings to a specific port.
|
||||
|
||||
@ -318,14 +309,13 @@ class EthernetSwitch(Device):
|
||||
"""
|
||||
|
||||
if settings["type"] == "access":
|
||||
yield from self.set_access_port(port_number, settings["vlan"])
|
||||
await self.set_access_port(port_number, settings["vlan"])
|
||||
elif settings["type"] == "dot1q":
|
||||
yield from self.set_dot1q_port(port_number, settings["vlan"])
|
||||
await self.set_dot1q_port(port_number, settings["vlan"])
|
||||
elif settings["type"] == "qinq":
|
||||
yield from self.set_qinq_port(port_number, settings["vlan"], settings.get("ethertype"))
|
||||
await self.set_qinq_port(port_number, settings["vlan"], settings.get("ethertype"))
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_access_port(self, port_number, vlan_id):
|
||||
async def set_access_port(self, port_number, vlan_id):
|
||||
"""
|
||||
Sets the specified port as an ACCESS port.
|
||||
|
||||
@ -337,7 +327,7 @@ class EthernetSwitch(Device):
|
||||
raise DynamipsError("Port {} is not allocated".format(port_number))
|
||||
|
||||
nio = self._nios[port_number]
|
||||
yield from self._hypervisor.send('ethsw set_access_port "{name}" {nio} {vlan_id}'.format(name=self._name,
|
||||
await self._hypervisor.send('ethsw set_access_port "{name}" {nio} {vlan_id}'.format(name=self._name,
|
||||
nio=nio,
|
||||
vlan_id=vlan_id))
|
||||
|
||||
@ -347,8 +337,7 @@ class EthernetSwitch(Device):
|
||||
vlan_id=vlan_id))
|
||||
self._mappings[port_number] = ("access", vlan_id)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_dot1q_port(self, port_number, native_vlan):
|
||||
async def set_dot1q_port(self, port_number, native_vlan):
|
||||
"""
|
||||
Sets the specified port as a 802.1Q trunk port.
|
||||
|
||||
@ -360,7 +349,7 @@ class EthernetSwitch(Device):
|
||||
raise DynamipsError("Port {} is not allocated".format(port_number))
|
||||
|
||||
nio = self._nios[port_number]
|
||||
yield from self._hypervisor.send('ethsw set_dot1q_port "{name}" {nio} {native_vlan}'.format(name=self._name,
|
||||
await self._hypervisor.send('ethsw set_dot1q_port "{name}" {nio} {native_vlan}'.format(name=self._name,
|
||||
nio=nio,
|
||||
native_vlan=native_vlan))
|
||||
|
||||
@ -371,8 +360,7 @@ class EthernetSwitch(Device):
|
||||
|
||||
self._mappings[port_number] = ("dot1q", native_vlan)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_qinq_port(self, port_number, outer_vlan, ethertype):
|
||||
async def set_qinq_port(self, port_number, outer_vlan, ethertype):
|
||||
"""
|
||||
Sets the specified port as a trunk (QinQ) port.
|
||||
|
||||
@ -387,7 +375,7 @@ class EthernetSwitch(Device):
|
||||
if ethertype != "0x8100" and parse_version(self.hypervisor.version) < parse_version('0.2.16'):
|
||||
raise DynamipsError("Dynamips version required is >= 0.2.16 to change the default QinQ Ethernet type, detected version is {}".format(self.hypervisor.version))
|
||||
|
||||
yield from self._hypervisor.send('ethsw set_qinq_port "{name}" {nio} {outer_vlan} {ethertype}'.format(name=self._name,
|
||||
await self._hypervisor.send('ethsw set_qinq_port "{name}" {nio} {outer_vlan} {ethertype}'.format(name=self._name,
|
||||
nio=nio,
|
||||
outer_vlan=outer_vlan,
|
||||
ethertype=ethertype if ethertype != "0x8100" else ""))
|
||||
@ -399,27 +387,24 @@ class EthernetSwitch(Device):
|
||||
ethertype=ethertype))
|
||||
self._mappings[port_number] = ("qinq", outer_vlan, ethertype)
|
||||
|
||||
@asyncio.coroutine
|
||||
def get_mac_addr_table(self):
|
||||
async def get_mac_addr_table(self):
|
||||
"""
|
||||
Returns the MAC address table for this Ethernet switch.
|
||||
|
||||
:returns: list of entries (Ethernet address, VLAN, NIO)
|
||||
"""
|
||||
|
||||
mac_addr_table = yield from self._hypervisor.send('ethsw show_mac_addr_table "{}"'.format(self._name))
|
||||
mac_addr_table = await self._hypervisor.send('ethsw show_mac_addr_table "{}"'.format(self._name))
|
||||
return mac_addr_table
|
||||
|
||||
@asyncio.coroutine
|
||||
def clear_mac_addr_table(self):
|
||||
async def clear_mac_addr_table(self):
|
||||
"""
|
||||
Clears the MAC address table for this Ethernet switch.
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('ethsw clear_mac_addr_table "{}"'.format(self._name))
|
||||
await self._hypervisor.send('ethsw clear_mac_addr_table "{}"'.format(self._name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -443,15 +428,14 @@ class EthernetSwitch(Device):
|
||||
if nio.input_filter[0] is not None and nio.output_filter[0] is not None:
|
||||
raise DynamipsError("Port {} has already a filter applied".format(port_number))
|
||||
|
||||
yield from nio.bind_filter("both", "capture")
|
||||
yield from nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
await nio.bind_filter("both", "capture")
|
||||
await nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
|
||||
log.info('Ethernet switch "{name}" [{id}]: starting packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@ -466,7 +450,7 @@ class EthernetSwitch(Device):
|
||||
if not nio:
|
||||
raise DynamipsError("Port {} is not connected".format(port_number))
|
||||
|
||||
yield from nio.unbind_filter("both")
|
||||
await nio.unbind_filter("both")
|
||||
log.info('Ethernet switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
|
@ -63,26 +63,24 @@ class FrameRelaySwitch(Device):
|
||||
"mappings": mappings,
|
||||
"status": "started"}
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
if self._hypervisor is None:
|
||||
module_workdir = self.project.module_working_directory(self.manager.module_name.lower())
|
||||
self._hypervisor = yield from self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
self._hypervisor = await self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
|
||||
yield from self._hypervisor.send('frsw create "{}"'.format(self._name))
|
||||
await self._hypervisor.send('frsw create "{}"'.format(self._name))
|
||||
log.info('Frame Relay switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
self._hypervisor.devices.append(self)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_name(self, new_name):
|
||||
async def set_name(self, new_name):
|
||||
"""
|
||||
Renames this Frame Relay switch.
|
||||
|
||||
:param new_name: New name for this switch
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('frsw rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
|
||||
await self._hypervisor.send('frsw rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
|
||||
log.info('Frame Relay switch "{name}" [{id}]: renamed to "{new_name}"'.format(name=self._name,
|
||||
id=self._id,
|
||||
new_name=new_name))
|
||||
@ -118,15 +116,14 @@ class FrameRelaySwitch(Device):
|
||||
|
||||
self._mappings = mappings
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
for nio in self._nios.values():
|
||||
if nio:
|
||||
yield from nio.close()
|
||||
await nio.close()
|
||||
|
||||
if self._hypervisor:
|
||||
try:
|
||||
yield from self._hypervisor.send('frsw delete "{}"'.format(self._name))
|
||||
await self._hypervisor.send('frsw delete "{}"'.format(self._name))
|
||||
log.info('Frame Relay switch "{name}" [{id}] has been deleted'.format(name=self._name, id=self._id))
|
||||
except DynamipsError:
|
||||
log.debug("Could not properly delete Frame relay switch {}".format(self._name))
|
||||
@ -134,15 +131,14 @@ class FrameRelaySwitch(Device):
|
||||
if self._hypervisor and self in self._hypervisor.devices:
|
||||
self._hypervisor.devices.remove(self)
|
||||
if self._hypervisor and not self._hypervisor.devices:
|
||||
yield from self.hypervisor.stop()
|
||||
await self.hypervisor.stop()
|
||||
self._hypervisor = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes this Frame Relay switch.
|
||||
"""
|
||||
yield from self.close()
|
||||
await self.close()
|
||||
return True
|
||||
|
||||
def has_port(self, port):
|
||||
@ -156,8 +152,7 @@ class FrameRelaySwitch(Device):
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on Frame Relay switch.
|
||||
|
||||
@ -174,10 +169,9 @@ class FrameRelaySwitch(Device):
|
||||
port=port_number))
|
||||
|
||||
self._nios[port_number] = nio
|
||||
yield from self.set_mappings(self._mappings)
|
||||
await self.set_mappings(self._mappings)
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of this Frame Relay switch.
|
||||
|
||||
@ -200,8 +194,8 @@ class FrameRelaySwitch(Device):
|
||||
source_dlci=source_dlci,
|
||||
destination_port=destination_port,
|
||||
destination_dlci=destination_dlci))
|
||||
yield from self.unmap_vc(source_port, source_dlci, destination_port, destination_dlci)
|
||||
yield from self.unmap_vc(destination_port, destination_dlci, source_port, source_dlci)
|
||||
await self.unmap_vc(source_port, source_dlci, destination_port, destination_dlci)
|
||||
await self.unmap_vc(destination_port, destination_dlci, source_port, source_dlci)
|
||||
|
||||
nio = self._nios[port_number]
|
||||
if isinstance(nio, NIOUDP):
|
||||
@ -215,8 +209,7 @@ class FrameRelaySwitch(Device):
|
||||
del self._nios[port_number]
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_mappings(self, mappings):
|
||||
async def set_mappings(self, mappings):
|
||||
"""
|
||||
Applies VC mappings
|
||||
|
||||
@ -237,11 +230,10 @@ class FrameRelaySwitch(Device):
|
||||
destination_port=destination_port,
|
||||
destination_dlci=destination_dlci))
|
||||
|
||||
yield from self.map_vc(source_port, source_dlci, destination_port, destination_dlci)
|
||||
yield from self.map_vc(destination_port, destination_dlci, source_port, source_dlci)
|
||||
await self.map_vc(source_port, source_dlci, destination_port, destination_dlci)
|
||||
await self.map_vc(destination_port, destination_dlci, source_port, source_dlci)
|
||||
|
||||
@asyncio.coroutine
|
||||
def map_vc(self, port1, dlci1, port2, dlci2):
|
||||
async def map_vc(self, port1, dlci1, port2, dlci2):
|
||||
"""
|
||||
Creates a new Virtual Circuit connection (unidirectional).
|
||||
|
||||
@ -260,7 +252,7 @@ class FrameRelaySwitch(Device):
|
||||
nio1 = self._nios[port1]
|
||||
nio2 = self._nios[port2]
|
||||
|
||||
yield from self._hypervisor.send('frsw create_vc "{name}" {input_nio} {input_dlci} {output_nio} {output_dlci}'.format(name=self._name,
|
||||
await self._hypervisor.send('frsw create_vc "{name}" {input_nio} {input_dlci} {output_nio} {output_dlci}'.format(name=self._name,
|
||||
input_nio=nio1,
|
||||
input_dlci=dlci1,
|
||||
output_nio=nio2,
|
||||
@ -275,8 +267,7 @@ class FrameRelaySwitch(Device):
|
||||
|
||||
self._active_mappings[(port1, dlci1)] = (port2, dlci2)
|
||||
|
||||
@asyncio.coroutine
|
||||
def unmap_vc(self, port1, dlci1, port2, dlci2):
|
||||
async def unmap_vc(self, port1, dlci1, port2, dlci2):
|
||||
"""
|
||||
Deletes a Virtual Circuit connection (unidirectional).
|
||||
|
||||
@ -295,7 +286,7 @@ class FrameRelaySwitch(Device):
|
||||
nio1 = self._nios[port1]
|
||||
nio2 = self._nios[port2]
|
||||
|
||||
yield from self._hypervisor.send('frsw delete_vc "{name}" {input_nio} {input_dlci} {output_nio} {output_dlci}'.format(name=self._name,
|
||||
await self._hypervisor.send('frsw delete_vc "{name}" {input_nio} {input_dlci} {output_nio} {output_dlci}'.format(name=self._name,
|
||||
input_nio=nio1,
|
||||
input_dlci=dlci1,
|
||||
output_nio=nio2,
|
||||
@ -309,8 +300,7 @@ class FrameRelaySwitch(Device):
|
||||
dlci2=dlci2))
|
||||
del self._active_mappings[(port1, dlci1)]
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_FRELAY"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_FRELAY"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -331,15 +321,14 @@ class FrameRelaySwitch(Device):
|
||||
if nio.input_filter[0] is not None and nio.output_filter[0] is not None:
|
||||
raise DynamipsError("Port {} has already a filter applied".format(port_number))
|
||||
|
||||
yield from nio.bind_filter("both", "capture")
|
||||
yield from nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
await nio.bind_filter("both", "capture")
|
||||
await nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
|
||||
log.info('Frame relay switch "{name}" [{id}]: starting packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@ -350,7 +339,7 @@ class FrameRelaySwitch(Device):
|
||||
raise DynamipsError("Port {} is not allocated".format(port_number))
|
||||
|
||||
nio = self._nios[port_number]
|
||||
yield from nio.unbind_filter("both")
|
||||
await nio.unbind_filter("both")
|
||||
log.info('Frame relay switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -41,20 +41,19 @@ class IOU(BaseManager):
|
||||
super().__init__()
|
||||
self._iou_id_lock = asyncio.Lock()
|
||||
|
||||
@asyncio.coroutine
|
||||
def create_node(self, *args, **kwargs):
|
||||
async def create_node(self, *args, **kwargs):
|
||||
"""
|
||||
Creates a new IOU VM.
|
||||
|
||||
:returns: IOUVM instance
|
||||
"""
|
||||
|
||||
with (yield from self._iou_id_lock):
|
||||
async with self._iou_id_lock:
|
||||
# wait for a node to be completely created before adding a new one
|
||||
# this is important otherwise we allocate the same application ID
|
||||
# when creating multiple IOU node at the same time
|
||||
application_id = get_next_application_id(self.nodes)
|
||||
node = yield from super().create_node(*args, application_id=application_id, **kwargs)
|
||||
node = await super().create_node(*args, application_id=application_id, **kwargs)
|
||||
return node
|
||||
|
||||
@staticmethod
|
||||
|
@ -101,13 +101,12 @@ class IOUVM(BaseNode):
|
||||
self.save_configs()
|
||||
self.updated()
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this IOU VM.
|
||||
"""
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
adapters = self._ethernet_adapters + self._serial_adapters
|
||||
@ -117,7 +116,7 @@ class IOUVM(BaseNode):
|
||||
if nio and isinstance(nio, NIOUDP):
|
||||
self.manager.port_manager.release_udp_port(nio.lport, self._project)
|
||||
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
@ -164,14 +163,13 @@ class IOUVM(BaseNode):
|
||||
else:
|
||||
log.info('IOU "{name}" [{id}]: does not use the default IOU image values'.format(name=self._name, id=self._id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_default_iou_values(self):
|
||||
async def update_default_iou_values(self):
|
||||
"""
|
||||
Finds the default RAM and NVRAM values for the IOU image.
|
||||
"""
|
||||
|
||||
try:
|
||||
output = yield from gns3server.utils.asyncio.subprocess_check_output(self._path, "-h", cwd=self.working_dir, stderr=True)
|
||||
output = await gns3server.utils.asyncio.subprocess_check_output(self._path, "-h", cwd=self.working_dir, stderr=True)
|
||||
match = re.search("-n <n>\s+Size of nvram in Kb \(default ([0-9]+)KB\)", output)
|
||||
if match:
|
||||
self.nvram = int(match.group(1))
|
||||
@ -181,10 +179,9 @@ class IOUVM(BaseNode):
|
||||
except (ValueError, OSError, subprocess.SubprocessError) as e:
|
||||
log.warning("could not find default RAM and NVRAM values for {}: {}".format(os.path.basename(self._path), e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self.update_default_iou_values()
|
||||
await self.update_default_iou_values()
|
||||
|
||||
def _check_requirements(self):
|
||||
"""
|
||||
@ -361,14 +358,13 @@ class IOUVM(BaseNode):
|
||||
except OSError as e:
|
||||
raise IOUError("Could not write the iourc file {}: {}".format(path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _library_check(self):
|
||||
async def _library_check(self):
|
||||
"""
|
||||
Checks for missing shared library dependencies in the IOU image.
|
||||
"""
|
||||
|
||||
try:
|
||||
output = yield from gns3server.utils.asyncio.subprocess_check_output("ldd", self._path)
|
||||
output = await gns3server.utils.asyncio.subprocess_check_output("ldd", self._path)
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
log.warning("Could not determine the shared library dependencies for {}: {}".format(self._path, e))
|
||||
return
|
||||
@ -379,8 +375,7 @@ class IOUVM(BaseNode):
|
||||
raise IOUError("The following shared library dependencies cannot be found for IOU image {}: {}".format(self._path,
|
||||
", ".join(missing_libs)))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_iou_licence(self):
|
||||
async def _check_iou_licence(self):
|
||||
"""
|
||||
Checks for a valid IOU key in the iourc file (paranoid mode).
|
||||
"""
|
||||
@ -419,7 +414,7 @@ class IOUVM(BaseNode):
|
||||
# in tests or generating one
|
||||
if not hasattr(sys, "_called_from_test"):
|
||||
try:
|
||||
hostid = (yield from gns3server.utils.asyncio.subprocess_check_output("hostid")).strip()
|
||||
hostid = (await gns3server.utils.asyncio.subprocess_check_output("hostid")).strip()
|
||||
except FileNotFoundError as e:
|
||||
raise IOUError("Could not find hostid: {}".format(e))
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
@ -477,8 +472,7 @@ class IOUVM(BaseNode):
|
||||
except OSError as e:
|
||||
raise IOUError("Cannot write nvram file {}: {}".format(nvram_file, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts the IOU process.
|
||||
"""
|
||||
@ -486,7 +480,7 @@ class IOUVM(BaseNode):
|
||||
self._check_requirements()
|
||||
if not self.is_running():
|
||||
|
||||
yield from self._library_check()
|
||||
await self._library_check()
|
||||
|
||||
try:
|
||||
self._rename_nvram_file()
|
||||
@ -499,13 +493,13 @@ class IOUVM(BaseNode):
|
||||
if not os.path.isfile(iourc_path):
|
||||
raise IOUError("The iourc path '{}' is not a regular file".format(iourc_path))
|
||||
|
||||
yield from self._check_iou_licence()
|
||||
yield from self._start_ubridge()
|
||||
await self._check_iou_licence()
|
||||
await self._start_ubridge()
|
||||
|
||||
self._create_netmap_config()
|
||||
if self.use_default_iou_values:
|
||||
# make sure we have the default nvram amount to correctly push the configs
|
||||
yield from self.update_default_iou_values()
|
||||
await self.update_default_iou_values()
|
||||
self._push_configs_to_nvram()
|
||||
|
||||
# check if there is enough RAM to run
|
||||
@ -518,11 +512,11 @@ class IOUVM(BaseNode):
|
||||
|
||||
if "IOURC" not in os.environ:
|
||||
env["IOURC"] = iourc_path
|
||||
command = yield from self._build_command()
|
||||
command = await self._build_command()
|
||||
try:
|
||||
log.info("Starting IOU: {}".format(command))
|
||||
self.command_line = ' '.join(command)
|
||||
self._iou_process = yield from asyncio.create_subprocess_exec(
|
||||
self._iou_process = await asyncio.create_subprocess_exec(
|
||||
*command,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
@ -544,17 +538,16 @@ class IOUVM(BaseNode):
|
||||
if self.console and self.console_type == "telnet":
|
||||
server = AsyncioTelnetServer(reader=self._iou_process.stdout, writer=self._iou_process.stdin, binary=True, echo=True)
|
||||
try:
|
||||
self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
self._telnet_server = await asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
except OSError as e:
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
raise IOUError("Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e))
|
||||
|
||||
# configure networking support
|
||||
yield from self._networking()
|
||||
await self._networking()
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def _networking(self):
|
||||
async def _networking(self):
|
||||
"""
|
||||
Configures the IOL bridge in uBridge.
|
||||
"""
|
||||
@ -562,10 +555,10 @@ class IOUVM(BaseNode):
|
||||
bridge_name = "IOL-BRIDGE-{}".format(self.application_id + 512)
|
||||
try:
|
||||
# delete any previous bridge if it exists
|
||||
yield from self._ubridge_send("iol_bridge delete {name}".format(name=bridge_name))
|
||||
await self._ubridge_send("iol_bridge delete {name}".format(name=bridge_name))
|
||||
except UbridgeError:
|
||||
pass
|
||||
yield from self._ubridge_send("iol_bridge create {name} {bridge_id}".format(name=bridge_name, bridge_id=self.application_id + 512))
|
||||
await self._ubridge_send("iol_bridge create {name} {bridge_id}".format(name=bridge_name, bridge_id=self.application_id + 512))
|
||||
|
||||
bay_id = 0
|
||||
for adapter in self._adapters:
|
||||
@ -573,7 +566,7 @@ class IOUVM(BaseNode):
|
||||
for unit in adapter.ports.keys():
|
||||
nio = adapter.get_nio(unit)
|
||||
if nio and isinstance(nio, NIOUDP):
|
||||
yield from self._ubridge_send("iol_bridge add_nio_udp {name} {iol_id} {bay} {unit} {lport} {rhost} {rport}".format(name=bridge_name,
|
||||
await self._ubridge_send("iol_bridge add_nio_udp {name} {iol_id} {bay} {unit} {lport} {rhost} {rport}".format(name=bridge_name,
|
||||
iol_id=self.application_id,
|
||||
bay=bay_id,
|
||||
unit=unit_id,
|
||||
@ -581,15 +574,15 @@ class IOUVM(BaseNode):
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport))
|
||||
if nio.capturing:
|
||||
yield from self._ubridge_send('iol_bridge start_capture {name} "{output_file}" {data_link_type}'.format(name=bridge_name,
|
||||
await self._ubridge_send('iol_bridge start_capture {name} "{output_file}" {data_link_type}'.format(name=bridge_name,
|
||||
output_file=nio.pcap_output_file,
|
||||
data_link_type=re.sub("^DLT_", "", nio.pcap_data_link_type)))
|
||||
|
||||
yield from self._ubridge_apply_filters(bay_id, unit_id, nio.filters)
|
||||
await self._ubridge_apply_filters(bay_id, unit_id, nio.filters)
|
||||
unit_id += 1
|
||||
bay_id += 1
|
||||
|
||||
yield from self._ubridge_send("iol_bridge start {name}".format(name=bridge_name))
|
||||
await self._ubridge_send("iol_bridge start {name}".format(name=bridge_name))
|
||||
|
||||
def _termination_callback(self, process_name, returncode):
|
||||
"""
|
||||
@ -624,13 +617,12 @@ class IOUVM(BaseNode):
|
||||
for file_path in glob.glob(os.path.join(glob.escape(self.working_dir), "vlan.dat-*")):
|
||||
shutil.move(file_path, destination)
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the IOU process.
|
||||
"""
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
if self._nvram_watcher:
|
||||
self._nvram_watcher.close()
|
||||
self._nvram_watcher = None
|
||||
@ -643,7 +635,7 @@ class IOUVM(BaseNode):
|
||||
self._terminate_process_iou()
|
||||
if self._iou_process.returncode is None:
|
||||
try:
|
||||
yield from gns3server.utils.asyncio.wait_for_process_termination(self._iou_process, timeout=3)
|
||||
await gns3server.utils.asyncio.wait_for_process_termination(self._iou_process, timeout=3)
|
||||
except asyncio.TimeoutError:
|
||||
if self._iou_process.returncode is None:
|
||||
log.warning("IOU process {} is still running... killing it".format(self._iou_process.pid))
|
||||
@ -671,14 +663,13 @@ class IOUVM(BaseNode):
|
||||
self._started = False
|
||||
self.status = "stopped"
|
||||
|
||||
@asyncio.coroutine
|
||||
def reload(self):
|
||||
async def reload(self):
|
||||
"""
|
||||
Reloads the IOU process (stop & start).
|
||||
"""
|
||||
|
||||
yield from self.stop()
|
||||
yield from self.start()
|
||||
await self.stop()
|
||||
await self.start()
|
||||
|
||||
def is_running(self):
|
||||
"""
|
||||
@ -723,8 +714,7 @@ class IOUVM(BaseNode):
|
||||
except OSError as e:
|
||||
raise IOUError("Could not create {}: {}".format(netmap_path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _build_command(self):
|
||||
async def _build_command(self):
|
||||
"""
|
||||
Command to start the IOU process.
|
||||
(to be passed to subprocess.Popen())
|
||||
@ -769,7 +759,7 @@ class IOUVM(BaseNode):
|
||||
# command.extend(["-c", os.path.basename(startup_config_file)])
|
||||
|
||||
if self._l1_keepalives:
|
||||
yield from self._enable_l1_keepalives(command)
|
||||
await self._enable_l1_keepalives(command)
|
||||
command.extend([str(self.application_id)])
|
||||
return command
|
||||
|
||||
@ -848,8 +838,7 @@ class IOUVM(BaseNode):
|
||||
|
||||
self._adapters = self._ethernet_adapters + self._serial_adapters
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_add_nio_binding(self, adapter_number, port_number, nio):
|
||||
async def adapter_add_nio_binding(self, adapter_number, port_number, nio):
|
||||
"""
|
||||
Adds a adapter NIO binding.
|
||||
|
||||
@ -877,17 +866,16 @@ class IOUVM(BaseNode):
|
||||
|
||||
if self.ubridge:
|
||||
bridge_name = "IOL-BRIDGE-{}".format(self.application_id + 512)
|
||||
yield from self._ubridge_send("iol_bridge add_nio_udp {name} {iol_id} {bay} {unit} {lport} {rhost} {rport}".format(name=bridge_name,
|
||||
await self._ubridge_send("iol_bridge add_nio_udp {name} {iol_id} {bay} {unit} {lport} {rhost} {rport}".format(name=bridge_name,
|
||||
iol_id=self.application_id,
|
||||
bay=adapter_number,
|
||||
unit=port_number,
|
||||
lport=nio.lport,
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport))
|
||||
yield from self._ubridge_apply_filters(adapter_number, port_number, nio.filters)
|
||||
await self._ubridge_apply_filters(adapter_number, port_number, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_update_nio_binding(self, adapter_number, port_number, nio):
|
||||
async def adapter_update_nio_binding(self, adapter_number, port_number, nio):
|
||||
"""
|
||||
Update a port NIO binding.
|
||||
|
||||
@ -897,10 +885,9 @@ class IOUVM(BaseNode):
|
||||
"""
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_apply_filters(adapter_number, port_number, nio.filters)
|
||||
await self._ubridge_apply_filters(adapter_number, port_number, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _ubridge_apply_filters(self, adapter_number, port_number, filters):
|
||||
async def _ubridge_apply_filters(self, adapter_number, port_number, filters):
|
||||
"""
|
||||
Apply filter like rate limiting
|
||||
|
||||
@ -913,15 +900,14 @@ class IOUVM(BaseNode):
|
||||
bridge_name=bridge_name,
|
||||
bay=adapter_number,
|
||||
unit=port_number)
|
||||
yield from self._ubridge_send('iol_bridge reset_packet_filters ' + location)
|
||||
await self._ubridge_send('iol_bridge reset_packet_filters ' + location)
|
||||
for filter in self._build_filter_list(filters):
|
||||
cmd = 'iol_bridge add_packet_filter {} {}'.format(
|
||||
location,
|
||||
filter)
|
||||
yield from self._ubridge_send(cmd)
|
||||
await self._ubridge_send(cmd)
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_remove_nio_binding(self, adapter_number, port_number):
|
||||
async def adapter_remove_nio_binding(self, adapter_number, port_number):
|
||||
"""
|
||||
Removes an adapter NIO binding.
|
||||
|
||||
@ -952,7 +938,7 @@ class IOUVM(BaseNode):
|
||||
|
||||
if self.ubridge:
|
||||
bridge_name = "IOL-BRIDGE-{}".format(self.application_id + 512)
|
||||
yield from self._ubridge_send("iol_bridge delete_nio_udp {name} {bay} {unit}".format(name=bridge_name,
|
||||
await self._ubridge_send("iol_bridge delete_nio_udp {name} {bay} {unit}".format(name=bridge_name,
|
||||
bay=adapter_number,
|
||||
unit=port_number))
|
||||
|
||||
@ -982,8 +968,7 @@ class IOUVM(BaseNode):
|
||||
else:
|
||||
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):
|
||||
async def _enable_l1_keepalives(self, command):
|
||||
"""
|
||||
Enables L1 keepalive messages if supported.
|
||||
|
||||
@ -994,7 +979,7 @@ class IOUVM(BaseNode):
|
||||
if "IOURC" not in os.environ:
|
||||
env["IOURC"] = self.iourc_path
|
||||
try:
|
||||
output = yield from gns3server.utils.asyncio.subprocess_check_output(self._path, "-h", cwd=self.working_dir, env=env, stderr=True)
|
||||
output = await gns3server.utils.asyncio.subprocess_check_output(self._path, "-h", cwd=self.working_dir, env=env, stderr=True)
|
||||
if re.search("-l\s+Enable Layer 1 keepalive messages", output):
|
||||
command.extend(["-l"])
|
||||
else:
|
||||
@ -1226,8 +1211,7 @@ class IOUVM(BaseNode):
|
||||
except (binascii.Error, OSError) as e:
|
||||
raise IOUError("Could not save the private configuration {}: {}".format(config_path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, adapter_number, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
async def start_capture(self, adapter_number, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -1265,14 +1249,13 @@ class IOUVM(BaseNode):
|
||||
|
||||
if self.ubridge:
|
||||
bridge_name = "IOL-BRIDGE-{}".format(self.application_id + 512)
|
||||
yield from self._ubridge_send('iol_bridge start_capture {name} {bay} {unit} "{output_file}" {data_link_type}'.format(name=bridge_name,
|
||||
await self._ubridge_send('iol_bridge start_capture {name} {bay} {unit} "{output_file}" {data_link_type}'.format(name=bridge_name,
|
||||
bay=adapter_number,
|
||||
unit=port_number,
|
||||
output_file=output_file,
|
||||
data_link_type=re.sub("^DLT_", "", data_link_type)))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, adapter_number, port_number):
|
||||
async def stop_capture(self, adapter_number, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@ -1302,6 +1285,6 @@ class IOUVM(BaseNode):
|
||||
port_number=port_number))
|
||||
if self.ubridge:
|
||||
bridge_name = "IOL-BRIDGE-{}".format(self.application_id + 512)
|
||||
yield from self._ubridge_send('iol_bridge stop_capture {name} {bay} {unit}'.format(name=bridge_name,
|
||||
await self._ubridge_send('iol_bridge stop_capture {name} {bay} {unit}'.format(name=bridge_name,
|
||||
bay=adapter_number,
|
||||
unit=port_number))
|
||||
|
@ -26,7 +26,7 @@ from uuid import UUID, uuid4
|
||||
from .port_manager import PortManager
|
||||
from .notification_manager import NotificationManager
|
||||
from ..config import Config
|
||||
from ..utils.asyncio import wait_run_in_executor, asyncio_ensure_future
|
||||
from ..utils.asyncio import wait_run_in_executor
|
||||
from ..utils.path import check_path_allowed, get_default_project_directory
|
||||
|
||||
import logging
|
||||
@ -282,7 +282,7 @@ class Project:
|
||||
|
||||
raise aiohttp.web.HTTPNotFound(text="Node ID {} doesn't exist".format(node_id))
|
||||
|
||||
def remove_node(self, node):
|
||||
async def remove_node(self, node):
|
||||
"""
|
||||
Removes a node from the project.
|
||||
In theory this should be called by the node manager.
|
||||
@ -291,11 +291,10 @@ class Project:
|
||||
"""
|
||||
|
||||
if node in self._nodes:
|
||||
yield from node.delete()
|
||||
await node.delete()
|
||||
self._nodes.remove(node)
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self, variables=None, **kwargs):
|
||||
async def update(self, variables=None, **kwargs):
|
||||
original_variables = self.variables
|
||||
self.variables = variables
|
||||
|
||||
@ -303,10 +302,9 @@ class Project:
|
||||
if original_variables != variables:
|
||||
for node in self.nodes:
|
||||
if hasattr(node, 'update'):
|
||||
yield from node.update()
|
||||
await node.update()
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes the project, but keep project data on disk
|
||||
"""
|
||||
@ -317,15 +315,15 @@ class Project:
|
||||
module_nodes_id = set([n.id for n in module.instance().nodes])
|
||||
# We close the project only for the modules using it
|
||||
if len(module_nodes_id & project_nodes_id):
|
||||
yield from module.instance().project_closing(self)
|
||||
await module.instance().project_closing(self)
|
||||
|
||||
yield from self._close_and_clean(False)
|
||||
await self._close_and_clean(False)
|
||||
|
||||
for module in self.compute():
|
||||
module_nodes_id = set([n.id for n in module.instance().nodes])
|
||||
# We close the project only for the modules using it
|
||||
if len(module_nodes_id & project_nodes_id):
|
||||
yield from module.instance().project_closed(self)
|
||||
await module.instance().project_closed(self)
|
||||
|
||||
try:
|
||||
if os.path.exists(self.tmp_working_directory()):
|
||||
@ -333,8 +331,7 @@ class Project:
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
@asyncio.coroutine
|
||||
def _close_and_clean(self, cleanup):
|
||||
async def _close_and_clean(self, cleanup):
|
||||
"""
|
||||
Closes the project, and cleanup the disk if cleanup is True
|
||||
|
||||
@ -343,10 +340,10 @@ class Project:
|
||||
|
||||
tasks = []
|
||||
for node in self._nodes:
|
||||
tasks.append(asyncio_ensure_future(node.manager.close_node(node.id)))
|
||||
tasks.append(asyncio.ensure_future(node.manager.close_node(node.id)))
|
||||
|
||||
if tasks:
|
||||
done, _ = yield from asyncio.wait(tasks)
|
||||
done, _ = await asyncio.wait(tasks)
|
||||
for future in done:
|
||||
try:
|
||||
future.result()
|
||||
@ -356,7 +353,7 @@ class Project:
|
||||
if cleanup and os.path.exists(self.path):
|
||||
self._deleted = True
|
||||
try:
|
||||
yield from wait_run_in_executor(shutil.rmtree, self.path)
|
||||
await wait_run_in_executor(shutil.rmtree, self.path)
|
||||
log.info("Project {id} with path '{path}' deleted".format(path=self._path, id=self._id))
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not delete the project directory: {}".format(e))
|
||||
@ -375,17 +372,16 @@ class Project:
|
||||
for port in self._used_udp_ports.copy():
|
||||
port_manager.release_udp_port(port, self)
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Removes project from disk
|
||||
"""
|
||||
|
||||
for module in self.compute():
|
||||
yield from module.instance().project_closing(self)
|
||||
yield from self._close_and_clean(True)
|
||||
await module.instance().project_closing(self)
|
||||
await self._close_and_clean(True)
|
||||
for module in self.compute():
|
||||
yield from module.instance().project_closed(self)
|
||||
await module.instance().project_closed(self)
|
||||
|
||||
def compute(self):
|
||||
"""
|
||||
@ -405,8 +401,7 @@ class Project:
|
||||
"""
|
||||
NotificationManager.instance().emit(action, event, project_id=self.id)
|
||||
|
||||
@asyncio.coroutine
|
||||
def list_files(self):
|
||||
async def list_files(self):
|
||||
"""
|
||||
:returns: Array of files in project without temporary files. The files are dictionary {"path": "test.bin", "md5sum": "aaaaa"}
|
||||
"""
|
||||
@ -421,7 +416,7 @@ class Project:
|
||||
file_info = {"path": path}
|
||||
|
||||
try:
|
||||
file_info["md5sum"] = yield from wait_run_in_executor(self._hash_file, os.path.join(dirpath, filename))
|
||||
file_info["md5sum"] = await wait_run_in_executor(self._hash_file, os.path.join(dirpath, filename))
|
||||
except OSError:
|
||||
continue
|
||||
files.append(file_info)
|
||||
|
@ -41,8 +41,7 @@ class Qemu(BaseManager):
|
||||
_NODE_TYPE = "qemu"
|
||||
|
||||
@staticmethod
|
||||
@asyncio.coroutine
|
||||
def get_kvm_archs():
|
||||
async def get_kvm_archs():
|
||||
"""
|
||||
Gets a list of architectures for which KVM is available on this server.
|
||||
|
||||
@ -108,7 +107,7 @@ class Qemu(BaseManager):
|
||||
return paths
|
||||
|
||||
@staticmethod
|
||||
def binary_list(archs=None):
|
||||
async def binary_list(archs=None):
|
||||
"""
|
||||
Gets QEMU binaries list available on the host.
|
||||
|
||||
@ -128,11 +127,11 @@ class Qemu(BaseManager):
|
||||
for arch in archs:
|
||||
if f.endswith(arch) or f.endswith("{}.exe".format(arch)) or f.endswith("{}w.exe".format(arch)):
|
||||
qemu_path = os.path.join(path, f)
|
||||
version = yield from Qemu.get_qemu_version(qemu_path)
|
||||
version = await Qemu.get_qemu_version(qemu_path)
|
||||
qemus.append({"path": qemu_path, "version": version})
|
||||
else:
|
||||
qemu_path = os.path.join(path, f)
|
||||
version = yield from Qemu.get_qemu_version(qemu_path)
|
||||
version = await Qemu.get_qemu_version(qemu_path)
|
||||
qemus.append({"path": qemu_path, "version": version})
|
||||
|
||||
except OSError:
|
||||
@ -141,7 +140,7 @@ class Qemu(BaseManager):
|
||||
return qemus
|
||||
|
||||
@staticmethod
|
||||
def img_binary_list():
|
||||
async def img_binary_list():
|
||||
"""
|
||||
Gets QEMU-img binaries list available on the host.
|
||||
|
||||
@ -155,7 +154,7 @@ class Qemu(BaseManager):
|
||||
os.access(os.path.join(path, f), os.X_OK) and \
|
||||
os.path.isfile(os.path.join(path, f)):
|
||||
qemu_path = os.path.join(path, f)
|
||||
version = yield from Qemu._get_qemu_img_version(qemu_path)
|
||||
version = await Qemu._get_qemu_img_version(qemu_path)
|
||||
qemu_imgs.append({"path": qemu_path, "version": version})
|
||||
except OSError:
|
||||
continue
|
||||
@ -163,8 +162,7 @@ class Qemu(BaseManager):
|
||||
return qemu_imgs
|
||||
|
||||
@staticmethod
|
||||
@asyncio.coroutine
|
||||
def get_qemu_version(qemu_path):
|
||||
async def get_qemu_version(qemu_path):
|
||||
"""
|
||||
Gets the Qemu version.
|
||||
|
||||
@ -187,7 +185,7 @@ class Qemu(BaseManager):
|
||||
return ""
|
||||
else:
|
||||
try:
|
||||
output = yield from subprocess_check_output(qemu_path, "-version")
|
||||
output = await subprocess_check_output(qemu_path, "-version")
|
||||
match = re.search("version\s+([0-9a-z\-\.]+)", output)
|
||||
if match:
|
||||
version = match.group(1)
|
||||
@ -198,8 +196,7 @@ class Qemu(BaseManager):
|
||||
raise QemuError("Error while looking for the Qemu version: {}".format(e))
|
||||
|
||||
@staticmethod
|
||||
@asyncio.coroutine
|
||||
def _get_qemu_img_version(qemu_img_path):
|
||||
async def _get_qemu_img_version(qemu_img_path):
|
||||
"""
|
||||
Gets the Qemu-img version.
|
||||
|
||||
@ -207,7 +204,7 @@ class Qemu(BaseManager):
|
||||
"""
|
||||
|
||||
try:
|
||||
output = yield from subprocess_check_output(qemu_img_path, "--version")
|
||||
output = await subprocess_check_output(qemu_img_path, "--version")
|
||||
match = re.search("version\s+([0-9a-z\-\.]+)", output)
|
||||
if match:
|
||||
version = match.group(1)
|
||||
@ -258,8 +255,7 @@ class Qemu(BaseManager):
|
||||
|
||||
return os.path.join("qemu", "vm-{}".format(legacy_vm_id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def create_disk(self, qemu_img, path, options):
|
||||
async def create_disk(self, qemu_img, path, options):
|
||||
"""
|
||||
Create a Qemu disk with qemu-img
|
||||
|
||||
@ -290,13 +286,12 @@ class Qemu(BaseManager):
|
||||
command.append(path)
|
||||
command.append("{}M".format(img_size))
|
||||
|
||||
process = yield from asyncio.create_subprocess_exec(*command)
|
||||
yield from process.wait()
|
||||
process = await asyncio.create_subprocess_exec(*command)
|
||||
await process.wait()
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
raise QemuError("Could not create disk image {}:{}".format(path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def resize_disk(self, qemu_img, path, extend):
|
||||
async def resize_disk(self, qemu_img, path, extend):
|
||||
"""
|
||||
Resize a Qemu disk with qemu-img
|
||||
|
||||
@ -314,8 +309,8 @@ class Qemu(BaseManager):
|
||||
if not os.path.exists(path):
|
||||
raise QemuError("Qemu disk '{}' does not exist".format(path))
|
||||
command = [qemu_img, "resize", path, "+{}M".format(extend)]
|
||||
process = yield from asyncio.create_subprocess_exec(*command)
|
||||
yield from process.wait()
|
||||
process = await asyncio.create_subprocess_exec(*command)
|
||||
await process.wait()
|
||||
log.info("Qemu disk '{}' extended by {} MB".format(path, extend))
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
raise QemuError("Could not update disk image {}:{}".format(path, e))
|
||||
|
@ -87,8 +87,7 @@ class Qcow2:
|
||||
return None
|
||||
return path
|
||||
|
||||
@asyncio.coroutine
|
||||
def rebase(self, qemu_img, base_image):
|
||||
async def rebase(self, qemu_img, base_image):
|
||||
"""
|
||||
Rebase a linked clone in order to use the correct disk
|
||||
|
||||
@ -99,8 +98,8 @@ class Qcow2:
|
||||
if not os.path.exists(base_image):
|
||||
raise FileNotFoundError(base_image)
|
||||
command = [qemu_img, "rebase", "-u", "-b", base_image, self._path]
|
||||
process = yield from asyncio.create_subprocess_exec(*command)
|
||||
retcode = yield from process.wait()
|
||||
process = await asyncio.create_subprocess_exec(*command)
|
||||
retcode = await process.wait()
|
||||
if retcode != 0:
|
||||
raise Qcow2Error("Could not rebase the image")
|
||||
self._reload()
|
||||
|
@ -795,8 +795,7 @@ class QemuVM(BaseNode):
|
||||
kernel_command_line=kernel_command_line))
|
||||
self._kernel_command_line = kernel_command_line
|
||||
|
||||
@asyncio.coroutine
|
||||
def _set_process_priority(self):
|
||||
async def _set_process_priority(self):
|
||||
"""
|
||||
Changes the process priority
|
||||
"""
|
||||
@ -844,8 +843,8 @@ class QemuVM(BaseNode):
|
||||
else:
|
||||
priority = 0
|
||||
try:
|
||||
process = yield from asyncio.create_subprocess_exec('renice', '-n', str(priority), '-p', str(self._process.pid))
|
||||
yield from process.wait()
|
||||
process = await asyncio.create_subprocess_exec('renice', '-n', str(priority), '-p', str(self._process.pid))
|
||||
await process.wait()
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
log.error('Could not change process priority for QEMU VM "{}": {}'.format(self._name, e))
|
||||
|
||||
@ -881,8 +880,7 @@ class QemuVM(BaseNode):
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
raise QemuError("Could not throttle CPU: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Creates QEMU VM and sets proper MD5 hashes
|
||||
"""
|
||||
@ -890,23 +888,22 @@ class QemuVM(BaseNode):
|
||||
# In case user upload image manually we don't have md5 sums.
|
||||
# We need generate hashes at this point, otherwise they will be generated
|
||||
# at __json__ but not on separate thread.
|
||||
yield from cancellable_wait_run_in_executor(md5sum, self._hda_disk_image)
|
||||
yield from cancellable_wait_run_in_executor(md5sum, self._hdb_disk_image)
|
||||
yield from cancellable_wait_run_in_executor(md5sum, self._hdc_disk_image)
|
||||
yield from cancellable_wait_run_in_executor(md5sum, self._hdd_disk_image)
|
||||
await cancellable_wait_run_in_executor(md5sum, self._hda_disk_image)
|
||||
await cancellable_wait_run_in_executor(md5sum, self._hdb_disk_image)
|
||||
await cancellable_wait_run_in_executor(md5sum, self._hdc_disk_image)
|
||||
await cancellable_wait_run_in_executor(md5sum, self._hdd_disk_image)
|
||||
|
||||
super(QemuVM, self).create()
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts this QEMU VM.
|
||||
"""
|
||||
|
||||
with (yield from self._execute_lock):
|
||||
async with self._execute_lock:
|
||||
if self.is_running():
|
||||
# resume the VM if it is paused
|
||||
yield from self.resume()
|
||||
await self.resume()
|
||||
return
|
||||
|
||||
if self._manager.config.get_section_config("Qemu").getboolean("monitor", True):
|
||||
@ -927,7 +924,7 @@ class QemuVM(BaseNode):
|
||||
# check if there is enough RAM to run
|
||||
self.check_available_ram(self.ram)
|
||||
|
||||
command = yield from self._build_command()
|
||||
command = await self._build_command()
|
||||
command_string = " ".join(shlex.quote(s) for s in command)
|
||||
try:
|
||||
log.info("Starting QEMU with: {}".format(command_string))
|
||||
@ -936,7 +933,7 @@ class QemuVM(BaseNode):
|
||||
with open(self._stdout_file, "w", encoding="utf-8") as fd:
|
||||
fd.write("Start QEMU with {}\n\nExecution log:\n".format(command_string))
|
||||
self.command_line = ' '.join(command)
|
||||
self._process = yield from asyncio.create_subprocess_exec(*command,
|
||||
self._process = await asyncio.create_subprocess_exec(*command,
|
||||
stdout=fd,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=self.working_dir)
|
||||
@ -948,34 +945,33 @@ class QemuVM(BaseNode):
|
||||
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))
|
||||
|
||||
yield from self._set_process_priority()
|
||||
await self._set_process_priority()
|
||||
if self._cpu_throttling:
|
||||
self._set_cpu_throttling()
|
||||
|
||||
if "-enable-kvm" in command_string or "-enable-hax" in command_string:
|
||||
self._hw_virtualization = True
|
||||
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
set_link_commands = []
|
||||
for adapter_number, adapter in enumerate(self._ethernet_adapters):
|
||||
nio = adapter.get_nio(0)
|
||||
if nio:
|
||||
yield from self.add_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
|
||||
await self.add_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
|
||||
self._local_udp_tunnels[adapter_number][1],
|
||||
nio)
|
||||
if nio.suspend:
|
||||
set_link_commands.append("set_link gns3-{} off".format(adapter_number))
|
||||
else:
|
||||
set_link_commands.append("set_link gns3-{} off".format(adapter_number))
|
||||
yield from self._control_vm_commands(set_link_commands)
|
||||
await self._control_vm_commands(set_link_commands)
|
||||
|
||||
try:
|
||||
yield from self.start_wrap_console()
|
||||
await self.start_wrap_console()
|
||||
except OSError as e:
|
||||
raise QemuError("Could not start Telnet QEMU console {}\n".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _termination_callback(self, returncode):
|
||||
async def _termination_callback(self, returncode):
|
||||
"""
|
||||
Called when the process has stopped.
|
||||
|
||||
@ -984,19 +980,18 @@ class QemuVM(BaseNode):
|
||||
|
||||
if self.started:
|
||||
log.info("QEMU process has stopped, return code: %d", returncode)
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
# A return code of 1 seem fine on Windows
|
||||
if returncode != 0 and (returncode != 1 or not sys.platform.startswith("win")):
|
||||
self.project.emit("log.error", {"message": "QEMU process has stopped, return code: {}\n{}".format(returncode, self.read_stdout())})
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops this QEMU VM.
|
||||
"""
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
with (yield from self._execute_lock):
|
||||
await self._stop_ubridge()
|
||||
async with self._execute_lock:
|
||||
# stop the QEMU process
|
||||
self._hw_virtualization = False
|
||||
if self.is_running():
|
||||
@ -1004,22 +999,22 @@ class QemuVM(BaseNode):
|
||||
try:
|
||||
|
||||
if self.on_close == "save_vm_state":
|
||||
yield from self._control_vm("stop")
|
||||
yield from self._control_vm("savevm GNS3_SAVED_STATE")
|
||||
await self._control_vm("stop")
|
||||
await self._control_vm("savevm GNS3_SAVED_STATE")
|
||||
wait_for_savevm = 120
|
||||
while wait_for_savevm:
|
||||
yield from asyncio.sleep(1)
|
||||
status = yield from self._saved_state_option()
|
||||
await asyncio.sleep(1)
|
||||
status = await self._saved_state_option()
|
||||
wait_for_savevm -= 1
|
||||
if status != []:
|
||||
break
|
||||
|
||||
if self.on_close == "shutdown_signal":
|
||||
yield from self._control_vm("system_powerdown")
|
||||
yield from gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=30)
|
||||
await self._control_vm("system_powerdown")
|
||||
await gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=30)
|
||||
else:
|
||||
self._process.terminate()
|
||||
yield from gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=3)
|
||||
await gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=3)
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
except asyncio.TimeoutError:
|
||||
@ -1033,11 +1028,10 @@ class QemuVM(BaseNode):
|
||||
self._process = None
|
||||
self._stop_cpulimit()
|
||||
if self.on_close != "save_vm_state":
|
||||
yield from self._clear_save_vm_stated()
|
||||
yield from super().stop()
|
||||
await self._clear_save_vm_stated()
|
||||
await super().stop()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _open_qemu_monitor_connection_vm(self, timeout=10):
|
||||
async def _open_qemu_monitor_connection_vm(self, timeout=10):
|
||||
"""
|
||||
Opens a connection to the QEMU monitor.
|
||||
|
||||
@ -1050,10 +1044,10 @@ class QemuVM(BaseNode):
|
||||
last_exception = None
|
||||
reader = writer = None
|
||||
while time.time() - begin < timeout:
|
||||
yield from asyncio.sleep(0.01)
|
||||
await asyncio.sleep(0.01)
|
||||
try:
|
||||
log.debug("Connecting to Qemu monitor on {}:{}".format(self._monitor_host, self._monitor))
|
||||
reader, writer = yield from asyncio.open_connection(self._monitor_host, self._monitor)
|
||||
reader, writer = await asyncio.open_connection(self._monitor_host, self._monitor)
|
||||
except (asyncio.TimeoutError, OSError) as e:
|
||||
last_exception = e
|
||||
continue
|
||||
@ -1067,8 +1061,7 @@ class QemuVM(BaseNode):
|
||||
log.info("Connected to QEMU monitor on {}:{} after {:.4f} seconds".format(self._monitor_host, self._monitor, time.time() - begin))
|
||||
return reader, writer
|
||||
|
||||
@asyncio.coroutine
|
||||
def _control_vm(self, command, expected=None):
|
||||
async def _control_vm(self, command, expected=None):
|
||||
"""
|
||||
Executes a command with QEMU monitor when this VM is running.
|
||||
|
||||
@ -1081,7 +1074,7 @@ class QemuVM(BaseNode):
|
||||
result = None
|
||||
if self.is_running() and self._monitor:
|
||||
log.info("Execute QEMU monitor command: {}".format(command))
|
||||
reader, writer = yield from self._open_qemu_monitor_connection_vm()
|
||||
reader, writer = await self._open_qemu_monitor_connection_vm()
|
||||
if reader is None and writer is None:
|
||||
return result
|
||||
|
||||
@ -1094,7 +1087,7 @@ class QemuVM(BaseNode):
|
||||
if expected:
|
||||
try:
|
||||
while result is None:
|
||||
line = yield from reader.readline()
|
||||
line = await reader.readline()
|
||||
if not line:
|
||||
break
|
||||
for expect in expected:
|
||||
@ -1106,8 +1099,7 @@ class QemuVM(BaseNode):
|
||||
writer.close()
|
||||
return result
|
||||
|
||||
@asyncio.coroutine
|
||||
def _control_vm_commands(self, commands):
|
||||
async def _control_vm_commands(self, commands):
|
||||
"""
|
||||
Executes commands with QEMU monitor when this VM is running.
|
||||
|
||||
@ -1116,7 +1108,7 @@ class QemuVM(BaseNode):
|
||||
|
||||
if self.is_running() and self._monitor:
|
||||
|
||||
reader, writer = yield from self._open_qemu_monitor_connection_vm()
|
||||
reader, writer = await self._open_qemu_monitor_connection_vm()
|
||||
if reader is None and writer is None:
|
||||
return
|
||||
|
||||
@ -1128,17 +1120,16 @@ class QemuVM(BaseNode):
|
||||
log.warning("Could not write to QEMU monitor: {}".format(e))
|
||||
writer.close()
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this QEMU VM.
|
||||
"""
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
self.on_close = "power_off"
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
|
||||
for adapter in self._ethernet_adapters:
|
||||
if adapter is not None:
|
||||
@ -1151,8 +1142,7 @@ class QemuVM(BaseNode):
|
||||
self.manager.port_manager.release_udp_port(udp_tunnel[1].lport, self._project)
|
||||
self._local_udp_tunnels = {}
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_vm_status(self):
|
||||
async def _get_vm_status(self):
|
||||
"""
|
||||
Returns this VM suspend status.
|
||||
|
||||
@ -1162,7 +1152,7 @@ class QemuVM(BaseNode):
|
||||
:returns: status (string)
|
||||
"""
|
||||
|
||||
result = yield from self._control_vm("info status", [
|
||||
result = await self._control_vm("info status", [
|
||||
b"debug", b"inmigrate", b"internal-error", b"io-error",
|
||||
b"paused", b"postmigrate", b"prelaunch", b"finish-migrate",
|
||||
b"restore-vm", b"running", b"save-vm", b"shutdown", b"suspended",
|
||||
@ -1179,49 +1169,45 @@ class QemuVM(BaseNode):
|
||||
self.status = "stopped"
|
||||
return status
|
||||
|
||||
@asyncio.coroutine
|
||||
def suspend(self):
|
||||
async def suspend(self):
|
||||
"""
|
||||
Suspends this QEMU VM.
|
||||
"""
|
||||
|
||||
if self.is_running():
|
||||
vm_status = yield from self._get_vm_status()
|
||||
vm_status = await self._get_vm_status()
|
||||
if vm_status is None:
|
||||
raise QemuError("Suspending a QEMU VM is not supported")
|
||||
elif vm_status == "running" or vm_status == "prelaunch":
|
||||
yield from self._control_vm("stop")
|
||||
await self._control_vm("stop")
|
||||
self.status = "suspended"
|
||||
log.debug("QEMU VM has been suspended")
|
||||
else:
|
||||
log.info("QEMU VM is not running to be suspended, current status is {}".format(vm_status))
|
||||
|
||||
@asyncio.coroutine
|
||||
def reload(self):
|
||||
async def reload(self):
|
||||
"""
|
||||
Reloads this QEMU VM.
|
||||
"""
|
||||
|
||||
yield from self._control_vm("system_reset")
|
||||
await self._control_vm("system_reset")
|
||||
log.debug("QEMU VM has been reset")
|
||||
|
||||
@asyncio.coroutine
|
||||
def resume(self):
|
||||
async def resume(self):
|
||||
"""
|
||||
Resumes this QEMU VM.
|
||||
"""
|
||||
|
||||
vm_status = yield from self._get_vm_status()
|
||||
vm_status = await self._get_vm_status()
|
||||
if vm_status is None:
|
||||
raise QemuError("Resuming a QEMU VM is not supported")
|
||||
elif vm_status == "paused":
|
||||
yield from self._control_vm("cont")
|
||||
await self._control_vm("cont")
|
||||
log.debug("QEMU VM has been resumed")
|
||||
else:
|
||||
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_number, nio):
|
||||
async def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Adds a port NIO binding.
|
||||
|
||||
@ -1237,10 +1223,10 @@ class QemuVM(BaseNode):
|
||||
|
||||
if self.is_running():
|
||||
try:
|
||||
yield from self.add_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
|
||||
await self.add_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
|
||||
self._local_udp_tunnels[adapter_number][1],
|
||||
nio)
|
||||
yield from self._control_vm("set_link gns3-{} on".format(adapter_number))
|
||||
await self._control_vm("set_link gns3-{} on".format(adapter_number))
|
||||
except (IndexError, KeyError):
|
||||
raise QemuError('Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(name=self._name,
|
||||
adapter_number=adapter_number))
|
||||
@ -1251,8 +1237,7 @@ class QemuVM(BaseNode):
|
||||
nio=nio,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Update a port NIO binding.
|
||||
|
||||
@ -1262,19 +1247,18 @@ class QemuVM(BaseNode):
|
||||
|
||||
if self.is_running():
|
||||
try:
|
||||
yield from self.update_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
|
||||
await self.update_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
|
||||
self._local_udp_tunnels[adapter_number][1],
|
||||
nio)
|
||||
if nio.suspend:
|
||||
yield from self._control_vm("set_link gns3-{} off".format(adapter_number))
|
||||
await self._control_vm("set_link gns3-{} off".format(adapter_number))
|
||||
else:
|
||||
yield from self._control_vm("set_link gns3-{} on".format(adapter_number))
|
||||
await self._control_vm("set_link gns3-{} on".format(adapter_number))
|
||||
except IndexError:
|
||||
raise QemuError('Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(name=self._name,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_remove_nio_binding(self, adapter_number):
|
||||
async def adapter_remove_nio_binding(self, adapter_number):
|
||||
"""
|
||||
Removes a port NIO binding.
|
||||
|
||||
@ -1290,8 +1274,8 @@ class QemuVM(BaseNode):
|
||||
adapter_number=adapter_number))
|
||||
|
||||
if self.is_running():
|
||||
yield from self._control_vm("set_link gns3-{} off".format(adapter_number))
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name="QEMU-{}-{}".format(self._id, adapter_number)))
|
||||
await self._control_vm("set_link gns3-{} off".format(adapter_number))
|
||||
await self._ubridge_send("bridge delete {name}".format(name="QEMU-{}-{}".format(self._id, adapter_number)))
|
||||
|
||||
nio = adapter.get_nio(0)
|
||||
if isinstance(nio, NIOUDP):
|
||||
@ -1304,8 +1288,7 @@ class QemuVM(BaseNode):
|
||||
adapter_number=adapter_number))
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, adapter_number, output_file):
|
||||
async def start_capture(self, adapter_number, output_file):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -1330,14 +1313,14 @@ class QemuVM(BaseNode):
|
||||
nio.startPacketCapture(output_file)
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="QEMU-{}-{}".format(self._id, adapter_number),
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="QEMU-{}-{}".format(self._id, adapter_number),
|
||||
output_file=output_file))
|
||||
|
||||
log.info("QEMU VM '{name}' [{id}]: starting packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
def stop_capture(self, adapter_number):
|
||||
async def stop_capture(self, adapter_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@ -1357,7 +1340,7 @@ class QemuVM(BaseNode):
|
||||
nio.stopPacketCapture()
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge stop_capture {name}'.format(name="QEMU-{}-{}".format(self._id, adapter_number)))
|
||||
await self._ubridge_send('bridge stop_capture {name}'.format(name="QEMU-{}-{}".format(self._id, adapter_number)))
|
||||
|
||||
log.info("QEMU VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
@ -1511,21 +1494,19 @@ class QemuVM(BaseNode):
|
||||
|
||||
return qemu_img_path
|
||||
|
||||
@asyncio.coroutine
|
||||
def _qemu_img_exec(self, command):
|
||||
async def _qemu_img_exec(self, command):
|
||||
|
||||
self._qemu_img_stdout_file = os.path.join(self.working_dir, "qemu-img.log")
|
||||
log.info("logging to {}".format(self._qemu_img_stdout_file))
|
||||
command_string = " ".join(shlex.quote(s) for s in command)
|
||||
log.info("Executing qemu-img with: {}".format(command_string))
|
||||
with open(self._qemu_img_stdout_file, "w", encoding="utf-8") as fd:
|
||||
process = yield from asyncio.create_subprocess_exec(*command, stdout=fd, stderr=subprocess.STDOUT, cwd=self.working_dir)
|
||||
retcode = yield from process.wait()
|
||||
process = await asyncio.create_subprocess_exec(*command, stdout=fd, stderr=subprocess.STDOUT, cwd=self.working_dir)
|
||||
retcode = await process.wait()
|
||||
log.info("{} returned with {}".format(self._get_qemu_img(), retcode))
|
||||
return retcode
|
||||
|
||||
@asyncio.coroutine
|
||||
def _disk_options(self):
|
||||
async def _disk_options(self):
|
||||
options = []
|
||||
qemu_img_path = self._get_qemu_img()
|
||||
|
||||
@ -1548,16 +1529,16 @@ class QemuVM(BaseNode):
|
||||
else:
|
||||
try:
|
||||
# check for corrupt disk image
|
||||
retcode = yield from self._qemu_img_exec([qemu_img_path, "check", disk_image])
|
||||
retcode = await self._qemu_img_exec([qemu_img_path, "check", disk_image])
|
||||
if retcode == 3:
|
||||
# image has leaked clusters, but is not corrupted, let's try to fix it
|
||||
log.warning("Qemu image {} has leaked clusters".format(disk_image))
|
||||
if (yield from self._qemu_img_exec([qemu_img_path, "check", "-r", "leaks", "{}".format(disk_image)])) == 3:
|
||||
if (await self._qemu_img_exec([qemu_img_path, "check", "-r", "leaks", "{}".format(disk_image)])) == 3:
|
||||
self.project.emit("log.warning", {"message": "Qemu image '{}' has leaked clusters and could not be fixed".format(disk_image)})
|
||||
elif retcode == 2:
|
||||
# image is corrupted, let's try to fix it
|
||||
log.warning("Qemu image {} is corrupted".format(disk_image))
|
||||
if (yield from self._qemu_img_exec([qemu_img_path, "check", "-r", "all", "{}".format(disk_image)])) == 2:
|
||||
if (await self._qemu_img_exec([qemu_img_path, "check", "-r", "all", "{}".format(disk_image)])) == 2:
|
||||
self.project.emit("log.warning", {"message": "Qemu image '{}' is corrupted and could not be fixed".format(disk_image)})
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
stdout = self.read_qemu_img_stdout()
|
||||
@ -1569,7 +1550,7 @@ class QemuVM(BaseNode):
|
||||
# create the disk
|
||||
try:
|
||||
command = [qemu_img_path, "create", "-o", "backing_file={}".format(disk_image), "-f", "qcow2", disk]
|
||||
retcode = yield from self._qemu_img_exec(command)
|
||||
retcode = await self._qemu_img_exec(command)
|
||||
if retcode:
|
||||
stdout = self.read_qemu_img_stdout()
|
||||
raise QemuError("Could not create '{}' disk image: qemu-img returned with {}\n{}".format(disk_name,
|
||||
@ -1582,7 +1563,7 @@ class QemuVM(BaseNode):
|
||||
# The disk exists we check if the clone works
|
||||
try:
|
||||
qcow2 = Qcow2(disk)
|
||||
yield from qcow2.rebase(qemu_img_path, disk_image)
|
||||
await qcow2.rebase(qemu_img_path, disk_image)
|
||||
except (Qcow2Error, OSError) as e:
|
||||
raise QemuError("Could not use qcow2 disk image '{}' for {} {}".format(disk_image, disk_name, e))
|
||||
|
||||
@ -1599,8 +1580,7 @@ class QemuVM(BaseNode):
|
||||
|
||||
return options
|
||||
|
||||
@asyncio.coroutine
|
||||
def resize_disk(self, drive_name, extend):
|
||||
async def resize_disk(self, drive_name, extend):
|
||||
|
||||
if self.is_running():
|
||||
raise QemuError("Cannot resize {} while the VM is running".format(drive_name))
|
||||
@ -1613,7 +1593,7 @@ class QemuVM(BaseNode):
|
||||
if not os.path.exists(disk_image_path):
|
||||
raise QemuError("Disk path '{}' does not exist".format(disk_image_path))
|
||||
qemu_img_path = self._get_qemu_img()
|
||||
yield from self.manager.resize_disk(qemu_img_path, disk_image_path, extend)
|
||||
await self.manager.resize_disk(qemu_img_path, disk_image_path, extend)
|
||||
|
||||
def _cdrom_option(self):
|
||||
|
||||
@ -1663,15 +1643,14 @@ class QemuVM(BaseNode):
|
||||
|
||||
return options
|
||||
|
||||
@asyncio.coroutine
|
||||
def _network_options(self):
|
||||
async def _network_options(self):
|
||||
|
||||
network_options = []
|
||||
network_options.extend(["-net", "none"]) # we do not want any user networking back-end if no adapter is connected.
|
||||
|
||||
patched_qemu = False
|
||||
if self._legacy_networking:
|
||||
version = yield from self.manager.get_qemu_version(self.qemu_path)
|
||||
version = await self.manager.get_qemu_version(self.qemu_path)
|
||||
if version and parse_version(version) < parse_version("1.1.0"):
|
||||
# this is a patched Qemu if version is below 1.1.0
|
||||
patched_qemu = True
|
||||
@ -1684,7 +1663,7 @@ class QemuVM(BaseNode):
|
||||
network_options.extend(["-device", "pci-bridge,id=pci-bridge{bridge_id},bus=dmi_pci_bridge{bridge_id},chassis_nr=0x1,addr=0x{bridge_id},shpc=off".format(bridge_id=bridge_id)])
|
||||
|
||||
if bridge_id > 1:
|
||||
qemu_version = yield from self.manager.get_qemu_version(self.qemu_path)
|
||||
qemu_version = await self.manager.get_qemu_version(self.qemu_path)
|
||||
if qemu_version and parse_version(qemu_version) < parse_version("2.4.0"):
|
||||
raise QemuError("Qemu version 2.4 or later is required to run this VM with a large number of network adapters")
|
||||
|
||||
@ -1765,8 +1744,7 @@ class QemuVM(BaseNode):
|
||||
return ["-nographic"]
|
||||
return []
|
||||
|
||||
@asyncio.coroutine
|
||||
def _run_with_hardware_acceleration(self, qemu_path, options):
|
||||
async def _run_with_hardware_acceleration(self, qemu_path, options):
|
||||
"""
|
||||
Check if we can run Qemu with hardware acceleration
|
||||
|
||||
@ -1806,7 +1784,7 @@ class QemuVM(BaseNode):
|
||||
elif sys.platform.startswith("win"):
|
||||
if require_hardware_accel:
|
||||
# HAXM is only available starting with Qemu version 2.9.0
|
||||
version = yield from self.manager.get_qemu_version(self.qemu_path)
|
||||
version = await self.manager.get_qemu_version(self.qemu_path)
|
||||
if version and parse_version(version) < parse_version("2.9.0"):
|
||||
raise QemuError("HAXM acceleration can only be enable for Qemu version 2.9.0 and above (current version: {})".format(version))
|
||||
|
||||
@ -1824,8 +1802,8 @@ class QemuVM(BaseNode):
|
||||
else:
|
||||
return False
|
||||
elif sys.platform.startswith("darwin"):
|
||||
process = yield from asyncio.create_subprocess_shell("kextstat | grep com.intel.kext.intelhaxm")
|
||||
yield from process.wait()
|
||||
process = await asyncio.create_subprocess_shell("kextstat | grep com.intel.kext.intelhaxm")
|
||||
await process.wait()
|
||||
if process.returncode != 0:
|
||||
if require_hardware_accel:
|
||||
raise QemuError("HAXM acceleration support is not installed on this host (com.intel.kext.intelhaxm extension not loaded)")
|
||||
@ -1834,8 +1812,7 @@ class QemuVM(BaseNode):
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def _clear_save_vm_stated(self, snapshot_name="GNS3_SAVED_STATE"):
|
||||
async def _clear_save_vm_stated(self, snapshot_name="GNS3_SAVED_STATE"):
|
||||
|
||||
drives = ["a", "b", "c", "d"]
|
||||
qemu_img_path = self._get_qemu_img()
|
||||
@ -1851,7 +1828,7 @@ class QemuVM(BaseNode):
|
||||
if not os.path.exists(disk):
|
||||
continue
|
||||
command = [qemu_img_path, "snapshot", "-d", snapshot_name, disk]
|
||||
retcode = yield from self._qemu_img_exec(command)
|
||||
retcode = await self._qemu_img_exec(command)
|
||||
if retcode:
|
||||
stdout = self.read_qemu_img_stdout()
|
||||
log.warning("Could not delete saved VM state from disk {}: {}".format(disk, stdout))
|
||||
@ -1860,8 +1837,7 @@ class QemuVM(BaseNode):
|
||||
except subprocess.SubprocessError as e:
|
||||
raise QemuError("Error while looking for the Qemu VM saved state snapshot: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _saved_state_option(self, snapshot_name="GNS3_SAVED_STATE"):
|
||||
async def _saved_state_option(self, snapshot_name="GNS3_SAVED_STATE"):
|
||||
|
||||
drives = ["a", "b", "c", "d"]
|
||||
qemu_img_path = self._get_qemu_img()
|
||||
@ -1876,7 +1852,7 @@ class QemuVM(BaseNode):
|
||||
disk = disk_image
|
||||
if not os.path.exists(disk):
|
||||
continue
|
||||
output = yield from subprocess_check_output(qemu_img_path, "info", "--output=json", disk)
|
||||
output = await subprocess_check_output(qemu_img_path, "info", "--output=json", disk)
|
||||
json_data = json.loads(output)
|
||||
if "snapshots" in json_data:
|
||||
for snapshot in json_data["snapshots"]:
|
||||
@ -1889,8 +1865,7 @@ class QemuVM(BaseNode):
|
||||
raise QemuError("Error while looking for the Qemu VM saved state snapshot: {}".format(e))
|
||||
return []
|
||||
|
||||
@asyncio.coroutine
|
||||
def _build_command(self):
|
||||
async def _build_command(self):
|
||||
"""
|
||||
Command to start the QEMU process.
|
||||
(to be passed to subprocess.Popen())
|
||||
@ -1905,10 +1880,10 @@ class QemuVM(BaseNode):
|
||||
command.extend(["-name", self._name])
|
||||
command.extend(["-m", "{}M".format(self._ram)])
|
||||
command.extend(["-smp", "cpus={}".format(self._cpus)])
|
||||
if (yield from self._run_with_hardware_acceleration(self.qemu_path, self._options)):
|
||||
if (await self._run_with_hardware_acceleration(self.qemu_path, self._options)):
|
||||
if sys.platform.startswith("linux"):
|
||||
command.extend(["-enable-kvm"])
|
||||
version = yield from self.manager.get_qemu_version(self.qemu_path)
|
||||
version = await self.manager.get_qemu_version(self.qemu_path)
|
||||
# Issue on some combo Intel CPU + KVM + Qemu 2.4.0
|
||||
# https://github.com/GNS3/gns3-server/issues/685
|
||||
if version and parse_version(version) >= parse_version("2.4.0") and self.platform == "x86_64":
|
||||
@ -1918,7 +1893,7 @@ class QemuVM(BaseNode):
|
||||
command.extend(["-boot", "order={}".format(self._boot_priority)])
|
||||
command.extend(self._bios_option())
|
||||
command.extend(self._cdrom_option())
|
||||
command.extend((yield from self._disk_options()))
|
||||
command.extend((await self._disk_options()))
|
||||
command.extend(self._linux_boot_options())
|
||||
if "-uuid" not in additional_options:
|
||||
command.extend(["-uuid", self._id])
|
||||
@ -1933,12 +1908,12 @@ class QemuVM(BaseNode):
|
||||
elif self._console_type != "none":
|
||||
raise QemuError("Console type {} is unknown".format(self._console_type))
|
||||
command.extend(self._monitor_options())
|
||||
command.extend((yield from self._network_options()))
|
||||
command.extend((await self._network_options()))
|
||||
command.extend(self._graphic())
|
||||
if self.on_close != "save_vm_state":
|
||||
yield from self._clear_save_vm_stated()
|
||||
await self._clear_save_vm_stated()
|
||||
else:
|
||||
command.extend((yield from self._saved_state_option()))
|
||||
command.extend((await self._saved_state_option()))
|
||||
|
||||
if additional_options:
|
||||
try:
|
||||
|
@ -34,12 +34,11 @@ class TraceNG(BaseManager):
|
||||
|
||||
super().__init__()
|
||||
|
||||
@asyncio.coroutine
|
||||
def create_node(self, *args, **kwargs):
|
||||
async def create_node(self, *args, **kwargs):
|
||||
"""
|
||||
Creates a new TraceNG VM.
|
||||
|
||||
:returns: TraceNGVM instance
|
||||
"""
|
||||
|
||||
return (yield from super().create_node(*args, **kwargs))
|
||||
return (await super().create_node(*args, **kwargs))
|
||||
|
@ -68,13 +68,12 @@ class TraceNGVM(BaseNode):
|
||||
def ethernet_adapter(self):
|
||||
return self._ethernet_adapter
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this TraceNG VM.
|
||||
"""
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
nio = self._ethernet_adapter.get_nio(0)
|
||||
@ -86,15 +85,14 @@ class TraceNGVM(BaseNode):
|
||||
self.manager.port_manager.release_udp_port(self._local_udp_tunnel[1].lport, self._project)
|
||||
self._local_udp_tunnel = None
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
|
||||
if self.is_running():
|
||||
self._terminate_process()
|
||||
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_requirements(self):
|
||||
async def _check_requirements(self):
|
||||
"""
|
||||
Check if TraceNG is available.
|
||||
"""
|
||||
@ -193,33 +191,32 @@ class TraceNGVM(BaseNode):
|
||||
id=self.id,
|
||||
destination=destination))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self, destination=None):
|
||||
async def start(self, destination=None):
|
||||
"""
|
||||
Starts the TraceNG process.
|
||||
"""
|
||||
|
||||
if not sys.platform.startswith("win"):
|
||||
raise TraceNGError("Sorry, TraceNG can only run on Windows")
|
||||
yield from self._check_requirements()
|
||||
await self._check_requirements()
|
||||
if not self.is_running():
|
||||
nio = self._ethernet_adapter.get_nio(0)
|
||||
command = self._build_command(destination)
|
||||
yield from self._stop_ubridge() # make use we start with a fresh uBridge instance
|
||||
await self._stop_ubridge() # make use we start with a fresh uBridge instance
|
||||
try:
|
||||
log.info("Starting TraceNG: {}".format(command))
|
||||
flags = 0
|
||||
if hasattr(subprocess, "CREATE_NEW_CONSOLE"):
|
||||
flags = subprocess.CREATE_NEW_CONSOLE
|
||||
self.command_line = ' '.join(command)
|
||||
self._process = yield from asyncio.create_subprocess_exec(*command,
|
||||
self._process = await asyncio.create_subprocess_exec(*command,
|
||||
cwd=self.working_dir,
|
||||
creationflags=flags)
|
||||
monitor_process(self._process, self._termination_callback)
|
||||
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
if nio:
|
||||
yield from self.add_ubridge_udp_connection("TraceNG-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
await self.add_ubridge_udp_connection("TraceNG-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
|
||||
log.info("TraceNG instance {} started PID={}".format(self.name, self._process.pid))
|
||||
self._started = True
|
||||
@ -243,18 +240,17 @@ class TraceNGVM(BaseNode):
|
||||
if returncode != 0:
|
||||
self.project.emit("log.error", {"message": "TraceNG process has stopped, return code: {}\n".format(returncode)})
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the TraceNG process.
|
||||
"""
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
if self.is_running():
|
||||
self._terminate_process()
|
||||
if self._process.returncode is None:
|
||||
try:
|
||||
yield from wait_for_process_termination(self._process, timeout=3)
|
||||
await wait_for_process_termination(self._process, timeout=3)
|
||||
except asyncio.TimeoutError:
|
||||
if self._process.returncode is None:
|
||||
try:
|
||||
@ -266,16 +262,15 @@ class TraceNGVM(BaseNode):
|
||||
|
||||
self._process = None
|
||||
self._started = False
|
||||
yield from super().stop()
|
||||
await super().stop()
|
||||
|
||||
@asyncio.coroutine
|
||||
def reload(self):
|
||||
async def reload(self):
|
||||
"""
|
||||
Reloads the TraceNG process (stop & start).
|
||||
"""
|
||||
|
||||
yield from self.stop()
|
||||
yield from self.start(self._destination)
|
||||
await self.stop()
|
||||
await self.start(self._destination)
|
||||
|
||||
def _terminate_process(self):
|
||||
"""
|
||||
@ -303,8 +298,7 @@ class TraceNGVM(BaseNode):
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_add_nio_binding(self, port_number, nio):
|
||||
async def port_add_nio_binding(self, port_number, nio):
|
||||
"""
|
||||
Adds a port NIO binding.
|
||||
|
||||
@ -317,7 +311,7 @@ class TraceNGVM(BaseNode):
|
||||
port_number=port_number))
|
||||
|
||||
if self.is_running():
|
||||
yield from self.add_ubridge_udp_connection("TraceNG-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
await self.add_ubridge_udp_connection("TraceNG-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
|
||||
self._ethernet_adapter.add_nio(port_number, nio)
|
||||
log.info('TraceNG "{name}" [{id}]: {nio} added to port {port_number}'.format(name=self._name,
|
||||
@ -327,16 +321,14 @@ class TraceNGVM(BaseNode):
|
||||
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_update_nio_binding(self, port_number, nio):
|
||||
async def port_update_nio_binding(self, port_number, nio):
|
||||
if not self._ethernet_adapter.port_exists(port_number):
|
||||
raise TraceNGError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=self._ethernet_adapter,
|
||||
port_number=port_number))
|
||||
if self.is_running():
|
||||
yield from self.update_ubridge_udp_connection("TraceNG-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
await self.update_ubridge_udp_connection("TraceNG-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_remove_nio_binding(self, port_number):
|
||||
async def port_remove_nio_binding(self, port_number):
|
||||
"""
|
||||
Removes a port NIO binding.
|
||||
|
||||
@ -350,7 +342,7 @@ class TraceNGVM(BaseNode):
|
||||
port_number=port_number))
|
||||
|
||||
if self.is_running():
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name="TraceNG-{}".format(self._id)))
|
||||
await self._ubridge_send("bridge delete {name}".format(name="TraceNG-{}".format(self._id)))
|
||||
|
||||
nio = self._ethernet_adapter.get_nio(port_number)
|
||||
if isinstance(nio, NIOUDP):
|
||||
@ -363,8 +355,7 @@ class TraceNGVM(BaseNode):
|
||||
port_number=port_number))
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file):
|
||||
async def start_capture(self, port_number, output_file):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -387,15 +378,14 @@ class TraceNGVM(BaseNode):
|
||||
nio.startPacketCapture(output_file)
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="TraceNG-{}".format(self._id),
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="TraceNG-{}".format(self._id),
|
||||
output_file=output_file))
|
||||
|
||||
log.info("TraceNG '{name}' [{id}]: starting packet capture on port {port_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
port_number=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@ -414,7 +404,7 @@ class TraceNGVM(BaseNode):
|
||||
nio.stopPacketCapture()
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge stop_capture {name}'.format(name="TraceNG-{}".format(self._id)))
|
||||
await self._ubridge_send('bridge stop_capture {name}'.format(name="TraceNG-{}".format(self._id)))
|
||||
|
||||
log.info("TraceNG '{name}' [{id}]: stopping packet capture on port {port_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
|
@ -93,13 +93,12 @@ class VirtualBox(BaseManager):
|
||||
self._vboxmanage_path = vboxmanage_path
|
||||
return vboxmanage_path
|
||||
|
||||
@asyncio.coroutine
|
||||
def execute(self, subcommand, args, timeout=60):
|
||||
async def execute(self, subcommand, args, timeout=60):
|
||||
|
||||
# We use a lock prevent parallel execution due to strange errors
|
||||
# reported by a user and reproduced by us.
|
||||
# https://github.com/GNS3/gns3-gui/issues/261
|
||||
with (yield from self._execute_lock):
|
||||
async with self._execute_lock:
|
||||
vboxmanage_path = self.vboxmanage_path
|
||||
if not vboxmanage_path:
|
||||
vboxmanage_path = self.find_vboxmanage()
|
||||
@ -111,12 +110,12 @@ class VirtualBox(BaseManager):
|
||||
command_string = " ".join(command)
|
||||
log.info("Executing VBoxManage with command: {}".format(command_string))
|
||||
try:
|
||||
process = yield from asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
|
||||
process = await asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
raise VirtualBoxError("Could not execute VBoxManage: {}".format(e))
|
||||
|
||||
try:
|
||||
stdout_data, stderr_data = yield from asyncio.wait_for(process.communicate(), timeout=timeout)
|
||||
stdout_data, stderr_data = await asyncio.wait_for(process.communicate(), timeout=timeout)
|
||||
except asyncio.TimeoutError:
|
||||
raise VirtualBoxError("VBoxManage has timed out after {} seconds!".format(timeout))
|
||||
|
||||
@ -126,15 +125,14 @@ class VirtualBox(BaseManager):
|
||||
|
||||
return stdout_data.decode("utf-8", errors="ignore").splitlines()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _find_inaccessible_hdd_files(self):
|
||||
async def _find_inaccessible_hdd_files(self):
|
||||
"""
|
||||
Finds inaccessible disk files (to clean up the VirtualBox media manager)
|
||||
"""
|
||||
|
||||
hdds = []
|
||||
try:
|
||||
properties = yield from self.execute("list", ["hdds"])
|
||||
properties = await self.execute("list", ["hdds"])
|
||||
# If VirtualBox is not available we have no inaccessible hdd
|
||||
except VirtualBoxError:
|
||||
return hdds
|
||||
@ -152,32 +150,30 @@ class VirtualBox(BaseManager):
|
||||
flag_inaccessible = False
|
||||
return reversed(hdds)
|
||||
|
||||
@asyncio.coroutine
|
||||
def project_closed(self, project):
|
||||
async def project_closed(self, project):
|
||||
"""
|
||||
Called when a project is closed.
|
||||
|
||||
:param project: Project instance
|
||||
"""
|
||||
|
||||
yield from super().project_closed(project)
|
||||
hdd_files_to_close = yield from self._find_inaccessible_hdd_files()
|
||||
await super().project_closed(project)
|
||||
hdd_files_to_close = await self._find_inaccessible_hdd_files()
|
||||
for hdd_file in hdd_files_to_close:
|
||||
log.info("Closing VirtualBox VM disk file {}".format(os.path.basename(hdd_file)))
|
||||
try:
|
||||
yield from self.execute("closemedium", ["disk", hdd_file])
|
||||
await self.execute("closemedium", ["disk", hdd_file])
|
||||
except VirtualBoxError as e:
|
||||
log.warning("Could not close VirtualBox VM disk file {}: {}".format(os.path.basename(hdd_file), e))
|
||||
continue
|
||||
|
||||
@asyncio.coroutine
|
||||
def list_vms(self, allow_clone=False):
|
||||
async def list_vms(self, allow_clone=False):
|
||||
"""
|
||||
Gets VirtualBox VM list.
|
||||
"""
|
||||
|
||||
vbox_vms = []
|
||||
result = yield from self.execute("list", ["vms"])
|
||||
result = await self.execute("list", ["vms"])
|
||||
for line in result:
|
||||
if len(line) == 0 or line[0] != '"' or line[-1:] != "}":
|
||||
continue # Broken output (perhaps a carriage return in VM name)
|
||||
@ -185,10 +181,10 @@ class VirtualBox(BaseManager):
|
||||
vmname = vmname.strip('"')
|
||||
if vmname == "<inaccessible>":
|
||||
continue # ignore inaccessible VMs
|
||||
extra_data = yield from self.execute("getextradata", [vmname, "GNS3/Clone"])
|
||||
extra_data = await self.execute("getextradata", [vmname, "GNS3/Clone"])
|
||||
if allow_clone or len(extra_data) == 0 or not extra_data[0].strip() == "Value: yes":
|
||||
# get the amount of RAM
|
||||
info_results = yield from self.execute("showvminfo", [vmname, "--machinereadable"])
|
||||
info_results = await self.execute("showvminfo", [vmname, "--machinereadable"])
|
||||
ram = 0
|
||||
for info in info_results:
|
||||
try:
|
||||
|
@ -98,10 +98,9 @@ class VirtualBoxVM(BaseNode):
|
||||
def ethernet_adapters(self):
|
||||
return self._ethernet_adapters
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_system_properties(self):
|
||||
async def _get_system_properties(self):
|
||||
|
||||
properties = yield from self.manager.execute("list", ["systemproperties"])
|
||||
properties = await self.manager.execute("list", ["systemproperties"])
|
||||
for prop in properties:
|
||||
try:
|
||||
name, value = prop.split(':', 1)
|
||||
@ -109,15 +108,14 @@ class VirtualBoxVM(BaseNode):
|
||||
continue
|
||||
self._system_properties[name.strip()] = value.strip()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_vm_state(self):
|
||||
async def _get_vm_state(self):
|
||||
"""
|
||||
Returns the VM state (e.g. running, paused etc.)
|
||||
|
||||
:returns: state (string)
|
||||
"""
|
||||
|
||||
results = yield from self.manager.execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
results = await self.manager.execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
for info in results:
|
||||
if '=' in info:
|
||||
name, value = info.split('=', 1)
|
||||
@ -125,8 +123,7 @@ class VirtualBoxVM(BaseNode):
|
||||
return value.strip('"')
|
||||
raise VirtualBoxError("Could not get VM state for {}".format(self._vmname))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _control_vm(self, params):
|
||||
async def _control_vm(self, params):
|
||||
"""
|
||||
Change setting in this VM when running.
|
||||
|
||||
@ -136,11 +133,10 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
|
||||
args = shlex.split(params)
|
||||
result = yield from self.manager.execute("controlvm", [self._vmname] + args)
|
||||
result = await self.manager.execute("controlvm", [self._vmname] + args)
|
||||
return result
|
||||
|
||||
@asyncio.coroutine
|
||||
def _modify_vm(self, params):
|
||||
async def _modify_vm(self, params):
|
||||
"""
|
||||
Change setting in this VM when not running.
|
||||
|
||||
@ -148,10 +144,9 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
|
||||
args = shlex.split(params)
|
||||
yield from self.manager.execute("modifyvm", [self._vmname] + args)
|
||||
await self.manager.execute("modifyvm", [self._vmname] + args)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_duplicate_linked_clone(self):
|
||||
async def _check_duplicate_linked_clone(self):
|
||||
"""
|
||||
Without linked clone two VM using the same image can't run
|
||||
at the same time.
|
||||
@ -175,14 +170,13 @@ class VirtualBoxVM(BaseNode):
|
||||
if not found:
|
||||
return
|
||||
trial += 1
|
||||
yield from asyncio.sleep(1)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
if not self.linked_clone:
|
||||
yield from self._check_duplicate_linked_clone()
|
||||
await self._check_duplicate_linked_clone()
|
||||
|
||||
yield from self._get_system_properties()
|
||||
await self._get_system_properties()
|
||||
if "API version" not in self._system_properties:
|
||||
raise VirtualBoxError("Can't access to VirtualBox API version:\n{}".format(self._system_properties))
|
||||
if parse_version(self._system_properties["API version"]) < parse_version("4_3"):
|
||||
@ -192,15 +186,15 @@ class VirtualBoxVM(BaseNode):
|
||||
if self.linked_clone:
|
||||
if self.id and os.path.isdir(os.path.join(self.working_dir, self._vmname)):
|
||||
self._patch_vm_uuid()
|
||||
yield from self.manager.execute("registervm", [self._linked_vbox_file()])
|
||||
yield from self._reattach_linked_hdds()
|
||||
await self.manager.execute("registervm", [self._linked_vbox_file()])
|
||||
await self._reattach_linked_hdds()
|
||||
else:
|
||||
yield from self._create_linked_clone()
|
||||
await self._create_linked_clone()
|
||||
|
||||
if self._adapters:
|
||||
yield from self.set_adapters(self._adapters)
|
||||
await self.set_adapters(self._adapters)
|
||||
|
||||
vm_info = yield from self._get_vm_info()
|
||||
vm_info = await self._get_vm_info()
|
||||
if "memory" in vm_info:
|
||||
self._ram = int(vm_info["memory"])
|
||||
|
||||
@ -234,22 +228,20 @@ class VirtualBoxVM(BaseNode):
|
||||
machine.set("uuid", "{" + self.id + "}")
|
||||
tree.write(self._linked_vbox_file())
|
||||
|
||||
@asyncio.coroutine
|
||||
def check_hw_virtualization(self):
|
||||
async def check_hw_virtualization(self):
|
||||
"""
|
||||
Returns either hardware virtualization is activated or not.
|
||||
|
||||
:returns: boolean
|
||||
"""
|
||||
|
||||
vm_info = yield from self._get_vm_info()
|
||||
vm_info = await self._get_vm_info()
|
||||
if "hwvirtex" in vm_info and vm_info["hwvirtex"] == "on":
|
||||
return True
|
||||
return False
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts this VirtualBox VM.
|
||||
"""
|
||||
@ -258,17 +250,17 @@ class VirtualBoxVM(BaseNode):
|
||||
return
|
||||
|
||||
# resume the VM if it is paused
|
||||
vm_state = yield from self._get_vm_state()
|
||||
vm_state = await self._get_vm_state()
|
||||
if vm_state == "paused":
|
||||
yield from self.resume()
|
||||
await self.resume()
|
||||
return
|
||||
|
||||
# VM must be powered off to start it
|
||||
if vm_state != "poweroff":
|
||||
raise VirtualBoxError("VirtualBox VM not powered off")
|
||||
|
||||
yield from self._set_network_options()
|
||||
yield from self._set_serial_console()
|
||||
await self._set_network_options()
|
||||
await self._set_serial_console()
|
||||
|
||||
# check if there is enough RAM to run
|
||||
self.check_available_ram(self.ram)
|
||||
@ -276,92 +268,90 @@ class VirtualBoxVM(BaseNode):
|
||||
args = [self._vmname]
|
||||
if self._headless:
|
||||
args.extend(["--type", "headless"])
|
||||
result = yield from self.manager.execute("startvm", args)
|
||||
result = await self.manager.execute("startvm", args)
|
||||
self.status = "started"
|
||||
log.info("VirtualBox VM '{name}' [{id}] started".format(name=self.name, id=self.id))
|
||||
log.debug("Start result: {}".format(result))
|
||||
|
||||
# add a guest property to let the VM know about the GNS3 name
|
||||
yield from self.manager.execute("guestproperty", ["set", self._vmname, "NameInGNS3", self.name])
|
||||
await self.manager.execute("guestproperty", ["set", self._vmname, "NameInGNS3", self.name])
|
||||
# add a guest property to let the VM know about the GNS3 project directory
|
||||
yield from self.manager.execute("guestproperty", ["set", self._vmname, "ProjectDirInGNS3", self.working_dir])
|
||||
await self.manager.execute("guestproperty", ["set", self._vmname, "ProjectDirInGNS3", self.working_dir])
|
||||
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
for adapter_number in range(0, self._adapters):
|
||||
nio = self._ethernet_adapters[adapter_number].get_nio(0)
|
||||
if nio:
|
||||
yield from self.add_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
|
||||
await self.add_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
|
||||
self._local_udp_tunnels[adapter_number][1],
|
||||
nio)
|
||||
|
||||
yield from self._start_console()
|
||||
await self._start_console()
|
||||
|
||||
if (yield from self.check_hw_virtualization()):
|
||||
if (await self.check_hw_virtualization()):
|
||||
self._hw_virtualization = True
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops this VirtualBox VM.
|
||||
"""
|
||||
|
||||
self._hw_virtualization = False
|
||||
yield from self._stop_ubridge()
|
||||
yield from self._stop_remote_console()
|
||||
vm_state = yield from self._get_vm_state()
|
||||
await self._stop_ubridge()
|
||||
await self._stop_remote_console()
|
||||
vm_state = await self._get_vm_state()
|
||||
if vm_state == "running" or vm_state == "paused" or vm_state == "stuck":
|
||||
|
||||
if self.on_close == "save_vm_state":
|
||||
result = yield from self._control_vm("savestate")
|
||||
result = await self._control_vm("savestate")
|
||||
self.status = "stopped"
|
||||
log.debug("Stop result: {}".format(result))
|
||||
elif self.on_close == "shutdown_signal":
|
||||
# use ACPI to shutdown the VM
|
||||
result = yield from self._control_vm("acpipowerbutton")
|
||||
result = await self._control_vm("acpipowerbutton")
|
||||
trial = 0
|
||||
while True:
|
||||
vm_state = yield from self._get_vm_state()
|
||||
vm_state = await self._get_vm_state()
|
||||
if vm_state == "poweroff":
|
||||
break
|
||||
yield from asyncio.sleep(1)
|
||||
await asyncio.sleep(1)
|
||||
trial += 1
|
||||
if trial >= 120:
|
||||
yield from self._control_vm("poweroff")
|
||||
await self._control_vm("poweroff")
|
||||
break
|
||||
self.status = "stopped"
|
||||
log.debug("ACPI shutdown result: {}".format(result))
|
||||
else:
|
||||
# power off the VM
|
||||
result = yield from self._control_vm("poweroff")
|
||||
result = await self._control_vm("poweroff")
|
||||
self.status = "stopped"
|
||||
log.debug("Stop result: {}".format(result))
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}] stopped".format(name=self.name, id=self.id))
|
||||
yield from asyncio.sleep(0.5) # give some time for VirtualBox to unlock the VM
|
||||
await asyncio.sleep(0.5) # give some time for VirtualBox to unlock the VM
|
||||
try:
|
||||
# deactivate the first serial port
|
||||
yield from self._modify_vm("--uart1 off")
|
||||
await self._modify_vm("--uart1 off")
|
||||
except VirtualBoxError as e:
|
||||
log.warning("Could not deactivate the first serial port: {}".format(e))
|
||||
|
||||
for adapter_number in range(0, self._adapters):
|
||||
nio = self._ethernet_adapters[adapter_number].get_nio(0)
|
||||
if nio:
|
||||
yield from self._modify_vm("--nictrace{} off".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--nic{} null".format(adapter_number + 1))
|
||||
yield from super().stop()
|
||||
await self._modify_vm("--nictrace{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--nic{} null".format(adapter_number + 1))
|
||||
await super().stop()
|
||||
|
||||
@asyncio.coroutine
|
||||
def suspend(self):
|
||||
async def suspend(self):
|
||||
"""
|
||||
Suspends this VirtualBox VM.
|
||||
"""
|
||||
|
||||
vm_state = yield from self._get_vm_state()
|
||||
vm_state = await self._get_vm_state()
|
||||
if vm_state == "running":
|
||||
yield from self._control_vm("pause")
|
||||
await self._control_vm("pause")
|
||||
self.status = "suspended"
|
||||
log.info("VirtualBox VM '{name}' [{id}] suspended".format(name=self.name, id=self.id))
|
||||
else:
|
||||
@ -369,31 +359,28 @@ class VirtualBoxVM(BaseNode):
|
||||
id=self.id,
|
||||
state=vm_state))
|
||||
|
||||
@asyncio.coroutine
|
||||
def resume(self):
|
||||
async def resume(self):
|
||||
"""
|
||||
Resumes this VirtualBox VM.
|
||||
"""
|
||||
|
||||
yield from self._control_vm("resume")
|
||||
await self._control_vm("resume")
|
||||
self.status = "started"
|
||||
log.info("VirtualBox VM '{name}' [{id}] resumed".format(name=self.name, id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def reload(self):
|
||||
async def reload(self):
|
||||
"""
|
||||
Reloads this VirtualBox VM.
|
||||
"""
|
||||
|
||||
result = yield from self._control_vm("reset")
|
||||
result = await self._control_vm("reset")
|
||||
log.info("VirtualBox VM '{name}' [{id}] reloaded".format(name=self.name, id=self.id))
|
||||
log.debug("Reload result: {}".format(result))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_all_hdd_files(self):
|
||||
async def _get_all_hdd_files(self):
|
||||
|
||||
hdds = []
|
||||
properties = yield from self.manager.execute("list", ["hdds"])
|
||||
properties = await self.manager.execute("list", ["hdds"])
|
||||
for prop in properties:
|
||||
try:
|
||||
name, value = prop.split(':', 1)
|
||||
@ -403,8 +390,7 @@ class VirtualBoxVM(BaseNode):
|
||||
hdds.append(value.strip())
|
||||
return hdds
|
||||
|
||||
@asyncio.coroutine
|
||||
def _reattach_linked_hdds(self):
|
||||
async def _reattach_linked_hdds(self):
|
||||
"""
|
||||
Reattach linked cloned hard disks.
|
||||
"""
|
||||
@ -428,7 +414,7 @@ class VirtualBoxVM(BaseNode):
|
||||
medium=hdd_file))
|
||||
|
||||
try:
|
||||
yield from self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium "{}"'.format(hdd_info["controller"],
|
||||
await self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium "{}"'.format(hdd_info["controller"],
|
||||
hdd_info["port"],
|
||||
hdd_info["device"],
|
||||
hdd_file))
|
||||
@ -443,8 +429,7 @@ class VirtualBoxVM(BaseNode):
|
||||
error=e))
|
||||
continue
|
||||
|
||||
@asyncio.coroutine
|
||||
def save_linked_hdds_info(self):
|
||||
async def save_linked_hdds_info(self):
|
||||
"""
|
||||
Save linked cloned hard disks information.
|
||||
|
||||
@ -454,8 +439,8 @@ class VirtualBoxVM(BaseNode):
|
||||
hdd_table = []
|
||||
if self.linked_clone:
|
||||
if os.path.exists(self.working_dir):
|
||||
hdd_files = yield from self._get_all_hdd_files()
|
||||
vm_info = yield from self._get_vm_info()
|
||||
hdd_files = await self._get_all_hdd_files()
|
||||
vm_info = await self._get_vm_info()
|
||||
for entry, value in vm_info.items():
|
||||
match = re.search("^([\s\w]+)\-(\d)\-(\d)$", entry) # match Controller-PortNumber-DeviceNumber entry
|
||||
if match:
|
||||
@ -489,8 +474,7 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
return hdd_table
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this VirtualBox VM.
|
||||
"""
|
||||
@ -499,7 +483,7 @@ class VirtualBoxVM(BaseNode):
|
||||
# VM is already closed
|
||||
return
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
log.debug("VirtualBox VM '{name}' [{id}] is closing".format(name=self.name, id=self.id))
|
||||
@ -519,10 +503,10 @@ class VirtualBoxVM(BaseNode):
|
||||
self._local_udp_tunnels = {}
|
||||
|
||||
self.on_close = "power_off"
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
|
||||
if self.linked_clone:
|
||||
hdd_table = yield from self.save_linked_hdds_info()
|
||||
hdd_table = await self.save_linked_hdds_info()
|
||||
for hdd in hdd_table.copy():
|
||||
log.info("VirtualBox VM '{name}' [{id}] detaching HDD {controller} {port} {device}".format(name=self.name,
|
||||
id=self.id,
|
||||
@ -530,7 +514,7 @@ class VirtualBoxVM(BaseNode):
|
||||
port=hdd["port"],
|
||||
device=hdd["device"]))
|
||||
try:
|
||||
yield from self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium none'.format(hdd["controller"],
|
||||
await self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium none'.format(hdd["controller"],
|
||||
hdd["port"],
|
||||
hdd["device"]))
|
||||
except VirtualBoxError as e:
|
||||
@ -543,7 +527,7 @@ class VirtualBoxVM(BaseNode):
|
||||
continue
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}] unregistering".format(name=self.name, id=self.id))
|
||||
yield from self.manager.execute("unregistervm", [self._name])
|
||||
await self.manager.execute("unregistervm", [self._name])
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}] closed".format(name=self.name, id=self.id))
|
||||
self._closed = True
|
||||
@ -603,8 +587,7 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
return self._ram
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_ram(self, ram):
|
||||
async def set_ram(self, ram):
|
||||
"""
|
||||
Set the amount of RAM allocated to this VirtualBox VM.
|
||||
|
||||
@ -614,7 +597,7 @@ class VirtualBoxVM(BaseNode):
|
||||
if ram == 0:
|
||||
return
|
||||
|
||||
yield from self._modify_vm('--memory {}'.format(ram))
|
||||
await self._modify_vm('--memory {}'.format(ram))
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}] has set amount of RAM to {ram}".format(name=self.name, id=self.id, ram=ram))
|
||||
self._ram = ram
|
||||
@ -629,8 +612,7 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
return self._vmname
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_vmname(self, vmname):
|
||||
async def set_vmname(self, vmname):
|
||||
"""
|
||||
Renames the VirtualBox VM.
|
||||
|
||||
@ -644,10 +626,10 @@ class VirtualBoxVM(BaseNode):
|
||||
if self.status == "started":
|
||||
raise VirtualBoxError("You can't change the name of running VM {}".format(self._name))
|
||||
# We can't rename a VM to name that already exists
|
||||
vms = yield from self.manager.list_vms(allow_clone=True)
|
||||
vms = await self.manager.list_vms(allow_clone=True)
|
||||
if vmname in [vm["vmname"] for vm in vms]:
|
||||
raise VirtualBoxError("You can't change the name to {} it's already use in VirtualBox".format(vmname))
|
||||
yield from self._modify_vm('--name "{}"'.format(vmname))
|
||||
await self._modify_vm('--name "{}"'.format(vmname))
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}] has set the VM name to '{vmname}'".format(name=self.name, id=self.id, vmname=vmname))
|
||||
self._vmname = vmname
|
||||
@ -662,8 +644,7 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
return self._adapters
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_adapters(self, adapters):
|
||||
async def set_adapters(self, adapters):
|
||||
"""
|
||||
Sets the number of Ethernet adapters for this VirtualBox VM instance.
|
||||
|
||||
@ -671,7 +652,7 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
|
||||
# check for the maximum adapters supported by the VM
|
||||
vm_info = yield from self._get_vm_info()
|
||||
vm_info = await self._get_vm_info()
|
||||
chipset = "piix3" # default chipset for VirtualBox VMs
|
||||
self._maximum_adapters = 8 # default maximum network adapter count for PIIX3 chipset
|
||||
if "chipset" in vm_info:
|
||||
@ -749,8 +730,7 @@ class VirtualBoxVM(BaseNode):
|
||||
id=self.id,
|
||||
adapter_type=adapter_type))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_vm_info(self):
|
||||
async def _get_vm_info(self):
|
||||
"""
|
||||
Returns this VM info.
|
||||
|
||||
@ -758,7 +738,7 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
|
||||
vm_info = {}
|
||||
results = yield from self.manager.execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
results = await self.manager.execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
for info in results:
|
||||
try:
|
||||
name, value = info.split('=', 1)
|
||||
@ -784,22 +764,20 @@ class VirtualBoxVM(BaseNode):
|
||||
raise VirtualBoxError("Could not create the VirtualBox pipe directory: {}".format(e))
|
||||
return pipe_name
|
||||
|
||||
@asyncio.coroutine
|
||||
def _set_serial_console(self):
|
||||
async def _set_serial_console(self):
|
||||
"""
|
||||
Configures the first serial port to allow a serial console connection.
|
||||
"""
|
||||
|
||||
# activate the first serial port
|
||||
yield from self._modify_vm("--uart1 0x3F8 4")
|
||||
await self._modify_vm("--uart1 0x3F8 4")
|
||||
|
||||
# set server mode with a pipe on the first serial port
|
||||
pipe_name = self._get_pipe_name()
|
||||
args = [self._vmname, "--uartmode1", "server", pipe_name]
|
||||
yield from self.manager.execute("modifyvm", args)
|
||||
await self.manager.execute("modifyvm", args)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _storage_attach(self, params):
|
||||
async def _storage_attach(self, params):
|
||||
"""
|
||||
Change storage medium in this VM.
|
||||
|
||||
@ -807,10 +785,9 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
|
||||
args = shlex.split(params)
|
||||
yield from self.manager.execute("storageattach", [self._vmname] + args)
|
||||
await self.manager.execute("storageattach", [self._vmname] + args)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_nic_attachements(self, maximum_adapters):
|
||||
async def _get_nic_attachements(self, maximum_adapters):
|
||||
"""
|
||||
Returns NIC attachements.
|
||||
|
||||
@ -819,7 +796,7 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
|
||||
nics = []
|
||||
vm_info = yield from self._get_vm_info()
|
||||
vm_info = await self._get_vm_info()
|
||||
for adapter_number in range(0, maximum_adapters):
|
||||
entry = "nic{}".format(adapter_number + 1)
|
||||
if entry in vm_info:
|
||||
@ -829,22 +806,21 @@ class VirtualBoxVM(BaseNode):
|
||||
nics.append(None)
|
||||
return nics
|
||||
|
||||
@asyncio.coroutine
|
||||
def _set_network_options(self):
|
||||
async def _set_network_options(self):
|
||||
"""
|
||||
Configures network options.
|
||||
"""
|
||||
|
||||
nic_attachments = yield from self._get_nic_attachements(self._maximum_adapters)
|
||||
nic_attachments = await self._get_nic_attachements(self._maximum_adapters)
|
||||
for adapter_number in range(0, self._adapters):
|
||||
attachment = nic_attachments[adapter_number]
|
||||
if attachment == "null":
|
||||
# disconnect the cable if no backend is attached.
|
||||
yield from self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
if attachment == "none":
|
||||
# set the backend to null to avoid a difference in the number of interfaces in the Guest.
|
||||
yield from self._modify_vm("--nic{} null".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--nic{} null".format(adapter_number + 1))
|
||||
await self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
|
||||
# use a local UDP tunnel to connect to uBridge instead
|
||||
if adapter_number not in self._local_udp_tunnels:
|
||||
@ -855,7 +831,7 @@ class VirtualBoxVM(BaseNode):
|
||||
if not self._use_any_adapter and attachment in ("nat", "bridged", "intnet", "hostonly", "natnetwork"):
|
||||
continue
|
||||
|
||||
yield from self._modify_vm("--nictrace{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--nictrace{} off".format(adapter_number + 1))
|
||||
|
||||
custom_adapter = self._get_custom_adapter_settings(adapter_number)
|
||||
adapter_type = custom_adapter.get("adapter_type", self._adapter_type)
|
||||
@ -874,45 +850,44 @@ class VirtualBoxVM(BaseNode):
|
||||
if adapter_type == "Paravirtualized Network (virtio-net)":
|
||||
vbox_adapter_type = "virtio"
|
||||
args = [self._vmname, "--nictype{}".format(adapter_number + 1), vbox_adapter_type]
|
||||
yield from self.manager.execute("modifyvm", args)
|
||||
await self.manager.execute("modifyvm", args)
|
||||
|
||||
if isinstance(nio, NIOUDP):
|
||||
log.debug("setting UDP params on adapter {}".format(adapter_number))
|
||||
yield from self._modify_vm("--nic{} generic".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--nicgenericdrv{} UDPTunnel".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--nicproperty{} sport={}".format(adapter_number + 1, nio.lport))
|
||||
yield from self._modify_vm("--nicproperty{} dest={}".format(adapter_number + 1, nio.rhost))
|
||||
yield from self._modify_vm("--nicproperty{} dport={}".format(adapter_number + 1, nio.rport))
|
||||
await self._modify_vm("--nic{} generic".format(adapter_number + 1))
|
||||
await self._modify_vm("--nicgenericdrv{} UDPTunnel".format(adapter_number + 1))
|
||||
await self._modify_vm("--nicproperty{} sport={}".format(adapter_number + 1, nio.lport))
|
||||
await self._modify_vm("--nicproperty{} dest={}".format(adapter_number + 1, nio.rhost))
|
||||
await self._modify_vm("--nicproperty{} dport={}".format(adapter_number + 1, nio.rport))
|
||||
if nio.suspend:
|
||||
yield from self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
else:
|
||||
yield from self._modify_vm("--cableconnected{} on".format(adapter_number + 1))
|
||||
await self._modify_vm("--cableconnected{} on".format(adapter_number + 1))
|
||||
|
||||
if nio.capturing:
|
||||
yield from self._modify_vm("--nictrace{} on".format(adapter_number + 1))
|
||||
yield from self._modify_vm('--nictracefile{} "{}"'.format(adapter_number + 1, nio.pcap_output_file))
|
||||
await self._modify_vm("--nictrace{} on".format(adapter_number + 1))
|
||||
await self._modify_vm('--nictracefile{} "{}"'.format(adapter_number + 1, nio.pcap_output_file))
|
||||
|
||||
if not self._ethernet_adapters[adapter_number].get_nio(0):
|
||||
yield from self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
|
||||
for adapter_number in range(self._adapters, self._maximum_adapters):
|
||||
log.debug("disabling remaining adapter {}".format(adapter_number))
|
||||
yield from self._modify_vm("--nic{} none".format(adapter_number + 1))
|
||||
await self._modify_vm("--nic{} none".format(adapter_number + 1))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _create_linked_clone(self):
|
||||
async def _create_linked_clone(self):
|
||||
"""
|
||||
Creates a new linked clone.
|
||||
"""
|
||||
|
||||
gns3_snapshot_exists = False
|
||||
vm_info = yield from self._get_vm_info()
|
||||
vm_info = await self._get_vm_info()
|
||||
for entry, value in vm_info.items():
|
||||
if entry.startswith("SnapshotName") and value == "GNS3 Linked Base for clones":
|
||||
gns3_snapshot_exists = True
|
||||
|
||||
if not gns3_snapshot_exists:
|
||||
result = yield from self.manager.execute("snapshot", [self._vmname, "take", "GNS3 Linked Base for clones"])
|
||||
result = await self.manager.execute("snapshot", [self._vmname, "take", "GNS3 Linked Base for clones"])
|
||||
log.debug("GNS3 snapshot created: {}".format(result))
|
||||
|
||||
args = [self._vmname,
|
||||
@ -926,17 +901,17 @@ class VirtualBoxVM(BaseNode):
|
||||
self.working_dir,
|
||||
"--register"]
|
||||
|
||||
result = yield from self.manager.execute("clonevm", args)
|
||||
result = await self.manager.execute("clonevm", args)
|
||||
log.debug("VirtualBox VM: {} cloned".format(result))
|
||||
|
||||
self._vmname = self._name
|
||||
yield from self.manager.execute("setextradata", [self._vmname, "GNS3/Clone", "yes"])
|
||||
await self.manager.execute("setextradata", [self._vmname, "GNS3/Clone", "yes"])
|
||||
|
||||
# We create a reset snapshot in order to simplify life of user who want to rollback their VM
|
||||
# Warning: Do not document this it's seem buggy we keep it because Raizo students use it.
|
||||
try:
|
||||
args = [self._vmname, "take", "reset"]
|
||||
result = yield from self.manager.execute("snapshot", args)
|
||||
result = await self.manager.execute("snapshot", args)
|
||||
log.debug("Snapshot 'reset' created: {}".format(result))
|
||||
# It seem sometimes this failed due to internal race condition of Vbox
|
||||
# we have no real explanation of this.
|
||||
@ -945,8 +920,7 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
os.makedirs(os.path.join(self.working_dir, self._vmname), exist_ok=True)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_console(self):
|
||||
async def _start_console(self):
|
||||
"""
|
||||
Starts remote console support for this VM.
|
||||
"""
|
||||
@ -954,7 +928,7 @@ class VirtualBoxVM(BaseNode):
|
||||
if self.console and self.console_type == "telnet":
|
||||
pipe_name = self._get_pipe_name()
|
||||
try:
|
||||
self._remote_pipe = yield from asyncio_open_serial(pipe_name)
|
||||
self._remote_pipe = await asyncio_open_serial(pipe_name)
|
||||
except OSError as e:
|
||||
raise VirtualBoxError("Could not open serial pipe '{}': {}".format(pipe_name, e))
|
||||
server = AsyncioTelnetServer(reader=self._remote_pipe,
|
||||
@ -962,18 +936,17 @@ class VirtualBoxVM(BaseNode):
|
||||
binary=True,
|
||||
echo=True)
|
||||
try:
|
||||
self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
self._telnet_server = await asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
except OSError as e:
|
||||
self.project.emit("log.warning", {"message": "Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e)})
|
||||
|
||||
@asyncio.coroutine
|
||||
def _stop_remote_console(self):
|
||||
async def _stop_remote_console(self):
|
||||
"""
|
||||
Stops remote console support for this VM.
|
||||
"""
|
||||
if self._telnet_server:
|
||||
self._telnet_server.close()
|
||||
yield from self._telnet_server.wait_closed()
|
||||
await self._telnet_server.wait_closed()
|
||||
self._remote_pipe.close()
|
||||
self._telnet_server = None
|
||||
|
||||
@ -990,8 +963,7 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
super(VirtualBoxVM, VirtualBoxVM).console_type.__set__(self, new_console_type)
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Adds an adapter NIO binding.
|
||||
|
||||
@ -1006,7 +978,7 @@ class VirtualBoxVM(BaseNode):
|
||||
adapter_number=adapter_number))
|
||||
|
||||
# check if trying to connect to a nat, bridged, host-only or any other special adapter
|
||||
nic_attachments = yield from self._get_nic_attachements(self._maximum_adapters)
|
||||
nic_attachments = await self._get_nic_attachements(self._maximum_adapters)
|
||||
attachment = nic_attachments[adapter_number]
|
||||
if attachment in ("nat", "bridged", "intnet", "hostonly", "natnetwork"):
|
||||
if not self._use_any_adapter:
|
||||
@ -1018,21 +990,21 @@ class VirtualBoxVM(BaseNode):
|
||||
# dynamically configure an UDP tunnel attachment if the VM is already running
|
||||
local_nio = self._local_udp_tunnels[adapter_number][0]
|
||||
if local_nio and isinstance(local_nio, NIOUDP):
|
||||
yield from self._control_vm("nic{} generic UDPTunnel".format(adapter_number + 1))
|
||||
yield from self._control_vm("nicproperty{} sport={}".format(adapter_number + 1, local_nio.lport))
|
||||
yield from self._control_vm("nicproperty{} dest={}".format(adapter_number + 1, local_nio.rhost))
|
||||
yield from self._control_vm("nicproperty{} dport={}".format(adapter_number + 1, local_nio.rport))
|
||||
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
await self._control_vm("nic{} generic UDPTunnel".format(adapter_number + 1))
|
||||
await self._control_vm("nicproperty{} sport={}".format(adapter_number + 1, local_nio.lport))
|
||||
await self._control_vm("nicproperty{} dest={}".format(adapter_number + 1, local_nio.rhost))
|
||||
await self._control_vm("nicproperty{} dport={}".format(adapter_number + 1, local_nio.rport))
|
||||
await self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
|
||||
if self.is_running():
|
||||
try:
|
||||
yield from self.add_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
|
||||
await self.add_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
|
||||
self._local_udp_tunnels[adapter_number][1],
|
||||
nio)
|
||||
except KeyError:
|
||||
raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format(name=self.name,
|
||||
adapter_number=adapter_number))
|
||||
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
await self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
|
||||
adapter.add_nio(0, nio)
|
||||
log.info("VirtualBox VM '{name}' [{id}]: {nio} added to adapter {adapter_number}".format(name=self.name,
|
||||
@ -1040,8 +1012,7 @@ class VirtualBoxVM(BaseNode):
|
||||
nio=nio,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Update a port NIO binding.
|
||||
|
||||
@ -1051,21 +1022,20 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
if self.is_running():
|
||||
try:
|
||||
yield from self.update_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
|
||||
await self.update_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
|
||||
self._local_udp_tunnels[adapter_number][1],
|
||||
nio)
|
||||
if nio.suspend:
|
||||
yield from self._control_vm("setlinkstate{} off".format(adapter_number + 1))
|
||||
await self._control_vm("setlinkstate{} off".format(adapter_number + 1))
|
||||
else:
|
||||
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
await self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
except IndexError:
|
||||
raise VirtualBoxError('Adapter {adapter_number} does not exist on VirtualBox VM "{name}"'.format(
|
||||
name=self._name,
|
||||
adapter_number=adapter_number
|
||||
))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_remove_nio_binding(self, adapter_number):
|
||||
async def adapter_remove_nio_binding(self, adapter_number):
|
||||
"""
|
||||
Removes an adapter NIO binding.
|
||||
|
||||
@ -1081,10 +1051,10 @@ class VirtualBoxVM(BaseNode):
|
||||
adapter_number=adapter_number))
|
||||
|
||||
if self.is_running():
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name="VBOX-{}-{}".format(self._id, adapter_number)))
|
||||
vm_state = yield from self._get_vm_state()
|
||||
await self._ubridge_send("bridge delete {name}".format(name="VBOX-{}-{}".format(self._id, adapter_number)))
|
||||
vm_state = await self._get_vm_state()
|
||||
if vm_state == "running":
|
||||
yield from self._control_vm("setlinkstate{} off".format(adapter_number + 1))
|
||||
await self._control_vm("setlinkstate{} off".format(adapter_number + 1))
|
||||
|
||||
nio = adapter.get_nio(0)
|
||||
if isinstance(nio, NIOUDP):
|
||||
@ -1103,8 +1073,7 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
return self.ubridge is not None
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, adapter_number, output_file):
|
||||
async def start_capture(self, adapter_number, output_file):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -1129,14 +1098,14 @@ class VirtualBoxVM(BaseNode):
|
||||
nio.startPacketCapture(output_file)
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="VBOX-{}-{}".format(self._id, adapter_number),
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="VBOX-{}-{}".format(self._id, adapter_number),
|
||||
output_file=output_file))
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}]: starting packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
def stop_capture(self, adapter_number):
|
||||
async def stop_capture(self, adapter_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@ -1157,7 +1126,7 @@ class VirtualBoxVM(BaseNode):
|
||||
nio.stopPacketCapture()
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge stop_capture {name}'.format(name="VBOX-{}-{}".format(self._id, adapter_number)))
|
||||
await self._ubridge_send('bridge stop_capture {name}'.format(name="VBOX-{}-{}".format(self._id, adapter_number)))
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
|
@ -32,7 +32,6 @@ import shlex
|
||||
from collections import OrderedDict
|
||||
from gns3server.utils.interfaces import interfaces
|
||||
from gns3server.utils.asyncio import subprocess_check_output
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
from gns3server.utils import parse_version
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -40,7 +39,6 @@ log = logging.getLogger(__name__)
|
||||
from gns3server.compute.base_manager import BaseManager
|
||||
from gns3server.compute.vmware.vmware_vm import VMwareVM
|
||||
from gns3server.compute.vmware.vmware_error import VMwareError
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
|
||||
|
||||
class VMware(BaseManager):
|
||||
@ -142,8 +140,7 @@ class VMware(BaseManager):
|
||||
version = match.group(1)
|
||||
return version
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_vmware_player_requirements(self, player_version):
|
||||
async def _check_vmware_player_requirements(self, player_version):
|
||||
"""
|
||||
Check minimum requirements to use VMware Player.
|
||||
|
||||
@ -159,17 +156,16 @@ class VMware(BaseManager):
|
||||
if player_version < 6:
|
||||
raise VMwareError("Using VMware Player requires version 6 or above")
|
||||
elif player_version == 6:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.13.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.13.0")
|
||||
elif player_version == 7:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.14.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.14.0")
|
||||
elif player_version >= 12:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.15.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.15.0")
|
||||
elif player_version >= 14:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.17.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.17.0")
|
||||
self._host_type = "player"
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_vmware_workstation_requirements(self, ws_version):
|
||||
async def _check_vmware_workstation_requirements(self, ws_version):
|
||||
"""
|
||||
Check minimum requirements to use VMware Workstation.
|
||||
|
||||
@ -185,17 +181,16 @@ class VMware(BaseManager):
|
||||
if ws_version < 10:
|
||||
raise VMwareError("Using VMware Workstation requires version 10 or above")
|
||||
elif ws_version == 10:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.13.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.13.0")
|
||||
elif ws_version == 11:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.14.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.14.0")
|
||||
elif ws_version >= 12:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.15.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.15.0")
|
||||
elif ws_version >= 14:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.17.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.17.0")
|
||||
self._host_type = "ws"
|
||||
|
||||
@asyncio.coroutine
|
||||
def check_vmware_version(self):
|
||||
async def check_vmware_version(self):
|
||||
"""
|
||||
Check VMware version
|
||||
"""
|
||||
@ -207,13 +202,13 @@ class VMware(BaseManager):
|
||||
player_version = self._find_vmware_version_registry(r"SOFTWARE\Wow6432Node\VMware, Inc.\VMware Player")
|
||||
if player_version:
|
||||
log.debug("VMware Player version {} detected".format(player_version))
|
||||
yield from self._check_vmware_player_requirements(player_version)
|
||||
await self._check_vmware_player_requirements(player_version)
|
||||
else:
|
||||
log.warning("Could not find VMware version")
|
||||
self._host_type = "ws"
|
||||
else:
|
||||
log.debug("VMware Workstation version {} detected".format(ws_version))
|
||||
yield from self._check_vmware_workstation_requirements(ws_version)
|
||||
await self._check_vmware_workstation_requirements(ws_version)
|
||||
else:
|
||||
if sys.platform.startswith("darwin"):
|
||||
if not os.path.isdir("/Applications/VMware Fusion.app"):
|
||||
@ -226,20 +221,20 @@ class VMware(BaseManager):
|
||||
raise VMwareError("VMware is not installed (vmware or vmplayer executable could not be found in $PATH)")
|
||||
|
||||
try:
|
||||
output = yield from subprocess_check_output(vmware_path, "-v")
|
||||
output = await subprocess_check_output(vmware_path, "-v")
|
||||
match = re.search("VMware Workstation ([0-9]+)\.", output)
|
||||
version = None
|
||||
if match:
|
||||
# VMware Workstation has been detected
|
||||
version = match.group(1)
|
||||
log.debug("VMware Workstation version {} detected".format(version))
|
||||
yield from self._check_vmware_workstation_requirements(version)
|
||||
await self._check_vmware_workstation_requirements(version)
|
||||
match = re.search("VMware Player ([0-9]+)\.", output)
|
||||
if match:
|
||||
# VMware Player has been detected
|
||||
version = match.group(1)
|
||||
log.debug("VMware Player version {} detected".format(version))
|
||||
yield from self._check_vmware_player_requirements(version)
|
||||
await self._check_vmware_player_requirements(version)
|
||||
if version is None:
|
||||
log.warning("Could not find VMware version. Output of VMware: {}".format(output))
|
||||
raise VMwareError("Could not find VMware version. Output of VMware: {}".format(output))
|
||||
@ -365,28 +360,26 @@ class VMware(BaseManager):
|
||||
|
||||
return self._host_type
|
||||
|
||||
@asyncio.coroutine
|
||||
def execute(self, subcommand, args, timeout=120, log_level=logging.INFO):
|
||||
async def execute(self, subcommand, args, timeout=120, log_level=logging.INFO):
|
||||
trial = 2
|
||||
|
||||
while True:
|
||||
try:
|
||||
return (yield from self._execute(subcommand, args, timeout=timeout, log_level=log_level))
|
||||
return (await self._execute(subcommand, args, timeout=timeout, log_level=log_level))
|
||||
except VMwareError as e:
|
||||
# We can fail to detect that it's VMware player instead of Workstation (due to marketing change Player is now Player Workstation)
|
||||
if self.host_type == "ws" and "VIX_SERVICEPROVIDER_VMWARE_WORKSTATION" in str(e):
|
||||
self._host_type = "player"
|
||||
return (yield from self._execute(subcommand, args, timeout=timeout, log_level=log_level))
|
||||
return (await self._execute(subcommand, args, timeout=timeout, log_level=log_level))
|
||||
else:
|
||||
if trial <= 0:
|
||||
raise e
|
||||
trial -= 1
|
||||
yield from asyncio.sleep(0.5)
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _execute(self, subcommand, args, timeout=120, log_level=logging.INFO):
|
||||
async def _execute(self, subcommand, args, timeout=120, log_level=logging.INFO):
|
||||
if self.host_type is None:
|
||||
yield from self.check_vmware_version()
|
||||
await self.check_vmware_version()
|
||||
|
||||
vmrun_path = self.vmrun_path
|
||||
if not vmrun_path:
|
||||
@ -397,12 +390,12 @@ class VMware(BaseManager):
|
||||
command_string = " ".join([shlex.quote(c) for c in command])
|
||||
log.log(log_level, "Executing vmrun with command: {}".format(command_string))
|
||||
try:
|
||||
process = yield from asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
|
||||
process = await asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
raise VMwareError("Could not execute vmrun: {}".format(e))
|
||||
|
||||
try:
|
||||
stdout_data, _ = yield from asyncio.wait_for(process.communicate(), timeout=timeout)
|
||||
stdout_data, _ = await asyncio.wait_for(process.communicate(), timeout=timeout)
|
||||
except asyncio.TimeoutError:
|
||||
raise VMwareError("vmrun has timed out after {} seconds!\nTry to run {} in a terminal to see more details.\n\nMake sure GNS3 and VMware run under the same user and whitelist vmrun.exe in your antivirus.".format(timeout, command_string))
|
||||
|
||||
@ -413,8 +406,7 @@ class VMware(BaseManager):
|
||||
|
||||
return stdout_data.decode("utf-8", errors="ignore").splitlines()
|
||||
|
||||
@asyncio.coroutine
|
||||
def check_vmrun_version(self, minimum_required_version="1.13.0"):
|
||||
async def check_vmrun_version(self, minimum_required_version="1.13.0"):
|
||||
"""
|
||||
Checks the vmrun version.
|
||||
|
||||
@ -431,7 +423,7 @@ class VMware(BaseManager):
|
||||
vmrun_path = self.find_vmrun()
|
||||
|
||||
try:
|
||||
output = yield from subprocess_check_output(vmrun_path)
|
||||
output = await subprocess_check_output(vmrun_path)
|
||||
match = re.search("vmrun version ([0-9\.]+)", output)
|
||||
version = None
|
||||
if match:
|
||||
@ -446,15 +438,14 @@ class VMware(BaseManager):
|
||||
log.error("Error while looking for the VMware vmrun version: {}".format(e))
|
||||
raise VMwareError("Error while looking for the VMware vmrun version: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_from_vmware_inventory(self, vmx_path):
|
||||
async def remove_from_vmware_inventory(self, vmx_path):
|
||||
"""
|
||||
Removes a linked clone from the VMware inventory file.
|
||||
|
||||
:param vmx_path: path of the linked clone VMX file
|
||||
"""
|
||||
|
||||
with (yield from self._vmware_inventory_lock):
|
||||
async with self._vmware_inventory_lock:
|
||||
inventory_path = self.get_vmware_inventory_path()
|
||||
if os.path.exists(inventory_path):
|
||||
try:
|
||||
@ -677,14 +668,13 @@ class VMware(BaseManager):
|
||||
else:
|
||||
return [os.path.expanduser("~/vmware")]
|
||||
|
||||
@asyncio.coroutine
|
||||
def list_vms(self):
|
||||
async def list_vms(self):
|
||||
"""
|
||||
Gets VMware VM list.
|
||||
"""
|
||||
|
||||
# check for the right VMware version
|
||||
yield from self.check_vmware_version()
|
||||
await self.check_vmware_version()
|
||||
vmware_vms = []
|
||||
inventory_path = self.get_vmware_inventory_path()
|
||||
if os.path.exists(inventory_path) and self.host_type != "player":
|
||||
@ -746,4 +736,4 @@ if __name__ == '__main__':
|
||||
loop = asyncio.get_event_loop()
|
||||
vmware = VMware.instance()
|
||||
print("=> Check version")
|
||||
loop.run_until_complete(asyncio_ensure_future(vmware.check_vmware_version()))
|
||||
loop.run_until_complete(asyncio.ensure_future(vmware.check_vmware_version()))
|
||||
|
@ -95,12 +95,11 @@ class VMwareVM(BaseNode):
|
||||
return self._vmnets
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def _control_vm(self, subcommand, *additional_args):
|
||||
async def _control_vm(self, subcommand, *additional_args):
|
||||
|
||||
args = [self._vmx_path]
|
||||
args.extend(additional_args)
|
||||
result = yield from self.manager.execute(subcommand, args)
|
||||
result = await self.manager.execute(subcommand, args)
|
||||
log.debug("Control VM '{}' result: {}".format(subcommand, result))
|
||||
return result
|
||||
|
||||
@ -124,16 +123,14 @@ class VMwareVM(BaseNode):
|
||||
except OSError as e:
|
||||
raise VMwareError('Could not write VMware VMX file "{}": {}'.format(self._vmx_path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def is_running(self):
|
||||
async def is_running(self):
|
||||
|
||||
result = yield from self.manager.execute("list", [])
|
||||
result = await self.manager.execute("list", [])
|
||||
if self._vmx_path in result:
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_duplicate_linked_clone(self):
|
||||
async def _check_duplicate_linked_clone(self):
|
||||
"""
|
||||
Without linked clone two VM using the same image can't run
|
||||
at the same time.
|
||||
@ -157,17 +154,16 @@ class VMwareVM(BaseNode):
|
||||
if not found:
|
||||
return
|
||||
trial += 1
|
||||
yield from asyncio.sleep(1)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Creates this VM and handle linked clones.
|
||||
"""
|
||||
if not self.linked_clone:
|
||||
yield from self._check_duplicate_linked_clone()
|
||||
await self._check_duplicate_linked_clone()
|
||||
|
||||
yield from self.manager.check_vmrun_version()
|
||||
await self.manager.check_vmrun_version()
|
||||
if self.linked_clone and not os.path.exists(os.path.join(self.working_dir, os.path.basename(self._vmx_path))):
|
||||
if self.manager.host_type == "player":
|
||||
raise VMwareError("Linked clones are not supported by VMware Player")
|
||||
@ -187,11 +183,11 @@ class VMwareVM(BaseNode):
|
||||
break
|
||||
if not gns3_snapshot_exists:
|
||||
log.info("Creating snapshot '{}'".format(base_snapshot_name))
|
||||
yield from self._control_vm("snapshot", base_snapshot_name)
|
||||
await self._control_vm("snapshot", base_snapshot_name)
|
||||
|
||||
# create the linked clone based on the base snapshot
|
||||
new_vmx_path = os.path.join(self.working_dir, self.name + ".vmx")
|
||||
yield from self._control_vm("clone",
|
||||
await self._control_vm("clone",
|
||||
new_vmx_path,
|
||||
"linked",
|
||||
"-snapshot={}".format(base_snapshot_name),
|
||||
@ -323,8 +319,7 @@ class VMwareVM(BaseNode):
|
||||
raise VMwareError("vnet {} not in VMX file".format(vnet))
|
||||
return vnet
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_ubridge_connection(self, nio, adapter_number):
|
||||
async def _add_ubridge_connection(self, nio, adapter_number):
|
||||
"""
|
||||
Creates a connection in uBridge.
|
||||
|
||||
@ -333,30 +328,29 @@ class VMwareVM(BaseNode):
|
||||
"""
|
||||
|
||||
vnet = self._get_vnet(adapter_number)
|
||||
yield from self._ubridge_send("bridge create {name}".format(name=vnet))
|
||||
await self._ubridge_send("bridge create {name}".format(name=vnet))
|
||||
vmnet_interface = os.path.basename(self._vmx_pairs[vnet])
|
||||
|
||||
if sys.platform.startswith("darwin"):
|
||||
# special case on OSX, we cannot bind VMnet interfaces using the libpcap
|
||||
yield from self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet, interface=vmnet_interface))
|
||||
await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet, interface=vmnet_interface))
|
||||
else:
|
||||
block_host_traffic = self.manager.config.get_section_config("VMware").getboolean("block_host_traffic", False)
|
||||
yield from self._add_ubridge_ethernet_connection(vnet, vmnet_interface, block_host_traffic)
|
||||
await self._add_ubridge_ethernet_connection(vnet, vmnet_interface, block_host_traffic)
|
||||
|
||||
if isinstance(nio, NIOUDP):
|
||||
yield from self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=vnet,
|
||||
await self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=vnet,
|
||||
lport=nio.lport,
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport))
|
||||
|
||||
if nio.capturing:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{pcap_file}"'.format(name=vnet, pcap_file=nio.pcap_output_file))
|
||||
await self._ubridge_send('bridge start_capture {name} "{pcap_file}"'.format(name=vnet, pcap_file=nio.pcap_output_file))
|
||||
|
||||
yield from self._ubridge_send('bridge start {name}'.format(name=vnet))
|
||||
yield from self._ubridge_apply_filters(vnet, nio.filters)
|
||||
await self._ubridge_send('bridge start {name}'.format(name=vnet))
|
||||
await self._ubridge_apply_filters(vnet, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _update_ubridge_connection(self, adapter_number, nio):
|
||||
async def _update_ubridge_connection(self, adapter_number, nio):
|
||||
"""
|
||||
Update a connection in uBridge.
|
||||
|
||||
@ -367,10 +361,9 @@ class VMwareVM(BaseNode):
|
||||
bridge_name = self._get_vnet(adapter_number)
|
||||
except VMwareError:
|
||||
return # vnet not yet available
|
||||
yield from self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
await self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _delete_ubridge_connection(self, adapter_number):
|
||||
async def _delete_ubridge_connection(self, adapter_number):
|
||||
"""
|
||||
Deletes a connection in uBridge.
|
||||
|
||||
@ -380,10 +373,9 @@ class VMwareVM(BaseNode):
|
||||
vnet = "ethernet{}.vnet".format(adapter_number)
|
||||
if vnet not in self._vmx_pairs:
|
||||
raise VMwareError("vnet {} not in VMX file".format(vnet))
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name=vnet))
|
||||
await self._ubridge_send("bridge delete {name}".format(name=vnet))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_ubridge_capture(self, adapter_number, output_file):
|
||||
async def _start_ubridge_capture(self, adapter_number, output_file):
|
||||
"""
|
||||
Start a packet capture in uBridge.
|
||||
|
||||
@ -396,11 +388,10 @@ class VMwareVM(BaseNode):
|
||||
raise VMwareError("vnet {} not in VMX file".format(vnet))
|
||||
if not self._ubridge_hypervisor:
|
||||
raise VMwareError("Cannot start the packet capture: uBridge is not running")
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name=vnet,
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name=vnet,
|
||||
output_file=output_file))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _stop_ubridge_capture(self, adapter_number):
|
||||
async def _stop_ubridge_capture(self, adapter_number):
|
||||
"""
|
||||
Stop a packet capture in uBridge.
|
||||
|
||||
@ -412,7 +403,7 @@ class VMwareVM(BaseNode):
|
||||
raise VMwareError("vnet {} not in VMX file".format(vnet))
|
||||
if not self._ubridge_hypervisor:
|
||||
raise VMwareError("Cannot stop the packet capture: uBridge is not running")
|
||||
yield from self._ubridge_send("bridge stop_capture {name}".format(name=vnet))
|
||||
await self._ubridge_send("bridge stop_capture {name}".format(name=vnet))
|
||||
|
||||
def check_hw_virtualization(self):
|
||||
"""
|
||||
@ -426,8 +417,7 @@ class VMwareVM(BaseNode):
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts this VMware VM.
|
||||
"""
|
||||
@ -435,14 +425,14 @@ class VMwareVM(BaseNode):
|
||||
if self.status == "started":
|
||||
return
|
||||
|
||||
if (yield from self.is_running()):
|
||||
if (await self.is_running()):
|
||||
raise VMwareError("The VM is already running in VMware")
|
||||
|
||||
ubridge_path = self.ubridge_path
|
||||
if not ubridge_path or not os.path.isfile(ubridge_path):
|
||||
raise VMwareError("ubridge is necessary to start a VMware VM")
|
||||
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
self._read_vmx_file()
|
||||
# check if there is enough RAM to run
|
||||
if "memsize" in self._vmx_pairs:
|
||||
@ -452,20 +442,20 @@ class VMwareVM(BaseNode):
|
||||
self._write_vmx_file()
|
||||
|
||||
if self._headless:
|
||||
yield from self._control_vm("start", "nogui")
|
||||
await self._control_vm("start", "nogui")
|
||||
else:
|
||||
yield from self._control_vm("start")
|
||||
await self._control_vm("start")
|
||||
|
||||
try:
|
||||
if self._ubridge_hypervisor:
|
||||
for adapter_number in range(0, self._adapters):
|
||||
nio = self._ethernet_adapters[adapter_number].get_nio(0)
|
||||
if nio:
|
||||
yield from self._add_ubridge_connection(nio, adapter_number)
|
||||
await self._add_ubridge_connection(nio, adapter_number)
|
||||
|
||||
yield from self._start_console()
|
||||
await self._start_console()
|
||||
except VMwareError:
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
raise
|
||||
|
||||
if self._get_vmx_setting("vhv.enable", "TRUE"):
|
||||
@ -475,25 +465,24 @@ class VMwareVM(BaseNode):
|
||||
self.status = "started"
|
||||
log.info("VMware VM '{name}' [{id}] started".format(name=self.name, id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops this VMware VM.
|
||||
"""
|
||||
|
||||
self._hw_virtualization = False
|
||||
yield from self._stop_remote_console()
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_remote_console()
|
||||
await self._stop_ubridge()
|
||||
|
||||
try:
|
||||
if (yield from self.is_running()):
|
||||
if (await self.is_running()):
|
||||
if self.on_close == "save_vm_state":
|
||||
yield from self._control_vm("suspend")
|
||||
await self._control_vm("suspend")
|
||||
elif self.on_close == "shutdown_signal":
|
||||
# use ACPI to shutdown the VM
|
||||
yield from self._control_vm("stop", "soft")
|
||||
await self._control_vm("stop", "soft")
|
||||
else:
|
||||
yield from self._control_vm("stop")
|
||||
await self._control_vm("stop")
|
||||
finally:
|
||||
self._started = False
|
||||
self.status = "stopped"
|
||||
@ -519,49 +508,45 @@ class VMwareVM(BaseNode):
|
||||
self._vmx_pairs["ethernet{}.startconnected".format(adapter_number)] = "TRUE"
|
||||
self._write_vmx_file()
|
||||
|
||||
yield from super().stop()
|
||||
await super().stop()
|
||||
log.info("VMware VM '{name}' [{id}] stopped".format(name=self.name, id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def suspend(self):
|
||||
async def suspend(self):
|
||||
"""
|
||||
Suspends this VMware VM.
|
||||
"""
|
||||
|
||||
if self.manager.host_type != "ws":
|
||||
raise VMwareError("Pausing a VM is only supported by VMware Workstation")
|
||||
yield from self._control_vm("pause")
|
||||
await self._control_vm("pause")
|
||||
self.status = "suspended"
|
||||
log.info("VMware VM '{name}' [{id}] paused".format(name=self.name, id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def resume(self):
|
||||
async def resume(self):
|
||||
"""
|
||||
Resumes this VMware VM.
|
||||
"""
|
||||
|
||||
if self.manager.host_type != "ws":
|
||||
raise VMwareError("Unpausing a VM is only supported by VMware Workstation")
|
||||
yield from self._control_vm("unpause")
|
||||
await self._control_vm("unpause")
|
||||
self.status = "started"
|
||||
log.info("VMware VM '{name}' [{id}] resumed".format(name=self.name, id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def reload(self):
|
||||
async def reload(self):
|
||||
"""
|
||||
Reloads this VMware VM.
|
||||
"""
|
||||
|
||||
yield from self._control_vm("reset")
|
||||
await self._control_vm("reset")
|
||||
log.info("VMware VM '{name}' [{id}] reloaded".format(name=self.name, id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this VMware VM.
|
||||
"""
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
for adapter in self._ethernet_adapters.values():
|
||||
@ -571,12 +556,12 @@ class VMwareVM(BaseNode):
|
||||
self.manager.port_manager.release_udp_port(nio.lport, self._project)
|
||||
try:
|
||||
self.on_close = "power_off"
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
except VMwareError:
|
||||
pass
|
||||
|
||||
if self.linked_clone:
|
||||
yield from self.manager.remove_from_vmware_inventory(self._vmx_path)
|
||||
await self.manager.remove_from_vmware_inventory(self._vmx_path)
|
||||
|
||||
@property
|
||||
def headless(self):
|
||||
@ -722,8 +707,7 @@ class VMwareVM(BaseNode):
|
||||
log.info("VMware VM '{name}' [{id}] is not allowed to use any adapter".format(name=self.name, id=self.id))
|
||||
self._use_any_adapter = use_any_adapter
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Adds an adapter NIO binding.
|
||||
|
||||
@ -743,7 +727,7 @@ class VMwareVM(BaseNode):
|
||||
# check for the connection type
|
||||
connection_type = "ethernet{}.connectiontype".format(adapter_number)
|
||||
if not self._use_any_adapter and connection_type in self._vmx_pairs and self._vmx_pairs[connection_type] in ("nat", "bridged", "hostonly"):
|
||||
if (yield from self.is_running()):
|
||||
if (await self.is_running()):
|
||||
raise VMwareError("Attachment '{attachment}' is configured on network adapter {adapter_number}. "
|
||||
"Please stop VMware VM '{name}' to link to this adapter and allow GNS3 to change the attachment type.".format(attachment=self._vmx_pairs[connection_type],
|
||||
adapter_number=adapter_number,
|
||||
@ -757,15 +741,14 @@ class VMwareVM(BaseNode):
|
||||
|
||||
adapter.add_nio(0, nio)
|
||||
if self._started and self._ubridge_hypervisor:
|
||||
yield from self._add_ubridge_connection(nio, adapter_number)
|
||||
await self._add_ubridge_connection(nio, adapter_number)
|
||||
|
||||
log.info("VMware 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_update_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Update a port NIO binding.
|
||||
|
||||
@ -775,15 +758,14 @@ class VMwareVM(BaseNode):
|
||||
|
||||
if self._ubridge_hypervisor:
|
||||
try:
|
||||
yield from self._update_ubridge_connection(adapter_number, nio)
|
||||
await self._update_ubridge_connection(adapter_number, nio)
|
||||
except IndexError:
|
||||
raise VMwareError('Adapter {adapter_number} does not exist on VMware VM "{name}"'.format(
|
||||
name=self._name,
|
||||
adapter_number=adapter_number
|
||||
))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_remove_nio_binding(self, adapter_number):
|
||||
async def adapter_remove_nio_binding(self, adapter_number):
|
||||
"""
|
||||
Removes an adapter NIO binding.
|
||||
|
||||
@ -803,7 +785,7 @@ class VMwareVM(BaseNode):
|
||||
self.manager.port_manager.release_udp_port(nio.lport, self._project)
|
||||
adapter.remove_nio(0)
|
||||
if self._started and self._ubridge_hypervisor:
|
||||
yield from self._delete_ubridge_connection(adapter_number)
|
||||
await self._delete_ubridge_connection(adapter_number)
|
||||
|
||||
log.info("VMware VM '{name}' [{id}]: {nio} removed from adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
@ -842,8 +824,7 @@ class VMwareVM(BaseNode):
|
||||
"serial0.startconnected": "TRUE"}
|
||||
self._vmx_pairs.update(serial_port)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_console(self):
|
||||
async def _start_console(self):
|
||||
"""
|
||||
Starts remote console support for this VM.
|
||||
"""
|
||||
@ -851,7 +832,7 @@ class VMwareVM(BaseNode):
|
||||
if self.console and self.console_type == "telnet":
|
||||
pipe_name = self._get_pipe_name()
|
||||
try:
|
||||
self._remote_pipe = yield from asyncio_open_serial(self._get_pipe_name())
|
||||
self._remote_pipe = await asyncio_open_serial(self._get_pipe_name())
|
||||
except OSError as e:
|
||||
raise VMwareError("Could not open serial pipe '{}': {}".format(pipe_name, e))
|
||||
server = AsyncioTelnetServer(reader=self._remote_pipe,
|
||||
@ -859,18 +840,17 @@ class VMwareVM(BaseNode):
|
||||
binary=True,
|
||||
echo=True)
|
||||
try:
|
||||
self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
self._telnet_server = await asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
except OSError as e:
|
||||
self.project.emit("log.warning", {"message": "Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e)})
|
||||
|
||||
@asyncio.coroutine
|
||||
def _stop_remote_console(self):
|
||||
async def _stop_remote_console(self):
|
||||
"""
|
||||
Stops remote console support for this VM.
|
||||
"""
|
||||
if self._telnet_server:
|
||||
self._telnet_server.close()
|
||||
yield from self._telnet_server.wait_closed()
|
||||
await self._telnet_server.wait_closed()
|
||||
self._remote_pipe.close()
|
||||
self._telnet_server = None
|
||||
|
||||
@ -887,8 +867,7 @@ class VMwareVM(BaseNode):
|
||||
|
||||
super(VMwareVM, VMwareVM).console_type.__set__(self, new_console_type)
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, adapter_number, output_file):
|
||||
async def start_capture(self, adapter_number, output_file):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -913,13 +892,13 @@ class VMwareVM(BaseNode):
|
||||
nio.startPacketCapture(output_file)
|
||||
|
||||
if self._started:
|
||||
yield from self._start_ubridge_capture(adapter_number, output_file)
|
||||
await self._start_ubridge_capture(adapter_number, output_file)
|
||||
|
||||
log.info("VMware VM '{name}' [{id}]: starting packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
def stop_capture(self, adapter_number):
|
||||
async def stop_capture(self, adapter_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@ -940,7 +919,7 @@ class VMwareVM(BaseNode):
|
||||
nio.stopPacketCapture()
|
||||
|
||||
if self._started:
|
||||
yield from self._stop_ubridge_capture(adapter_number)
|
||||
await self._stop_ubridge_capture(adapter_number)
|
||||
|
||||
log.info("VMware VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
|
@ -37,15 +37,14 @@ class VPCS(BaseManager):
|
||||
self._free_mac_ids = {}
|
||||
self._used_mac_ids = {}
|
||||
|
||||
@asyncio.coroutine
|
||||
def create_node(self, *args, **kwargs):
|
||||
async def create_node(self, *args, **kwargs):
|
||||
"""
|
||||
Creates a new VPCS VM.
|
||||
|
||||
:returns: VPCSVM instance
|
||||
"""
|
||||
|
||||
node = yield from super().create_node(*args, **kwargs)
|
||||
node = await super().create_node(*args, **kwargs)
|
||||
self._free_mac_ids.setdefault(node.project.id, list(range(0, 255)))
|
||||
try:
|
||||
self._used_mac_ids[node.id] = self._free_mac_ids[node.project.id].pop(0)
|
||||
@ -53,8 +52,7 @@ class VPCS(BaseManager):
|
||||
raise VPCSError("Cannot create a new VPCS VM (limit of 255 VMs reached on this host)")
|
||||
return node
|
||||
|
||||
@asyncio.coroutine
|
||||
def close_node(self, node_id, *args, **kwargs):
|
||||
async def close_node(self, node_id, *args, **kwargs):
|
||||
"""
|
||||
Closes a VPCS VM.
|
||||
|
||||
@ -66,7 +64,7 @@ class VPCS(BaseManager):
|
||||
i = self._used_mac_ids[node_id]
|
||||
self._free_mac_ids[node.project.id].insert(0, i)
|
||||
del self._used_mac_ids[node_id]
|
||||
yield from super().close_node(node_id, *args, **kwargs)
|
||||
await super().close_node(node_id, *args, **kwargs)
|
||||
return node
|
||||
|
||||
def get_mac_id(self, node_id):
|
||||
|
@ -77,13 +77,12 @@ class VPCSVM(BaseNode):
|
||||
def ethernet_adapter(self):
|
||||
return self._ethernet_adapter
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this VPCS VM.
|
||||
"""
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
nio = self._ethernet_adapter.get_nio(0)
|
||||
@ -95,15 +94,14 @@ class VPCSVM(BaseNode):
|
||||
self.manager.port_manager.release_udp_port(self._local_udp_tunnel[1].lport, self._project)
|
||||
self._local_udp_tunnel = None
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
|
||||
if self.is_running():
|
||||
self._terminate_process()
|
||||
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_requirements(self):
|
||||
async def _check_requirements(self):
|
||||
"""
|
||||
Check if VPCS is available with the correct version.
|
||||
"""
|
||||
@ -121,7 +119,7 @@ class VPCSVM(BaseNode):
|
||||
if not os.access(path, os.X_OK):
|
||||
raise VPCSError("VPCS program '{}' is not executable".format(path))
|
||||
|
||||
yield from self._check_vpcs_version()
|
||||
await self._check_vpcs_version()
|
||||
|
||||
def __json__(self):
|
||||
|
||||
@ -200,13 +198,12 @@ class VPCSVM(BaseNode):
|
||||
except OSError as e:
|
||||
raise VPCSError('Cannot write the startup script file "{}": {}'.format(startup_script_path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_vpcs_version(self):
|
||||
async def _check_vpcs_version(self):
|
||||
"""
|
||||
Checks if the VPCS executable version is >= 0.8b or == 0.6.1.
|
||||
"""
|
||||
try:
|
||||
output = yield from subprocess_check_output(self._vpcs_path(), "-v", cwd=self.working_dir)
|
||||
output = await subprocess_check_output(self._vpcs_path(), "-v", cwd=self.working_dir)
|
||||
match = re.search("Welcome to Virtual PC Simulator, version ([0-9a-z\.]+)", output)
|
||||
if match:
|
||||
version = match.group(1)
|
||||
@ -218,13 +215,12 @@ class VPCSVM(BaseNode):
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
raise VPCSError("Error while looking for the VPCS version: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts the VPCS process.
|
||||
"""
|
||||
|
||||
yield from self._check_requirements()
|
||||
await self._check_requirements()
|
||||
if not self.is_running():
|
||||
nio = self._ethernet_adapter.get_nio(0)
|
||||
command = self._build_command()
|
||||
@ -237,18 +233,18 @@ class VPCSVM(BaseNode):
|
||||
flags = subprocess.CREATE_NEW_PROCESS_GROUP
|
||||
with open(self._vpcs_stdout_file, "w", encoding="utf-8") as fd:
|
||||
self.command_line = ' '.join(command)
|
||||
self._process = yield from asyncio.create_subprocess_exec(*command,
|
||||
self._process = await asyncio.create_subprocess_exec(*command,
|
||||
stdout=fd,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=self.working_dir,
|
||||
creationflags=flags)
|
||||
monitor_process(self._process, self._termination_callback)
|
||||
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
if nio:
|
||||
yield from self.add_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
await self.add_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
|
||||
yield from self.start_wrap_console()
|
||||
await self.start_wrap_console()
|
||||
|
||||
log.info("VPCS instance {} started PID={}".format(self.name, self._process.pid))
|
||||
self._started = True
|
||||
@ -273,18 +269,17 @@ class VPCSVM(BaseNode):
|
||||
if returncode != 0:
|
||||
self.project.emit("log.error", {"message": "VPCS process has stopped, return code: {}\n{}".format(returncode, self.read_vpcs_stdout())})
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the VPCS process.
|
||||
"""
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
if self.is_running():
|
||||
self._terminate_process()
|
||||
if self._process.returncode is None:
|
||||
try:
|
||||
yield from wait_for_process_termination(self._process, timeout=3)
|
||||
await wait_for_process_termination(self._process, timeout=3)
|
||||
except asyncio.TimeoutError:
|
||||
if self._process.returncode is None:
|
||||
try:
|
||||
@ -296,16 +291,15 @@ class VPCSVM(BaseNode):
|
||||
|
||||
self._process = None
|
||||
self._started = False
|
||||
yield from super().stop()
|
||||
await super().stop()
|
||||
|
||||
@asyncio.coroutine
|
||||
def reload(self):
|
||||
async def reload(self):
|
||||
"""
|
||||
Reloads the VPCS process (stop & start).
|
||||
"""
|
||||
|
||||
yield from self.stop()
|
||||
yield from self.start()
|
||||
await self.stop()
|
||||
await self.start()
|
||||
|
||||
def _terminate_process(self):
|
||||
"""
|
||||
@ -364,8 +358,7 @@ class VPCSVM(BaseNode):
|
||||
|
||||
super(VPCSVM, VPCSVM).console_type.__set__(self, new_console_type)
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_add_nio_binding(self, port_number, nio):
|
||||
async def port_add_nio_binding(self, port_number, nio):
|
||||
"""
|
||||
Adds a port NIO binding.
|
||||
|
||||
@ -378,7 +371,7 @@ class VPCSVM(BaseNode):
|
||||
port_number=port_number))
|
||||
|
||||
if self.is_running():
|
||||
yield from self.add_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
await self.add_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
|
||||
self._ethernet_adapter.add_nio(port_number, nio)
|
||||
log.info('VPCS "{name}" [{id}]: {nio} added to port {port_number}'.format(name=self._name,
|
||||
@ -388,16 +381,14 @@ class VPCSVM(BaseNode):
|
||||
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_update_nio_binding(self, port_number, nio):
|
||||
async def port_update_nio_binding(self, port_number, nio):
|
||||
if not self._ethernet_adapter.port_exists(port_number):
|
||||
raise VPCSError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=self._ethernet_adapter,
|
||||
port_number=port_number))
|
||||
if self.is_running():
|
||||
yield from self.update_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
await self.update_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_remove_nio_binding(self, port_number):
|
||||
async def port_remove_nio_binding(self, port_number):
|
||||
"""
|
||||
Removes a port NIO binding.
|
||||
|
||||
@ -411,7 +402,7 @@ class VPCSVM(BaseNode):
|
||||
port_number=port_number))
|
||||
|
||||
if self.is_running():
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name="VPCS-{}".format(self._id)))
|
||||
await self._ubridge_send("bridge delete {name}".format(name="VPCS-{}".format(self._id)))
|
||||
|
||||
nio = self._ethernet_adapter.get_nio(port_number)
|
||||
if isinstance(nio, NIOUDP):
|
||||
@ -424,8 +415,7 @@ class VPCSVM(BaseNode):
|
||||
port_number=port_number))
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file):
|
||||
async def start_capture(self, port_number, output_file):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@ -448,15 +438,14 @@ class VPCSVM(BaseNode):
|
||||
nio.startPacketCapture(output_file)
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="VPCS-{}".format(self._id),
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="VPCS-{}".format(self._id),
|
||||
output_file=output_file))
|
||||
|
||||
log.info("VPCS '{name}' [{id}]: starting packet capture on port {port_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
port_number=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@ -475,7 +464,7 @@ class VPCSVM(BaseNode):
|
||||
nio.stopPacketCapture()
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge stop_capture {name}'.format(name="VPCS-{}".format(self._id)))
|
||||
await self._ubridge_send('bridge stop_capture {name}'.format(name="VPCS-{}".format(self._id)))
|
||||
|
||||
log.info("VPCS '{name}' [{id}]: stopping packet capture on port {port_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
|
@ -65,8 +65,7 @@ class Controller:
|
||||
log.info("Load controller configuration file {}".format(self._config_file))
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def download_appliance_templates(self):
|
||||
async def download_appliance_templates(self):
|
||||
|
||||
session = aiohttp.ClientSession()
|
||||
try:
|
||||
@ -74,7 +73,7 @@ class Controller:
|
||||
if self._appliance_templates_etag:
|
||||
log.info("Checking if appliance templates are up-to-date (ETag {})".format(self._appliance_templates_etag))
|
||||
headers["If-None-Match"] = self._appliance_templates_etag
|
||||
response = yield from session.get('https://api.github.com/repos/GNS3/gns3-registry/contents/appliances', headers=headers)
|
||||
response = await session.get('https://api.github.com/repos/GNS3/gns3-registry/contents/appliances', headers=headers)
|
||||
if response.status == 304:
|
||||
log.info("Appliance templates are already up-to-date (ETag {})".format(self._appliance_templates_etag))
|
||||
return
|
||||
@ -84,19 +83,19 @@ class Controller:
|
||||
if etag:
|
||||
self._appliance_templates_etag = etag
|
||||
self.save()
|
||||
json_data = yield from response.json()
|
||||
json_data = await response.json()
|
||||
response.close()
|
||||
appliances_dir = get_resource('appliances')
|
||||
for appliance in json_data:
|
||||
if appliance["type"] == "file":
|
||||
appliance_name = appliance["name"]
|
||||
log.info("Download appliance template file from '{}'".format(appliance["download_url"]))
|
||||
response = yield from session.get(appliance["download_url"])
|
||||
response = await session.get(appliance["download_url"])
|
||||
if response.status != 200:
|
||||
log.warning("Could not download '{}' due to HTTP error code {}".format(appliance["download_url"], response.status))
|
||||
continue
|
||||
try:
|
||||
appliance_data = yield from response.read()
|
||||
appliance_data = await response.read()
|
||||
except asyncio.TimeoutError:
|
||||
log.warning("Timeout while downloading '{}'".format(appliance["download_url"]))
|
||||
continue
|
||||
@ -215,8 +214,7 @@ class Controller:
|
||||
for b in builtins:
|
||||
self._appliances[b.id] = b
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
|
||||
log.info("Controller is starting")
|
||||
self.load_base_files()
|
||||
@ -235,9 +233,9 @@ class Controller:
|
||||
if name == "gns3vm":
|
||||
name = "Main server"
|
||||
|
||||
computes = yield from self._load_controller_settings()
|
||||
computes = await self._load_controller_settings()
|
||||
try:
|
||||
self._local_server = yield from self.add_compute(compute_id="local",
|
||||
self._local_server = await self.add_compute(compute_id="local",
|
||||
name=name,
|
||||
protocol=server_config.get("protocol", "http"),
|
||||
host=host,
|
||||
@ -251,15 +249,15 @@ class Controller:
|
||||
sys.exit(1)
|
||||
for c in computes:
|
||||
try:
|
||||
yield from self.add_compute(**c)
|
||||
await self.add_compute(**c)
|
||||
except (aiohttp.web.HTTPError, KeyError):
|
||||
pass # Skip not available servers at loading
|
||||
yield from self.load_projects()
|
||||
await self.load_projects()
|
||||
try:
|
||||
yield from self.gns3vm.auto_start_vm()
|
||||
await self.gns3vm.auto_start_vm()
|
||||
except GNS3VMError as e:
|
||||
log.warning(str(e))
|
||||
yield from self._project_auto_open()
|
||||
await self._project_auto_open()
|
||||
|
||||
def _update_config(self):
|
||||
"""
|
||||
@ -271,19 +269,18 @@ class Controller:
|
||||
self._local_server.user = server_config.get("user")
|
||||
self._local_server.password = server_config.get("password")
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
|
||||
log.info("Controller is Stopping")
|
||||
for project in self._projects.values():
|
||||
yield from project.close()
|
||||
await project.close()
|
||||
for compute in self._computes.values():
|
||||
try:
|
||||
yield from compute.close()
|
||||
await compute.close()
|
||||
# We don't care if a compute is down at this step
|
||||
except (ComputeError, aiohttp.web.HTTPError, OSError):
|
||||
pass
|
||||
yield from self.gns3vm.exit_vm()
|
||||
await self.gns3vm.exit_vm()
|
||||
self._computes = {}
|
||||
self._projects = {}
|
||||
|
||||
@ -321,15 +318,14 @@ class Controller:
|
||||
except OSError as e:
|
||||
log.error("Cannnot write configuration file '{}': {}".format(self._config_file, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _load_controller_settings(self):
|
||||
async def _load_controller_settings(self):
|
||||
"""
|
||||
Reload the controller configuration from disk
|
||||
"""
|
||||
|
||||
try:
|
||||
if not os.path.exists(self._config_file):
|
||||
yield from self._import_gns3_gui_conf()
|
||||
await self._import_gns3_gui_conf()
|
||||
self.save()
|
||||
with open(self._config_file) as f:
|
||||
data = json.load(f)
|
||||
@ -350,8 +346,7 @@ class Controller:
|
||||
self.load_appliances()
|
||||
return data.get("computes", [])
|
||||
|
||||
@asyncio.coroutine
|
||||
def load_projects(self):
|
||||
async def load_projects(self):
|
||||
"""
|
||||
Preload the list of projects from disk
|
||||
"""
|
||||
@ -366,7 +361,7 @@ class Controller:
|
||||
for file in os.listdir(project_dir):
|
||||
if file.endswith(".gns3"):
|
||||
try:
|
||||
yield from self.load_project(os.path.join(project_dir, file), load=False)
|
||||
await self.load_project(os.path.join(project_dir, file), load=False)
|
||||
except (aiohttp.web.HTTPConflict, NotImplementedError):
|
||||
pass # Skip not compatible projects
|
||||
except OSError as e:
|
||||
@ -417,8 +412,7 @@ class Controller:
|
||||
os.makedirs(appliances_path, exist_ok=True)
|
||||
return appliances_path
|
||||
|
||||
@asyncio.coroutine
|
||||
def _import_gns3_gui_conf(self):
|
||||
async def _import_gns3_gui_conf(self):
|
||||
"""
|
||||
Import old config from GNS3 GUI
|
||||
"""
|
||||
@ -430,7 +424,7 @@ class Controller:
|
||||
server_settings = data.get("Servers", {})
|
||||
for remote in server_settings.get("remote_servers", []):
|
||||
try:
|
||||
yield from self.add_compute(
|
||||
await self.add_compute(
|
||||
host=remote.get("host", "localhost"),
|
||||
port=remote.get("port", 3080),
|
||||
protocol=remote.get("protocol", "http"),
|
||||
@ -488,8 +482,7 @@ class Controller:
|
||||
self.load_appliances()
|
||||
self.notification.controller_emit("settings.updated", val)
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_compute(self, compute_id=None, name=None, force=False, connect=True, **kwargs):
|
||||
async def add_compute(self, compute_id=None, name=None, force=False, connect=True, **kwargs):
|
||||
"""
|
||||
Add a server to the dictionary of compute servers controlled by this controller
|
||||
|
||||
@ -519,24 +512,23 @@ class Controller:
|
||||
self._computes[compute.id] = compute
|
||||
self.save()
|
||||
if connect:
|
||||
yield from compute.connect()
|
||||
await compute.connect()
|
||||
self.notification.controller_emit("compute.created", compute.__json__())
|
||||
return compute
|
||||
else:
|
||||
if connect:
|
||||
yield from self._computes[compute_id].connect()
|
||||
await self._computes[compute_id].connect()
|
||||
self.notification.controller_emit("compute.updated", self._computes[compute_id].__json__())
|
||||
return self._computes[compute_id]
|
||||
|
||||
@asyncio.coroutine
|
||||
def close_compute_projects(self, compute):
|
||||
async def close_compute_projects(self, compute):
|
||||
"""
|
||||
Close projects running on a compute
|
||||
"""
|
||||
|
||||
for project in self._projects.values():
|
||||
if compute in project.computes:
|
||||
yield from project.close()
|
||||
await project.close()
|
||||
|
||||
def compute_has_open_project(self, compute):
|
||||
"""
|
||||
@ -550,8 +542,7 @@ class Controller:
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete_compute(self, compute_id):
|
||||
async def delete_compute(self, compute_id):
|
||||
"""
|
||||
Delete a compute node. Project using this compute will be close
|
||||
|
||||
@ -562,8 +553,8 @@ class Controller:
|
||||
compute = self.get_compute(compute_id)
|
||||
except aiohttp.web.HTTPNotFound:
|
||||
return
|
||||
yield from self.close_compute_projects(compute)
|
||||
yield from compute.close()
|
||||
await self.close_compute_projects(compute)
|
||||
await compute.close()
|
||||
del self._computes[compute_id]
|
||||
self.save()
|
||||
self.notification.controller_emit("compute.deleted", compute.__json__())
|
||||
@ -603,8 +594,7 @@ class Controller:
|
||||
|
||||
return compute_id in self._computes
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_project(self, project_id=None, name=None, path=None, **kwargs):
|
||||
async def add_project(self, project_id=None, name=None, path=None, **kwargs):
|
||||
"""
|
||||
Creates a project or returns an existing project
|
||||
|
||||
@ -635,8 +625,7 @@ class Controller:
|
||||
except KeyError:
|
||||
raise aiohttp.web.HTTPNotFound(text="Project ID {} doesn't exist".format(project_id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def get_loaded_project(self, project_id):
|
||||
async def get_loaded_project(self, project_id):
|
||||
"""
|
||||
Returns a project or raise a 404 error.
|
||||
|
||||
@ -644,7 +633,7 @@ class Controller:
|
||||
"""
|
||||
|
||||
project = self.get_project(project_id)
|
||||
yield from project.wait_loaded()
|
||||
await project.wait_loaded()
|
||||
return project
|
||||
|
||||
def remove_project(self, project):
|
||||
@ -652,8 +641,7 @@ class Controller:
|
||||
if project.id in self._projects:
|
||||
del self._projects[project.id]
|
||||
|
||||
@asyncio.coroutine
|
||||
def load_project(self, path, load=True):
|
||||
async def load_project(self, path, load=True):
|
||||
"""
|
||||
Load a project from a .gns3
|
||||
|
||||
@ -670,20 +658,19 @@ class Controller:
|
||||
if topo_data["project_id"] in self._projects:
|
||||
project = self._projects[topo_data["project_id"]]
|
||||
else:
|
||||
project = yield from self.add_project(path=os.path.dirname(path), status="closed", filename=os.path.basename(path), **topo_data)
|
||||
project = await self.add_project(path=os.path.dirname(path), status="closed", filename=os.path.basename(path), **topo_data)
|
||||
if load or project.auto_open:
|
||||
yield from project.open()
|
||||
await project.open()
|
||||
return project
|
||||
|
||||
@asyncio.coroutine
|
||||
def _project_auto_open(self):
|
||||
async def _project_auto_open(self):
|
||||
"""
|
||||
Auto open the project with auto open enable
|
||||
"""
|
||||
|
||||
for project in self._projects.values():
|
||||
if project.auto_open:
|
||||
yield from project.open()
|
||||
await project.open()
|
||||
|
||||
def get_free_project_name(self, base_name):
|
||||
"""
|
||||
@ -747,8 +734,7 @@ class Controller:
|
||||
Controller._instance = Controller()
|
||||
return Controller._instance
|
||||
|
||||
@asyncio.coroutine
|
||||
def autoidlepc(self, compute_id, platform, image, ram):
|
||||
async def autoidlepc(self, compute_id, platform, image, ram):
|
||||
"""
|
||||
Compute and IDLE PC value for an image
|
||||
|
||||
@ -761,17 +747,16 @@ class Controller:
|
||||
compute = self.get_compute(compute_id)
|
||||
for project in list(self._projects.values()):
|
||||
if project.name == "AUTOIDLEPC":
|
||||
yield from project.delete()
|
||||
await project.delete()
|
||||
self.remove_project(project)
|
||||
project = yield from self.add_project(name="AUTOIDLEPC")
|
||||
node = yield from project.add_node(compute, "AUTOIDLEPC", str(uuid.uuid4()), node_type="dynamips", platform=platform, image=image, ram=ram)
|
||||
res = yield from node.dynamips_auto_idlepc()
|
||||
yield from project.delete()
|
||||
project = await self.add_project(name="AUTOIDLEPC")
|
||||
node = await project.add_node(compute, "AUTOIDLEPC", str(uuid.uuid4()), node_type="dynamips", platform=platform, image=image, ram=ram)
|
||||
res = await node.dynamips_auto_idlepc()
|
||||
await project.delete()
|
||||
self.remove_project(project)
|
||||
return res
|
||||
|
||||
@asyncio.coroutine
|
||||
def compute_ports(self, compute_id):
|
||||
async def compute_ports(self, compute_id):
|
||||
"""
|
||||
Get the ports used by a compute.
|
||||
|
||||
@ -779,5 +764,5 @@ class Controller:
|
||||
"""
|
||||
|
||||
compute = self.get_compute(compute_id)
|
||||
response = yield from compute.get("/network/ports")
|
||||
response = await compute.get("/network/ports")
|
||||
return response.json
|
||||
|
@ -27,7 +27,7 @@ from operator import itemgetter
|
||||
|
||||
from ..utils import parse_version
|
||||
from ..utils.images import list_images
|
||||
from ..utils.asyncio import locking, asyncio_ensure_future
|
||||
from ..utils.asyncio import locking
|
||||
from ..controller.controller_error import ControllerError
|
||||
from ..version import __version__, __version_info__
|
||||
|
||||
@ -146,18 +146,16 @@ class Compute:
|
||||
"""
|
||||
self._last_error = msg
|
||||
|
||||
@asyncio.coroutine
|
||||
def interfaces(self):
|
||||
async def interfaces(self):
|
||||
"""
|
||||
Get the list of network on compute
|
||||
"""
|
||||
if not self._interfaces_cache:
|
||||
response = yield from self.get("/network/interfaces")
|
||||
response = await self.get("/network/interfaces")
|
||||
self._interfaces_cache = response.json
|
||||
return self._interfaces_cache
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self, **kwargs):
|
||||
async def update(self, **kwargs):
|
||||
for kw in kwargs:
|
||||
if kw not in ("user", "password"):
|
||||
setattr(self, kw, kwargs[kw])
|
||||
@ -170,13 +168,12 @@ class Compute:
|
||||
self._controller.notification.controller_emit("compute.updated", self.__json__())
|
||||
self._controller.save()
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
self._connected = False
|
||||
if self._http_session:
|
||||
self._http_session.close()
|
||||
if self._ws:
|
||||
yield from self._ws.close()
|
||||
await self._ws.close()
|
||||
self._ws = None
|
||||
self._closed = True
|
||||
|
||||
@ -314,8 +311,7 @@ class Compute:
|
||||
"last_error": self._last_error
|
||||
}
|
||||
|
||||
@asyncio.coroutine
|
||||
def download_file(self, project, path):
|
||||
async def download_file(self, project, path):
|
||||
"""
|
||||
Read file of a project and download it
|
||||
|
||||
@ -325,13 +321,12 @@ class Compute:
|
||||
"""
|
||||
|
||||
url = self._getUrl("/projects/{}/files/{}".format(project.id, path))
|
||||
response = yield from self._session().request("GET", url, auth=self._auth)
|
||||
response = await self._session().request("GET", url, auth=self._auth)
|
||||
if response.status == 404:
|
||||
raise aiohttp.web.HTTPNotFound(text="{} not found on compute".format(path))
|
||||
return response
|
||||
|
||||
@asyncio.coroutine
|
||||
def download_image(self, image_type, image):
|
||||
async def download_image(self, image_type, image):
|
||||
"""
|
||||
Read file of a project and download it
|
||||
|
||||
@ -341,13 +336,12 @@ class Compute:
|
||||
"""
|
||||
|
||||
url = self._getUrl("/{}/images/{}".format(image_type, image))
|
||||
response = yield from self._session().request("GET", url, auth=self._auth)
|
||||
response = await self._session().request("GET", url, auth=self._auth)
|
||||
if response.status == 404:
|
||||
raise aiohttp.web.HTTPNotFound(text="{} not found on compute".format(image))
|
||||
return response
|
||||
|
||||
@asyncio.coroutine
|
||||
def stream_file(self, project, path, timeout=None):
|
||||
async def stream_file(self, project, path, timeout=None):
|
||||
"""
|
||||
Read file of a project and stream it
|
||||
|
||||
@ -372,7 +366,7 @@ class Compute:
|
||||
self._response.close()
|
||||
|
||||
url = self._getUrl("/projects/{}/stream/{}".format(project.id, path))
|
||||
response = yield from self._session().request("GET", url, auth=self._auth, timeout=timeout)
|
||||
response = await self._session().request("GET", url, auth=self._auth, timeout=timeout)
|
||||
if response.status == 404:
|
||||
raise aiohttp.web.HTTPNotFound(text="{} not found on compute".format(path))
|
||||
elif response.status == 403:
|
||||
@ -383,34 +377,31 @@ class Compute:
|
||||
path))
|
||||
return StreamResponse(response)
|
||||
|
||||
@asyncio.coroutine
|
||||
def http_query(self, method, path, data=None, dont_connect=False, **kwargs):
|
||||
async def http_query(self, method, path, data=None, dont_connect=False, **kwargs):
|
||||
"""
|
||||
:param dont_connect: If true do not reconnect if not connected
|
||||
"""
|
||||
|
||||
if not self._connected and not dont_connect:
|
||||
if self._id == "vm" and not self._controller.gns3vm.running:
|
||||
yield from self._controller.gns3vm.start()
|
||||
yield from self.connect()
|
||||
await self._controller.gns3vm.start()
|
||||
await self.connect()
|
||||
if not self._connected and not dont_connect:
|
||||
raise ComputeError("Cannot connect to compute '{}' with request {} {}".format(self._name, method, path))
|
||||
response = yield from self._run_http_query(method, path, data=data, **kwargs)
|
||||
response = await self._run_http_query(method, path, data=data, **kwargs)
|
||||
return response
|
||||
|
||||
@asyncio.coroutine
|
||||
def _try_reconnect(self):
|
||||
async def _try_reconnect(self):
|
||||
"""
|
||||
We catch error during reconnect
|
||||
"""
|
||||
try:
|
||||
yield from self.connect()
|
||||
await self.connect()
|
||||
except aiohttp.web.HTTPConflict:
|
||||
pass
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def connect(self):
|
||||
async def connect(self):
|
||||
"""
|
||||
Check if remote server is accessible
|
||||
"""
|
||||
@ -418,7 +409,7 @@ class Compute:
|
||||
if not self._connected and not self._closed and self.host:
|
||||
try:
|
||||
log.info("Connecting to compute '{}'".format(self._id))
|
||||
response = yield from self._run_http_query("GET", "/capabilities")
|
||||
response = await self._run_http_query("GET", "/capabilities")
|
||||
except ComputeError as e:
|
||||
log.warning("Cannot connect to compute '{}': {}".format(self._id, e))
|
||||
# Try to reconnect after 2 seconds if server unavailable only if not during tests (otherwise we create a ressource usage bomb)
|
||||
@ -430,8 +421,8 @@ class Compute:
|
||||
# After 5 failure we close the project using the compute to avoid sync issues
|
||||
if self._connection_failure == 10:
|
||||
log.error("Could not connect to compute '{}' after multiple attempts: {}".format(self._id, e))
|
||||
yield from self._controller.close_compute_projects(self)
|
||||
asyncio.get_event_loop().call_later(2, lambda: asyncio_ensure_future(self._try_reconnect()))
|
||||
await self._controller.close_compute_projects(self)
|
||||
asyncio.get_event_loop().call_later(2, lambda: asyncio.ensure_future(self._try_reconnect()))
|
||||
return
|
||||
except aiohttp.web.HTTPNotFound:
|
||||
raise aiohttp.web.HTTPConflict(text="The server {} is not a GNS3 server or it's a 1.X server".format(self._id))
|
||||
@ -479,18 +470,17 @@ class Compute:
|
||||
self._last_error = None
|
||||
self._controller.notification.controller_emit("compute.updated", self.__json__())
|
||||
|
||||
@asyncio.coroutine
|
||||
def _connect_notification(self):
|
||||
async def _connect_notification(self):
|
||||
"""
|
||||
Connect to the notification stream
|
||||
"""
|
||||
try:
|
||||
self._ws = yield from self._session().ws_connect(self._getUrl("/notifications/ws"), auth=self._auth)
|
||||
self._ws = await self._session().ws_connect(self._getUrl("/notifications/ws"), auth=self._auth)
|
||||
except (aiohttp.WSServerHandshakeError, aiohttp.ClientResponseError):
|
||||
self._ws = None
|
||||
while self._ws is not None:
|
||||
try:
|
||||
response = yield from self._ws.receive()
|
||||
response = await self._ws.receive()
|
||||
except aiohttp.WSServerHandshakeError:
|
||||
self._ws = None
|
||||
break
|
||||
@ -505,13 +495,13 @@ class Compute:
|
||||
self._memory_usage_percent = event["memory_usage_percent"]
|
||||
self._controller.notification.controller_emit("compute.updated", self.__json__())
|
||||
else:
|
||||
yield from self._controller.notification.dispatch(action, event, compute_id=self.id)
|
||||
await self._controller.notification.dispatch(action, event, compute_id=self.id)
|
||||
if self._ws:
|
||||
yield from self._ws.close()
|
||||
await self._ws.close()
|
||||
|
||||
# Try to reconnect after 1 seconds if server unavailable only if not during tests (otherwise we create a ressources usage bomb)
|
||||
if not hasattr(sys, "_called_from_test") or not sys._called_from_test:
|
||||
asyncio.get_event_loop().call_later(1, lambda: asyncio_ensure_future(self.connect()))
|
||||
asyncio.get_event_loop().call_later(1, lambda: asyncio.ensure_future(self.connect()))
|
||||
self._ws = None
|
||||
self._cpu_usage_percent = None
|
||||
self._memory_usage_percent = None
|
||||
@ -536,8 +526,7 @@ class Compute:
|
||||
""" Returns URL for specific path at Compute"""
|
||||
return self._getUrl(path)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _run_http_query(self, method, path, data=None, timeout=20, raw=False):
|
||||
async def _run_http_query(self, method, path, data=None, timeout=20, raw=False):
|
||||
with Timeout(timeout):
|
||||
url = self._getUrl(path)
|
||||
headers = {}
|
||||
@ -562,13 +551,13 @@ class Compute:
|
||||
data = json.dumps(data).encode("utf-8")
|
||||
try:
|
||||
log.debug("Attempting request to compute: {method} {url} {headers}".format(method=method, url=url, headers=headers))
|
||||
response = yield from self._session().request(method, url, headers=headers, data=data, auth=self._auth, chunked=chunked, timeout=timeout)
|
||||
response = await self._session().request(method, url, headers=headers, data=data, auth=self._auth, chunked=chunked, timeout=timeout)
|
||||
except asyncio.TimeoutError:
|
||||
raise ComputeError("Timeout error for {} call to {} after {}s".format(method, url, timeout))
|
||||
except (aiohttp.ClientError, aiohttp.ServerDisconnectedError, ValueError, KeyError, socket.gaierror) as e:
|
||||
# aiohttp 2.3.1 raises socket.gaierror when cannot find host
|
||||
raise ComputeError(str(e))
|
||||
body = yield from response.read()
|
||||
body = await response.read()
|
||||
if body and not raw:
|
||||
body = body.decode()
|
||||
|
||||
@ -617,46 +606,40 @@ class Compute:
|
||||
response.body = b""
|
||||
return response
|
||||
|
||||
@asyncio.coroutine
|
||||
def get(self, path, **kwargs):
|
||||
return (yield from self.http_query("GET", path, **kwargs))
|
||||
async def get(self, path, **kwargs):
|
||||
return (await self.http_query("GET", path, **kwargs))
|
||||
|
||||
@asyncio.coroutine
|
||||
def post(self, path, data={}, **kwargs):
|
||||
response = yield from self.http_query("POST", path, data, **kwargs)
|
||||
async def post(self, path, data={}, **kwargs):
|
||||
response = await self.http_query("POST", path, data, **kwargs)
|
||||
return response
|
||||
|
||||
@asyncio.coroutine
|
||||
def put(self, path, data={}, **kwargs):
|
||||
response = yield from self.http_query("PUT", path, data, **kwargs)
|
||||
async def put(self, path, data={}, **kwargs):
|
||||
response = await self.http_query("PUT", path, data, **kwargs)
|
||||
return response
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self, path, **kwargs):
|
||||
return (yield from self.http_query("DELETE", path, **kwargs))
|
||||
async def delete(self, path, **kwargs):
|
||||
return (await self.http_query("DELETE", path, **kwargs))
|
||||
|
||||
@asyncio.coroutine
|
||||
def forward(self, method, type, path, data=None):
|
||||
async def forward(self, method, type, path, data=None):
|
||||
"""
|
||||
Forward a call to the emulator on compute
|
||||
"""
|
||||
try:
|
||||
action = "/{}/{}".format(type, path)
|
||||
res = yield from self.http_query(method, action, data=data, timeout=None)
|
||||
res = await self.http_query(method, action, data=data, timeout=None)
|
||||
except aiohttp.ServerDisconnectedError:
|
||||
log.error("Connection lost to %s during %s %s", self._id, method, action)
|
||||
raise aiohttp.web.HTTPGatewayTimeout()
|
||||
return res.json
|
||||
|
||||
@asyncio.coroutine
|
||||
def images(self, type):
|
||||
async def images(self, type):
|
||||
"""
|
||||
Return the list of images available for this type on controller
|
||||
and on the compute node.
|
||||
"""
|
||||
images = []
|
||||
|
||||
res = yield from self.http_query("GET", "/{}/images".format(type), timeout=None)
|
||||
res = await self.http_query("GET", "/{}/images".format(type), timeout=None)
|
||||
images = res.json
|
||||
|
||||
try:
|
||||
@ -671,17 +654,15 @@ class Compute:
|
||||
raise ComputeError("Cannot list images: {}".format(str(e)))
|
||||
return images
|
||||
|
||||
@asyncio.coroutine
|
||||
def list_files(self, project):
|
||||
async def list_files(self, project):
|
||||
"""
|
||||
List files in the project on computes
|
||||
"""
|
||||
path = "/projects/{}/files".format(project.id)
|
||||
res = yield from self.http_query("GET", path, timeout=None)
|
||||
res = await self.http_query("GET", path, timeout=None)
|
||||
return res.json
|
||||
|
||||
@asyncio.coroutine
|
||||
def get_ip_on_same_subnet(self, other_compute):
|
||||
async def get_ip_on_same_subnet(self, other_compute):
|
||||
"""
|
||||
Try to find the best ip for communication from one compute
|
||||
to another
|
||||
@ -695,8 +676,8 @@ class Compute:
|
||||
if (self.host_ip not in ('0.0.0.0', '127.0.0.1') and other_compute.host_ip not in ('0.0.0.0', '127.0.0.1')):
|
||||
return (self.host_ip, other_compute.host_ip)
|
||||
|
||||
this_compute_interfaces = yield from self.interfaces()
|
||||
other_compute_interfaces = yield from other_compute.interfaces()
|
||||
this_compute_interfaces = await self.interfaces()
|
||||
other_compute_interfaces = await other_compute.interfaces()
|
||||
|
||||
# Sort interface to put the compute host in first position
|
||||
# we guess that if user specified this host it could have a reason (VMware Nat / Host only interface)
|
||||
|
@ -165,8 +165,7 @@ class Drawing:
|
||||
def rotation(self, val):
|
||||
self._rotation = val
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self, **kwargs):
|
||||
async def update(self, **kwargs):
|
||||
"""
|
||||
Update the drawing
|
||||
|
||||
|
@ -30,8 +30,7 @@ import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def export_project(project, temporary_dir, include_images=False, keep_compute_id=False, allow_all_nodes=False):
|
||||
async def export_project(project, temporary_dir, include_images=False, keep_compute_id=False, allow_all_nodes=False):
|
||||
"""
|
||||
Export a project to a zip file.
|
||||
|
||||
@ -61,7 +60,7 @@ def export_project(project, temporary_dir, include_images=False, keep_compute_id
|
||||
# First we process the .gns3 in order to be sure we don't have an error
|
||||
for file in os.listdir(project._path):
|
||||
if file.endswith(".gns3"):
|
||||
yield from _patch_project_file(project, os.path.join(project._path, file), zstream, include_images, keep_compute_id, allow_all_nodes, temporary_dir)
|
||||
await _patch_project_file(project, os.path.join(project._path, file), zstream, include_images, keep_compute_id, allow_all_nodes, temporary_dir)
|
||||
|
||||
# Export the local files
|
||||
for root, dirs, files in os.walk(project._path, topdown=True):
|
||||
@ -86,15 +85,15 @@ def export_project(project, temporary_dir, include_images=False, keep_compute_id
|
||||
downloaded_files = set()
|
||||
for compute in project.computes:
|
||||
if compute.id != "local":
|
||||
compute_files = yield from compute.list_files(project)
|
||||
compute_files = await compute.list_files(project)
|
||||
for compute_file in compute_files:
|
||||
if _is_exportable(compute_file["path"]):
|
||||
(fd, temp_path) = tempfile.mkstemp(dir=temporary_dir)
|
||||
f = open(fd, "wb", closefd=True)
|
||||
response = yield from compute.download_file(project, compute_file["path"])
|
||||
response = await compute.download_file(project, compute_file["path"])
|
||||
while True:
|
||||
try:
|
||||
data = yield from response.content.read(1024)
|
||||
data = await response.content.read(1024)
|
||||
except asyncio.TimeoutError:
|
||||
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when downloading file '{}' from remote compute server {}:{}".format(compute_file["path"], compute.host, compute.port))
|
||||
if not data:
|
||||
@ -154,8 +153,7 @@ def _is_exportable(path):
|
||||
return True
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def _patch_project_file(project, path, zstream, include_images, keep_compute_id, allow_all_nodes, temporary_dir):
|
||||
async def _patch_project_file(project, path, zstream, include_images, keep_compute_id, allow_all_nodes, temporary_dir):
|
||||
"""
|
||||
Patch a project file (.gns3) to export a project.
|
||||
The .gns3 file is renamed to project.gns3
|
||||
@ -219,7 +217,7 @@ def _patch_project_file(project, path, zstream, include_images, keep_compute_id,
|
||||
for i in images if i['compute_id'] != 'local'])
|
||||
|
||||
for compute_id, image_type, image in remote_images:
|
||||
yield from _export_remote_images(project, compute_id, image_type, image, zstream, temporary_dir)
|
||||
await _export_remote_images(project, compute_id, image_type, image, zstream, temporary_dir)
|
||||
|
||||
zstream.writestr("project.gns3", json.dumps(topology).encode())
|
||||
return images
|
||||
@ -253,8 +251,7 @@ def _export_local_image(image, zstream):
|
||||
return
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def _export_remote_images(project, compute_id, image_type, image, project_zipfile, temporary_dir):
|
||||
async def _export_remote_images(project, compute_id, image_type, image, project_zipfile, temporary_dir):
|
||||
"""
|
||||
Export specific image from remote compute.
|
||||
"""
|
||||
@ -268,14 +265,14 @@ def _export_remote_images(project, compute_id, image_type, image, project_zipfil
|
||||
|
||||
(fd, temp_path) = tempfile.mkstemp(dir=temporary_dir)
|
||||
f = open(fd, "wb", closefd=True)
|
||||
response = yield from compute.download_image(image_type, image)
|
||||
response = await compute.download_image(image_type, image)
|
||||
|
||||
if response.status != 200:
|
||||
raise aiohttp.web.HTTPConflict(text="Cannot export image from '{}' compute. Compute returned status code {}.".format(compute_id, response.status))
|
||||
|
||||
while True:
|
||||
try:
|
||||
data = yield from response.content.read(1024)
|
||||
data = await response.content.read(1024)
|
||||
except asyncio.TimeoutError:
|
||||
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when downloading image '{}' from remote compute server {}:{}".format(image, compute.host, compute.port))
|
||||
if not data:
|
||||
|
@ -21,7 +21,7 @@ import asyncio
|
||||
import aiohttp
|
||||
import ipaddress
|
||||
|
||||
from ...utils.asyncio import locking, asyncio_ensure_future
|
||||
from ...utils.asyncio import locking
|
||||
from .vmware_gns3_vm import VMwareGNS3VM
|
||||
from .virtualbox_gns3_vm import VirtualBoxGNS3VM
|
||||
from .hyperv_gns3_vm import HyperVGNS3VM
|
||||
@ -204,8 +204,7 @@ class GNS3VM:
|
||||
|
||||
self._settings.update(val)
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_settings(self, settings):
|
||||
async def update_settings(self, settings):
|
||||
"""
|
||||
Update settings and will restart the VM if require
|
||||
"""
|
||||
@ -213,15 +212,15 @@ class GNS3VM:
|
||||
new_settings = copy.copy(self._settings)
|
||||
new_settings.update(settings)
|
||||
if self.settings != new_settings:
|
||||
yield from self._stop()
|
||||
await self._stop()
|
||||
self._settings = settings
|
||||
self._controller.save()
|
||||
if self.enable:
|
||||
yield from self.start()
|
||||
await self.start()
|
||||
else:
|
||||
# When user fix something on his system and try again
|
||||
if self.enable and not self.current_engine().running:
|
||||
yield from self.start()
|
||||
await self.start()
|
||||
|
||||
def _get_engine(self, engine):
|
||||
"""
|
||||
@ -248,8 +247,7 @@ class GNS3VM:
|
||||
def __json__(self):
|
||||
return self._settings
|
||||
|
||||
@asyncio.coroutine
|
||||
def list(self, engine):
|
||||
async def list(self, engine):
|
||||
"""
|
||||
List VMS for an engine
|
||||
"""
|
||||
@ -257,7 +255,7 @@ class GNS3VM:
|
||||
engine = self._get_engine(engine)
|
||||
vms = []
|
||||
try:
|
||||
for vm in (yield from engine.list()):
|
||||
for vm in (await engine.list()):
|
||||
vms.append({"vmname": vm["vmname"]})
|
||||
except GNS3VMError as e:
|
||||
# We raise error only if user activated the GNS3 VM
|
||||
@ -266,19 +264,18 @@ class GNS3VM:
|
||||
raise e
|
||||
return vms
|
||||
|
||||
@asyncio.coroutine
|
||||
def auto_start_vm(self):
|
||||
async def auto_start_vm(self):
|
||||
"""
|
||||
Auto start the GNS3 VM if require
|
||||
"""
|
||||
|
||||
if self.enable:
|
||||
try:
|
||||
yield from self.start()
|
||||
await self.start()
|
||||
except GNS3VMError as e:
|
||||
# User will receive the error later when they will try to use the node
|
||||
try:
|
||||
compute = yield from self._controller.add_compute(compute_id="vm",
|
||||
compute = await self._controller.add_compute(compute_id="vm",
|
||||
name="GNS3 VM ({})".format(self.current_engine().vmname),
|
||||
host=None,
|
||||
force=True)
|
||||
@ -288,21 +285,19 @@ class GNS3VM:
|
||||
pass
|
||||
log.error("Cannot start the GNS3 VM: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def exit_vm(self):
|
||||
async def exit_vm(self):
|
||||
|
||||
if self.enable:
|
||||
try:
|
||||
if self._settings["when_exit"] == "stop":
|
||||
yield from self._stop()
|
||||
await self._stop()
|
||||
elif self._settings["when_exit"] == "suspend":
|
||||
yield from self._suspend()
|
||||
await self._suspend()
|
||||
except GNS3VMError as e:
|
||||
log.warning(str(e))
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Start the GNS3 VM
|
||||
"""
|
||||
@ -316,22 +311,22 @@ class GNS3VM:
|
||||
engine.ram = self._settings["ram"]
|
||||
engine.vcpus = self._settings["vcpus"]
|
||||
engine.headless = self._settings["headless"]
|
||||
compute = yield from self._controller.add_compute(compute_id="vm",
|
||||
compute = await self._controller.add_compute(compute_id="vm",
|
||||
name="GNS3 VM is starting ({})".format(engine.vmname),
|
||||
host=None,
|
||||
force=True,
|
||||
connect=False)
|
||||
|
||||
try:
|
||||
yield from engine.start()
|
||||
await engine.start()
|
||||
except Exception as e:
|
||||
yield from self._controller.delete_compute("vm")
|
||||
await self._controller.delete_compute("vm")
|
||||
log.error("Cannot start the GNS3 VM: {}".format(str(e)))
|
||||
yield from compute.update(name="GNS3 VM ({})".format(engine.vmname))
|
||||
await compute.update(name="GNS3 VM ({})".format(engine.vmname))
|
||||
compute.set_last_error(str(e))
|
||||
raise e
|
||||
yield from compute.connect() # we can connect now that the VM has started
|
||||
yield from compute.update(name="GNS3 VM ({})".format(engine.vmname),
|
||||
await compute.connect() # we can connect now that the VM has started
|
||||
await compute.update(name="GNS3 VM ({})".format(engine.vmname),
|
||||
protocol=self.protocol,
|
||||
host=self.ip_address,
|
||||
port=self.port,
|
||||
@ -340,16 +335,15 @@ class GNS3VM:
|
||||
|
||||
# check if the VM is in the same subnet as the local server, start 10 seconds later to give
|
||||
# some time for the compute in the VM to be ready for requests
|
||||
asyncio.get_event_loop().call_later(10, lambda: asyncio_ensure_future(self._check_network(compute)))
|
||||
asyncio.get_event_loop().call_later(10, lambda: asyncio.ensure_future(self._check_network(compute)))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_network(self, compute):
|
||||
async def _check_network(self, compute):
|
||||
"""
|
||||
Check that the VM is in the same subnet as the local server
|
||||
"""
|
||||
|
||||
try:
|
||||
vm_interfaces = yield from compute.interfaces()
|
||||
vm_interfaces = await compute.interfaces()
|
||||
vm_interface_netmask = None
|
||||
for interface in vm_interfaces:
|
||||
if interface["ip_address"] == self.ip_address:
|
||||
@ -360,7 +354,7 @@ class GNS3VM:
|
||||
for compute_id in self._controller.computes:
|
||||
if compute_id == "local":
|
||||
compute = self._controller.get_compute(compute_id)
|
||||
interfaces = yield from compute.interfaces()
|
||||
interfaces = await compute.interfaces()
|
||||
netmask = None
|
||||
for interface in interfaces:
|
||||
if interface["ip_address"] == compute.host_ip:
|
||||
@ -378,27 +372,25 @@ class GNS3VM:
|
||||
log.warning("Could not check the VM is in the same subnet as the local server: {}".format(e.text))
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def _suspend(self):
|
||||
async def _suspend(self):
|
||||
"""
|
||||
Suspend the GNS3 VM
|
||||
"""
|
||||
engine = self.current_engine()
|
||||
if "vm" in self._controller.computes:
|
||||
yield from self._controller.delete_compute("vm")
|
||||
await self._controller.delete_compute("vm")
|
||||
if engine.running:
|
||||
log.info("Suspend the GNS3 VM")
|
||||
yield from engine.suspend()
|
||||
await engine.suspend()
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def _stop(self):
|
||||
async def _stop(self):
|
||||
"""
|
||||
Stop the GNS3 VM
|
||||
"""
|
||||
engine = self.current_engine()
|
||||
if "vm" in self._controller.computes:
|
||||
yield from self._controller.delete_compute("vm")
|
||||
await self._controller.delete_compute("vm")
|
||||
if engine.running:
|
||||
log.info("Stop the GNS3 VM")
|
||||
yield from engine.stop()
|
||||
await engine.stop()
|
||||
|
@ -254,32 +254,28 @@ class BaseGNS3VM:
|
||||
|
||||
return self._engine
|
||||
|
||||
@asyncio.coroutine
|
||||
def list(self):
|
||||
async def list(self):
|
||||
"""
|
||||
List all VMs
|
||||
"""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts the GNS3 VM.
|
||||
"""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def suspend(self):
|
||||
async def suspend(self):
|
||||
"""
|
||||
Suspend the GNS3 VM.
|
||||
"""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the GNS3 VM.
|
||||
"""
|
||||
|
@ -164,8 +164,7 @@ class HyperVGNS3VM(BaseGNS3VM):
|
||||
except Exception as e:
|
||||
raise GNS3VMError("Could not set to {} and RAM amount set to {}: {}".format(vcpus, ram, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def list(self):
|
||||
async def list(self):
|
||||
"""
|
||||
List all Hyper-V VMs
|
||||
"""
|
||||
@ -189,8 +188,7 @@ class HyperVGNS3VM(BaseGNS3VM):
|
||||
|
||||
return wmi.WMI(moniker=path.replace('\\', '/'))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _set_state(self, state):
|
||||
async def _set_state(self, state):
|
||||
"""
|
||||
Set the desired state of the VM
|
||||
"""
|
||||
@ -203,15 +201,14 @@ class HyperVGNS3VM(BaseGNS3VM):
|
||||
if ret == HyperVGNS3VM._WMI_JOB_STATUS_STARTED:
|
||||
job = self._get_wmi_obj(job_path)
|
||||
while job.JobState == HyperVGNS3VM._WMI_JOB_STATE_RUNNING:
|
||||
yield from asyncio.sleep(0.1)
|
||||
await asyncio.sleep(0.1)
|
||||
job = self._get_wmi_obj(job_path)
|
||||
if job.JobState != HyperVGNS3VM._WMI_JOB_STATE_COMPLETED:
|
||||
raise GNS3VMError("Error while changing state: {}".format(job.ErrorSummaryDescription))
|
||||
elif ret != 0 or ret != 32775:
|
||||
raise GNS3VMError("Failed to change state to {}".format(state))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts the GNS3 VM.
|
||||
"""
|
||||
@ -228,7 +225,7 @@ class HyperVGNS3VM(BaseGNS3VM):
|
||||
|
||||
# start the VM
|
||||
try:
|
||||
yield from self._set_state(HyperVGNS3VM._HYPERV_VM_STATE_ENABLED)
|
||||
await self._set_state(HyperVGNS3VM._HYPERV_VM_STATE_ENABLED)
|
||||
except GNS3VMError as e:
|
||||
raise GNS3VMError("Failed to start the GNS3 VM: {}".format(e))
|
||||
log.info("GNS3 VM has been started")
|
||||
@ -259,32 +256,30 @@ class HyperVGNS3VM(BaseGNS3VM):
|
||||
break
|
||||
elif trial == 0:
|
||||
raise GNS3VMError("Could not find guest IP address for {}".format(self.vmname))
|
||||
yield from asyncio.sleep(1)
|
||||
await asyncio.sleep(1)
|
||||
self.ip_address = guest_ip_address
|
||||
log.info("GNS3 VM IP address set to {}".format(guest_ip_address))
|
||||
self.running = True
|
||||
|
||||
@asyncio.coroutine
|
||||
def suspend(self):
|
||||
async def suspend(self):
|
||||
"""
|
||||
Suspends the GNS3 VM.
|
||||
"""
|
||||
|
||||
try:
|
||||
yield from self._set_state(HyperVGNS3VM._HYPERV_VM_STATE_PAUSED)
|
||||
await self._set_state(HyperVGNS3VM._HYPERV_VM_STATE_PAUSED)
|
||||
except GNS3VMError as e:
|
||||
raise GNS3VMError("Failed to suspend the GNS3 VM: {}".format(e))
|
||||
log.info("GNS3 VM has been suspended")
|
||||
self.running = False
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the GNS3 VM.
|
||||
"""
|
||||
|
||||
try:
|
||||
yield from self._set_state(HyperVGNS3VM._HYPERV_VM_STATE_SHUTDOWN)
|
||||
await self._set_state(HyperVGNS3VM._HYPERV_VM_STATE_SHUTDOWN)
|
||||
except GNS3VMError as e:
|
||||
raise GNS3VMError("Failed to stop the GNS3 VM: {}".format(e))
|
||||
log.info("GNS3 VM has been stopped")
|
||||
|
@ -32,8 +32,7 @@ class RemoteGNS3VM(BaseGNS3VM):
|
||||
self._engine = "remote"
|
||||
super().__init__(controller)
|
||||
|
||||
@asyncio.coroutine
|
||||
def list(self):
|
||||
async def list(self):
|
||||
"""
|
||||
List all VMs
|
||||
"""
|
||||
@ -45,8 +44,7 @@ class RemoteGNS3VM(BaseGNS3VM):
|
||||
res.append({"vmname": compute.name})
|
||||
return res
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts the GNS3 VM.
|
||||
"""
|
||||
@ -65,15 +63,13 @@ class RemoteGNS3VM(BaseGNS3VM):
|
||||
return
|
||||
raise GNS3VMError("Can't start the GNS3 VM remote VM {} not found".format(self.vmname))
|
||||
|
||||
@asyncio.coroutine
|
||||
def suspend(self):
|
||||
async def suspend(self):
|
||||
"""
|
||||
Suspend do nothing for remote server
|
||||
"""
|
||||
self.running = False
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the GNS3 VM.
|
||||
"""
|
||||
|
@ -40,24 +40,22 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
super().__init__(controller)
|
||||
self._virtualbox_manager = VirtualBox()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _execute(self, subcommand, args, timeout=60):
|
||||
async def _execute(self, subcommand, args, timeout=60):
|
||||
|
||||
try:
|
||||
result = yield from self._virtualbox_manager.execute(subcommand, args, timeout)
|
||||
result = await self._virtualbox_manager.execute(subcommand, args, timeout)
|
||||
return ("\n".join(result))
|
||||
except VirtualBoxError as e:
|
||||
raise GNS3VMError("Error while executing VBoxManage command: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_state(self):
|
||||
async def _get_state(self):
|
||||
"""
|
||||
Returns the VM state (e.g. running, paused etc.)
|
||||
|
||||
:returns: state (string)
|
||||
"""
|
||||
|
||||
result = yield from self._execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
result = await self._execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
for info in result.splitlines():
|
||||
if '=' in info:
|
||||
name, value = info.split('=', 1)
|
||||
@ -65,15 +63,14 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
return value.strip('"')
|
||||
return "unknown"
|
||||
|
||||
@asyncio.coroutine
|
||||
def _look_for_interface(self, network_backend):
|
||||
async def _look_for_interface(self, network_backend):
|
||||
"""
|
||||
Look for an interface with a specific network backend.
|
||||
|
||||
:returns: interface number or -1 if none is found
|
||||
"""
|
||||
|
||||
result = yield from self._execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
result = await self._execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
interface = -1
|
||||
for info in result.splitlines():
|
||||
if '=' in info:
|
||||
@ -86,15 +83,14 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
continue
|
||||
return interface
|
||||
|
||||
@asyncio.coroutine
|
||||
def _look_for_vboxnet(self, interface_number):
|
||||
async def _look_for_vboxnet(self, interface_number):
|
||||
"""
|
||||
Look for the VirtualBox network name associated with a host only interface.
|
||||
|
||||
:returns: None or vboxnet name
|
||||
"""
|
||||
|
||||
result = yield from self._execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
result = await self._execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
for info in result.splitlines():
|
||||
if '=' in info:
|
||||
name, value = info.split('=', 1)
|
||||
@ -102,8 +98,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
return value.strip('"')
|
||||
return None
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_dhcp_server(self, vboxnet):
|
||||
async def _check_dhcp_server(self, vboxnet):
|
||||
"""
|
||||
Check if the DHCP server associated with a vboxnet is enabled.
|
||||
|
||||
@ -111,7 +106,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
:returns: boolean
|
||||
"""
|
||||
|
||||
properties = yield from self._execute("list", ["dhcpservers"])
|
||||
properties = await self._execute("list", ["dhcpservers"])
|
||||
flag_dhcp_server_found = False
|
||||
for prop in properties.splitlines():
|
||||
try:
|
||||
@ -125,8 +120,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_vboxnet_exists(self, vboxnet):
|
||||
async def _check_vboxnet_exists(self, vboxnet):
|
||||
"""
|
||||
Check if the vboxnet interface exists
|
||||
|
||||
@ -134,7 +128,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
:returns: boolean
|
||||
"""
|
||||
|
||||
properties = yield from self._execute("list", ["hostonlyifs"])
|
||||
properties = await self._execute("list", ["hostonlyifs"])
|
||||
for prop in properties.splitlines():
|
||||
try:
|
||||
name, value = prop.split(':', 1)
|
||||
@ -144,13 +138,12 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def _find_first_available_vboxnet(self):
|
||||
async def _find_first_available_vboxnet(self):
|
||||
"""
|
||||
Find the first available vboxnet.
|
||||
"""
|
||||
|
||||
properties = yield from self._execute("list", ["hostonlyifs"])
|
||||
properties = await self._execute("list", ["hostonlyifs"])
|
||||
for prop in properties.splitlines():
|
||||
try:
|
||||
name, value = prop.split(':', 1)
|
||||
@ -160,15 +153,14 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
return value.strip()
|
||||
return None
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_vbox_port_forwarding(self):
|
||||
async def _check_vbox_port_forwarding(self):
|
||||
"""
|
||||
Checks if the NAT port forwarding rule exists.
|
||||
|
||||
:returns: boolean
|
||||
"""
|
||||
|
||||
result = yield from self._execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
result = await self._execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
for info in result.splitlines():
|
||||
if '=' in info:
|
||||
name, value = info.split('=', 1)
|
||||
@ -176,66 +168,64 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def list(self):
|
||||
async def list(self):
|
||||
"""
|
||||
List all VirtualBox VMs
|
||||
"""
|
||||
|
||||
return (yield from self._virtualbox_manager.list_vms())
|
||||
return (await self._virtualbox_manager.list_vms())
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Start the GNS3 VM.
|
||||
"""
|
||||
|
||||
# get a NAT interface number
|
||||
nat_interface_number = yield from self._look_for_interface("nat")
|
||||
nat_interface_number = await self._look_for_interface("nat")
|
||||
if nat_interface_number < 0:
|
||||
raise GNS3VMError('VM "{}" must have a NAT interface configured in order to start'.format(self.vmname))
|
||||
|
||||
hostonly_interface_number = yield from self._look_for_interface("hostonly")
|
||||
hostonly_interface_number = await self._look_for_interface("hostonly")
|
||||
if hostonly_interface_number < 0:
|
||||
raise GNS3VMError('VM "{}" must have a host-only interface configured in order to start'.format(self.vmname))
|
||||
|
||||
vboxnet = yield from self._look_for_vboxnet(hostonly_interface_number)
|
||||
vboxnet = await self._look_for_vboxnet(hostonly_interface_number)
|
||||
if vboxnet is None:
|
||||
raise GNS3VMError('A VirtualBox host-only network could not be found on network adapter {} for "{}"'.format(hostonly_interface_number, self._vmname))
|
||||
|
||||
if not (yield from self._check_vboxnet_exists(vboxnet)):
|
||||
if not (await self._check_vboxnet_exists(vboxnet)):
|
||||
if sys.platform.startswith("win") and vboxnet == "vboxnet0":
|
||||
# The GNS3 VM is configured with vboxnet0 by default which is not available
|
||||
# on Windows. Try to patch this with the first available vboxnet we find.
|
||||
first_available_vboxnet = yield from self._find_first_available_vboxnet()
|
||||
first_available_vboxnet = await self._find_first_available_vboxnet()
|
||||
if first_available_vboxnet is None:
|
||||
raise GNS3VMError('Please add a VirtualBox host-only network with DHCP enabled and attached it to network adapter {} for "{}"'.format(hostonly_interface_number, self._vmname))
|
||||
yield from self.set_hostonly_network(hostonly_interface_number, first_available_vboxnet)
|
||||
await self.set_hostonly_network(hostonly_interface_number, first_available_vboxnet)
|
||||
vboxnet = first_available_vboxnet
|
||||
else:
|
||||
raise GNS3VMError('VirtualBox host-only network "{}" does not exist, please make the sure the network adapter {} configuration is valid for "{}"'.format(vboxnet,
|
||||
hostonly_interface_number,
|
||||
self._vmname))
|
||||
|
||||
if not (yield from self._check_dhcp_server(vboxnet)):
|
||||
if not (await self._check_dhcp_server(vboxnet)):
|
||||
raise GNS3VMError('DHCP must be enabled on VirtualBox host-only network "{}"'.format(vboxnet))
|
||||
|
||||
vm_state = yield from self._get_state()
|
||||
vm_state = await self._get_state()
|
||||
log.info('"{}" state is {}'.format(self._vmname, vm_state))
|
||||
|
||||
if vm_state == "poweroff":
|
||||
yield from self.set_vcpus(self.vcpus)
|
||||
yield from self.set_ram(self.ram)
|
||||
await self.set_vcpus(self.vcpus)
|
||||
await self.set_ram(self.ram)
|
||||
|
||||
if vm_state in ("poweroff", "saved"):
|
||||
# start the VM if it is not running
|
||||
args = [self._vmname]
|
||||
if self._headless:
|
||||
args.extend(["--type", "headless"])
|
||||
yield from self._execute("startvm", args)
|
||||
await self._execute("startvm", args)
|
||||
elif vm_state == "paused":
|
||||
args = [self._vmname, "resume"]
|
||||
yield from self._execute("controlvm", args)
|
||||
await self._execute("controlvm", args)
|
||||
ip_address = "127.0.0.1"
|
||||
try:
|
||||
# get a random port on localhost
|
||||
@ -246,23 +236,22 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
except OSError as e:
|
||||
raise GNS3VMError("Error while getting random port: {}".format(e))
|
||||
|
||||
if (yield from self._check_vbox_port_forwarding()):
|
||||
if (await self._check_vbox_port_forwarding()):
|
||||
# delete the GNS3VM NAT port forwarding rule if it exists
|
||||
log.info("Removing GNS3VM NAT port forwarding rule from interface {}".format(nat_interface_number))
|
||||
yield from self._execute("controlvm", [self._vmname, "natpf{}".format(nat_interface_number), "delete", "GNS3VM"])
|
||||
await self._execute("controlvm", [self._vmname, "natpf{}".format(nat_interface_number), "delete", "GNS3VM"])
|
||||
|
||||
# add a GNS3VM NAT port forwarding rule to redirect 127.0.0.1 with random port to port 3080 in the VM
|
||||
log.info("Adding GNS3VM NAT port forwarding rule with port {} to interface {}".format(api_port, nat_interface_number))
|
||||
yield from self._execute("controlvm", [self._vmname, "natpf{}".format(nat_interface_number),
|
||||
await self._execute("controlvm", [self._vmname, "natpf{}".format(nat_interface_number),
|
||||
"GNS3VM,tcp,{},{},,3080".format(ip_address, api_port)])
|
||||
|
||||
self.ip_address = yield from self._get_ip(hostonly_interface_number, api_port)
|
||||
self.ip_address = await self._get_ip(hostonly_interface_number, api_port)
|
||||
self.port = 3080
|
||||
log.info("GNS3 VM has been started with IP {}".format(self.ip_address))
|
||||
self.running = True
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_ip(self, hostonly_interface_number, api_port):
|
||||
async def _get_ip(self, hostonly_interface_number, api_port):
|
||||
"""
|
||||
Get the IP from VirtualBox.
|
||||
|
||||
@ -276,14 +265,14 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
session = aiohttp.ClientSession()
|
||||
try:
|
||||
resp = None
|
||||
resp = yield from session.get('http://127.0.0.1:{}/v2/compute/network/interfaces'.format(api_port))
|
||||
resp = await session.get('http://127.0.0.1:{}/v2/compute/network/interfaces'.format(api_port))
|
||||
except (OSError, aiohttp.ClientError, TimeoutError, asyncio.TimeoutError):
|
||||
pass
|
||||
|
||||
if resp:
|
||||
if resp.status < 300:
|
||||
try:
|
||||
json_data = yield from resp.json()
|
||||
json_data = await resp.json()
|
||||
except ValueError:
|
||||
pass
|
||||
resp.close()
|
||||
@ -296,35 +285,33 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
if "ip_address" in interface and len(interface["ip_address"]) > 0:
|
||||
return interface["ip_address"]
|
||||
remaining_try -= 1
|
||||
yield from asyncio.sleep(1)
|
||||
await asyncio.sleep(1)
|
||||
raise GNS3VMError("Could not get the GNS3 VM ip make sure the VM receive an IP from VirtualBox")
|
||||
|
||||
@asyncio.coroutine
|
||||
def suspend(self):
|
||||
async def suspend(self):
|
||||
"""
|
||||
Suspend the GNS3 VM.
|
||||
"""
|
||||
|
||||
yield from self._execute("controlvm", [self._vmname, "savestate"], timeout=3)
|
||||
await self._execute("controlvm", [self._vmname, "savestate"], timeout=3)
|
||||
log.info("GNS3 VM has been suspend")
|
||||
self.running = False
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the GNS3 VM.
|
||||
"""
|
||||
|
||||
vm_state = yield from self._get_state()
|
||||
vm_state = await self._get_state()
|
||||
if vm_state == "poweroff":
|
||||
self.running = False
|
||||
return
|
||||
|
||||
yield from self._execute("controlvm", [self._vmname, "acpipowerbutton"], timeout=3)
|
||||
await self._execute("controlvm", [self._vmname, "acpipowerbutton"], timeout=3)
|
||||
trial = 120
|
||||
while True:
|
||||
try:
|
||||
vm_state = yield from self._get_state()
|
||||
vm_state = await self._get_state()
|
||||
# During a small amount of time the command will fail
|
||||
except GNS3VMError:
|
||||
vm_state = "running"
|
||||
@ -332,37 +319,34 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
break
|
||||
trial -= 1
|
||||
if trial == 0:
|
||||
yield from self._execute("controlvm", [self._vmname, "poweroff"], timeout=3)
|
||||
await self._execute("controlvm", [self._vmname, "poweroff"], timeout=3)
|
||||
break
|
||||
yield from asyncio.sleep(1)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
log.info("GNS3 VM has been stopped")
|
||||
self.running = False
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_vcpus(self, vcpus):
|
||||
async def set_vcpus(self, vcpus):
|
||||
"""
|
||||
Set the number of vCPU cores for the GNS3 VM.
|
||||
|
||||
:param vcpus: number of vCPU cores
|
||||
"""
|
||||
|
||||
yield from self._execute("modifyvm", [self._vmname, "--cpus", str(vcpus)], timeout=3)
|
||||
await self._execute("modifyvm", [self._vmname, "--cpus", str(vcpus)], timeout=3)
|
||||
log.info("GNS3 VM vCPU count set to {}".format(vcpus))
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_ram(self, ram):
|
||||
async def set_ram(self, ram):
|
||||
"""
|
||||
Set the RAM amount for the GNS3 VM.
|
||||
|
||||
:param ram: amount of memory
|
||||
"""
|
||||
|
||||
yield from self._execute("modifyvm", [self._vmname, "--memory", str(ram)], timeout=3)
|
||||
await self._execute("modifyvm", [self._vmname, "--memory", str(ram)], timeout=3)
|
||||
log.info("GNS3 VM RAM amount set to {}".format(ram))
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_hostonly_network(self, adapter_number, hostonly_network_name):
|
||||
async def set_hostonly_network(self, adapter_number, hostonly_network_name):
|
||||
"""
|
||||
Set a VirtualBox host-only network on a network adapter for the GNS3 VM.
|
||||
|
||||
@ -370,7 +354,7 @@ class VirtualBoxGNS3VM(BaseGNS3VM):
|
||||
:param hostonly_network_name: name of the VirtualBox host-only network
|
||||
"""
|
||||
|
||||
yield from self._execute("modifyvm", [self._vmname, "--hostonlyadapter{}".format(adapter_number), hostonly_network_name], timeout=3)
|
||||
await self._execute("modifyvm", [self._vmname, "--hostonlyadapter{}".format(adapter_number), hostonly_network_name], timeout=3)
|
||||
log.info('VirtualBox host-only network "{}" set on network adapter {} for "{}"'.format(hostonly_network_name,
|
||||
adapter_number,
|
||||
self._vmname))
|
||||
|
@ -43,24 +43,21 @@ class VMwareGNS3VM(BaseGNS3VM):
|
||||
def vmx_path(self):
|
||||
return self._vmx_path
|
||||
|
||||
@asyncio.coroutine
|
||||
def _execute(self, subcommand, args, timeout=60, log_level=logging.INFO):
|
||||
async def _execute(self, subcommand, args, timeout=60, log_level=logging.INFO):
|
||||
|
||||
try:
|
||||
result = yield from self._vmware_manager.execute(subcommand, args, timeout, log_level=log_level)
|
||||
result = await self._vmware_manager.execute(subcommand, args, timeout, log_level=log_level)
|
||||
return (''.join(result))
|
||||
except VMwareError as e:
|
||||
raise GNS3VMError("Error while executing VMware command: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _is_running(self):
|
||||
result = yield from self._vmware_manager.execute("list", [])
|
||||
async def _is_running(self):
|
||||
result = await self._vmware_manager.execute("list", [])
|
||||
if self._vmx_path in result:
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def _set_vcpus_ram(self, vcpus, ram):
|
||||
async def _set_vcpus_ram(self, vcpus, ram):
|
||||
"""
|
||||
Set the number of vCPU cores and amount of RAM for the GNS3 VM.
|
||||
|
||||
@ -85,8 +82,7 @@ class VMwareGNS3VM(BaseGNS3VM):
|
||||
except OSError as e:
|
||||
raise GNS3VMError('Could not read/write VMware VMX file "{}": {}'.format(self._vmx_path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _set_extra_options(self):
|
||||
async def _set_extra_options(self):
|
||||
try:
|
||||
"""
|
||||
Due to bug/change in VMWare 14 we're not able to pass Hardware Virtualization in GNS3VM.
|
||||
@ -109,23 +105,21 @@ class VMwareGNS3VM(BaseGNS3VM):
|
||||
except OSError as e:
|
||||
raise GNS3VMError('Could not read/write VMware VMX file "{}": {}'.format(self._vmx_path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def list(self):
|
||||
async def list(self):
|
||||
"""
|
||||
List all VMware VMs
|
||||
"""
|
||||
try:
|
||||
return (yield from self._vmware_manager.list_vms())
|
||||
return (await self._vmware_manager.list_vms())
|
||||
except VMwareError as e:
|
||||
raise GNS3VMError("Could not list VMware VMs: {}".format(str(e)))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts the GNS3 VM.
|
||||
"""
|
||||
|
||||
vms = yield from self.list()
|
||||
vms = await self.list()
|
||||
for vm in vms:
|
||||
if vm["vmname"] == self.vmname:
|
||||
self._vmx_path = vm["vmx_path"]
|
||||
@ -138,25 +132,25 @@ class VMwareGNS3VM(BaseGNS3VM):
|
||||
raise GNS3VMError("VMware VMX file {} doesn't exist".format(self._vmx_path))
|
||||
|
||||
# check if the VMware guest tools are installed
|
||||
vmware_tools_state = yield from self._execute("checkToolsState", [self._vmx_path])
|
||||
vmware_tools_state = await self._execute("checkToolsState", [self._vmx_path])
|
||||
if vmware_tools_state not in ("installed", "running"):
|
||||
raise GNS3VMError("VMware tools are not installed in {}".format(self.vmname))
|
||||
|
||||
try:
|
||||
running = yield from self._is_running()
|
||||
running = await self._is_running()
|
||||
except VMwareError as e:
|
||||
raise GNS3VMError("Could not list VMware VMs: {}".format(str(e)))
|
||||
if not running:
|
||||
log.info("Update GNS3 VM settings")
|
||||
# set the number of vCPUs and amount of RAM
|
||||
yield from self._set_vcpus_ram(self.vcpus, self.ram)
|
||||
yield from self._set_extra_options()
|
||||
await self._set_vcpus_ram(self.vcpus, self.ram)
|
||||
await self._set_extra_options()
|
||||
|
||||
# start the VM
|
||||
args = [self._vmx_path]
|
||||
if self._headless:
|
||||
args.extend(["nogui"])
|
||||
yield from self._execute("start", args)
|
||||
await self._execute("start", args)
|
||||
log.info("GNS3 VM has been started")
|
||||
|
||||
# get the guest IP address (first adapter only)
|
||||
@ -164,7 +158,7 @@ class VMwareGNS3VM(BaseGNS3VM):
|
||||
guest_ip_address = ""
|
||||
log.info("Waiting for GNS3 VM IP")
|
||||
while True:
|
||||
guest_ip_address = yield from self._execute("readVariable", [self._vmx_path, "guestVar", "gns3.eth0"], timeout=120, log_level=logging.DEBUG)
|
||||
guest_ip_address = await self._execute("readVariable", [self._vmx_path, "guestVar", "gns3.eth0"], timeout=120, log_level=logging.DEBUG)
|
||||
guest_ip_address = guest_ip_address.strip()
|
||||
if len(guest_ip_address) != 0:
|
||||
break
|
||||
@ -172,15 +166,14 @@ class VMwareGNS3VM(BaseGNS3VM):
|
||||
# If ip not found fallback on old method
|
||||
if trial == 0:
|
||||
log.warning("No IP found for the VM via readVariable fallback to getGuestIPAddress")
|
||||
guest_ip_address = yield from self._execute("getGuestIPAddress", [self._vmx_path, "-wait"], timeout=120)
|
||||
guest_ip_address = await self._execute("getGuestIPAddress", [self._vmx_path, "-wait"], timeout=120)
|
||||
break
|
||||
yield from asyncio.sleep(1)
|
||||
await asyncio.sleep(1)
|
||||
self.ip_address = guest_ip_address
|
||||
log.info("GNS3 VM IP address set to {}".format(guest_ip_address))
|
||||
self.running = True
|
||||
|
||||
@asyncio.coroutine
|
||||
def suspend(self):
|
||||
async def suspend(self):
|
||||
"""
|
||||
Suspend the GNS3 VM.
|
||||
"""
|
||||
@ -188,14 +181,13 @@ class VMwareGNS3VM(BaseGNS3VM):
|
||||
if self._vmx_path is None:
|
||||
raise GNS3VMError("No VMX path configured, can't suspend the VM")
|
||||
try:
|
||||
yield from self._execute("suspend", [self._vmx_path])
|
||||
await self._execute("suspend", [self._vmx_path])
|
||||
except GNS3VMError as e:
|
||||
log.warning("Error when suspending the VM: {}".format(str(e)))
|
||||
log.info("GNS3 VM has been suspended")
|
||||
self.running = False
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the GNS3 VM.
|
||||
"""
|
||||
@ -203,7 +195,7 @@ class VMwareGNS3VM(BaseGNS3VM):
|
||||
if self._vmx_path is None:
|
||||
raise GNS3VMError("No VMX path configured, can't stop the VM")
|
||||
try:
|
||||
yield from self._execute("stop", [self._vmx_path, "soft"])
|
||||
await self._execute("stop", [self._vmx_path, "soft"])
|
||||
except GNS3VMError as e:
|
||||
log.warning("Error when stopping the VM: {}".format(str(e)))
|
||||
log.info("GNS3 VM has been stopped")
|
||||
|
@ -33,8 +33,7 @@ Handle the import of project from a .gns3project
|
||||
"""
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def import_project(controller, project_id, stream, location=None, name=None, keep_compute_id=False):
|
||||
async def import_project(controller, project_id, stream, location=None, name=None, keep_compute_id=False):
|
||||
"""
|
||||
Import a project contain in a zip file
|
||||
|
||||
@ -87,7 +86,7 @@ def import_project(controller, project_id, stream, location=None, name=None, kee
|
||||
|
||||
try:
|
||||
with zipfile.ZipFile(stream) as zip_file:
|
||||
yield from wait_run_in_executor(zip_file.extractall, path)
|
||||
await wait_run_in_executor(zip_file.extractall, path)
|
||||
except zipfile.BadZipFile:
|
||||
raise aiohttp.web.HTTPConflict(text="Cannot extract files from GNS3 project (invalid zip)")
|
||||
|
||||
@ -138,9 +137,9 @@ def import_project(controller, project_id, stream, location=None, name=None, kee
|
||||
# Project created on the remote GNS3 VM?
|
||||
if node["compute_id"] not in compute_created:
|
||||
compute = controller.get_compute(node["compute_id"])
|
||||
yield from compute.post("/projects", data={"name": project_name, "project_id": project_id,})
|
||||
await compute.post("/projects", data={"name": project_name, "project_id": project_id,})
|
||||
compute_created.add(node["compute_id"])
|
||||
yield from _move_files_to_compute(compute, project_id, path, os.path.join("project-files", node["node_type"], node["node_id"]))
|
||||
await _move_files_to_compute(compute, project_id, path, os.path.join("project-files", node["node_type"], node["node_id"]))
|
||||
|
||||
# And we dump the updated.gns3
|
||||
dot_gns3_path = os.path.join(path, project_name + ".gns3")
|
||||
@ -153,7 +152,7 @@ def import_project(controller, project_id, stream, location=None, name=None, kee
|
||||
if os.path.exists(os.path.join(path, "images")):
|
||||
_import_images(controller, path)
|
||||
|
||||
project = yield from controller.load_project(dot_gns3_path, load=False)
|
||||
project = await controller.load_project(dot_gns3_path, load=False)
|
||||
return project
|
||||
|
||||
|
||||
@ -176,8 +175,7 @@ def _move_node_file(path, old_id, new_id):
|
||||
shutil.move(node_dir, os.path.join(module_dir, new_id))
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def _move_files_to_compute(compute, project_id, directory, files_path):
|
||||
async def _move_files_to_compute(compute, project_id, directory, files_path):
|
||||
"""
|
||||
Move files to a remote compute
|
||||
"""
|
||||
@ -188,12 +186,11 @@ def _move_files_to_compute(compute, project_id, directory, files_path):
|
||||
for filename in filenames:
|
||||
path = os.path.join(dirpath, filename)
|
||||
dst = os.path.relpath(path, directory)
|
||||
yield from _upload_file(compute, project_id, path, dst)
|
||||
yield from wait_run_in_executor(shutil.rmtree, os.path.join(directory, files_path))
|
||||
await _upload_file(compute, project_id, path, dst)
|
||||
await wait_run_in_executor(shutil.rmtree, os.path.join(directory, files_path))
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def _upload_file(compute, project_id, file_path, path):
|
||||
async def _upload_file(compute, project_id, file_path, path):
|
||||
"""
|
||||
Upload a file to a remote project
|
||||
|
||||
@ -203,7 +200,7 @@ def _upload_file(compute, project_id, file_path, path):
|
||||
|
||||
path = "/projects/{}/files/{}".format(project_id, path.replace("\\", "/"))
|
||||
with open(file_path, "rb") as f:
|
||||
yield from compute.http_query("POST", path, f, timeout=None)
|
||||
await compute.http_query("POST", path, f, timeout=None)
|
||||
|
||||
|
||||
def _import_images(controller, path):
|
||||
|
@ -22,8 +22,6 @@ import html
|
||||
import asyncio
|
||||
import aiohttp
|
||||
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -151,8 +149,7 @@ class Link:
|
||||
return {"frequency_drop": [-1]}
|
||||
return self._filters
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_filters(self, filters):
|
||||
async def update_filters(self, filters):
|
||||
"""
|
||||
Modify the filters list.
|
||||
|
||||
@ -173,15 +170,14 @@ class Link:
|
||||
if new_filters != self.filters:
|
||||
self._filters = new_filters
|
||||
if self._created:
|
||||
yield from self.update()
|
||||
await self.update()
|
||||
self._project.controller.notification.project_emit("link.updated", self.__json__())
|
||||
self._project.dump()
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_suspend(self, value):
|
||||
async def update_suspend(self, value):
|
||||
if value != self._suspended:
|
||||
self._suspended = value
|
||||
yield from self.update()
|
||||
await self.update()
|
||||
self._project.controller.notification.project_emit("link.updated", self.__json__())
|
||||
self._project.dump()
|
||||
|
||||
@ -192,8 +188,7 @@ class Link:
|
||||
"""
|
||||
return self._created
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_node(self, node, adapter_number, port_number, label=None, dump=True):
|
||||
async def add_node(self, node, adapter_number, port_number, label=None, dump=True):
|
||||
"""
|
||||
Add a node to the link
|
||||
|
||||
@ -241,7 +236,7 @@ class Link:
|
||||
})
|
||||
|
||||
if len(self._nodes) == 2:
|
||||
yield from self.create()
|
||||
await self.create()
|
||||
for n in self._nodes:
|
||||
n["node"].add_link(self)
|
||||
n["port"].link = self
|
||||
@ -251,8 +246,7 @@ class Link:
|
||||
if dump:
|
||||
self._project.dump()
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_nodes(self, nodes):
|
||||
async def update_nodes(self, nodes):
|
||||
for node_data in nodes:
|
||||
node = self._project.get_node(node_data["node_id"])
|
||||
for port in self._nodes:
|
||||
@ -263,23 +257,20 @@ class Link:
|
||||
self._project.controller.notification.project_emit("link.updated", self.__json__())
|
||||
self._project.dump()
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Create the link
|
||||
"""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self):
|
||||
async def update(self):
|
||||
"""
|
||||
Update a link
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Delete the link
|
||||
"""
|
||||
@ -289,8 +280,7 @@ class Link:
|
||||
n["port"].link = None
|
||||
n["node"].remove_link(self)
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, data_link_type="DLT_EN10MB", capture_file_name=None):
|
||||
async def start_capture(self, data_link_type="DLT_EN10MB", capture_file_name=None):
|
||||
"""
|
||||
Start capture on the link
|
||||
|
||||
@ -299,11 +289,10 @@ class Link:
|
||||
|
||||
self._capturing = True
|
||||
self._capture_file_name = capture_file_name
|
||||
self._streaming_pcap = asyncio_ensure_future(self._start_streaming_pcap())
|
||||
self._streaming_pcap = asyncio.ensure_future(self._start_streaming_pcap())
|
||||
self._project.controller.notification.project_emit("link.updated", self.__json__())
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_streaming_pcap(self):
|
||||
async def _start_streaming_pcap(self):
|
||||
"""
|
||||
Dump a pcap file on disk
|
||||
"""
|
||||
@ -315,7 +304,7 @@ class Link:
|
||||
raise aiohttp.web.HTTPConflict(text="Could not delete old capture file '{}': {}".format(self.capture_file_path, e))
|
||||
|
||||
try:
|
||||
stream_content = yield from self.read_pcap_from_source()
|
||||
stream_content = await self.read_pcap_from_source()
|
||||
except aiohttp.web.HTTPException as e:
|
||||
error_msg = "Could not stream PCAP file: error {}: {}".format(e.status, e.text)
|
||||
log.error(error_msg)
|
||||
@ -328,7 +317,7 @@ class Link:
|
||||
with open(self.capture_file_path, "wb") as f:
|
||||
while self._capturing:
|
||||
# We read 1 bytes by 1 otherwise the remaining data is not read if the traffic stops
|
||||
data = yield from stream.read(1)
|
||||
data = await stream.read(1)
|
||||
if data:
|
||||
f.write(data)
|
||||
# Flush to disk otherwise the live is not really live
|
||||
@ -338,8 +327,7 @@ class Link:
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPConflict(text="Could not write capture file '{}': {}".format(self.capture_file_path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self):
|
||||
async def stop_capture(self):
|
||||
"""
|
||||
Stop capture on the link
|
||||
"""
|
||||
@ -347,16 +335,14 @@ class Link:
|
||||
self._capturing = False
|
||||
self._project.controller.notification.project_emit("link.updated", self.__json__())
|
||||
|
||||
@asyncio.coroutine
|
||||
def _read_pcap_from_source(self):
|
||||
async def _read_pcap_from_source(self):
|
||||
"""
|
||||
Return a FileStream of the Pcap from the compute server
|
||||
"""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def node_updated(self, node):
|
||||
async def node_updated(self, node):
|
||||
"""
|
||||
Called when a node member of the link is updated
|
||||
"""
|
||||
|
@ -343,8 +343,7 @@ class Node:
|
||||
def links(self):
|
||||
return self._links
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Create the node on the compute server
|
||||
"""
|
||||
@ -358,21 +357,20 @@ class Node:
|
||||
trial = 0
|
||||
while trial != 6:
|
||||
try:
|
||||
response = yield from self._compute.post("/projects/{}/{}/nodes".format(self._project.id, self._node_type), data=data, timeout=timeout)
|
||||
response = await self._compute.post("/projects/{}/{}/nodes".format(self._project.id, self._node_type), data=data, timeout=timeout)
|
||||
except ComputeConflict as e:
|
||||
if e.response.get("exception") == "ImageMissingError":
|
||||
res = yield from self._upload_missing_image(self._node_type, e.response["image"])
|
||||
res = await self._upload_missing_image(self._node_type, e.response["image"])
|
||||
if not res:
|
||||
raise e
|
||||
else:
|
||||
raise e
|
||||
else:
|
||||
yield from self.parse_node_response(response.json)
|
||||
await self.parse_node_response(response.json)
|
||||
return True
|
||||
trial += 1
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self, **kwargs):
|
||||
async def update(self, **kwargs):
|
||||
"""
|
||||
Update the node on the compute server
|
||||
|
||||
@ -403,15 +401,14 @@ class Node:
|
||||
self._list_ports()
|
||||
if update_compute:
|
||||
data = self._node_data(properties=compute_properties)
|
||||
response = yield from self.put(None, data=data)
|
||||
yield from self.parse_node_response(response.json)
|
||||
response = await self.put(None, data=data)
|
||||
await self.parse_node_response(response.json)
|
||||
elif old_json != self.__json__():
|
||||
# We send notif only if object has changed
|
||||
self.project.controller.notification.project_emit("node.updated", self.__json__())
|
||||
self.project.dump()
|
||||
|
||||
@asyncio.coroutine
|
||||
def parse_node_response(self, response):
|
||||
async def parse_node_response(self, response):
|
||||
"""
|
||||
Update the object with the remote node object
|
||||
"""
|
||||
@ -439,7 +436,7 @@ class Node:
|
||||
self._properties[key] = value
|
||||
self._list_ports()
|
||||
for link in self._links:
|
||||
yield from link.node_updated(self)
|
||||
await link.node_updated(self)
|
||||
|
||||
def _node_data(self, properties=None):
|
||||
"""
|
||||
@ -477,12 +474,10 @@ class Node:
|
||||
del data[key]
|
||||
return data
|
||||
|
||||
@asyncio.coroutine
|
||||
def destroy(self):
|
||||
yield from self.delete()
|
||||
async def destroy(self):
|
||||
await self.delete()
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self, data=None):
|
||||
async def start(self, data=None):
|
||||
"""
|
||||
Start a node
|
||||
"""
|
||||
@ -493,57 +488,52 @@ class Node:
|
||||
licence = self._project.controller.settings["IOU"]["iourc_content"]
|
||||
except KeyError:
|
||||
raise aiohttp.web.HTTPConflict(text="IOU licence is not configured")
|
||||
yield from self.post("/start", timeout=240, data={"iourc_content": licence})
|
||||
await self.post("/start", timeout=240, data={"iourc_content": licence})
|
||||
else:
|
||||
yield from self.post("/start", data=data, timeout=240)
|
||||
await self.post("/start", data=data, timeout=240)
|
||||
except asyncio.TimeoutError:
|
||||
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when starting {}".format(self._name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stop a node
|
||||
"""
|
||||
try:
|
||||
yield from self.post("/stop", timeout=240, dont_connect=True)
|
||||
await self.post("/stop", timeout=240, dont_connect=True)
|
||||
# We don't care if a node is down at this step
|
||||
except (ComputeError, aiohttp.ClientError, aiohttp.web.HTTPError):
|
||||
pass
|
||||
except asyncio.TimeoutError:
|
||||
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when stopping {}".format(self._name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def suspend(self):
|
||||
async def suspend(self):
|
||||
"""
|
||||
Suspend a node
|
||||
"""
|
||||
try:
|
||||
yield from self.post("/suspend", timeout=240)
|
||||
await self.post("/suspend", timeout=240)
|
||||
except asyncio.TimeoutError:
|
||||
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when reloading {}".format(self._name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def reload(self):
|
||||
async def reload(self):
|
||||
"""
|
||||
Suspend a node
|
||||
"""
|
||||
try:
|
||||
yield from self.post("/reload", timeout=240)
|
||||
await self.post("/reload", timeout=240)
|
||||
except asyncio.TimeoutError:
|
||||
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when reloading {}".format(self._name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def post(self, path, data=None, **kwargs):
|
||||
async def post(self, path, data=None, **kwargs):
|
||||
"""
|
||||
HTTP post on the node
|
||||
"""
|
||||
if data:
|
||||
return (yield from self._compute.post("/projects/{}/{}/nodes/{}{}".format(self._project.id, self._node_type, self._id, path), data=data, **kwargs))
|
||||
return (await self._compute.post("/projects/{}/{}/nodes/{}{}".format(self._project.id, self._node_type, self._id, path), data=data, **kwargs))
|
||||
else:
|
||||
return (yield from self._compute.post("/projects/{}/{}/nodes/{}{}".format(self._project.id, self._node_type, self._id, path), **kwargs))
|
||||
return (await self._compute.post("/projects/{}/{}/nodes/{}{}".format(self._project.id, self._node_type, self._id, path), **kwargs))
|
||||
|
||||
@asyncio.coroutine
|
||||
def put(self, path, data=None, **kwargs):
|
||||
async def put(self, path, data=None, **kwargs):
|
||||
"""
|
||||
HTTP post on the node
|
||||
"""
|
||||
@ -552,22 +542,20 @@ class Node:
|
||||
else:
|
||||
path = "/projects/{}/{}/nodes/{}{}".format(self._project.id, self._node_type, self._id, path)
|
||||
if data:
|
||||
return (yield from self._compute.put(path, data=data, **kwargs))
|
||||
return (await self._compute.put(path, data=data, **kwargs))
|
||||
else:
|
||||
return (yield from self._compute.put(path, **kwargs))
|
||||
return (await self._compute.put(path, **kwargs))
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self, path=None, **kwargs):
|
||||
async def delete(self, path=None, **kwargs):
|
||||
"""
|
||||
HTTP post on the node
|
||||
"""
|
||||
if path is None:
|
||||
return (yield from self._compute.delete("/projects/{}/{}/nodes/{}".format(self._project.id, self._node_type, self._id), **kwargs))
|
||||
return (await self._compute.delete("/projects/{}/{}/nodes/{}".format(self._project.id, self._node_type, self._id), **kwargs))
|
||||
else:
|
||||
return (yield from self._compute.delete("/projects/{}/{}/nodes/{}{}".format(self._project.id, self._node_type, self._id, path), **kwargs))
|
||||
return (await self._compute.delete("/projects/{}/{}/nodes/{}{}".format(self._project.id, self._node_type, self._id, path), **kwargs))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _upload_missing_image(self, type, img):
|
||||
async def _upload_missing_image(self, type, img):
|
||||
"""
|
||||
Search an image on local computer and upload it to remote compute
|
||||
if the image exists
|
||||
@ -578,26 +566,24 @@ class Node:
|
||||
self.project.controller.notification.project_emit("log.info", {"message": "Uploading missing image {}".format(img)})
|
||||
try:
|
||||
with open(image, 'rb') as f:
|
||||
yield from self._compute.post("/{}/images/{}".format(self._node_type, os.path.basename(img)), data=f, timeout=None)
|
||||
await self._compute.post("/{}/images/{}".format(self._node_type, os.path.basename(img)), data=f, timeout=None)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPConflict(text="Can't upload {}: {}".format(image, str(e)))
|
||||
self.project.controller.notification.project_emit("log.info", {"message": "Upload finished for {}".format(img)})
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def dynamips_auto_idlepc(self):
|
||||
async def dynamips_auto_idlepc(self):
|
||||
"""
|
||||
Compute the idle PC for a dynamips node
|
||||
"""
|
||||
return (yield from self._compute.get("/projects/{}/{}/nodes/{}/auto_idlepc".format(self._project.id, self._node_type, self._id), timeout=240)).json
|
||||
return (await self._compute.get("/projects/{}/{}/nodes/{}/auto_idlepc".format(self._project.id, self._node_type, self._id), timeout=240)).json
|
||||
|
||||
@asyncio.coroutine
|
||||
def dynamips_idlepc_proposals(self):
|
||||
async def dynamips_idlepc_proposals(self):
|
||||
"""
|
||||
Compute a list of potential idle PC
|
||||
"""
|
||||
return (yield from self._compute.get("/projects/{}/{}/nodes/{}/idlepc_proposals".format(self._project.id, self._node_type, self._id), timeout=240)).json
|
||||
return (await self._compute.get("/projects/{}/{}/nodes/{}/idlepc_proposals".format(self._project.id, self._node_type, self._id), timeout=240)).json
|
||||
|
||||
def get_port(self, adapter_number, port_number):
|
||||
"""
|
||||
|
@ -88,8 +88,7 @@ class Notification:
|
||||
"""
|
||||
return project.id in self._project_listeners and len(self._project_listeners[project.id]) > 0
|
||||
|
||||
@asyncio.coroutine
|
||||
def dispatch(self, action, event, compute_id):
|
||||
async def dispatch(self, action, event, compute_id):
|
||||
"""
|
||||
Notification received from compute node. Send it directly
|
||||
to clients or process it
|
||||
@ -103,7 +102,7 @@ class Notification:
|
||||
# Update controller node data and send the event node.updated
|
||||
project = self._controller.get_project(event["project_id"])
|
||||
node = project.get_node(event["node_id"])
|
||||
yield from node.parse_node_response(event)
|
||||
await node.parse_node_response(event)
|
||||
|
||||
self.project_emit("node.updated", node.__json__())
|
||||
except (aiohttp.web.HTTPNotFound, aiohttp.web.HTTPForbidden): # Project closing
|
||||
|
@ -38,7 +38,6 @@ from ..utils.path import check_path_allowed, get_default_project_directory
|
||||
from ..utils.asyncio.pool import Pool
|
||||
from ..utils.asyncio import locking
|
||||
from ..utils.asyncio import wait_run_in_executor
|
||||
from ..utils.asyncio import asyncio_ensure_future
|
||||
from .export_project import export_project
|
||||
from .import_project import import_project
|
||||
|
||||
@ -122,8 +121,7 @@ class Project:
|
||||
assert self._status != "closed"
|
||||
self.dump()
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self, **kwargs):
|
||||
async def update(self, **kwargs):
|
||||
"""
|
||||
Update the project
|
||||
:param kwargs: Project properties
|
||||
@ -141,7 +139,7 @@ class Project:
|
||||
|
||||
# update on computes
|
||||
for compute in list(self._project_created_on_compute):
|
||||
yield from compute.put(
|
||||
await compute.put(
|
||||
"/projects/{}".format(self._id), {
|
||||
"variables": self.variables
|
||||
}
|
||||
@ -462,8 +460,7 @@ class Project:
|
||||
return new_name
|
||||
|
||||
@open_required
|
||||
@asyncio.coroutine
|
||||
def add_node_from_appliance(self, appliance_id, x=0, y=0, compute_id=None):
|
||||
async def add_node_from_appliance(self, appliance_id, x=0, y=0, compute_id=None):
|
||||
"""
|
||||
Create a node from an appliance
|
||||
"""
|
||||
@ -481,12 +478,11 @@ class Project:
|
||||
default_name_format = template.pop("default_name_format", "{name}-{0}")
|
||||
name = default_name_format.replace("{name}", name)
|
||||
node_id = str(uuid.uuid4())
|
||||
node = yield from self.add_node(compute, name, node_id, node_type=node_type, appliance_id=appliance_id, **template)
|
||||
node = await self.add_node(compute, name, node_id, node_type=node_type, appliance_id=appliance_id, **template)
|
||||
return node
|
||||
|
||||
@open_required
|
||||
@asyncio.coroutine
|
||||
def add_node(self, compute, name, node_id, dump=True, node_type=None, **kwargs):
|
||||
async def add_node(self, compute, name, node_id, dump=True, node_type=None, **kwargs):
|
||||
"""
|
||||
Create a node or return an existing node
|
||||
|
||||
@ -515,10 +511,10 @@ class Project:
|
||||
if self._variables:
|
||||
data["variables"] = self._variables
|
||||
|
||||
yield from compute.post("/projects", data=data)
|
||||
await compute.post("/projects", data=data)
|
||||
|
||||
self._project_created_on_compute.add(compute)
|
||||
yield from node.create()
|
||||
await node.create()
|
||||
self._nodes[node.id] = node
|
||||
self.controller.notification.project_emit("node.created", node.__json__())
|
||||
if dump:
|
||||
@ -526,8 +522,7 @@ class Project:
|
||||
return node
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def __delete_node_links(self, node):
|
||||
async def __delete_node_links(self, node):
|
||||
"""
|
||||
Delete all link connected to this node.
|
||||
|
||||
@ -536,16 +531,15 @@ class Project:
|
||||
"""
|
||||
for link in list(self._links.values()):
|
||||
if node in link.nodes:
|
||||
yield from self.delete_link(link.id, force_delete=True)
|
||||
await self.delete_link(link.id, force_delete=True)
|
||||
|
||||
@open_required
|
||||
@asyncio.coroutine
|
||||
def delete_node(self, node_id):
|
||||
async def delete_node(self, node_id):
|
||||
node = self.get_node(node_id)
|
||||
yield from self.__delete_node_links(node)
|
||||
await self.__delete_node_links(node)
|
||||
self.remove_allocated_node_name(node.name)
|
||||
del self._nodes[node.id]
|
||||
yield from node.destroy()
|
||||
await node.destroy()
|
||||
self.dump()
|
||||
self.controller.notification.project_emit("node.deleted", node.__json__())
|
||||
|
||||
@ -602,8 +596,7 @@ class Project:
|
||||
return self._drawings
|
||||
|
||||
@open_required
|
||||
@asyncio.coroutine
|
||||
def add_drawing(self, drawing_id=None, dump=True, **kwargs):
|
||||
async def add_drawing(self, drawing_id=None, dump=True, **kwargs):
|
||||
"""
|
||||
Create an drawing or return an existing drawing
|
||||
|
||||
@ -630,16 +623,14 @@ class Project:
|
||||
raise aiohttp.web.HTTPNotFound(text="Drawing ID {} doesn't exist".format(drawing_id))
|
||||
|
||||
@open_required
|
||||
@asyncio.coroutine
|
||||
def delete_drawing(self, drawing_id):
|
||||
async def delete_drawing(self, drawing_id):
|
||||
drawing = self.get_drawing(drawing_id)
|
||||
del self._drawings[drawing.id]
|
||||
self.dump()
|
||||
self.controller.notification.project_emit("drawing.deleted", drawing.__json__())
|
||||
|
||||
@open_required
|
||||
@asyncio.coroutine
|
||||
def add_link(self, link_id=None, dump=True):
|
||||
async def add_link(self, link_id=None, dump=True):
|
||||
"""
|
||||
Create a link. By default the link is empty
|
||||
|
||||
@ -654,12 +645,11 @@ class Project:
|
||||
return link
|
||||
|
||||
@open_required
|
||||
@asyncio.coroutine
|
||||
def delete_link(self, link_id, force_delete=False):
|
||||
async def delete_link(self, link_id, force_delete=False):
|
||||
link = self.get_link(link_id)
|
||||
del self._links[link.id]
|
||||
try:
|
||||
yield from link.delete()
|
||||
await link.delete()
|
||||
except Exception:
|
||||
if force_delete is False:
|
||||
raise
|
||||
@ -703,8 +693,7 @@ class Project:
|
||||
raise aiohttp.web.HTTPNotFound(text="Snapshot ID {} doesn't exist".format(snapshot_id))
|
||||
|
||||
@open_required
|
||||
@asyncio.coroutine
|
||||
def snapshot(self, name):
|
||||
async def snapshot(self, name):
|
||||
"""
|
||||
Snapshot the project
|
||||
|
||||
@ -714,25 +703,23 @@ class Project:
|
||||
if name in [snap.name for snap in self._snapshots.values()]:
|
||||
raise aiohttp.web.HTTPConflict(text="The snapshot name {} already exists".format(name))
|
||||
snapshot = Snapshot(self, name=name)
|
||||
yield from snapshot.create()
|
||||
await snapshot.create()
|
||||
self._snapshots[snapshot.id] = snapshot
|
||||
return snapshot
|
||||
|
||||
@open_required
|
||||
@asyncio.coroutine
|
||||
def delete_snapshot(self, snapshot_id):
|
||||
async def delete_snapshot(self, snapshot_id):
|
||||
snapshot = self.get_snapshot(snapshot_id)
|
||||
del self._snapshots[snapshot.id]
|
||||
os.remove(snapshot.path)
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self, ignore_notification=False):
|
||||
async def close(self, ignore_notification=False):
|
||||
if self._status == "closed":
|
||||
return
|
||||
yield from self.stop_all()
|
||||
await self.stop_all()
|
||||
for compute in list(self._project_created_on_compute):
|
||||
try:
|
||||
yield from compute.post("/projects/{}/close".format(self._id), dont_connect=True)
|
||||
await compute.post("/projects/{}/close".format(self._id), dont_connect=True)
|
||||
# We don't care if a compute is down at this step
|
||||
except (ComputeError, aiohttp.web.HTTPError, aiohttp.ClientResponseError, TimeoutError):
|
||||
pass
|
||||
@ -771,30 +758,28 @@ class Project:
|
||||
except OSError as e:
|
||||
log.warning(str(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
|
||||
if self._status != "opened":
|
||||
try:
|
||||
yield from self.open()
|
||||
await self.open()
|
||||
except aiohttp.web.HTTPConflict as e:
|
||||
# ignore missing images or other conflicts when deleting a project
|
||||
log.warning("Conflict while deleting project: {}".format(e.text))
|
||||
yield from self.delete_on_computes()
|
||||
yield from self.close()
|
||||
await self.delete_on_computes()
|
||||
await self.close()
|
||||
try:
|
||||
shutil.rmtree(self.path)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPConflict(text="Can not delete project directory {}: {}".format(self.path, str(e)))
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete_on_computes(self):
|
||||
async def delete_on_computes(self):
|
||||
"""
|
||||
Delete the project on computes but not on controller
|
||||
"""
|
||||
for compute in list(self._project_created_on_compute):
|
||||
if compute.id != "local":
|
||||
yield from compute.delete("/projects/{}".format(self._id))
|
||||
await compute.delete("/projects/{}".format(self._id))
|
||||
self._project_created_on_compute.remove(compute)
|
||||
|
||||
@classmethod
|
||||
@ -817,8 +802,7 @@ class Project:
|
||||
return os.path.join(self.path, self._filename)
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def open(self):
|
||||
async def open(self):
|
||||
"""
|
||||
Load topology elements
|
||||
"""
|
||||
@ -862,19 +846,19 @@ class Project:
|
||||
|
||||
topology = project_data["topology"]
|
||||
for compute in topology.get("computes", []):
|
||||
yield from self.controller.add_compute(**compute)
|
||||
await self.controller.add_compute(**compute)
|
||||
for node in topology.get("nodes", []):
|
||||
compute = self.controller.get_compute(node.pop("compute_id"))
|
||||
name = node.pop("name")
|
||||
node_id = node.pop("node_id", str(uuid.uuid4()))
|
||||
yield from self.add_node(compute, name, node_id, dump=False, **node)
|
||||
await self.add_node(compute, name, node_id, dump=False, **node)
|
||||
for link_data in topology.get("links", []):
|
||||
if 'link_id' not in link_data.keys():
|
||||
# skip the link
|
||||
continue
|
||||
link = yield from self.add_link(link_id=link_data["link_id"])
|
||||
link = await self.add_link(link_id=link_data["link_id"])
|
||||
if "filters" in link_data:
|
||||
yield from link.update_filters(link_data["filters"])
|
||||
await link.update_filters(link_data["filters"])
|
||||
for node_link in link_data.get("nodes", []):
|
||||
node = self.get_node(node_link["node_id"])
|
||||
port = node.get_port(node_link["adapter_number"], node_link["port_number"])
|
||||
@ -884,19 +868,19 @@ class Project:
|
||||
if port.link is not None:
|
||||
log.warning("Port {}/{} is already connected to link ID {}".format(node_link["adapter_number"], node_link["port_number"], port.link.id))
|
||||
continue
|
||||
yield from link.add_node(node, node_link["adapter_number"], node_link["port_number"], label=node_link.get("label"), dump=False)
|
||||
await link.add_node(node, node_link["adapter_number"], node_link["port_number"], label=node_link.get("label"), dump=False)
|
||||
if len(link.nodes) != 2:
|
||||
# a link should have 2 attached nodes, this can happen with corrupted projects
|
||||
yield from self.delete_link(link.id, force_delete=True)
|
||||
await self.delete_link(link.id, force_delete=True)
|
||||
for drawing_data in topology.get("drawings", []):
|
||||
yield from self.add_drawing(dump=False, **drawing_data)
|
||||
await self.add_drawing(dump=False, **drawing_data)
|
||||
|
||||
self.dump()
|
||||
# We catch all error to be able to rollback the .gns3 to the previous state
|
||||
except Exception as e:
|
||||
for compute in list(self._project_created_on_compute):
|
||||
try:
|
||||
yield from compute.post("/projects/{}/close".format(self._id))
|
||||
await compute.post("/projects/{}/close".format(self._id))
|
||||
# We don't care if a compute is down at this step
|
||||
except (ComputeError, aiohttp.web.HTTPNotFound, aiohttp.web.HTTPConflict, aiohttp.ServerDisconnectedError):
|
||||
pass
|
||||
@ -922,15 +906,14 @@ class Project:
|
||||
# Start all in the background without waiting for completion
|
||||
# we ignore errors because we want to let the user open
|
||||
# their project and fix it
|
||||
asyncio_ensure_future(self.start_all())
|
||||
asyncio.ensure_future(self.start_all())
|
||||
|
||||
@asyncio.coroutine
|
||||
def wait_loaded(self):
|
||||
async def wait_loaded(self):
|
||||
"""
|
||||
Wait until the project finish loading
|
||||
"""
|
||||
while self._loading:
|
||||
yield from asyncio.sleep(0.5)
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
def _create_duplicate_project_file(self, path, zipstream):
|
||||
"""
|
||||
@ -941,8 +924,7 @@ class Project:
|
||||
for data in zipstream:
|
||||
f.write(data)
|
||||
|
||||
@asyncio.coroutine
|
||||
def duplicate(self, name=None, location=None):
|
||||
async def duplicate(self, name=None, location=None):
|
||||
"""
|
||||
Duplicate a project
|
||||
|
||||
@ -956,22 +938,22 @@ class Project:
|
||||
# If the project was not open we open it temporary
|
||||
previous_status = self._status
|
||||
if self._status == "closed":
|
||||
yield from self.open()
|
||||
await self.open()
|
||||
|
||||
self.dump()
|
||||
assert self._status != "closed"
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
zipstream = yield from export_project(self, tmpdir, keep_compute_id=True, allow_all_nodes=True)
|
||||
zipstream = await export_project(self, tmpdir, keep_compute_id=True, allow_all_nodes=True)
|
||||
project_path = os.path.join(tmpdir, "project.gns3p")
|
||||
yield from wait_run_in_executor(self._create_duplicate_project_file, project_path, zipstream)
|
||||
await wait_run_in_executor(self._create_duplicate_project_file, project_path, zipstream)
|
||||
with open(project_path, "rb") as f:
|
||||
project = yield from import_project(self._controller, str(uuid.uuid4()), f, location=location, name=name, keep_compute_id=True)
|
||||
project = await import_project(self._controller, str(uuid.uuid4()), f, location=location, name=name, keep_compute_id=True)
|
||||
except (ValueError, OSError, UnicodeEncodeError) as e:
|
||||
raise aiohttp.web.HTTPConflict(text="Cannot duplicate project: {}".format(str(e)))
|
||||
|
||||
if previous_status == "closed":
|
||||
yield from self.close()
|
||||
await self.close()
|
||||
|
||||
return project
|
||||
|
||||
@ -999,38 +981,34 @@ class Project:
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not write topology: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_all(self):
|
||||
async def start_all(self):
|
||||
"""
|
||||
Start all nodes
|
||||
"""
|
||||
pool = Pool(concurrency=3)
|
||||
for node in self.nodes.values():
|
||||
pool.append(node.start)
|
||||
yield from pool.join()
|
||||
await pool.join()
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_all(self):
|
||||
async def stop_all(self):
|
||||
"""
|
||||
Stop all nodes
|
||||
"""
|
||||
pool = Pool(concurrency=3)
|
||||
for node in self.nodes.values():
|
||||
pool.append(node.stop)
|
||||
yield from pool.join()
|
||||
await pool.join()
|
||||
|
||||
@asyncio.coroutine
|
||||
def suspend_all(self):
|
||||
async def suspend_all(self):
|
||||
"""
|
||||
Suspend all nodes
|
||||
"""
|
||||
pool = Pool(concurrency=3)
|
||||
for node in self.nodes.values():
|
||||
pool.append(node.suspend)
|
||||
yield from pool.join()
|
||||
await pool.join()
|
||||
|
||||
@asyncio.coroutine
|
||||
def duplicate_node(self, node, x, y, z):
|
||||
async def duplicate_node(self, node, x, y, z):
|
||||
"""
|
||||
Duplicate a node
|
||||
|
||||
@ -1061,21 +1039,21 @@ class Project:
|
||||
data['y'] = y
|
||||
data['z'] = z
|
||||
new_node_uuid = str(uuid.uuid4())
|
||||
new_node = yield from self.add_node(
|
||||
new_node = await self.add_node(
|
||||
node.compute,
|
||||
node.name,
|
||||
new_node_uuid,
|
||||
node_type=node_type,
|
||||
**data)
|
||||
try:
|
||||
yield from node.post("/duplicate", timeout=None, data={
|
||||
await node.post("/duplicate", timeout=None, data={
|
||||
"destination_node_id": new_node_uuid
|
||||
})
|
||||
except aiohttp.web.HTTPNotFound as e:
|
||||
yield from self.delete_node(new_node_uuid)
|
||||
await self.delete_node(new_node_uuid)
|
||||
raise aiohttp.web.HTTPConflict(text="This node type cannot be duplicated")
|
||||
except aiohttp.web.HTTPConflict as e:
|
||||
yield from self.delete_node(new_node_uuid)
|
||||
await self.delete_node(new_node_uuid)
|
||||
raise e
|
||||
return new_node
|
||||
|
||||
|
@ -82,8 +82,7 @@ class Snapshot:
|
||||
for data in zipstream:
|
||||
f.write(data)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Create the snapshot
|
||||
"""
|
||||
@ -99,31 +98,30 @@ class Snapshot:
|
||||
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
zipstream = yield from export_project(self._project, tmpdir, keep_compute_id=True, allow_all_nodes=True)
|
||||
yield from wait_run_in_executor(self._create_snapshot_file, zipstream)
|
||||
zipstream = await export_project(self._project, tmpdir, keep_compute_id=True, allow_all_nodes=True)
|
||||
await wait_run_in_executor(self._create_snapshot_file, zipstream)
|
||||
except (ValueError, OSError, RuntimeError) as e:
|
||||
raise aiohttp.web.HTTPConflict(text="Could not create snapshot file '{}': {}".format(self.path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def restore(self):
|
||||
async def restore(self):
|
||||
"""
|
||||
Restore the snapshot
|
||||
"""
|
||||
|
||||
yield from self._project.delete_on_computes()
|
||||
await self._project.delete_on_computes()
|
||||
# We don't send close notification to clients because the close / open dance is purely internal
|
||||
yield from self._project.close(ignore_notification=True)
|
||||
await self._project.close(ignore_notification=True)
|
||||
|
||||
try:
|
||||
# delete the current project files
|
||||
project_files_path = os.path.join(self._project.path, "project-files")
|
||||
if os.path.exists(project_files_path):
|
||||
yield from wait_run_in_executor(shutil.rmtree, project_files_path)
|
||||
await wait_run_in_executor(shutil.rmtree, project_files_path)
|
||||
with open(self._path, "rb") as f:
|
||||
project = yield from import_project(self._project.controller, self._project.id, f, location=self._project.path)
|
||||
project = await import_project(self._project.controller, self._project.id, f, location=self._project.path)
|
||||
except (OSError, PermissionError) as e:
|
||||
raise aiohttp.web.HTTPConflict(text=str(e))
|
||||
yield from project.open()
|
||||
await project.open()
|
||||
self._project.controller.notification.project_emit("snapshot.restored", self.__json__())
|
||||
return self._project
|
||||
|
||||
|
@ -37,8 +37,7 @@ class UDPLink(Link):
|
||||
"""
|
||||
return self._link_data
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Create the link on the nodes
|
||||
"""
|
||||
@ -52,14 +51,14 @@ class UDPLink(Link):
|
||||
|
||||
# Get an IP allowing communication between both host
|
||||
try:
|
||||
(node1_host, node2_host) = yield from node1.compute.get_ip_on_same_subnet(node2.compute)
|
||||
(node1_host, node2_host) = await node1.compute.get_ip_on_same_subnet(node2.compute)
|
||||
except ValueError as e:
|
||||
raise aiohttp.web.HTTPConflict(text="Cannot get an IP address on same subnet: {}".format(e))
|
||||
|
||||
# Reserve a UDP port on both side
|
||||
response = yield from node1.compute.post("/projects/{}/ports/udp".format(self._project.id))
|
||||
response = await node1.compute.post("/projects/{}/ports/udp".format(self._project.id))
|
||||
self._node1_port = response.json["udp_port"]
|
||||
response = yield from node2.compute.post("/projects/{}/ports/udp".format(self._project.id))
|
||||
response = await node2.compute.post("/projects/{}/ports/udp".format(self._project.id))
|
||||
self._node2_port = response.json["udp_port"]
|
||||
|
||||
node1_filters = {}
|
||||
@ -79,7 +78,7 @@ class UDPLink(Link):
|
||||
"filters": node1_filters,
|
||||
"suspend": self._suspended
|
||||
})
|
||||
yield from node1.post("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1), data=self._link_data[0], timeout=120)
|
||||
await node1.post("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1), data=self._link_data[0], timeout=120)
|
||||
|
||||
self._link_data.append({
|
||||
"lport": self._node2_port,
|
||||
@ -90,15 +89,14 @@ class UDPLink(Link):
|
||||
"suspend": self._suspended
|
||||
})
|
||||
try:
|
||||
yield from node2.post("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number2, port_number=port_number2), data=self._link_data[1], timeout=120)
|
||||
await node2.post("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number2, port_number=port_number2), data=self._link_data[1], timeout=120)
|
||||
except Exception as e:
|
||||
# We clean the first NIO
|
||||
yield from node1.delete("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1), timeout=120)
|
||||
await node1.delete("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1), timeout=120)
|
||||
raise e
|
||||
self._created = True
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self):
|
||||
async def update(self):
|
||||
"""
|
||||
Update the link on the nodes
|
||||
"""
|
||||
@ -120,16 +118,15 @@ class UDPLink(Link):
|
||||
port_number1 = self._nodes[0]["port_number"]
|
||||
self._link_data[0]["filters"] = node1_filters
|
||||
self._link_data[0]["suspend"] = self._suspended
|
||||
yield from node1.put("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1), data=self._link_data[0], timeout=120)
|
||||
await node1.put("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1), data=self._link_data[0], timeout=120)
|
||||
|
||||
adapter_number2 = self._nodes[1]["adapter_number"]
|
||||
port_number2 = self._nodes[1]["port_number"]
|
||||
self._link_data[1]["filters"] = node2_filters
|
||||
self._link_data[1]["suspend"] = self._suspended
|
||||
yield from node2.put("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number2, port_number=port_number2), data=self._link_data[1], timeout=221)
|
||||
await node2.put("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number2, port_number=port_number2), data=self._link_data[1], timeout=221)
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Delete the link and free the resources
|
||||
"""
|
||||
@ -142,7 +139,7 @@ class UDPLink(Link):
|
||||
except IndexError:
|
||||
return
|
||||
try:
|
||||
yield from node1.delete("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1), timeout=120)
|
||||
await node1.delete("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number1, port_number=port_number1), timeout=120)
|
||||
# If the node is already delete (user selected multiple element and delete all in the same time)
|
||||
except aiohttp.web.HTTPNotFound:
|
||||
pass
|
||||
@ -154,14 +151,13 @@ class UDPLink(Link):
|
||||
except IndexError:
|
||||
return
|
||||
try:
|
||||
yield from node2.delete("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number2, port_number=port_number2), timeout=120)
|
||||
await node2.delete("/adapters/{adapter_number}/ports/{port_number}/nio".format(adapter_number=adapter_number2, port_number=port_number2), timeout=120)
|
||||
# If the node is already delete (user selected multiple element and delete all in the same time)
|
||||
except aiohttp.web.HTTPNotFound:
|
||||
pass
|
||||
yield from super().delete()
|
||||
await super().delete()
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, data_link_type="DLT_EN10MB", capture_file_name=None):
|
||||
async def start_capture(self, data_link_type="DLT_EN10MB", capture_file_name=None):
|
||||
"""
|
||||
Start capture on a link
|
||||
"""
|
||||
@ -172,18 +168,17 @@ class UDPLink(Link):
|
||||
"capture_file_name": capture_file_name,
|
||||
"data_link_type": data_link_type
|
||||
}
|
||||
yield from self._capture_node["node"].post("/adapters/{adapter_number}/ports/{port_number}/start_capture".format(adapter_number=self._capture_node["adapter_number"], port_number=self._capture_node["port_number"]), data=data)
|
||||
yield from super().start_capture(data_link_type=data_link_type, capture_file_name=capture_file_name)
|
||||
await self._capture_node["node"].post("/adapters/{adapter_number}/ports/{port_number}/start_capture".format(adapter_number=self._capture_node["adapter_number"], port_number=self._capture_node["port_number"]), data=data)
|
||||
await super().start_capture(data_link_type=data_link_type, capture_file_name=capture_file_name)
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self):
|
||||
async def stop_capture(self):
|
||||
"""
|
||||
Stop capture on a link
|
||||
"""
|
||||
if self._capture_node:
|
||||
yield from self._capture_node["node"].post("/adapters/{adapter_number}/ports/{port_number}/stop_capture".format(adapter_number=self._capture_node["adapter_number"], port_number=self._capture_node["port_number"]))
|
||||
await self._capture_node["node"].post("/adapters/{adapter_number}/ports/{port_number}/stop_capture".format(adapter_number=self._capture_node["adapter_number"], port_number=self._capture_node["port_number"]))
|
||||
self._capture_node = None
|
||||
yield from super().stop_capture()
|
||||
await super().stop_capture()
|
||||
|
||||
def _choose_capture_side(self):
|
||||
"""
|
||||
@ -215,8 +210,7 @@ class UDPLink(Link):
|
||||
|
||||
raise aiohttp.web.HTTPConflict(text="Cannot capture because there is no running device on this link")
|
||||
|
||||
@asyncio.coroutine
|
||||
def read_pcap_from_source(self):
|
||||
async def read_pcap_from_source(self):
|
||||
"""
|
||||
Return a FileStream of the Pcap from the compute node
|
||||
"""
|
||||
@ -224,10 +218,9 @@ class UDPLink(Link):
|
||||
compute = self._capture_node["node"].compute
|
||||
return compute.stream_file(self._project, "tmp/captures/" + self._capture_file_name)
|
||||
|
||||
@asyncio.coroutine
|
||||
def node_updated(self, node):
|
||||
async def node_updated(self, node):
|
||||
"""
|
||||
Called when a node member of the link is updated
|
||||
"""
|
||||
if self._capture_node and node == self._capture_node["node"] and node.status != "started":
|
||||
yield from self.stop_capture()
|
||||
await self.stop_capture()
|
||||
|
@ -48,11 +48,11 @@ class ATMSwitchHandler:
|
||||
description="Create a new ATM switch instance",
|
||||
input=ATM_SWITCH_CREATE_SCHEMA,
|
||||
output=ATM_SWITCH_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
# Use the Dynamips ATM switch to simulate this node
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = yield from dynamips_manager.create_node(request.json.pop("name"),
|
||||
node = await dynamips_manager.create_node(request.json.pop("name"),
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
node_type="atm_switch",
|
||||
@ -90,9 +90,9 @@ class ATMSwitchHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Duplicate an atm switch instance")
|
||||
def duplicate(request, response):
|
||||
async def duplicate(request, response):
|
||||
|
||||
new_node = yield from Dynamips.instance().duplicate_node(
|
||||
new_node = await Dynamips.instance().duplicate_node(
|
||||
request.match_info["node_id"],
|
||||
request.json["destination_node_id"]
|
||||
)
|
||||
@ -114,12 +114,12 @@ class ATMSwitchHandler:
|
||||
description="Update an ATM switch instance",
|
||||
input=ATM_SWITCH_UPDATE_SCHEMA,
|
||||
output=ATM_SWITCH_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
async def update(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
if "name" in request.json and node.name != request.json["name"]:
|
||||
yield from node.set_name(request.json["name"])
|
||||
await node.set_name(request.json["name"])
|
||||
if "mappings" in request.json:
|
||||
node.mappings = request.json["mappings"]
|
||||
node.updated()
|
||||
@ -137,10 +137,10 @@ class ATMSwitchHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete an ATM switch instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
yield from dynamips_manager.delete_node(request.match_info["node_id"])
|
||||
await dynamips_manager.delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -210,13 +210,13 @@ class ATMSwitchHandler:
|
||||
description="Add a NIO to an ATM switch instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = yield from dynamips_manager.create_nio(node, request.json)
|
||||
nio = await dynamips_manager.create_nio(node, request.json)
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.add_nio(nio, port_number)
|
||||
await node.add_nio(nio, port_number)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@ -234,13 +234,13 @@ class ATMSwitchHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from an ATM switch instance")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
nio = yield from node.remove_nio(port_number)
|
||||
yield from nio.delete()
|
||||
nio = await node.remove_nio(port_number)
|
||||
await nio.delete()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -258,13 +258,13 @@ class ATMSwitchHandler:
|
||||
},
|
||||
description="Start a packet capture on an ATM switch instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
yield from node.start_capture(port_number, pcap_file_path, request.json["data_link_type"])
|
||||
await node.start_capture(port_number, pcap_file_path, request.json["data_link_type"])
|
||||
response.json({"pcap_file_path": pcap_file_path})
|
||||
|
||||
@Route.post(
|
||||
@ -281,10 +281,10 @@ class ATMSwitchHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a packet capture on an ATM switch instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.stop_capture(port_number)
|
||||
await node.stop_capture(port_number)
|
||||
response.set_status(204)
|
||||
|
@ -49,10 +49,10 @@ class CloudHandler:
|
||||
description="Create a new cloud instance",
|
||||
input=CLOUD_CREATE_SCHEMA,
|
||||
output=CLOUD_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = yield from builtin_manager.create_node(request.json.pop("name"),
|
||||
node = await builtin_manager.create_node(request.json.pop("name"),
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
node_type="cloud",
|
||||
@ -123,10 +123,10 @@ class CloudHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete a cloud instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
yield from builtin_manager.delete_node(request.match_info["node_id"])
|
||||
await builtin_manager.delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -141,10 +141,10 @@ class CloudHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Start a cloud")
|
||||
def start(request, response):
|
||||
async def start(request, response):
|
||||
|
||||
node = Builtin.instance().get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from node.start()
|
||||
await node.start()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -197,13 +197,13 @@ class CloudHandler:
|
||||
description="Add a NIO to a cloud instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = builtin_manager.create_nio(request.json)
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.add_nio(nio, port_number)
|
||||
await node.add_nio(nio, port_number)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@ -223,7 +223,7 @@ class CloudHandler:
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA,
|
||||
description="Update a NIO from a Cloud instance")
|
||||
def update_nio(request, response):
|
||||
async def update_nio(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -236,7 +236,7 @@ class CloudHandler:
|
||||
|
||||
if "filters" in request.json and nio:
|
||||
nio.filters = request.json["filters"]
|
||||
yield from node.update_nio(int(request.match_info["port_number"]), nio)
|
||||
await node.update_nio(int(request.match_info["port_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(request.json)
|
||||
|
||||
@ -254,12 +254,12 @@ class CloudHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from a cloud instance")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.remove_nio(port_number)
|
||||
await node.remove_nio(port_number)
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -277,13 +277,13 @@ class CloudHandler:
|
||||
},
|
||||
description="Start a packet capture on a cloud instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
yield from node.start_capture(port_number, pcap_file_path, request.json["data_link_type"])
|
||||
await node.start_capture(port_number, pcap_file_path, request.json["data_link_type"])
|
||||
response.json({"pcap_file_path": pcap_file_path})
|
||||
|
||||
@Route.post(
|
||||
@ -300,10 +300,10 @@ class CloudHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a packet capture on a cloud instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.stop_capture(port_number)
|
||||
await node.stop_capture(port_number)
|
||||
response.set_status(204)
|
||||
|
@ -46,9 +46,9 @@ class DockerHandler:
|
||||
description="Create a new Docker container",
|
||||
input=DOCKER_CREATE_SCHEMA,
|
||||
output=DOCKER_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
docker_manager = Docker.instance()
|
||||
container = yield from docker_manager.create_node(request.json.pop("name"),
|
||||
container = await docker_manager.create_node(request.json.pop("name"),
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
image=request.json.pop("image"),
|
||||
@ -82,10 +82,10 @@ class DockerHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Start a Docker container")
|
||||
def start(request, response):
|
||||
async def start(request, response):
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from container.start()
|
||||
await container.start()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -100,10 +100,10 @@ class DockerHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a Docker container")
|
||||
def stop(request, response):
|
||||
async def stop(request, response):
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from container.stop()
|
||||
await container.stop()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -118,10 +118,10 @@ class DockerHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Suspend a Docker container")
|
||||
def suspend(request, response):
|
||||
async def suspend(request, response):
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from container.pause()
|
||||
await container.pause()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -136,10 +136,10 @@ class DockerHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Restart a Docker container")
|
||||
def reload(request, response):
|
||||
async def reload(request, response):
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from container.restart()
|
||||
await container.restart()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.delete(
|
||||
@ -154,10 +154,10 @@ class DockerHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete a Docker container")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from container.delete()
|
||||
await container.delete()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -171,9 +171,9 @@ class DockerHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Duplicate a Docker instance")
|
||||
def duplicate(request, response):
|
||||
async def duplicate(request, response):
|
||||
|
||||
new_node = yield from Docker.instance().duplicate_node(
|
||||
new_node = await Docker.instance().duplicate_node(
|
||||
request.match_info["node_id"],
|
||||
request.json["destination_node_id"]
|
||||
)
|
||||
@ -192,10 +192,10 @@ class DockerHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Pause a Docker container")
|
||||
def pause(request, response):
|
||||
async def pause(request, response):
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from container.pause()
|
||||
await container.pause()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -210,10 +210,10 @@ class DockerHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Unpause a Docker container")
|
||||
def unpause(request, response):
|
||||
async def unpause(request, response):
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from container.unpause()
|
||||
await container.unpause()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -232,14 +232,14 @@ class DockerHandler:
|
||||
description="Add a NIO to a Docker container",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio_type = request.json["type"]
|
||||
if nio_type != "nio_udp":
|
||||
raise HTTPConflict(text="NIO of type {} is not supported".format(nio_type))
|
||||
nio = docker_manager.create_nio(request.json)
|
||||
yield from container.adapter_add_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
await container.adapter_add_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@ -259,14 +259,14 @@ class DockerHandler:
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA,
|
||||
description="Update a NIO from a Docker instance")
|
||||
def update_nio(request, response):
|
||||
async def update_nio(request, response):
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = container.ethernet_adapters[int(request.match_info["adapter_number"])].get_nio(0)
|
||||
if "filters" in request.json and nio:
|
||||
nio.filters = request.json["filters"]
|
||||
yield from container.adapter_update_nio_binding(int(request.match_info["port_number"]), nio)
|
||||
await container.adapter_update_nio_binding(int(request.match_info["port_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(request.json)
|
||||
|
||||
@ -284,10 +284,10 @@ class DockerHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from a Docker container")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from container.adapter_remove_nio_binding(int(request.match_info["adapter_number"]))
|
||||
await container.adapter_remove_nio_binding(int(request.match_info["adapter_number"]))
|
||||
response.set_status(204)
|
||||
|
||||
@Route.put(
|
||||
@ -305,7 +305,7 @@ class DockerHandler:
|
||||
description="Update a Docker instance",
|
||||
input=DOCKER_OBJECT_SCHEMA,
|
||||
output=DOCKER_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
async def update(request, response):
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -323,7 +323,7 @@ class DockerHandler:
|
||||
changed = True
|
||||
# We don't call container.update for nothing because it will restart the container
|
||||
if changed:
|
||||
yield from container.update()
|
||||
await container.update()
|
||||
container.updated()
|
||||
response.json(container)
|
||||
|
||||
@ -343,14 +343,14 @@ class DockerHandler:
|
||||
},
|
||||
description="Start a packet capture on a Docker container instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
adapter_number = int(request.match_info["adapter_number"])
|
||||
pcap_file_path = os.path.join(container.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
|
||||
yield from container.start_capture(adapter_number, pcap_file_path)
|
||||
await container.start_capture(adapter_number, pcap_file_path)
|
||||
response.json({"pcap_file_path": str(pcap_file_path)})
|
||||
|
||||
@Route.post(
|
||||
@ -368,13 +368,13 @@ class DockerHandler:
|
||||
409: "Container not started"
|
||||
},
|
||||
description="Stop a packet capture on a Docker container instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
docker_manager = Docker.instance()
|
||||
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
|
||||
adapter_number = int(request.match_info["adapter_number"])
|
||||
yield from container.stop_capture(adapter_number)
|
||||
await container.stop_capture(adapter_number)
|
||||
response.set_status(204)
|
||||
|
||||
@Route.get(
|
||||
@ -384,7 +384,7 @@ class DockerHandler:
|
||||
},
|
||||
output=DOCKER_LIST_IMAGES_SCHEMA,
|
||||
description="Get all available Docker images")
|
||||
def show(request, response):
|
||||
async def show(request, response):
|
||||
docker_manager = Docker.instance()
|
||||
images = yield from docker_manager.list_images()
|
||||
images = await docker_manager.list_images()
|
||||
response.json(images)
|
||||
|
@ -62,14 +62,14 @@ class DynamipsVMHandler:
|
||||
description="Create a new Dynamips VM instance",
|
||||
input=VM_CREATE_SCHEMA,
|
||||
output=VM_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
platform = request.json.pop("platform")
|
||||
default_chassis = None
|
||||
if platform in DEFAULT_CHASSIS:
|
||||
default_chassis = DEFAULT_CHASSIS[platform]
|
||||
vm = yield from dynamips_manager.create_node(request.json.pop("name"),
|
||||
vm = await dynamips_manager.create_node(request.json.pop("name"),
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
dynamips_id=request.json.get("dynamips_id"),
|
||||
@ -79,7 +79,7 @@ class DynamipsVMHandler:
|
||||
aux=request.json.get("aux"),
|
||||
chassis=request.json.pop("chassis", default_chassis),
|
||||
node_type="dynamips")
|
||||
yield from dynamips_manager.update_vm_settings(vm, request.json)
|
||||
await dynamips_manager.update_vm_settings(vm, request.json)
|
||||
response.set_status(201)
|
||||
response.json(vm)
|
||||
|
||||
@ -117,11 +117,11 @@ class DynamipsVMHandler:
|
||||
description="Update a Dynamips VM instance",
|
||||
input=VM_UPDATE_SCHEMA,
|
||||
output=VM_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
async def update(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from dynamips_manager.update_vm_settings(vm, request.json)
|
||||
await dynamips_manager.update_vm_settings(vm, request.json)
|
||||
vm.updated()
|
||||
response.json(vm)
|
||||
|
||||
@ -137,11 +137,11 @@ class DynamipsVMHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete a Dynamips VM instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
# check the project_id exists
|
||||
ProjectManager.instance().get_project(request.match_info["project_id"])
|
||||
yield from Dynamips.instance().delete_node(request.match_info["node_id"])
|
||||
await Dynamips.instance().delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -156,15 +156,15 @@ class DynamipsVMHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Start a Dynamips VM instance")
|
||||
def start(request, response):
|
||||
async def start(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
try:
|
||||
yield from dynamips_manager.ghost_ios_support(vm)
|
||||
await dynamips_manager.ghost_ios_support(vm)
|
||||
except GeneratorExit:
|
||||
pass
|
||||
yield from vm.start()
|
||||
await vm.start()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -179,11 +179,11 @@ class DynamipsVMHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a Dynamips VM instance")
|
||||
def stop(request, response):
|
||||
async def stop(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.stop()
|
||||
await vm.stop()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -198,11 +198,11 @@ class DynamipsVMHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Suspend a Dynamips VM instance")
|
||||
def suspend(request, response):
|
||||
async def suspend(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.suspend()
|
||||
await vm.suspend()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -217,11 +217,11 @@ class DynamipsVMHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Resume a suspended Dynamips VM instance")
|
||||
def resume(request, response):
|
||||
async def resume(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.resume()
|
||||
await vm.resume()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -236,11 +236,11 @@ class DynamipsVMHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Reload a Dynamips VM instance")
|
||||
def reload(request, response):
|
||||
async def reload(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.reload()
|
||||
await vm.reload()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -259,14 +259,14 @@ class DynamipsVMHandler:
|
||||
description="Add a NIO to a Dynamips VM instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = yield from dynamips_manager.create_nio(vm, request.json)
|
||||
nio = await dynamips_manager.create_nio(vm, request.json)
|
||||
slot_number = int(request.match_info["adapter_number"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from vm.slot_add_nio_binding(slot_number, port_number, nio)
|
||||
await vm.slot_add_nio_binding(slot_number, port_number, nio)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@ -286,7 +286,7 @@ class DynamipsVMHandler:
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA,
|
||||
description="Update a NIO from a Dynamips instance")
|
||||
def update_nio(request, response):
|
||||
async def update_nio(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -295,7 +295,7 @@ class DynamipsVMHandler:
|
||||
nio = vm.slots[slot_number].get_nio(port_number)
|
||||
if "filters" in request.json and nio:
|
||||
nio.filters = request.json["filters"]
|
||||
yield from vm.slot_update_nio_binding(slot_number, port_number, nio)
|
||||
await vm.slot_update_nio_binding(slot_number, port_number, nio)
|
||||
response.set_status(201)
|
||||
response.json(request.json)
|
||||
|
||||
@ -313,14 +313,14 @@ class DynamipsVMHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from a Dynamips VM instance")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
slot_number = int(request.match_info["adapter_number"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
nio = yield from vm.slot_remove_nio_binding(slot_number, port_number)
|
||||
yield from nio.delete()
|
||||
nio = await vm.slot_remove_nio_binding(slot_number, port_number)
|
||||
await nio.delete()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -338,7 +338,7 @@ class DynamipsVMHandler:
|
||||
},
|
||||
description="Start a packet capture on a Dynamips VM instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -353,7 +353,7 @@ class DynamipsVMHandler:
|
||||
except UnicodeEncodeError:
|
||||
raise DynamipsError('The capture file path "{}" must only contain ASCII (English) characters'.format(pcap_file_path))
|
||||
|
||||
yield from vm.start_capture(slot_number, port_number, pcap_file_path, request.json["data_link_type"])
|
||||
await vm.start_capture(slot_number, port_number, pcap_file_path, request.json["data_link_type"])
|
||||
response.json({"pcap_file_path": pcap_file_path})
|
||||
|
||||
@Route.post(
|
||||
@ -370,13 +370,13 @@ class DynamipsVMHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a packet capture on a Dynamips VM instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
slot_number = int(request.match_info["adapter_number"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from vm.stop_capture(slot_number, port_number)
|
||||
await vm.stop_capture(slot_number, port_number)
|
||||
response.set_status(204)
|
||||
|
||||
@Route.get(
|
||||
@ -391,12 +391,12 @@ class DynamipsVMHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Retrieve the idlepc proposals")
|
||||
def get_idlepcs(request, response):
|
||||
async def get_idlepcs(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.set_idlepc("0x0")
|
||||
idlepcs = yield from vm.get_idle_pc_prop()
|
||||
await vm.set_idlepc("0x0")
|
||||
idlepcs = await vm.get_idle_pc_prop()
|
||||
response.set_status(200)
|
||||
response.json(idlepcs)
|
||||
|
||||
@ -412,11 +412,11 @@ class DynamipsVMHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Retrieve the idlepc proposals")
|
||||
def get_auto_idlepc(request, response):
|
||||
async def get_auto_idlepc(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
idlepc = yield from dynamips_manager.auto_idlepc(vm)
|
||||
idlepc = await dynamips_manager.auto_idlepc(vm)
|
||||
response.set_status(200)
|
||||
response.json({"idlepc": idlepc})
|
||||
|
||||
@ -427,10 +427,10 @@ class DynamipsVMHandler:
|
||||
},
|
||||
description="Retrieve the list of Dynamips IOS images",
|
||||
output=NODE_LIST_IMAGES_SCHEMA)
|
||||
def list_images(request, response):
|
||||
async def list_images(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
images = yield from dynamips_manager.list_images()
|
||||
images = await dynamips_manager.list_images()
|
||||
response.set_status(200)
|
||||
response.json(images)
|
||||
|
||||
@ -444,10 +444,10 @@ class DynamipsVMHandler:
|
||||
},
|
||||
raw=True,
|
||||
description="Upload a Dynamips IOS image")
|
||||
def upload_image(request, response):
|
||||
async def upload_image(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
yield from dynamips_manager.write_image(request.match_info["filename"], request.content)
|
||||
await dynamips_manager.write_image(request.match_info["filename"], request.content)
|
||||
response.set_status(204)
|
||||
|
||||
@Route.get(
|
||||
@ -460,7 +460,7 @@ class DynamipsVMHandler:
|
||||
},
|
||||
raw=True,
|
||||
description="Download a Dynamips IOS image")
|
||||
def download_image(request, response):
|
||||
async def download_image(request, response):
|
||||
filename = request.match_info["filename"]
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
@ -470,7 +470,7 @@ class DynamipsVMHandler:
|
||||
if filename[0] == ".":
|
||||
raise aiohttp.web.HTTPForbidden()
|
||||
|
||||
yield from response.file(image_path)
|
||||
await response.file(image_path)
|
||||
|
||||
@Route.post(
|
||||
r"/projects/{project_id}/dynamips/nodes/{node_id}/duplicate",
|
||||
@ -483,9 +483,9 @@ class DynamipsVMHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Duplicate a dynamips instance")
|
||||
def duplicate(request, response):
|
||||
async def duplicate(request, response):
|
||||
|
||||
new_node = yield from Dynamips.instance().duplicate_node(
|
||||
new_node = await Dynamips.instance().duplicate_node(
|
||||
request.match_info["node_id"],
|
||||
request.json["destination_node_id"]
|
||||
)
|
||||
|
@ -48,11 +48,11 @@ class EthernetHubHandler:
|
||||
description="Create a new Ethernet hub instance",
|
||||
input=ETHERNET_HUB_CREATE_SCHEMA,
|
||||
output=ETHERNET_HUB_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
# Use the Dynamips Ethernet hub to simulate this node
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = yield from dynamips_manager.create_node(request.json.pop("name"),
|
||||
node = await dynamips_manager.create_node(request.json.pop("name"),
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
node_type="ethernet_hub",
|
||||
@ -92,9 +92,9 @@ class EthernetHubHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Duplicate an ethernet hub instance")
|
||||
def duplicate(request, response):
|
||||
async def duplicate(request, response):
|
||||
|
||||
new_node = yield from Dynamips.instance().duplicate_node(
|
||||
new_node = await Dynamips.instance().duplicate_node(
|
||||
request.match_info["node_id"],
|
||||
request.json["destination_node_id"]
|
||||
)
|
||||
@ -116,12 +116,12 @@ class EthernetHubHandler:
|
||||
description="Update an Ethernet hub instance",
|
||||
input=ETHERNET_HUB_UPDATE_SCHEMA,
|
||||
output=ETHERNET_HUB_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
async def update(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
if "name" in request.json and node.name != request.json["name"]:
|
||||
yield from node.set_name(request.json["name"])
|
||||
await node.set_name(request.json["name"])
|
||||
if "ports_mapping" in request.json:
|
||||
node.ports_mapping = request.json["ports_mapping"]
|
||||
|
||||
@ -140,10 +140,10 @@ class EthernetHubHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete an Ethernet hub instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
yield from dynamips_manager.delete_node(request.match_info["node_id"])
|
||||
await dynamips_manager.delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -213,13 +213,13 @@ class EthernetHubHandler:
|
||||
description="Add a NIO to an Ethernet hub instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = yield from dynamips_manager.create_nio(node, request.json)
|
||||
nio = await dynamips_manager.create_nio(node, request.json)
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.add_nio(nio, port_number)
|
||||
await node.add_nio(nio, port_number)
|
||||
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
@ -238,13 +238,13 @@ class EthernetHubHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from an Ethernet hub instance")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
nio = yield from node.remove_nio(port_number)
|
||||
yield from nio.delete()
|
||||
nio = await node.remove_nio(port_number)
|
||||
await nio.delete()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -262,13 +262,13 @@ class EthernetHubHandler:
|
||||
},
|
||||
description="Start a packet capture on an Ethernet hub instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
yield from node.start_capture(port_number, pcap_file_path, request.json["data_link_type"])
|
||||
await node.start_capture(port_number, pcap_file_path, request.json["data_link_type"])
|
||||
response.json({"pcap_file_path": pcap_file_path})
|
||||
|
||||
@Route.post(
|
||||
@ -285,10 +285,10 @@ class EthernetHubHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a packet capture on an Ethernet hub instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.stop_capture(port_number)
|
||||
await node.stop_capture(port_number)
|
||||
response.set_status(204)
|
||||
|
@ -48,11 +48,11 @@ class EthernetSwitchHandler:
|
||||
description="Create a new Ethernet switch instance",
|
||||
input=ETHERNET_SWITCH_CREATE_SCHEMA,
|
||||
output=ETHERNET_SWITCH_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
# Use the Dynamips Ethernet switch to simulate this node
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = yield from dynamips_manager.create_node(request.json.pop("name"),
|
||||
node = await dynamips_manager.create_node(request.json.pop("name"),
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
console=request.json.get("console"),
|
||||
@ -62,7 +62,7 @@ class EthernetSwitchHandler:
|
||||
|
||||
# On Linux, use the generic switch
|
||||
# builtin_manager = Builtin.instance()
|
||||
# node = yield from builtin_manager.create_node(request.json.pop("name"),
|
||||
# node = await builtin_manager.create_node(request.json.pop("name"),
|
||||
# request.match_info["project_id"],
|
||||
# request.json.get("node_id"),
|
||||
# node_type="ethernet_switch")
|
||||
@ -103,9 +103,9 @@ class EthernetSwitchHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Duplicate an ethernet switch instance")
|
||||
def duplicate(request, response):
|
||||
async def duplicate(request, response):
|
||||
|
||||
new_node = yield from Dynamips.instance().duplicate_node(
|
||||
new_node = await Dynamips.instance().duplicate_node(
|
||||
request.match_info["node_id"],
|
||||
request.json["destination_node_id"]
|
||||
)
|
||||
@ -127,15 +127,15 @@ class EthernetSwitchHandler:
|
||||
description="Update an Ethernet switch instance",
|
||||
input=ETHERNET_SWITCH_UPDATE_SCHEMA,
|
||||
output=ETHERNET_SWITCH_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
async def update(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
if "name" in request.json and node.name != request.json["name"]:
|
||||
yield from node.set_name(request.json["name"])
|
||||
await node.set_name(request.json["name"])
|
||||
if "ports_mapping" in request.json:
|
||||
node.ports_mapping = request.json["ports_mapping"]
|
||||
yield from node.update_port_settings()
|
||||
await node.update_port_settings()
|
||||
if "console_type" in request.json:
|
||||
node.console_type = request.json["console_type"]
|
||||
|
||||
@ -157,10 +157,10 @@ class EthernetSwitchHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete an Ethernet switch instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
yield from dynamips_manager.delete_node(request.match_info["node_id"])
|
||||
await dynamips_manager.delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -230,17 +230,17 @@ class EthernetSwitchHandler:
|
||||
description="Add a NIO to an Ethernet switch instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = yield from dynamips_manager.create_nio(node, request.json)
|
||||
nio = await dynamips_manager.create_nio(node, request.json)
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.add_nio(nio, port_number)
|
||||
await node.add_nio(nio, port_number)
|
||||
|
||||
#builtin_manager = Builtin.instance()
|
||||
#node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
#nio = yield from builtin_manager.create_nio(request.json["nio"])
|
||||
#nio = await builtin_manager.create_nio(request.json["nio"])
|
||||
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
@ -259,15 +259,15 @@ class EthernetSwitchHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from an Ethernet switch instance")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
#builtin_manager = Builtin.instance()
|
||||
#node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
nio = yield from node.remove_nio(port_number)
|
||||
yield from nio.delete()
|
||||
nio = await node.remove_nio(port_number)
|
||||
await nio.delete()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -285,7 +285,7 @@ class EthernetSwitchHandler:
|
||||
},
|
||||
description="Start a packet capture on an Ethernet switch instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -293,7 +293,7 @@ class EthernetSwitchHandler:
|
||||
#node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
yield from node.start_capture(port_number, pcap_file_path, request.json["data_link_type"])
|
||||
await node.start_capture(port_number, pcap_file_path, request.json["data_link_type"])
|
||||
response.json({"pcap_file_path": pcap_file_path})
|
||||
|
||||
@Route.post(
|
||||
@ -310,12 +310,12 @@ class EthernetSwitchHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a packet capture on an Ethernet switch instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
#builtin_manager = Builtin.instance()
|
||||
#node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.stop_capture(port_number)
|
||||
await node.stop_capture(port_number)
|
||||
response.set_status(204)
|
||||
|
@ -48,11 +48,11 @@ class FrameRelaySwitchHandler:
|
||||
description="Create a new Frame Relay switch instance",
|
||||
input=FRAME_RELAY_SWITCH_CREATE_SCHEMA,
|
||||
output=FRAME_RELAY_SWITCH_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
# Use the Dynamips Frame Relay switch to simulate this node
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = yield from dynamips_manager.create_node(request.json.pop("name"),
|
||||
node = await dynamips_manager.create_node(request.json.pop("name"),
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
node_type="frame_relay_switch",
|
||||
@ -90,9 +90,9 @@ class FrameRelaySwitchHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Duplicate a frame relay switch instance")
|
||||
def duplicate(request, response):
|
||||
async def duplicate(request, response):
|
||||
|
||||
new_node = yield from Dynamips.instance().duplicate_node(
|
||||
new_node = await Dynamips.instance().duplicate_node(
|
||||
request.match_info["node_id"],
|
||||
request.json["destination_node_id"]
|
||||
)
|
||||
@ -114,12 +114,12 @@ class FrameRelaySwitchHandler:
|
||||
description="Update a Frame Relay switch instance",
|
||||
input=FRAME_RELAY_SWITCH_UPDATE_SCHEMA,
|
||||
output=FRAME_RELAY_SWITCH_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
async def update(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
if "name" in request.json and node.name != request.json["name"]:
|
||||
yield from node.set_name(request.json["name"])
|
||||
await node.set_name(request.json["name"])
|
||||
if "mappings" in request.json:
|
||||
node.mappings = request.json["mappings"]
|
||||
node.updated()
|
||||
@ -137,10 +137,10 @@ class FrameRelaySwitchHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete a Frame Relay switch instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
yield from dynamips_manager.delete_node(request.match_info["node_id"])
|
||||
await dynamips_manager.delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -210,13 +210,13 @@ class FrameRelaySwitchHandler:
|
||||
description="Add a NIO to a Frame Relay switch instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = yield from dynamips_manager.create_nio(node, request.json)
|
||||
nio = await dynamips_manager.create_nio(node, request.json)
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.add_nio(nio, port_number)
|
||||
await node.add_nio(nio, port_number)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@ -234,13 +234,13 @@ class FrameRelaySwitchHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from a Frame Relay switch instance")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
nio = yield from node.remove_nio(port_number)
|
||||
yield from nio.delete()
|
||||
nio = await node.remove_nio(port_number)
|
||||
await nio.delete()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -258,13 +258,13 @@ class FrameRelaySwitchHandler:
|
||||
},
|
||||
description="Start a packet capture on a Frame Relay switch instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
yield from node.start_capture(port_number, pcap_file_path, request.json["data_link_type"])
|
||||
await node.start_capture(port_number, pcap_file_path, request.json["data_link_type"])
|
||||
response.json({"pcap_file_path": pcap_file_path})
|
||||
|
||||
@Route.post(
|
||||
@ -281,10 +281,10 @@ class FrameRelaySwitchHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a packet capture on a Frame Relay switch instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
dynamips_manager = Dynamips.instance()
|
||||
node = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.stop_capture(port_number)
|
||||
await node.stop_capture(port_number)
|
||||
response.set_status(204)
|
||||
|
@ -54,10 +54,10 @@ class IOUHandler:
|
||||
description="Create a new IOU instance",
|
||||
input=IOU_CREATE_SCHEMA,
|
||||
output=IOU_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
iou = IOU.instance()
|
||||
vm = yield from iou.create_node(request.json.pop("name"),
|
||||
vm = await iou.create_node(request.json.pop("name"),
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
path=request.json.get("path"),
|
||||
@ -112,7 +112,7 @@ class IOUHandler:
|
||||
description="Update an IOU instance",
|
||||
input=IOU_OBJECT_SCHEMA,
|
||||
output=IOU_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
async def update(request, response):
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -126,7 +126,7 @@ class IOUHandler:
|
||||
if vm.use_default_iou_values:
|
||||
# update the default IOU values in case the image or use_default_iou_values have changed
|
||||
# this is important to have the correct NVRAM amount in order to correctly push the configs to the NVRAM
|
||||
yield from vm.update_default_iou_values()
|
||||
await vm.update_default_iou_values()
|
||||
vm.updated()
|
||||
response.json(vm)
|
||||
|
||||
@ -142,9 +142,9 @@ class IOUHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete an IOU instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
yield from IOU.instance().delete_node(request.match_info["node_id"])
|
||||
await IOU.instance().delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -158,9 +158,9 @@ class IOUHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Duplicate a IOU instance")
|
||||
def duplicate(request, response):
|
||||
async def duplicate(request, response):
|
||||
|
||||
new_node = yield from IOU.instance().duplicate_node(
|
||||
new_node = await IOU.instance().duplicate_node(
|
||||
request.match_info["node_id"],
|
||||
request.json["destination_node_id"]
|
||||
)
|
||||
@ -181,7 +181,7 @@ class IOUHandler:
|
||||
input=IOU_START_SCHEMA,
|
||||
output=IOU_OBJECT_SCHEMA,
|
||||
description="Start an IOU instance")
|
||||
def start(request, response):
|
||||
async def start(request, response):
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -190,7 +190,7 @@ class IOUHandler:
|
||||
if hasattr(vm, name) and getattr(vm, name) != value:
|
||||
setattr(vm, name, value)
|
||||
|
||||
yield from vm.start()
|
||||
await vm.start()
|
||||
response.json(vm)
|
||||
|
||||
@Route.post(
|
||||
@ -205,11 +205,11 @@ class IOUHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop an IOU instance")
|
||||
def stop(request, response):
|
||||
async def stop(request, response):
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.stop()
|
||||
await vm.stop()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -242,11 +242,11 @@ class IOUHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Reload an IOU instance")
|
||||
def reload(request, response):
|
||||
async def reload(request, response):
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.reload()
|
||||
await vm.reload()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -265,7 +265,7 @@ class IOUHandler:
|
||||
description="Add a NIO to a IOU instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -273,7 +273,7 @@ class IOUHandler:
|
||||
if nio_type not in ("nio_udp", "nio_tap", "nio_ethernet", "nio_generic_ethernet"):
|
||||
raise aiohttp.web.HTTPConflict(text="NIO of type {} is not supported".format(nio_type))
|
||||
nio = iou_manager.create_nio(request.json)
|
||||
yield from vm.adapter_add_nio_binding(int(request.match_info["adapter_number"]), int(request.match_info["port_number"]), nio)
|
||||
await vm.adapter_add_nio_binding(int(request.match_info["adapter_number"]), int(request.match_info["port_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@ -293,7 +293,7 @@ class IOUHandler:
|
||||
description="Update a NIO from a IOU instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def update_nio(request, response):
|
||||
async def update_nio(request, response):
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -302,7 +302,7 @@ class IOUHandler:
|
||||
nio = vm.adapters[adapter_number].get_nio(port_number)
|
||||
if "filters" in request.json and nio:
|
||||
nio.filters = request.json["filters"]
|
||||
yield from vm.adapter_update_nio_binding(
|
||||
await vm.adapter_update_nio_binding(
|
||||
adapter_number,
|
||||
port_number,
|
||||
nio)
|
||||
@ -323,11 +323,11 @@ class IOUHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from a IOU instance")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"]), int(request.match_info["port_number"]))
|
||||
await vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"]), int(request.match_info["port_number"]))
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -346,14 +346,14 @@ class IOUHandler:
|
||||
},
|
||||
description="Start a packet capture on an IOU VM instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
adapter_number = int(request.match_info["adapter_number"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
yield from vm.start_capture(adapter_number, port_number, pcap_file_path, request.json["data_link_type"])
|
||||
await vm.start_capture(adapter_number, port_number, pcap_file_path, request.json["data_link_type"])
|
||||
response.json({"pcap_file_path": str(pcap_file_path)})
|
||||
|
||||
@Route.post(
|
||||
@ -371,14 +371,14 @@ class IOUHandler:
|
||||
409: "VM not started"
|
||||
},
|
||||
description="Stop a packet capture on an IOU VM instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
|
||||
adapter_number = int(request.match_info["adapter_number"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from vm.stop_capture(adapter_number, port_number)
|
||||
await vm.stop_capture(adapter_number, port_number)
|
||||
response.set_status(204)
|
||||
|
||||
@Route.get(
|
||||
@ -388,10 +388,10 @@ class IOUHandler:
|
||||
},
|
||||
description="Retrieve the list of IOU images",
|
||||
output=NODE_LIST_IMAGES_SCHEMA)
|
||||
def list_iou_images(request, response):
|
||||
async def list_iou_images(request, response):
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
images = yield from iou_manager.list_images()
|
||||
images = await iou_manager.list_images()
|
||||
response.set_status(200)
|
||||
response.json(images)
|
||||
|
||||
@ -405,10 +405,10 @@ class IOUHandler:
|
||||
},
|
||||
raw=True,
|
||||
description="Upload an IOU image")
|
||||
def upload_image(request, response):
|
||||
async def upload_image(request, response):
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
yield from iou_manager.write_image(request.match_info["filename"], request.content)
|
||||
await iou_manager.write_image(request.match_info["filename"], request.content)
|
||||
response.set_status(204)
|
||||
|
||||
|
||||
@ -422,7 +422,7 @@ class IOUHandler:
|
||||
},
|
||||
raw=True,
|
||||
description="Download an IOU image")
|
||||
def download_image(request, response):
|
||||
async def download_image(request, response):
|
||||
filename = request.match_info["filename"]
|
||||
|
||||
iou_manager = IOU.instance()
|
||||
@ -432,4 +432,4 @@ class IOUHandler:
|
||||
if filename[0] == ".":
|
||||
raise aiohttp.web.HTTPForbidden()
|
||||
|
||||
yield from response.file(image_path)
|
||||
await response.file(image_path)
|
||||
|
@ -48,10 +48,10 @@ class NatHandler:
|
||||
description="Create a new nat instance",
|
||||
input=NAT_CREATE_SCHEMA,
|
||||
output=NAT_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = yield from builtin_manager.create_node(request.json.pop("name"),
|
||||
node = await builtin_manager.create_node(request.json.pop("name"),
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
node_type="nat",
|
||||
@ -115,10 +115,10 @@ class NatHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete a nat instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
yield from builtin_manager.delete_node(request.match_info["node_id"])
|
||||
await builtin_manager.delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -188,13 +188,13 @@ class NatHandler:
|
||||
description="Add a NIO to a nat instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = builtin_manager.create_nio(request.json)
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.add_nio(nio, port_number)
|
||||
await node.add_nio(nio, port_number)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@ -214,14 +214,14 @@ class NatHandler:
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA,
|
||||
description="Update a NIO from a NAT instance")
|
||||
def update_nio(request, response):
|
||||
async def update_nio(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = node.nios[int(request.match_info["adapter_number"])]
|
||||
if "filters" in request.json and nio:
|
||||
nio.filters = request.json["filters"]
|
||||
yield from node.update_nio(int(request.match_info["port_number"]), nio)
|
||||
await node.update_nio(int(request.match_info["port_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(request.json)
|
||||
|
||||
@ -239,12 +239,12 @@ class NatHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from a nat instance")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.remove_nio(port_number)
|
||||
await node.remove_nio(port_number)
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -262,13 +262,13 @@ class NatHandler:
|
||||
},
|
||||
description="Start a packet capture on a nat instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
pcap_file_path = os.path.join(node.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
yield from node.start_capture(port_number, pcap_file_path, request.json["data_link_type"])
|
||||
await node.start_capture(port_number, pcap_file_path, request.json["data_link_type"])
|
||||
response.json({"pcap_file_path": pcap_file_path})
|
||||
|
||||
@Route.post(
|
||||
@ -285,10 +285,10 @@ class NatHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a packet capture on a nat instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
builtin_manager = Builtin.instance()
|
||||
node = builtin_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from node.stop_capture(port_number)
|
||||
await node.stop_capture(port_number)
|
||||
response.set_status(204)
|
||||
|
@ -20,16 +20,14 @@ import aiohttp
|
||||
from aiohttp.web import WebSocketResponse
|
||||
from gns3server.web.route import Route
|
||||
from gns3server.compute.notification_manager import NotificationManager
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def process_websocket(ws):
|
||||
async def process_websocket(ws):
|
||||
"""
|
||||
Process ping / pong and close message
|
||||
"""
|
||||
try:
|
||||
yield from ws.receive()
|
||||
await ws.receive()
|
||||
except aiohttp.WSServerHandshakeError:
|
||||
pass
|
||||
|
||||
@ -39,17 +37,17 @@ class NotificationHandler:
|
||||
@Route.get(
|
||||
r"/notifications/ws",
|
||||
description="Send notifications using Websockets")
|
||||
def notifications(request, response):
|
||||
async def notifications(request, response):
|
||||
notifications = NotificationManager.instance()
|
||||
ws = WebSocketResponse()
|
||||
yield from ws.prepare(request)
|
||||
await ws.prepare(request)
|
||||
|
||||
asyncio_ensure_future(process_websocket(ws))
|
||||
asyncio.ensure_future(process_websocket(ws))
|
||||
|
||||
with notifications.queue() as queue:
|
||||
while True:
|
||||
try:
|
||||
notification = yield from queue.get_json(1)
|
||||
notification = await queue.get_json(1)
|
||||
except asyncio.futures.CancelledError:
|
||||
break
|
||||
if ws.closed:
|
||||
|
@ -88,11 +88,11 @@ class ProjectHandler:
|
||||
},
|
||||
output=PROJECT_OBJECT_SCHEMA,
|
||||
input=PROJECT_UPDATE_SCHEMA)
|
||||
def update_project(request, response):
|
||||
async def update_project(request, response):
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(request.match_info["project_id"])
|
||||
yield from project.update(
|
||||
await project.update(
|
||||
variables=request.json.get("variables", None)
|
||||
)
|
||||
response.set_status(200)
|
||||
@ -125,12 +125,12 @@ class ProjectHandler:
|
||||
204: "Project closed",
|
||||
404: "The project doesn't exist"
|
||||
})
|
||||
def close(request, response):
|
||||
async def close(request, response):
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(request.match_info["project_id"])
|
||||
if ProjectHandler._notifications_listening.setdefault(project.id, 0) <= 1:
|
||||
yield from project.close()
|
||||
await project.close()
|
||||
pm.remove_project(project.id)
|
||||
try:
|
||||
del ProjectHandler._notifications_listening[project.id]
|
||||
@ -150,11 +150,11 @@ class ProjectHandler:
|
||||
204: "Changes have been written on disk",
|
||||
404: "The project doesn't exist"
|
||||
})
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(request.match_info["project_id"])
|
||||
yield from project.delete()
|
||||
await project.delete()
|
||||
pm.remove_project(project.id)
|
||||
response.set_status(204)
|
||||
|
||||
@ -168,7 +168,7 @@ class ProjectHandler:
|
||||
200: "End of stream",
|
||||
404: "The project doesn't exist"
|
||||
})
|
||||
def notification(request, response):
|
||||
async def notification(request, response):
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(request.match_info["project_id"])
|
||||
@ -184,7 +184,7 @@ class ProjectHandler:
|
||||
response.write("{}\n".format(json.dumps(ProjectHandler._getPingMessage())).encode("utf-8"))
|
||||
while True:
|
||||
try:
|
||||
(action, msg) = yield from asyncio.wait_for(queue.get(), 5)
|
||||
(action, msg) = await asyncio.wait_for(queue.get(), 5)
|
||||
if hasattr(msg, "__json__"):
|
||||
msg = json.dumps({"action": action, "event": msg.__json__()}, sort_keys=True)
|
||||
else:
|
||||
@ -219,11 +219,11 @@ class ProjectHandler:
|
||||
404: "The project doesn't exist"
|
||||
},
|
||||
output=PROJECT_FILE_LIST_SCHEMA)
|
||||
def list_files(request, response):
|
||||
async def list_files(request, response):
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(request.match_info["project_id"])
|
||||
files = yield from project.list_files()
|
||||
files = await project.list_files()
|
||||
response.json(files)
|
||||
response.set_status(200)
|
||||
|
||||
@ -238,7 +238,7 @@ class ProjectHandler:
|
||||
403: "Permission denied",
|
||||
404: "The file doesn't exist"
|
||||
})
|
||||
def get_file(request, response):
|
||||
async def get_file(request, response):
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(request.match_info["project_id"])
|
||||
@ -256,12 +256,12 @@ class ProjectHandler:
|
||||
|
||||
try:
|
||||
with open(path, "rb") as f:
|
||||
yield from response.prepare(request)
|
||||
await response.prepare(request)
|
||||
while True:
|
||||
data = f.read(4096)
|
||||
if not data:
|
||||
break
|
||||
yield from response.write(data)
|
||||
await response.write(data)
|
||||
|
||||
except FileNotFoundError:
|
||||
raise aiohttp.web.HTTPNotFound()
|
||||
@ -279,7 +279,7 @@ class ProjectHandler:
|
||||
403: "Permission denied",
|
||||
404: "The file doesn't exist"
|
||||
})
|
||||
def stream_file(request, response):
|
||||
async def stream_file(request, response):
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(request.match_info["project_id"])
|
||||
@ -297,12 +297,12 @@ class ProjectHandler:
|
||||
|
||||
try:
|
||||
with open(path, "rb") as f:
|
||||
yield from response.prepare(request)
|
||||
await response.prepare(request)
|
||||
while True:
|
||||
data = f.read(4096)
|
||||
if not data:
|
||||
yield from asyncio.sleep(0.1)
|
||||
yield from response.write(data)
|
||||
await asyncio.sleep(0.1)
|
||||
await response.write(data)
|
||||
|
||||
except FileNotFoundError:
|
||||
raise aiohttp.web.HTTPNotFound()
|
||||
@ -321,7 +321,7 @@ class ProjectHandler:
|
||||
403: "Permission denied",
|
||||
404: "The path doesn't exist"
|
||||
})
|
||||
def write_file(request, response):
|
||||
async def write_file(request, response):
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(request.match_info["project_id"])
|
||||
@ -340,7 +340,7 @@ class ProjectHandler:
|
||||
with open(path, 'wb+') as f:
|
||||
while True:
|
||||
try:
|
||||
chunk = yield from request.content.read(1024)
|
||||
chunk = await request.content.read(1024)
|
||||
except asyncio.TimeoutError:
|
||||
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when writing to file '{}'".format(path))
|
||||
if not chunk:
|
||||
@ -363,21 +363,21 @@ class ProjectHandler:
|
||||
200: "File returned",
|
||||
404: "The project doesn't exist"
|
||||
})
|
||||
def export_project(request, response):
|
||||
async def export_project(request, response):
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project = pm.get_project(request.match_info["project_id"])
|
||||
response.content_type = 'application/gns3project'
|
||||
response.headers['CONTENT-DISPOSITION'] = 'attachment; filename="{}.gns3project"'.format(project.name)
|
||||
response.enable_chunked_encoding()
|
||||
yield from response.prepare(request)
|
||||
await response.prepare(request)
|
||||
|
||||
include_images = bool(int(request.json.get("include_images", "0")))
|
||||
for data in project.export(include_images=include_images):
|
||||
response.write(data)
|
||||
yield from response.drain()
|
||||
await response.drain()
|
||||
|
||||
yield from response.write_eof()
|
||||
await response.write_eof()
|
||||
|
||||
@Route.post(
|
||||
r"/projects/{project_id}/import",
|
||||
@ -391,7 +391,7 @@ class ProjectHandler:
|
||||
200: "Project imported",
|
||||
403: "Forbidden to import project"
|
||||
})
|
||||
def import_project(request, response):
|
||||
async def import_project(request, response):
|
||||
|
||||
pm = ProjectManager.instance()
|
||||
project_id = request.match_info["project_id"]
|
||||
@ -403,7 +403,7 @@ class ProjectHandler:
|
||||
try:
|
||||
with tempfile.SpooledTemporaryFile(max_size=10000) as temp:
|
||||
while True:
|
||||
chunk = yield from request.content.read(1024)
|
||||
chunk = await request.content.read(1024)
|
||||
if not chunk:
|
||||
break
|
||||
temp.write(chunk)
|
||||
|
@ -63,10 +63,10 @@ class QEMUHandler:
|
||||
description="Create a new Qemu VM instance",
|
||||
input=QEMU_CREATE_SCHEMA,
|
||||
output=QEMU_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
qemu = Qemu.instance()
|
||||
vm = yield from qemu.create_node(request.json.pop("name"),
|
||||
vm = await qemu.create_node(request.json.pop("name"),
|
||||
request.match_info["project_id"],
|
||||
request.json.pop("node_id", None),
|
||||
linked_clone=request.json.get("linked_clone", True),
|
||||
@ -141,9 +141,9 @@ class QEMUHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete a Qemu VM instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
yield from Qemu.instance().delete_node(request.match_info["node_id"])
|
||||
await Qemu.instance().delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -157,9 +157,9 @@ class QEMUHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Duplicate a Qemu instance")
|
||||
def duplicate(request, response):
|
||||
async def duplicate(request, response):
|
||||
|
||||
new_node = yield from Qemu.instance().duplicate_node(
|
||||
new_node = await Qemu.instance().duplicate_node(
|
||||
request.match_info["node_id"],
|
||||
request.json["destination_node_id"]
|
||||
)
|
||||
@ -178,11 +178,11 @@ class QEMUHandler:
|
||||
},
|
||||
description="Resize a Qemu VM disk image",
|
||||
input=QEMU_RESIZE_SCHEMA)
|
||||
def resize_disk(request, response):
|
||||
async def resize_disk(request, response):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.resize_disk(request.json["drive_name"], request.json["extend"])
|
||||
await vm.resize_disk(request.json["drive_name"], request.json["extend"])
|
||||
response.set_status(201)
|
||||
|
||||
@Route.post(
|
||||
@ -198,7 +198,7 @@ class QEMUHandler:
|
||||
},
|
||||
description="Start a Qemu VM instance",
|
||||
output=QEMU_OBJECT_SCHEMA)
|
||||
def start(request, response):
|
||||
async def start(request, response):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -212,7 +212,7 @@ class QEMUHandler:
|
||||
pm = ProjectManager.instance()
|
||||
if pm.check_hardware_virtualization(vm) is False:
|
||||
raise aiohttp.web.HTTPConflict(text="Cannot start VM with hardware acceleration (KVM/HAX) enabled because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or VirtualBox")
|
||||
yield from vm.start()
|
||||
await vm.start()
|
||||
response.json(vm)
|
||||
|
||||
@Route.post(
|
||||
@ -227,11 +227,11 @@ class QEMUHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a Qemu VM instance")
|
||||
def stop(request, response):
|
||||
async def stop(request, response):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.stop()
|
||||
await vm.stop()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -246,11 +246,11 @@ class QEMUHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Reload a Qemu VM instance")
|
||||
def reload(request, response):
|
||||
async def reload(request, response):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.reload()
|
||||
await vm.reload()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -265,11 +265,11 @@ class QEMUHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Suspend a Qemu VM instance")
|
||||
def suspend(request, response):
|
||||
async def suspend(request, response):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.suspend()
|
||||
await vm.suspend()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -284,11 +284,11 @@ class QEMUHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Resume a Qemu VM instance")
|
||||
def resume(request, response):
|
||||
async def resume(request, response):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.resume()
|
||||
await vm.resume()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -307,7 +307,7 @@ class QEMUHandler:
|
||||
description="Add a NIO to a Qemu VM instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -315,7 +315,7 @@ class QEMUHandler:
|
||||
if nio_type not in ("nio_udp"):
|
||||
raise aiohttp.web.HTTPConflict(text="NIO of type {} is not supported".format(nio_type))
|
||||
nio = qemu_manager.create_nio(request.json)
|
||||
yield from vm.adapter_add_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
await vm.adapter_add_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@ -335,7 +335,7 @@ class QEMUHandler:
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA,
|
||||
description="Update a NIO from a Qemu instance")
|
||||
def update_nio(request, response):
|
||||
async def update_nio(request, response):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -344,7 +344,7 @@ class QEMUHandler:
|
||||
nio.filters = request.json["filters"]
|
||||
if "suspend" in request.json and nio:
|
||||
nio.suspend = request.json["suspend"]
|
||||
yield from vm.adapter_update_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
await vm.adapter_update_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(request.json)
|
||||
|
||||
@ -362,11 +362,11 @@ class QEMUHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from a Qemu VM instance")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"]))
|
||||
await vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"]))
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -384,13 +384,13 @@ class QEMUHandler:
|
||||
},
|
||||
description="Start a packet capture on a Qemu VM instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
adapter_number = int(request.match_info["adapter_number"])
|
||||
pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
yield from vm.start_capture(adapter_number, pcap_file_path)
|
||||
await vm.start_capture(adapter_number, pcap_file_path)
|
||||
response.json({"pcap_file_path": pcap_file_path})
|
||||
|
||||
@Route.post(
|
||||
@ -407,12 +407,12 @@ class QEMUHandler:
|
||||
404: "Instance doesn't exist",
|
||||
},
|
||||
description="Stop a packet capture on a Qemu VM instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
adapter_number = int(request.match_info["adapter_number"])
|
||||
yield from vm.stop_capture(adapter_number)
|
||||
await vm.stop_capture(adapter_number)
|
||||
response.set_status(204)
|
||||
|
||||
@Route.get(
|
||||
@ -425,9 +425,9 @@ class QEMUHandler:
|
||||
description="Get a list of available Qemu binaries",
|
||||
input=QEMU_BINARY_FILTER_SCHEMA,
|
||||
output=QEMU_BINARY_LIST_SCHEMA)
|
||||
def list_binaries(request, response):
|
||||
async def list_binaries(request, response):
|
||||
|
||||
binaries = yield from Qemu.binary_list(request.json.get("archs", None))
|
||||
binaries = await Qemu.binary_list(request.json.get("archs", None))
|
||||
response.json(binaries)
|
||||
|
||||
@Route.get(
|
||||
@ -439,9 +439,9 @@ class QEMUHandler:
|
||||
},
|
||||
description="Get a list of available Qemu-img binaries",
|
||||
output=QEMU_BINARY_LIST_SCHEMA)
|
||||
def list_img_binaries(request, response):
|
||||
async def list_img_binaries(request, response):
|
||||
|
||||
binaries = yield from Qemu.img_binary_list()
|
||||
binaries = await Qemu.img_binary_list()
|
||||
response.json(binaries)
|
||||
|
||||
@Route.get(
|
||||
@ -452,9 +452,9 @@ class QEMUHandler:
|
||||
description="Get a list of Qemu capabilities on this server",
|
||||
output=QEMU_CAPABILITY_LIST_SCHEMA
|
||||
)
|
||||
def get_capabilities(request, response):
|
||||
async def get_capabilities(request, response):
|
||||
capabilities = {"kvm": []}
|
||||
kvms = yield from Qemu.get_kvm_archs()
|
||||
kvms = await Qemu.get_kvm_archs()
|
||||
if kvms:
|
||||
capabilities["kvm"] = kvms
|
||||
response.json(capabilities)
|
||||
@ -467,7 +467,7 @@ class QEMUHandler:
|
||||
description="Create a Qemu image",
|
||||
input=QEMU_IMAGE_CREATE_SCHEMA
|
||||
)
|
||||
def create_img(request, response):
|
||||
async def create_img(request, response):
|
||||
|
||||
qemu_img = request.json.pop("qemu_img")
|
||||
path = request.json.pop("path")
|
||||
@ -477,7 +477,7 @@ class QEMUHandler:
|
||||
response.set_status(403)
|
||||
return
|
||||
|
||||
yield from Qemu.instance().create_disk(qemu_img, path, request.json)
|
||||
await Qemu.instance().create_disk(qemu_img, path, request.json)
|
||||
response.set_status(201)
|
||||
|
||||
@Route.put(
|
||||
@ -488,7 +488,7 @@ class QEMUHandler:
|
||||
description="Update a Qemu image",
|
||||
input=QEMU_IMAGE_UPDATE_SCHEMA
|
||||
)
|
||||
def update_img(request, response):
|
||||
async def update_img(request, response):
|
||||
|
||||
qemu_img = request.json.pop("qemu_img")
|
||||
path = request.json.pop("path")
|
||||
@ -499,7 +499,7 @@ class QEMUHandler:
|
||||
return
|
||||
|
||||
if "extend" in request.json:
|
||||
yield from Qemu.instance().resize_disk(qemu_img, path, request.json.pop("extend"))
|
||||
await Qemu.instance().resize_disk(qemu_img, path, request.json.pop("extend"))
|
||||
response.set_status(201)
|
||||
|
||||
@Route.get(
|
||||
@ -509,10 +509,10 @@ class QEMUHandler:
|
||||
},
|
||||
description="Retrieve the list of Qemu images",
|
||||
output=NODE_LIST_IMAGES_SCHEMA)
|
||||
def list_qemu_images(request, response):
|
||||
async def list_qemu_images(request, response):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
images = yield from qemu_manager.list_images()
|
||||
images = await qemu_manager.list_images()
|
||||
response.set_status(200)
|
||||
response.json(images)
|
||||
|
||||
@ -526,10 +526,10 @@ class QEMUHandler:
|
||||
},
|
||||
raw=True,
|
||||
description="Upload Qemu image")
|
||||
def upload_image(request, response):
|
||||
async def upload_image(request, response):
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
yield from qemu_manager.write_image(request.match_info["filename"], request.content)
|
||||
await qemu_manager.write_image(request.match_info["filename"], request.content)
|
||||
response.set_status(204)
|
||||
|
||||
@Route.get(
|
||||
@ -542,7 +542,7 @@ class QEMUHandler:
|
||||
},
|
||||
raw=True,
|
||||
description="Download Qemu image")
|
||||
def download_image(request, response):
|
||||
async def download_image(request, response):
|
||||
filename = request.match_info["filename"]
|
||||
|
||||
qemu_manager = Qemu.instance()
|
||||
@ -552,4 +552,4 @@ class QEMUHandler:
|
||||
if filename[0] == ".":
|
||||
raise aiohttp.web.HTTPForbidden()
|
||||
|
||||
yield from response.file(image_path)
|
||||
await response.file(image_path)
|
||||
|
@ -48,10 +48,10 @@ class TraceNGHandler:
|
||||
description="Create a new TraceNG instance",
|
||||
input=TRACENG_CREATE_SCHEMA,
|
||||
output=TRACENG_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
traceng = TraceNG.instance()
|
||||
vm = yield from traceng.create_node(request.json["name"],
|
||||
vm = await traceng.create_node(request.json["name"],
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
console=request.json.get("console"))
|
||||
@ -116,9 +116,9 @@ class TraceNGHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete a TraceNG instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
yield from TraceNG.instance().delete_node(request.match_info["node_id"])
|
||||
await TraceNG.instance().delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -132,9 +132,9 @@ class TraceNGHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Duplicate a TraceNG instance")
|
||||
def duplicate(request, response):
|
||||
async def duplicate(request, response):
|
||||
|
||||
new_node = yield from TraceNG.instance().duplicate_node(
|
||||
new_node = await TraceNG.instance().duplicate_node(
|
||||
request.match_info["node_id"],
|
||||
request.json["destination_node_id"]
|
||||
)
|
||||
@ -155,11 +155,11 @@ class TraceNGHandler:
|
||||
description="Start a TraceNG instance",
|
||||
input=TRACENG_START_SCHEMA,
|
||||
output=TRACENG_OBJECT_SCHEMA)
|
||||
def start(request, response):
|
||||
async def start(request, response):
|
||||
|
||||
traceng_manager = TraceNG.instance()
|
||||
vm = traceng_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.start(request.get("destination"))
|
||||
await vm.start(request.get("destination"))
|
||||
response.json(vm)
|
||||
|
||||
@Route.post(
|
||||
@ -174,11 +174,11 @@ class TraceNGHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a TraceNG instance")
|
||||
def stop(request, response):
|
||||
async def stop(request, response):
|
||||
|
||||
traceng_manager = TraceNG.instance()
|
||||
vm = traceng_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.stop()
|
||||
await vm.stop()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -211,11 +211,11 @@ class TraceNGHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Reload a TraceNG instance")
|
||||
def reload(request, response):
|
||||
async def reload(request, response):
|
||||
|
||||
traceng_manager = TraceNG.instance()
|
||||
vm = traceng_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.reload()
|
||||
await vm.reload()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -234,7 +234,7 @@ class TraceNGHandler:
|
||||
description="Add a NIO to a TraceNG instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
|
||||
traceng_manager = TraceNG.instance()
|
||||
vm = traceng_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -242,7 +242,7 @@ class TraceNGHandler:
|
||||
if nio_type not in ("nio_udp"):
|
||||
raise HTTPConflict(text="NIO of type {} is not supported".format(nio_type))
|
||||
nio = traceng_manager.create_nio(request.json)
|
||||
yield from vm.port_add_nio_binding(int(request.match_info["port_number"]), nio)
|
||||
await vm.port_add_nio_binding(int(request.match_info["port_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@ -262,14 +262,14 @@ class TraceNGHandler:
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA,
|
||||
description="Update a NIO from a TraceNG instance")
|
||||
def update_nio(request, response):
|
||||
async def update_nio(request, response):
|
||||
|
||||
traceng_manager = TraceNG.instance()
|
||||
vm = traceng_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = vm.ethernet_adapter.get_nio(int(request.match_info["port_number"]))
|
||||
if "filters" in request.json and nio:
|
||||
nio.filters = request.json["filters"]
|
||||
yield from vm.port_update_nio_binding(int(request.match_info["port_number"]), nio)
|
||||
await vm.port_update_nio_binding(int(request.match_info["port_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(request.json)
|
||||
|
||||
@ -287,11 +287,11 @@ class TraceNGHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from a TraceNG instance")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
|
||||
traceng_manager = TraceNG.instance()
|
||||
vm = traceng_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.port_remove_nio_binding(int(request.match_info["port_number"]))
|
||||
await vm.port_remove_nio_binding(int(request.match_info["port_number"]))
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -309,13 +309,13 @@ class TraceNGHandler:
|
||||
},
|
||||
description="Start a packet capture on a TraceNG instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
traceng_manager = TraceNG.instance()
|
||||
vm = traceng_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
yield from vm.start_capture(port_number, pcap_file_path)
|
||||
await vm.start_capture(port_number, pcap_file_path)
|
||||
response.json({"pcap_file_path": pcap_file_path})
|
||||
|
||||
@Route.post(
|
||||
@ -332,10 +332,10 @@ class TraceNGHandler:
|
||||
404: "Instance doesn't exist",
|
||||
},
|
||||
description="Stop a packet capture on a TraceNG instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
traceng_manager = TraceNG.instance()
|
||||
vm = traceng_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from vm.stop_capture(port_number)
|
||||
await vm.stop_capture(port_number)
|
||||
response.set_status(204)
|
||||
|
@ -50,10 +50,10 @@ class VirtualBoxHandler:
|
||||
description="Create a new VirtualBox VM instance",
|
||||
input=VBOX_CREATE_SCHEMA,
|
||||
output=VBOX_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = yield from vbox_manager.create_node(request.json.pop("name"),
|
||||
vm = await vbox_manager.create_node(request.json.pop("name"),
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
request.json.pop("vmname"),
|
||||
@ -65,7 +65,7 @@ class VirtualBoxHandler:
|
||||
if "ram" in request.json:
|
||||
ram = request.json.pop("ram")
|
||||
if ram != vm.ram:
|
||||
yield from vm.set_ram(ram)
|
||||
await vm.set_ram(ram)
|
||||
|
||||
for name, value in request.json.items():
|
||||
if name != "node_id":
|
||||
@ -109,7 +109,7 @@ class VirtualBoxHandler:
|
||||
description="Update a VirtualBox VM instance",
|
||||
input=VBOX_OBJECT_SCHEMA,
|
||||
output=VBOX_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
async def update(request, response):
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -122,7 +122,7 @@ class VirtualBoxHandler:
|
||||
vm.name = name
|
||||
if vm.linked_clone:
|
||||
try:
|
||||
yield from vm.set_vmname(vm.name)
|
||||
await vm.set_vmname(vm.name)
|
||||
except VirtualBoxError as e: # In case of error we rollback (we can't change the name when running)
|
||||
vm.name = oldname
|
||||
vm.updated()
|
||||
@ -131,12 +131,12 @@ class VirtualBoxHandler:
|
||||
if "adapters" in request.json:
|
||||
adapters = int(request.json.pop("adapters"))
|
||||
if adapters != vm.adapters:
|
||||
yield from vm.set_adapters(adapters)
|
||||
await vm.set_adapters(adapters)
|
||||
|
||||
if "ram" in request.json:
|
||||
ram = request.json.pop("ram")
|
||||
if ram != vm.ram:
|
||||
yield from vm.set_ram(ram)
|
||||
await vm.set_ram(ram)
|
||||
|
||||
# update the console first to avoid issue if updating console type
|
||||
vm.console = request.json.pop("console", vm.console)
|
||||
@ -160,11 +160,11 @@ class VirtualBoxHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete a VirtualBox VM instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
# check the project_id exists
|
||||
ProjectManager.instance().get_project(request.match_info["project_id"])
|
||||
yield from VirtualBox.instance().delete_node(request.match_info["node_id"])
|
||||
await VirtualBox.instance().delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -179,15 +179,15 @@ class VirtualBoxHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Start a VirtualBox VM instance")
|
||||
def start(request, response):
|
||||
async def start(request, response):
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
if (yield from vm.check_hw_virtualization()):
|
||||
if (await vm.check_hw_virtualization()):
|
||||
pm = ProjectManager.instance()
|
||||
if pm.check_hardware_virtualization(vm) is False:
|
||||
raise HTTPConflict(text="Cannot start VM because hardware virtualization (VT-x/AMD-V) is already used by another software like VMware or KVM (on Linux)")
|
||||
yield from vm.start()
|
||||
await vm.start()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -202,11 +202,11 @@ class VirtualBoxHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a VirtualBox VM instance")
|
||||
def stop(request, response):
|
||||
async def stop(request, response):
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.stop()
|
||||
await vm.stop()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -221,11 +221,11 @@ class VirtualBoxHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Suspend a VirtualBox VM instance")
|
||||
def suspend(request, response):
|
||||
async def suspend(request, response):
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.suspend()
|
||||
await vm.suspend()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -240,11 +240,11 @@ class VirtualBoxHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Resume a suspended VirtualBox VM instance")
|
||||
def resume(request, response):
|
||||
async def resume(request, response):
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.resume()
|
||||
await vm.resume()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -259,11 +259,11 @@ class VirtualBoxHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Reload a VirtualBox VM instance")
|
||||
def reload(request, response):
|
||||
async def reload(request, response):
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.reload()
|
||||
await vm.reload()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -282,7 +282,7 @@ class VirtualBoxHandler:
|
||||
description="Add a NIO to a VirtualBox VM instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -290,7 +290,7 @@ class VirtualBoxHandler:
|
||||
if nio_type not in ("nio_udp", "nio_nat"):
|
||||
raise HTTPConflict(text="NIO of type {} is not supported".format(nio_type))
|
||||
nio = vbox_manager.create_nio(request.json)
|
||||
yield from vm.adapter_add_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
await vm.adapter_add_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@ -310,7 +310,7 @@ class VirtualBoxHandler:
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA,
|
||||
description="Update a NIO from a Virtualbox instance")
|
||||
def update_nio(request, response):
|
||||
async def update_nio(request, response):
|
||||
|
||||
virtualbox_manager = VirtualBox.instance()
|
||||
vm = virtualbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -319,7 +319,7 @@ class VirtualBoxHandler:
|
||||
nio.filters = request.json["filters"]
|
||||
if "suspend" in request.json and nio:
|
||||
nio.suspend = request.json["suspend"]
|
||||
yield from vm.adapter_update_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
await vm.adapter_update_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(request.json)
|
||||
|
||||
@ -337,11 +337,11 @@ class VirtualBoxHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from a VirtualBox VM instance")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"]))
|
||||
await vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"]))
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -359,13 +359,13 @@ class VirtualBoxHandler:
|
||||
},
|
||||
description="Start a packet capture on a VirtualBox VM instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
adapter_number = int(request.match_info["adapter_number"])
|
||||
pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
yield from vm.start_capture(adapter_number, pcap_file_path)
|
||||
await vm.start_capture(adapter_number, pcap_file_path)
|
||||
response.json({"pcap_file_path": pcap_file_path})
|
||||
|
||||
@Route.post(
|
||||
@ -382,11 +382,11 @@ class VirtualBoxHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a packet capture on a VirtualBox VM instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vm = vbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
vm.stop_capture(int(request.match_info["adapter_number"]))
|
||||
await vm.stop_capture(int(request.match_info["adapter_number"]))
|
||||
response.set_status(204)
|
||||
|
||||
@Route.get(
|
||||
@ -395,7 +395,7 @@ class VirtualBoxHandler:
|
||||
200: "Success",
|
||||
},
|
||||
description="Get all available VirtualBox VMs")
|
||||
def get_vms(request, response):
|
||||
async def get_vms(request, response):
|
||||
vbox_manager = VirtualBox.instance()
|
||||
vms = yield from vbox_manager.list_vms()
|
||||
vms = await vbox_manager.list_vms()
|
||||
response.json(vms)
|
||||
|
@ -49,10 +49,10 @@ class VMwareHandler:
|
||||
description="Create a new VMware VM instance",
|
||||
input=VMWARE_CREATE_SCHEMA,
|
||||
output=VMWARE_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = yield from vmware_manager.create_node(request.json.pop("name"),
|
||||
vm = await vmware_manager.create_node(request.json.pop("name"),
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
request.json.pop("vmx_path"),
|
||||
@ -127,11 +127,11 @@ class VMwareHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete a VMware VM instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
# check the project_id exists
|
||||
ProjectManager.instance().get_project(request.match_info["project_id"])
|
||||
yield from VMware.instance().delete_node(request.match_info["node_id"])
|
||||
await VMware.instance().delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -146,7 +146,7 @@ class VMwareHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Start a VMware VM instance")
|
||||
def start(request, response):
|
||||
async def start(request, response):
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -154,7 +154,7 @@ class VMwareHandler:
|
||||
pm = ProjectManager.instance()
|
||||
if pm.check_hardware_virtualization(vm) is False:
|
||||
raise HTTPConflict(text="Cannot start VM because hardware virtualization (VT-x/AMD-V) is already used by another software like VirtualBox or KVM (on Linux)")
|
||||
yield from vm.start()
|
||||
await vm.start()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -169,11 +169,11 @@ class VMwareHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a VMware VM instance")
|
||||
def stop(request, response):
|
||||
async def stop(request, response):
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.stop()
|
||||
await vm.stop()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -188,11 +188,11 @@ class VMwareHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Suspend a VMware VM instance")
|
||||
def suspend(request, response):
|
||||
async def suspend(request, response):
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.suspend()
|
||||
await vm.suspend()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -207,11 +207,11 @@ class VMwareHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Resume a suspended VMware VM instance")
|
||||
def resume(request, response):
|
||||
async def resume(request, response):
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.resume()
|
||||
await vm.resume()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -226,11 +226,11 @@ class VMwareHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Reload a VMware VM instance")
|
||||
def reload(request, response):
|
||||
async def reload(request, response):
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.reload()
|
||||
await vm.reload()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -249,7 +249,7 @@ class VMwareHandler:
|
||||
description="Add a NIO to a VMware VM instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -257,7 +257,7 @@ class VMwareHandler:
|
||||
if nio_type not in ("nio_udp", "nio_vmnet", "nio_nat", "nio_tap"):
|
||||
raise HTTPConflict(text="NIO of type {} is not supported".format(nio_type))
|
||||
nio = vmware_manager.create_nio(request.json)
|
||||
yield from vm.adapter_add_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
await vm.adapter_add_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@ -277,14 +277,14 @@ class VMwareHandler:
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA,
|
||||
description="Update a NIO from a Virtualbox instance")
|
||||
def update_nio(request, response):
|
||||
async def update_nio(request, response):
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = vm.ethernet_adapters[int(request.match_info["adapter_number"])]
|
||||
if "filters" in request.json and nio:
|
||||
nio.filters = request.json["filters"]
|
||||
yield from vm.adapter_update_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
await vm.adapter_update_nio_binding(int(request.match_info["adapter_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(request.json)
|
||||
|
||||
@ -302,11 +302,11 @@ class VMwareHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from a VMware VM instance")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"]))
|
||||
await vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"]))
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -324,13 +324,13 @@ class VMwareHandler:
|
||||
},
|
||||
description="Start a packet capture on a VMware VM instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
adapter_number = int(request.match_info["adapter_number"])
|
||||
pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
yield from vm.start_capture(adapter_number, pcap_file_path)
|
||||
await vm.start_capture(adapter_number, pcap_file_path)
|
||||
response.json({"pcap_file_path": pcap_file_path})
|
||||
|
||||
@Route.post(
|
||||
@ -347,12 +347,12 @@ class VMwareHandler:
|
||||
404: "Instance doesn't exist",
|
||||
},
|
||||
description="Stop a packet capture on a VMware VM instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
vmware_manager = VMware.instance()
|
||||
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
adapter_number = int(request.match_info["adapter_number"])
|
||||
yield from vm.stop_capture(adapter_number)
|
||||
await vm.stop_capture(adapter_number)
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -381,7 +381,7 @@ class VMwareHandler:
|
||||
200: "Success",
|
||||
},
|
||||
description="Get all VMware VMs available")
|
||||
def get_vms(request, response):
|
||||
async def get_vms(request, response):
|
||||
vmware_manager = VMware.instance()
|
||||
vms = yield from vmware_manager.list_vms()
|
||||
vms = await vmware_manager.list_vms()
|
||||
response.json(vms)
|
||||
|
@ -47,10 +47,10 @@ class VPCSHandler:
|
||||
description="Create a new VPCS instance",
|
||||
input=VPCS_CREATE_SCHEMA,
|
||||
output=VPCS_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
vpcs = VPCS.instance()
|
||||
vm = yield from vpcs.create_node(request.json["name"],
|
||||
vm = await vpcs.create_node(request.json["name"],
|
||||
request.match_info["project_id"],
|
||||
request.json.get("node_id"),
|
||||
console=request.json.get("console"),
|
||||
@ -115,9 +115,9 @@ class VPCSHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete a VPCS instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
yield from VPCS.instance().delete_node(request.match_info["node_id"])
|
||||
await VPCS.instance().delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -131,9 +131,9 @@ class VPCSHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Duplicate a VPCS instance")
|
||||
def duplicate(request, response):
|
||||
async def duplicate(request, response):
|
||||
|
||||
new_node = yield from VPCS.instance().duplicate_node(
|
||||
new_node = await VPCS.instance().duplicate_node(
|
||||
request.match_info["node_id"],
|
||||
request.json["destination_node_id"]
|
||||
)
|
||||
@ -153,11 +153,11 @@ class VPCSHandler:
|
||||
},
|
||||
description="Start a VPCS instance",
|
||||
output=VPCS_OBJECT_SCHEMA)
|
||||
def start(request, response):
|
||||
async def start(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.start()
|
||||
await vm.start()
|
||||
response.json(vm)
|
||||
|
||||
@Route.post(
|
||||
@ -172,11 +172,11 @@ class VPCSHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Stop a VPCS instance")
|
||||
def stop(request, response):
|
||||
async def stop(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.stop()
|
||||
await vm.stop()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -209,11 +209,11 @@ class VPCSHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Reload a VPCS instance")
|
||||
def reload(request, response):
|
||||
async def reload(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.reload()
|
||||
await vm.reload()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -232,7 +232,7 @@ class VPCSHandler:
|
||||
description="Add a NIO to a VPCS instance",
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA)
|
||||
def create_nio(request, response):
|
||||
async def create_nio(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
@ -240,7 +240,7 @@ class VPCSHandler:
|
||||
if nio_type not in ("nio_udp", "nio_tap"):
|
||||
raise HTTPConflict(text="NIO of type {} is not supported".format(nio_type))
|
||||
nio = vpcs_manager.create_nio(request.json)
|
||||
yield from vm.port_add_nio_binding(int(request.match_info["port_number"]), nio)
|
||||
await vm.port_add_nio_binding(int(request.match_info["port_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(nio)
|
||||
|
||||
@ -260,14 +260,14 @@ class VPCSHandler:
|
||||
input=NIO_SCHEMA,
|
||||
output=NIO_SCHEMA,
|
||||
description="Update a NIO from a VPCS instance")
|
||||
def update_nio(request, response):
|
||||
async def update_nio(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
nio = vm.ethernet_adapter.get_nio(int(request.match_info["port_number"]))
|
||||
if "filters" in request.json and nio:
|
||||
nio.filters = request.json["filters"]
|
||||
yield from vm.port_update_nio_binding(int(request.match_info["port_number"]), nio)
|
||||
await vm.port_update_nio_binding(int(request.match_info["port_number"]), nio)
|
||||
response.set_status(201)
|
||||
response.json(request.json)
|
||||
|
||||
@ -285,11 +285,11 @@ class VPCSHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Remove a NIO from a VPCS instance")
|
||||
def delete_nio(request, response):
|
||||
async def delete_nio(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
yield from vm.port_remove_nio_binding(int(request.match_info["port_number"]))
|
||||
await vm.port_remove_nio_binding(int(request.match_info["port_number"]))
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -307,13 +307,13 @@ class VPCSHandler:
|
||||
},
|
||||
description="Start a packet capture on a VPCS instance",
|
||||
input=NODE_CAPTURE_SCHEMA)
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"])
|
||||
yield from vm.start_capture(port_number, pcap_file_path)
|
||||
await vm.start_capture(port_number, pcap_file_path)
|
||||
response.json({"pcap_file_path": pcap_file_path})
|
||||
|
||||
@Route.post(
|
||||
@ -330,10 +330,10 @@ class VPCSHandler:
|
||||
404: "Instance doesn't exist",
|
||||
},
|
||||
description="Stop a packet capture on a VPCS instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
vpcs_manager = VPCS.instance()
|
||||
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
|
||||
port_number = int(request.match_info["port_number"])
|
||||
yield from vm.stop_capture(port_number)
|
||||
await vm.stop_capture(port_number)
|
||||
response.set_status(204)
|
||||
|
@ -34,11 +34,11 @@ class ApplianceHandler:
|
||||
status_codes={
|
||||
200: "Appliance template list returned"
|
||||
})
|
||||
def list_templates(request, response):
|
||||
async def list_templates(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
if request.query.get("update", "no") == "yes":
|
||||
yield from controller.download_appliance_templates()
|
||||
await controller.download_appliance_templates()
|
||||
controller.load_appliance_templates()
|
||||
response.json([c for c in controller.appliance_templates.values()])
|
||||
|
||||
@ -66,11 +66,11 @@ class ApplianceHandler:
|
||||
},
|
||||
input=APPLIANCE_USAGE_SCHEMA,
|
||||
output=NODE_OBJECT_SCHEMA)
|
||||
def create_node_from_appliance(request, response):
|
||||
async def create_node_from_appliance(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(request.match_info["project_id"])
|
||||
yield from project.add_node_from_appliance(request.match_info["appliance_id"],
|
||||
await project.add_node_from_appliance(request.match_info["appliance_id"],
|
||||
x=request.json["x"],
|
||||
y=request.json["y"],
|
||||
compute_id=request.json.get("compute_id"))
|
||||
|
@ -41,9 +41,9 @@ class ComputeHandler:
|
||||
},
|
||||
input=COMPUTE_CREATE_SCHEMA,
|
||||
output=COMPUTE_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
compute = yield from Controller.instance().add_compute(**request.json)
|
||||
compute = await Controller.instance().add_compute(**request.json)
|
||||
response.set_status(201)
|
||||
response.json(compute)
|
||||
|
||||
@ -68,14 +68,14 @@ class ComputeHandler:
|
||||
},
|
||||
input=COMPUTE_UPDATE_SCHEMA,
|
||||
output=COMPUTE_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
async def update(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
compute = controller.get_compute(request.match_info["compute_id"])
|
||||
|
||||
# Ignore these because we only use them when creating a node
|
||||
request.json.pop("compute_id", None)
|
||||
yield from compute.update(**request.json)
|
||||
await compute.update(**request.json)
|
||||
response.set_status(200)
|
||||
response.json(compute)
|
||||
|
||||
@ -89,10 +89,10 @@ class ComputeHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Return the list of images available on compute and controller for this emulator type")
|
||||
def images(request, response):
|
||||
async def images(request, response):
|
||||
controller = Controller.instance()
|
||||
compute = controller.get_compute(request.match_info["compute_id"])
|
||||
res = yield from compute.images(request.match_info["emulator"])
|
||||
res = await compute.images(request.match_info["emulator"])
|
||||
response.json(res)
|
||||
|
||||
@Route.get(
|
||||
@ -133,10 +133,10 @@ class ComputeHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Forward call specific to compute node. Read the full compute API for available actions")
|
||||
def get_forward(request, response):
|
||||
async def get_forward(request, response):
|
||||
controller = Controller.instance()
|
||||
compute = controller.get_compute(request.match_info["compute_id"])
|
||||
res = yield from compute.forward("GET", request.match_info["emulator"], request.match_info["action"])
|
||||
res = await compute.forward("GET", request.match_info["emulator"], request.match_info["action"])
|
||||
response.json(res)
|
||||
|
||||
@Route.post(
|
||||
@ -150,10 +150,10 @@ class ComputeHandler:
|
||||
},
|
||||
raw=True,
|
||||
description="Forward call specific to compute node. Read the full compute API for available actions")
|
||||
def post_forward(request, response):
|
||||
async def post_forward(request, response):
|
||||
controller = Controller.instance()
|
||||
compute = controller.get_compute(request.match_info["compute_id"])
|
||||
res = yield from compute.forward("POST", request.match_info["emulator"], request.match_info["action"], data=request.content)
|
||||
res = await compute.forward("POST", request.match_info["emulator"], request.match_info["action"], data=request.content)
|
||||
response.json(res)
|
||||
|
||||
@Route.put(
|
||||
@ -167,10 +167,10 @@ class ComputeHandler:
|
||||
},
|
||||
raw=True,
|
||||
description="Forward call specific to compute node. Read the full compute API for available actions")
|
||||
def put_forward(request, response):
|
||||
async def put_forward(request, response):
|
||||
controller = Controller.instance()
|
||||
compute = controller.get_compute(request.match_info["compute_id"])
|
||||
res = yield from compute.forward("PUT", request.match_info["emulator"], request.match_info["action"], data=request.content)
|
||||
res = await compute.forward("PUT", request.match_info["emulator"], request.match_info["action"], data=request.content)
|
||||
response.json(res)
|
||||
|
||||
@Route.get(
|
||||
@ -197,9 +197,9 @@ class ComputeHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete a compute instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
controller = Controller.instance()
|
||||
yield from controller.delete_compute(request.match_info["compute_id"])
|
||||
await controller.delete_compute(request.match_info["compute_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -211,9 +211,9 @@ class ComputeHandler:
|
||||
200: "Idle PC computed",
|
||||
},
|
||||
description="Compute IDLE PC value")
|
||||
def autoidlepc(request, response):
|
||||
async def autoidlepc(request, response):
|
||||
controller = Controller.instance()
|
||||
res = yield from controller.autoidlepc(request.match_info["compute_id"], request.json["platform"], request.json["image"], request.json["ram"])
|
||||
res = await controller.autoidlepc(request.match_info["compute_id"], request.json["platform"], request.json["image"], request.json["ram"])
|
||||
response.json(res)
|
||||
|
||||
@Route.get(
|
||||
@ -226,8 +226,8 @@ class ComputeHandler:
|
||||
},
|
||||
description="Get ports used by a compute",
|
||||
output=COMPUTE_PORTS_OBJECT_SCHEMA)
|
||||
def ports(request, response):
|
||||
async def ports(request, response):
|
||||
controller = Controller.instance()
|
||||
res = yield from controller.compute_ports(request.match_info["compute_id"])
|
||||
res = await controller.compute_ports(request.match_info["compute_id"])
|
||||
response.json(res)
|
||||
|
||||
|
@ -37,9 +37,9 @@ class DrawingHandler:
|
||||
200: "List of drawings returned",
|
||||
},
|
||||
description="List drawings of a project")
|
||||
def list_drawings(request, response):
|
||||
async def list_drawings(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
response.json([v for v in project.drawings.values()])
|
||||
|
||||
@Route.post(
|
||||
@ -54,10 +54,10 @@ class DrawingHandler:
|
||||
description="Create a new drawing instance",
|
||||
input=DRAWING_OBJECT_SCHEMA,
|
||||
output=DRAWING_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
drawing = yield from project.add_drawing(**request.json)
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
drawing = await project.add_drawing(**request.json)
|
||||
response.set_status(201)
|
||||
response.json(drawing)
|
||||
|
||||
@ -74,9 +74,9 @@ class DrawingHandler:
|
||||
},
|
||||
description="Get a drawing instance",
|
||||
output=DRAWING_OBJECT_SCHEMA)
|
||||
def get_drawing(request, response):
|
||||
async def get_drawing(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
drawing = project.get_drawing(request.match_info["drawing_id"])
|
||||
response.set_status(200)
|
||||
response.json(drawing)
|
||||
@ -94,11 +94,11 @@ class DrawingHandler:
|
||||
description="Update a drawing instance",
|
||||
input=DRAWING_OBJECT_SCHEMA,
|
||||
output=DRAWING_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
async def update(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
drawing = project.get_drawing(request.match_info["drawing_id"])
|
||||
yield from drawing.update(**request.json)
|
||||
await drawing.update(**request.json)
|
||||
response.set_status(201)
|
||||
response.json(drawing)
|
||||
|
||||
@ -113,8 +113,8 @@ class DrawingHandler:
|
||||
400: "Invalid request"
|
||||
},
|
||||
description="Delete a drawing instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
yield from project.delete_drawing(request.match_info["drawing_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
await project.delete_drawing(request.match_info["drawing_id"])
|
||||
response.set_status(204)
|
||||
|
@ -49,9 +49,9 @@ class GNS3VMHandler:
|
||||
400: "Invalid request",
|
||||
},
|
||||
description="Get all the available VMs for a specific virtualization engine")
|
||||
def get_vms(request, response):
|
||||
async def get_vms(request, response):
|
||||
|
||||
vms = yield from Controller.instance().gns3vm.list(request.match_info["engine"])
|
||||
vms = await Controller.instance().gns3vm.list(request.match_info["engine"])
|
||||
response.json(vms)
|
||||
|
||||
@Route.get(
|
||||
@ -72,9 +72,9 @@ class GNS3VMHandler:
|
||||
status_codes={
|
||||
201: "GNS3 VM updated"
|
||||
})
|
||||
def update(request, response):
|
||||
async def update(request, response):
|
||||
|
||||
gns3_vm = Controller().instance().gns3vm
|
||||
yield from gns3_vm.update_settings(request.json)
|
||||
await gns3_vm.update_settings(request.json)
|
||||
response.json(gns3_vm)
|
||||
response.set_status(201)
|
||||
|
@ -42,9 +42,9 @@ class LinkHandler:
|
||||
200: "List of links returned",
|
||||
},
|
||||
description="List links of a project")
|
||||
def list_links(request, response):
|
||||
async def list_links(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
response.json([v for v in project.links.values()])
|
||||
|
||||
@Route.post(
|
||||
@ -59,22 +59,22 @@ class LinkHandler:
|
||||
description="Create a new link instance",
|
||||
input=LINK_OBJECT_SCHEMA,
|
||||
output=LINK_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
link = yield from project.add_link()
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
link = await project.add_link()
|
||||
if "filters" in request.json:
|
||||
yield from link.update_filters(request.json["filters"])
|
||||
await link.update_filters(request.json["filters"])
|
||||
if "suspend" in request.json:
|
||||
yield from link.update_suspend(request.json["suspend"])
|
||||
await link.update_suspend(request.json["suspend"])
|
||||
try:
|
||||
for node in request.json["nodes"]:
|
||||
yield from link.add_node(project.get_node(node["node_id"]),
|
||||
await link.add_node(project.get_node(node["node_id"]),
|
||||
node.get("adapter_number", 0),
|
||||
node.get("port_number", 0),
|
||||
label=node.get("label"))
|
||||
except aiohttp.web.HTTPException as e:
|
||||
yield from project.delete_link(link.id)
|
||||
await project.delete_link(link.id)
|
||||
raise e
|
||||
response.set_status(201)
|
||||
response.json(link)
|
||||
@ -90,9 +90,9 @@ class LinkHandler:
|
||||
400: "Invalid request"
|
||||
},
|
||||
description="Return the list of filters available for this link")
|
||||
def list_filters(request, response):
|
||||
async def list_filters(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
link = project.get_link(request.match_info["link_id"])
|
||||
response.set_status(200)
|
||||
response.json(link.available_filters())
|
||||
@ -110,9 +110,9 @@ class LinkHandler:
|
||||
},
|
||||
description="Get a link instance",
|
||||
output=LINK_OBJECT_SCHEMA)
|
||||
def get_link(request, response):
|
||||
async def get_link(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
link = project.get_link(request.match_info["link_id"])
|
||||
response.set_status(200)
|
||||
response.json(link)
|
||||
@ -130,16 +130,16 @@ class LinkHandler:
|
||||
description="Update a link instance",
|
||||
input=LINK_OBJECT_SCHEMA,
|
||||
output=LINK_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
async def update(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
link = project.get_link(request.match_info["link_id"])
|
||||
if "filters" in request.json:
|
||||
yield from link.update_filters(request.json["filters"])
|
||||
await link.update_filters(request.json["filters"])
|
||||
if "suspend" in request.json:
|
||||
yield from link.update_suspend(request.json["suspend"])
|
||||
await link.update_suspend(request.json["suspend"])
|
||||
if "nodes" in request.json:
|
||||
yield from link.update_nodes(request.json["nodes"])
|
||||
await link.update_nodes(request.json["nodes"])
|
||||
response.set_status(201)
|
||||
response.json(link)
|
||||
|
||||
@ -156,11 +156,11 @@ class LinkHandler:
|
||||
input=LINK_CAPTURE_SCHEMA,
|
||||
output=LINK_OBJECT_SCHEMA,
|
||||
description="Start capture on a link instance. By default we consider it as an Ethernet link")
|
||||
def start_capture(request, response):
|
||||
async def start_capture(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
link = project.get_link(request.match_info["link_id"])
|
||||
yield from link.start_capture(data_link_type=request.json.get("data_link_type", "DLT_EN10MB"), capture_file_name=request.json.get("capture_file_name"))
|
||||
await link.start_capture(data_link_type=request.json.get("data_link_type", "DLT_EN10MB"), capture_file_name=request.json.get("capture_file_name"))
|
||||
response.set_status(201)
|
||||
response.json(link)
|
||||
|
||||
@ -175,11 +175,11 @@ class LinkHandler:
|
||||
400: "Invalid request"
|
||||
},
|
||||
description="Stop capture on a link instance")
|
||||
def stop_capture(request, response):
|
||||
async def stop_capture(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
link = project.get_link(request.match_info["link_id"])
|
||||
yield from link.stop_capture()
|
||||
await link.stop_capture()
|
||||
response.set_status(201)
|
||||
response.json(link)
|
||||
|
||||
@ -194,10 +194,10 @@ class LinkHandler:
|
||||
400: "Invalid request"
|
||||
},
|
||||
description="Delete a link instance")
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
yield from project.delete_link(request.match_info["link_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
await project.delete_link(request.match_info["link_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.get(
|
||||
@ -212,16 +212,16 @@ class LinkHandler:
|
||||
403: "Permission denied",
|
||||
404: "The file doesn't exist"
|
||||
})
|
||||
def pcap(request, response):
|
||||
async def pcap(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
link = project.get_link(request.match_info["link_id"])
|
||||
|
||||
while link.capture_file_path is None:
|
||||
raise aiohttp.web.HTTPNotFound(text="pcap file not found")
|
||||
|
||||
while not os.path.isfile(link.capture_file_path):
|
||||
yield from asyncio.sleep(0.5)
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
try:
|
||||
with open(link.capture_file_path, "rb") as f:
|
||||
@ -229,12 +229,12 @@ class LinkHandler:
|
||||
response.content_type = "application/vnd.tcpdump.pcap"
|
||||
response.set_status(200)
|
||||
response.enable_chunked_encoding()
|
||||
yield from response.prepare(request)
|
||||
await response.prepare(request)
|
||||
|
||||
while True:
|
||||
chunk = f.read(4096)
|
||||
if not chunk:
|
||||
yield from asyncio.sleep(0.1)
|
||||
yield from response.write(chunk)
|
||||
await asyncio.sleep(0.1)
|
||||
await response.write(chunk)
|
||||
except OSError:
|
||||
raise aiohttp.web.HTTPNotFound(text="pcap file {} not found or not accessible".format(link.capture_file_path))
|
||||
|
@ -46,12 +46,12 @@ class NodeHandler:
|
||||
description="Create a new node instance",
|
||||
input=NODE_CREATE_SCHEMA,
|
||||
output=NODE_OBJECT_SCHEMA)
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
compute = controller.get_compute(request.json.pop("compute_id"))
|
||||
project = yield from controller.get_loaded_project(request.match_info["project_id"])
|
||||
node = yield from project.add_node(compute, request.json.pop("name"), request.json.pop("node_id", None), **request.json)
|
||||
project = await controller.get_loaded_project(request.match_info["project_id"])
|
||||
node = await project.add_node(compute, request.json.pop("name"), request.json.pop("node_id", None), **request.json)
|
||||
response.set_status(201)
|
||||
response.json(node)
|
||||
|
||||
@ -79,9 +79,9 @@ class NodeHandler:
|
||||
200: "List of nodes returned",
|
||||
},
|
||||
description="List nodes of a project")
|
||||
def list_nodes(request, response):
|
||||
async def list_nodes(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
response.json([v for v in project.nodes.values()])
|
||||
|
||||
@Route.put(
|
||||
@ -94,8 +94,8 @@ class NodeHandler:
|
||||
description="Update a node instance",
|
||||
input=NODE_UPDATE_SCHEMA,
|
||||
output=NODE_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
async def update(request, response):
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
node = project.get_node(request.match_info["node_id"])
|
||||
|
||||
# Ignore these because we only use them when creating a node
|
||||
@ -103,7 +103,7 @@ class NodeHandler:
|
||||
request.json.pop("node_type", None)
|
||||
request.json.pop("compute_id", None)
|
||||
|
||||
yield from node.update(**request.json)
|
||||
await node.update(**request.json)
|
||||
response.set_status(200)
|
||||
response.json(node)
|
||||
|
||||
@ -119,10 +119,10 @@ class NodeHandler:
|
||||
},
|
||||
description="Start all nodes belonging to the project",
|
||||
output=NODE_OBJECT_SCHEMA)
|
||||
def start_all(request, response):
|
||||
async def start_all(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
yield from project.start_all()
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
await project.start_all()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -137,10 +137,10 @@ class NodeHandler:
|
||||
},
|
||||
description="Stop all nodes belonging to the project",
|
||||
output=NODE_OBJECT_SCHEMA)
|
||||
def stop_all(request, response):
|
||||
async def stop_all(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
yield from project.stop_all()
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
await project.stop_all()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -155,10 +155,10 @@ class NodeHandler:
|
||||
},
|
||||
description="Suspend all nodes belonging to the project",
|
||||
output=NODE_OBJECT_SCHEMA)
|
||||
def suspend_all(request, response):
|
||||
async def suspend_all(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
yield from project.suspend_all()
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
await project.suspend_all()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -173,11 +173,11 @@ class NodeHandler:
|
||||
},
|
||||
description="Reload all nodes belonging to the project",
|
||||
output=NODE_OBJECT_SCHEMA)
|
||||
def reload_all(request, response):
|
||||
async def reload_all(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
yield from project.stop_all()
|
||||
yield from project.start_all()
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
await project.stop_all()
|
||||
await project.start_all()
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -194,11 +194,11 @@ class NodeHandler:
|
||||
description="Duplicate a node instance",
|
||||
input=NODE_DUPLICATE_SCHEMA,
|
||||
output=NODE_OBJECT_SCHEMA)
|
||||
def duplicate(request, response):
|
||||
async def duplicate(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
node = project.get_node(request.match_info["node_id"])
|
||||
new_node = yield from project.duplicate_node(
|
||||
new_node = await project.duplicate_node(
|
||||
node,
|
||||
request.json["x"],
|
||||
request.json["y"],
|
||||
@ -219,11 +219,11 @@ class NodeHandler:
|
||||
},
|
||||
description="Start a node instance",
|
||||
output=NODE_OBJECT_SCHEMA)
|
||||
def start(request, response):
|
||||
async def start(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
node = project.get_node(request.match_info["node_id"])
|
||||
yield from node.start(data=request.json)
|
||||
await node.start(data=request.json)
|
||||
response.json(node)
|
||||
response.set_status(200)
|
||||
|
||||
@ -240,11 +240,11 @@ class NodeHandler:
|
||||
},
|
||||
description="Stop a node instance",
|
||||
output=NODE_OBJECT_SCHEMA)
|
||||
def stop(request, response):
|
||||
async def stop(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
node = project.get_node(request.match_info["node_id"])
|
||||
yield from node.stop()
|
||||
await node.stop()
|
||||
response.json(node)
|
||||
response.set_status(200)
|
||||
|
||||
@ -261,11 +261,11 @@ class NodeHandler:
|
||||
},
|
||||
description="Suspend a node instance",
|
||||
output=NODE_OBJECT_SCHEMA)
|
||||
def suspend(request, response):
|
||||
async def suspend(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
node = project.get_node(request.match_info["node_id"])
|
||||
yield from node.suspend()
|
||||
await node.suspend()
|
||||
response.json(node)
|
||||
response.set_status(200)
|
||||
|
||||
@ -282,11 +282,11 @@ class NodeHandler:
|
||||
},
|
||||
description="Reload a node instance",
|
||||
output=NODE_OBJECT_SCHEMA)
|
||||
def reload(request, response):
|
||||
async def reload(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
node = project.get_node(request.match_info["node_id"])
|
||||
#yield from node.reload()
|
||||
#await node.reload()
|
||||
response.json(node)
|
||||
response.set_status(200)
|
||||
|
||||
@ -302,9 +302,9 @@ class NodeHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Delete a node instance")
|
||||
def delete(request, response):
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
yield from project.delete_node(request.match_info["node_id"])
|
||||
async def delete(request, response):
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
await project.delete_node(request.match_info["node_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.get(
|
||||
@ -319,9 +319,9 @@ class NodeHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Return all the links connected to this node")
|
||||
def links(request, response):
|
||||
async def links(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
node = project.get_node(request.match_info["node_id"])
|
||||
links = []
|
||||
for link in node.links:
|
||||
@ -341,11 +341,11 @@ class NodeHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Compute the IDLE PC for a Dynamips node")
|
||||
def auto_idlepc(request, response):
|
||||
async def auto_idlepc(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
node = project.get_node(request.match_info["node_id"])
|
||||
idle = yield from node.dynamips_auto_idlepc()
|
||||
idle = await node.dynamips_auto_idlepc()
|
||||
response.json(idle)
|
||||
response.set_status(200)
|
||||
|
||||
@ -361,11 +361,11 @@ class NodeHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Compute a list of potential idle PC for a node")
|
||||
def idlepc_proposals(request, response):
|
||||
async def idlepc_proposals(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
node = project.get_node(request.match_info["node_id"])
|
||||
idle = yield from node.dynamips_idlepc_proposals()
|
||||
idle = await node.dynamips_idlepc_proposals()
|
||||
response.json(idle)
|
||||
response.set_status(200)
|
||||
|
||||
@ -381,11 +381,11 @@ class NodeHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Reload a node instance")
|
||||
def resize_disk(request, response):
|
||||
async def resize_disk(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
node = project.get_node(request.match_info["node_id"])
|
||||
yield from node.post("/resize_disk", request.json)
|
||||
await node.post("/resize_disk", request.json)
|
||||
response.set_status(201)
|
||||
|
||||
@Route.get(
|
||||
@ -400,9 +400,9 @@ class NodeHandler:
|
||||
404: "Instance doesn't exist"
|
||||
},
|
||||
description="Get a file in the node directory")
|
||||
def get_file(request, response):
|
||||
async def get_file(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
node = project.get_node(request.match_info["node_id"])
|
||||
path = request.match_info["path"]
|
||||
path = force_unix_path(path)
|
||||
@ -415,14 +415,14 @@ class NodeHandler:
|
||||
node_type = node.node_type
|
||||
path = "/project-files/{}/{}/{}".format(node_type, node.id, path)
|
||||
|
||||
res = yield from node.compute.http_query("GET", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), timeout=None, raw=True)
|
||||
res = await node.compute.http_query("GET", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), timeout=None, raw=True)
|
||||
response.set_status(200)
|
||||
response.content_type = "application/octet-stream"
|
||||
response.enable_chunked_encoding()
|
||||
yield from response.prepare(request)
|
||||
await response.prepare(request)
|
||||
|
||||
response.write(res.body)
|
||||
yield from response.write_eof()
|
||||
await response.write_eof()
|
||||
|
||||
@Route.post(
|
||||
r"/projects/{project_id}/nodes/{node_id}/files/{path:.+}",
|
||||
@ -437,9 +437,9 @@ class NodeHandler:
|
||||
},
|
||||
raw=True,
|
||||
description="Write a file in the node directory")
|
||||
def post_file(request, response):
|
||||
async def post_file(request, response):
|
||||
|
||||
project = yield from Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
|
||||
node = project.get_node(request.match_info["node_id"])
|
||||
path = request.match_info["path"]
|
||||
path = force_unix_path(path)
|
||||
@ -450,6 +450,6 @@ class NodeHandler:
|
||||
|
||||
node_type = node.node_type
|
||||
path = "/project-files/{}/{}/{}".format(node_type, node.id, path)
|
||||
data = yield from request.content.read() #FIXME: are we handling timeout or large files correctly?
|
||||
yield from node.compute.http_query("POST", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), data=data, timeout=None, raw=True)
|
||||
data = await request.content.read() #FIXME: are we handling timeout or large files correctly?
|
||||
await node.compute.http_query("POST", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), data=data, timeout=None, raw=True)
|
||||
response.set_status(201)
|
||||
|
@ -20,16 +20,14 @@ import aiohttp
|
||||
from aiohttp.web import WebSocketResponse
|
||||
from gns3server.web.route import Route
|
||||
from gns3server.controller import Controller
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def process_websocket(ws):
|
||||
async def process_websocket(ws):
|
||||
"""
|
||||
Process ping / pong and close message
|
||||
"""
|
||||
try:
|
||||
yield from ws.receive()
|
||||
await ws.receive()
|
||||
except aiohttp.WSServerHandshakeError:
|
||||
pass
|
||||
|
||||
@ -42,22 +40,22 @@ class NotificationHandler:
|
||||
status_codes={
|
||||
200: "End of stream"
|
||||
})
|
||||
def notification(request, response):
|
||||
async def notification(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
response.content_type = "application/json"
|
||||
response.set_status(200)
|
||||
response.enable_chunked_encoding()
|
||||
|
||||
yield from response.prepare(request)
|
||||
await response.prepare(request)
|
||||
with controller.notification.controller_queue() as queue:
|
||||
while True:
|
||||
try:
|
||||
msg = yield from queue.get_json(5)
|
||||
msg = await queue.get_json(5)
|
||||
response.write(("{}\n".format(msg)).encode("utf-8"))
|
||||
except asyncio.futures.CancelledError:
|
||||
break
|
||||
yield from response.drain()
|
||||
await response.drain()
|
||||
|
||||
@Route.get(
|
||||
r"/notifications/ws",
|
||||
@ -65,17 +63,17 @@ class NotificationHandler:
|
||||
status_codes={
|
||||
200: "End of stream"
|
||||
})
|
||||
def notification_ws(request, response):
|
||||
async def notification_ws(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
ws = aiohttp.web.WebSocketResponse()
|
||||
yield from ws.prepare(request)
|
||||
await ws.prepare(request)
|
||||
|
||||
asyncio_ensure_future(process_websocket(ws))
|
||||
asyncio.ensure_future(process_websocket(ws))
|
||||
with controller.notification.controller_queue() as queue:
|
||||
while True:
|
||||
try:
|
||||
notification = yield from queue.get_json(5)
|
||||
notification = await queue.get_json(5)
|
||||
except asyncio.futures.CancelledError:
|
||||
break
|
||||
if ws.closed:
|
||||
|
@ -26,7 +26,6 @@ from gns3server.controller import Controller
|
||||
from gns3server.controller.import_project import import_project
|
||||
from gns3server.controller.export_project import export_project
|
||||
from gns3server.config import Config
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
|
||||
|
||||
from gns3server.schemas.project import (
|
||||
@ -40,13 +39,12 @@ import logging
|
||||
log = logging.getLogger()
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def process_websocket(ws):
|
||||
async def process_websocket(ws):
|
||||
"""
|
||||
Process ping / pong and close message
|
||||
"""
|
||||
try:
|
||||
yield from ws.receive()
|
||||
await ws.receive()
|
||||
except aiohttp.WSServerHandshakeError:
|
||||
pass
|
||||
|
||||
@ -62,10 +60,10 @@ class ProjectHandler:
|
||||
},
|
||||
output=PROJECT_OBJECT_SCHEMA,
|
||||
input=PROJECT_CREATE_SCHEMA)
|
||||
def create_project(request, response):
|
||||
async def create_project(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = yield from controller.add_project(**request.json)
|
||||
project = await controller.add_project(**request.json)
|
||||
response.set_status(201)
|
||||
response.json(project)
|
||||
|
||||
@ -119,13 +117,13 @@ class ProjectHandler:
|
||||
description="Update a project instance",
|
||||
input=PROJECT_UPDATE_SCHEMA,
|
||||
output=PROJECT_OBJECT_SCHEMA)
|
||||
def update(request, response):
|
||||
async def update(request, response):
|
||||
project = Controller.instance().get_project(request.match_info["project_id"])
|
||||
|
||||
# Ignore these because we only use them when creating a project
|
||||
request.json.pop("project_id", None)
|
||||
|
||||
yield from project.update(**request.json)
|
||||
await project.update(**request.json)
|
||||
response.set_status(200)
|
||||
response.json(project)
|
||||
|
||||
@ -140,11 +138,11 @@ class ProjectHandler:
|
||||
404: "The project doesn't exist"
|
||||
},
|
||||
output=PROJECT_OBJECT_SCHEMA)
|
||||
def close(request, response):
|
||||
async def close(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(request.match_info["project_id"])
|
||||
yield from project.close()
|
||||
await project.close()
|
||||
response.set_status(201)
|
||||
response.json(project)
|
||||
|
||||
@ -159,11 +157,11 @@ class ProjectHandler:
|
||||
404: "The project doesn't exist"
|
||||
},
|
||||
output=PROJECT_OBJECT_SCHEMA)
|
||||
def open(request, response):
|
||||
async def open(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(request.match_info["project_id"])
|
||||
yield from project.open()
|
||||
await project.open()
|
||||
response.set_status(201)
|
||||
response.json(project)
|
||||
|
||||
@ -179,7 +177,7 @@ class ProjectHandler:
|
||||
},
|
||||
input=PROJECT_LOAD_SCHEMA,
|
||||
output=PROJECT_OBJECT_SCHEMA)
|
||||
def load(request, response):
|
||||
async def load(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
config = Config.instance()
|
||||
@ -187,7 +185,7 @@ class ProjectHandler:
|
||||
log.error("Can't load the project the server is not started with --local")
|
||||
response.set_status(403)
|
||||
return
|
||||
project = yield from controller.load_project(request.json.get("path"),)
|
||||
project = await controller.load_project(request.json.get("path"),)
|
||||
response.set_status(201)
|
||||
response.json(project)
|
||||
|
||||
@ -201,11 +199,11 @@ class ProjectHandler:
|
||||
204: "Changes have been written on disk",
|
||||
404: "The project doesn't exist"
|
||||
})
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(request.match_info["project_id"])
|
||||
yield from project.delete()
|
||||
await project.delete()
|
||||
controller.remove_project(project)
|
||||
response.set_status(204)
|
||||
|
||||
@ -219,7 +217,7 @@ class ProjectHandler:
|
||||
200: "End of stream",
|
||||
404: "The project doesn't exist"
|
||||
})
|
||||
def notification(request, response):
|
||||
async def notification(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(request.match_info["project_id"])
|
||||
@ -228,22 +226,22 @@ class ProjectHandler:
|
||||
response.set_status(200)
|
||||
response.enable_chunked_encoding()
|
||||
|
||||
yield from response.prepare(request)
|
||||
await response.prepare(request)
|
||||
with controller.notification.project_queue(project) as queue:
|
||||
while True:
|
||||
try:
|
||||
msg = yield from queue.get_json(5)
|
||||
msg = await queue.get_json(5)
|
||||
response.write(("{}\n".format(msg)).encode("utf-8"))
|
||||
except asyncio.futures.CancelledError as e:
|
||||
break
|
||||
yield from response.drain()
|
||||
await response.drain()
|
||||
|
||||
if project.auto_close:
|
||||
# To avoid trouble with client connecting disconnecting we sleep few seconds before checking
|
||||
# if someone else is not connected
|
||||
yield from asyncio.sleep(5)
|
||||
await asyncio.sleep(5)
|
||||
if not controller.notification.project_has_listeners(project):
|
||||
yield from project.close()
|
||||
await project.close()
|
||||
|
||||
@Route.get(
|
||||
r"/projects/{project_id}/notifications/ws",
|
||||
@ -255,20 +253,20 @@ class ProjectHandler:
|
||||
200: "End of stream",
|
||||
404: "The project doesn't exist"
|
||||
})
|
||||
def notification_ws(request, response):
|
||||
async def notification_ws(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(request.match_info["project_id"])
|
||||
|
||||
ws = aiohttp.web.WebSocketResponse()
|
||||
yield from ws.prepare(request)
|
||||
await ws.prepare(request)
|
||||
|
||||
asyncio_ensure_future(process_websocket(ws))
|
||||
asyncio.ensure_future(process_websocket(ws))
|
||||
|
||||
with controller.notification.project_queue(project) as queue:
|
||||
while True:
|
||||
try:
|
||||
notification = yield from queue.get_json(5)
|
||||
notification = await queue.get_json(5)
|
||||
except asyncio.futures.CancelledError as e:
|
||||
break
|
||||
if ws.closed:
|
||||
@ -278,9 +276,9 @@ class ProjectHandler:
|
||||
if project.auto_close:
|
||||
# To avoid trouble with client connecting disconnecting we sleep few seconds before checking
|
||||
# if someone else is not connected
|
||||
yield from asyncio.sleep(5)
|
||||
await asyncio.sleep(5)
|
||||
if not controller.notification.project_has_listeners(project):
|
||||
yield from project.close()
|
||||
await project.close()
|
||||
|
||||
return ws
|
||||
|
||||
@ -295,14 +293,14 @@ class ProjectHandler:
|
||||
200: "File returned",
|
||||
404: "The project doesn't exist"
|
||||
})
|
||||
def export_project(request, response):
|
||||
async def export_project(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = yield from controller.get_loaded_project(request.match_info["project_id"])
|
||||
project = await controller.get_loaded_project(request.match_info["project_id"])
|
||||
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
stream = yield from export_project(project,
|
||||
stream = await export_project(project,
|
||||
tmp_dir,
|
||||
include_images=bool(int(request.query.get("include_images", "0"))))
|
||||
# We need to do that now because export could failed and raise an HTTP error
|
||||
@ -310,13 +308,13 @@ class ProjectHandler:
|
||||
response.content_type = 'application/gns3project'
|
||||
response.headers['CONTENT-DISPOSITION'] = 'attachment; filename="{}.gns3project"'.format(project.name)
|
||||
response.enable_chunked_encoding()
|
||||
yield from response.prepare(request)
|
||||
await response.prepare(request)
|
||||
|
||||
for data in stream:
|
||||
response.write(data)
|
||||
yield from response.drain()
|
||||
await response.drain()
|
||||
|
||||
yield from response.write_eof()
|
||||
await response.write_eof()
|
||||
# Will be raise if you have no space left or permission issue on your temporary directory
|
||||
# RuntimeError: something was wrong during the zip process
|
||||
except (ValueError, OSError, RuntimeError) as e:
|
||||
@ -334,7 +332,7 @@ class ProjectHandler:
|
||||
200: "Project imported",
|
||||
403: "Forbidden to import project"
|
||||
})
|
||||
def import_project(request, response):
|
||||
async def import_project(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
|
||||
@ -355,19 +353,19 @@ class ProjectHandler:
|
||||
if sys.version_info >= (3, 7) and sys.version_info < (3, 8):
|
||||
with tempfile.TemporaryFile() as temp:
|
||||
while True:
|
||||
chunk = yield from request.content.read(1024)
|
||||
chunk = await request.content.read(1024)
|
||||
if not chunk:
|
||||
break
|
||||
temp.write(chunk)
|
||||
project = yield from import_project(controller, request.match_info["project_id"], temp, location=path, name=name)
|
||||
project = await import_project(controller, request.match_info["project_id"], temp, location=path, name=name)
|
||||
else:
|
||||
with tempfile.SpooledTemporaryFile(max_size=10000) as temp:
|
||||
while True:
|
||||
chunk = yield from request.content.read(1024)
|
||||
chunk = await request.content.read(1024)
|
||||
if not chunk:
|
||||
break
|
||||
temp.write(chunk)
|
||||
project = yield from import_project(controller, request.match_info["project_id"], temp, location=path, name=name)
|
||||
project = await import_project(controller, request.match_info["project_id"], temp, location=path, name=name)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not import the project: {}".format(e))
|
||||
|
||||
@ -387,10 +385,10 @@ class ProjectHandler:
|
||||
403: "The server is not the local server",
|
||||
404: "The project doesn't exist"
|
||||
})
|
||||
def duplicate(request, response):
|
||||
async def duplicate(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = yield from controller.get_loaded_project(request.match_info["project_id"])
|
||||
project = await controller.get_loaded_project(request.match_info["project_id"])
|
||||
|
||||
if request.json.get("path"):
|
||||
config = Config.instance()
|
||||
@ -401,7 +399,7 @@ class ProjectHandler:
|
||||
else:
|
||||
location = None
|
||||
|
||||
new_project = yield from project.duplicate(name=request.json.get("name"), location=location)
|
||||
new_project = await project.duplicate(name=request.json.get("name"), location=location)
|
||||
|
||||
response.json(new_project)
|
||||
response.set_status(201)
|
||||
@ -417,10 +415,10 @@ class ProjectHandler:
|
||||
403: "Permission denied",
|
||||
404: "The file doesn't exist"
|
||||
})
|
||||
def get_file(request, response):
|
||||
async def get_file(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = yield from controller.get_loaded_project(request.match_info["project_id"])
|
||||
project = await controller.get_loaded_project(request.match_info["project_id"])
|
||||
path = request.match_info["path"]
|
||||
path = os.path.normpath(path).strip('/')
|
||||
|
||||
@ -435,12 +433,12 @@ class ProjectHandler:
|
||||
|
||||
try:
|
||||
with open(path, "rb") as f:
|
||||
yield from response.prepare(request)
|
||||
await response.prepare(request)
|
||||
while True:
|
||||
data = f.read(4096)
|
||||
if not data:
|
||||
break
|
||||
yield from response.write(data)
|
||||
await response.write(data)
|
||||
|
||||
except FileNotFoundError:
|
||||
raise aiohttp.web.HTTPNotFound()
|
||||
@ -459,10 +457,10 @@ class ProjectHandler:
|
||||
403: "Permission denied",
|
||||
404: "The path doesn't exist"
|
||||
})
|
||||
def write_file(request, response):
|
||||
async def write_file(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = yield from controller.get_loaded_project(request.match_info["project_id"])
|
||||
project = await controller.get_loaded_project(request.match_info["project_id"])
|
||||
path = request.match_info["path"]
|
||||
path = os.path.normpath(path).strip("/")
|
||||
|
||||
@ -477,7 +475,7 @@ class ProjectHandler:
|
||||
with open(path, 'wb+') as f:
|
||||
while True:
|
||||
try:
|
||||
chunk = yield from request.content.read(1024)
|
||||
chunk = await request.content.read(1024)
|
||||
except asyncio.TimeoutError:
|
||||
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when writing to file '{}'".format(path))
|
||||
if not chunk:
|
||||
|
@ -20,7 +20,6 @@ from gns3server.config import Config
|
||||
from gns3server.controller import Controller
|
||||
from gns3server.schemas.version import VERSION_SCHEMA
|
||||
from gns3server.version import __version__
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
|
||||
from aiohttp.web import HTTPConflict, HTTPForbidden
|
||||
|
||||
@ -44,7 +43,7 @@ class ServerHandler:
|
||||
201: "Server is shutting down",
|
||||
403: "Server shutdown refused"
|
||||
})
|
||||
def shutdown(request, response):
|
||||
async def shutdown(request, response):
|
||||
|
||||
config = Config.instance()
|
||||
if config.get_section_config("Server").getboolean("local", False) is False:
|
||||
@ -58,10 +57,10 @@ class ServerHandler:
|
||||
|
||||
tasks = []
|
||||
for project in projects:
|
||||
tasks.append(asyncio_ensure_future(project.close()))
|
||||
tasks.append(asyncio.ensure_future(project.close()))
|
||||
|
||||
if tasks:
|
||||
done, _ = yield from asyncio.wait(tasks)
|
||||
done, _ = await asyncio.wait(tasks)
|
||||
for future in done:
|
||||
try:
|
||||
future.result()
|
||||
@ -73,7 +72,7 @@ class ServerHandler:
|
||||
from gns3server.web.web_server import WebServer
|
||||
server = WebServer.instance()
|
||||
try:
|
||||
asyncio_ensure_future(server.shutdown_server())
|
||||
asyncio.ensure_future(server.shutdown_server())
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
response.set_status(201)
|
||||
@ -105,7 +104,7 @@ class ServerHandler:
|
||||
@Route.get(
|
||||
r"/settings",
|
||||
description="Retrieve gui settings from the server. Temporary will we removed in later release")
|
||||
def read_settings(request, response):
|
||||
async def read_settings(request, response):
|
||||
|
||||
settings = None
|
||||
while True:
|
||||
@ -115,7 +114,7 @@ class ServerHandler:
|
||||
|
||||
if settings is not None:
|
||||
break
|
||||
yield from asyncio.sleep(0.5)
|
||||
await asyncio.sleep(0.5)
|
||||
response.json(settings)
|
||||
|
||||
@Route.post(
|
||||
@ -142,7 +141,7 @@ class ServerHandler:
|
||||
status_codes={
|
||||
201: "Written"
|
||||
})
|
||||
def debug(request, response):
|
||||
async def debug(request, response):
|
||||
|
||||
config = Config.instance()
|
||||
if config.get_section_config("Server").getboolean("local", False) is False:
|
||||
@ -170,7 +169,7 @@ class ServerHandler:
|
||||
|
||||
for compute in list(Controller.instance().computes.values()):
|
||||
try:
|
||||
r = yield from compute.get("/debug", raw=True)
|
||||
r = await compute.get("/debug", raw=True)
|
||||
data = r.body.decode("utf-8")
|
||||
except Exception as e:
|
||||
data = str(e)
|
||||
|
@ -43,10 +43,10 @@ class SnapshotHandler:
|
||||
201: "Snasphot created",
|
||||
404: "The project doesn't exist"
|
||||
})
|
||||
def create(request, response):
|
||||
async def create(request, response):
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(request.match_info["project_id"])
|
||||
snapshot = yield from project.snapshot(request.json["name"])
|
||||
snapshot = await project.snapshot(request.json["name"])
|
||||
response.json(snapshot)
|
||||
response.set_status(201)
|
||||
|
||||
@ -77,11 +77,11 @@ class SnapshotHandler:
|
||||
204: "Changes have been written on disk",
|
||||
404: "The project or snapshot doesn't exist"
|
||||
})
|
||||
def delete(request, response):
|
||||
async def delete(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(request.match_info["project_id"])
|
||||
yield from project.delete_snapshot(request.match_info["snapshot_id"])
|
||||
await project.delete_snapshot(request.match_info["snapshot_id"])
|
||||
response.set_status(204)
|
||||
|
||||
@Route.post(
|
||||
@ -96,11 +96,11 @@ class SnapshotHandler:
|
||||
201: "The snapshot has been restored",
|
||||
404: "The project or snapshot doesn't exist"
|
||||
})
|
||||
def restore(request, response):
|
||||
async def restore(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.get_project(request.match_info["project_id"])
|
||||
snapshot = project.get_snapshot(request.match_info["snapshot_id"])
|
||||
project = yield from snapshot.restore()
|
||||
project = await snapshot.restore()
|
||||
response.set_status(201)
|
||||
response.json(project)
|
||||
|
@ -47,11 +47,11 @@ class SymbolHandler:
|
||||
status_codes={
|
||||
200: "Symbol returned"
|
||||
})
|
||||
def raw(request, response):
|
||||
async def raw(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
try:
|
||||
yield from response.file(controller.symbols.get_path(request.match_info["symbol_id"]))
|
||||
await response.file(controller.symbols.get_path(request.match_info["symbol_id"]))
|
||||
except (KeyError, OSError) as e:
|
||||
log.warning("Could not get symbol file: {}".format(e))
|
||||
response.set_status(404)
|
||||
@ -63,14 +63,14 @@ class SymbolHandler:
|
||||
200: "Symbol returned"
|
||||
},
|
||||
raw=True)
|
||||
def upload(request, response):
|
||||
async def upload(request, response):
|
||||
controller = Controller.instance()
|
||||
path = os.path.join(controller.symbols.symbols_path(), os.path.basename(request.match_info["symbol_id"]))
|
||||
try:
|
||||
with open(path, "wb") as f:
|
||||
while True:
|
||||
try:
|
||||
chunk = yield from request.content.read(1024)
|
||||
chunk = await request.content.read(1024)
|
||||
except asyncio.TimeoutError:
|
||||
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when writing to symbol '{}'".format(path))
|
||||
if not chunk:
|
||||
|
@ -78,7 +78,7 @@ class IndexHandler:
|
||||
},
|
||||
raw=True,
|
||||
description="Get static resource")
|
||||
def webui(request, response):
|
||||
async def webui(request, response):
|
||||
filename = request.match_info["filename"]
|
||||
filename = os.path.normpath(filename).strip("/")
|
||||
filename = os.path.join('web-ui', filename)
|
||||
@ -92,7 +92,7 @@ class IndexHandler:
|
||||
if not os.path.exists(static):
|
||||
static = get_static_path(os.path.join('web-ui', 'index.html'))
|
||||
|
||||
yield from response.file(static)
|
||||
await response.file(static)
|
||||
|
||||
@Route.get(
|
||||
r"/v1/version",
|
||||
|
@ -30,8 +30,7 @@ class NotificationQueue(asyncio.Queue):
|
||||
super().__init__()
|
||||
self._first = True
|
||||
|
||||
@asyncio.coroutine
|
||||
def get(self, timeout):
|
||||
async def get(self, timeout):
|
||||
"""
|
||||
When timeout is expire we send a ping notification with server information
|
||||
"""
|
||||
@ -42,17 +41,16 @@ class NotificationQueue(asyncio.Queue):
|
||||
return ("ping", PingStats.get(), {})
|
||||
|
||||
try:
|
||||
(action, msg, kwargs) = yield from asyncio.wait_for(super().get(), timeout)
|
||||
(action, msg, kwargs) = await asyncio.wait_for(super().get(), timeout)
|
||||
except asyncio.futures.TimeoutError:
|
||||
return ("ping", PingStats.get(), {})
|
||||
return (action, msg, kwargs)
|
||||
|
||||
@asyncio.coroutine
|
||||
def get_json(self, timeout):
|
||||
async def get_json(self, timeout):
|
||||
"""
|
||||
Get a message as a JSON
|
||||
"""
|
||||
(action, msg, kwargs) = yield from self.get(timeout)
|
||||
(action, msg, kwargs) = await self.get(timeout)
|
||||
if hasattr(msg, "__json__"):
|
||||
msg = {"action": action, "event": msg.__json__()}
|
||||
else:
|
||||
|
@ -223,9 +223,9 @@ def run():
|
||||
if server_config.getboolean("local"):
|
||||
log.warning("Local mode is enabled. Beware, clients will have full control on your filesystem")
|
||||
|
||||
# we only support Python 3 version >= 3.4
|
||||
if sys.version_info < (3, 4):
|
||||
raise SystemExit("Python 3.4 or higher is required")
|
||||
# we only support Python 3 version >= 3.5
|
||||
if sys.version_info < (3, 5, 3):
|
||||
raise SystemExit("Python 3.5.3 or higher is required")
|
||||
|
||||
user_log.info("Running with Python {major}.{minor}.{micro} and has PID {pid}".format(
|
||||
major=sys.version_info[0], minor=sys.version_info[1],
|
||||
|
@ -129,13 +129,12 @@ class Hypervisor(UBridgeHypervisor):
|
||||
|
||||
return self._version
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_ubridge_version(self, env=None):
|
||||
async def _check_ubridge_version(self, env=None):
|
||||
"""
|
||||
Checks if the ubridge executable version
|
||||
"""
|
||||
try:
|
||||
output = yield from subprocess_check_output(self._path, "-v", cwd=self._working_dir, env=env)
|
||||
output = await subprocess_check_output(self._path, "-v", cwd=self._working_dir, env=env)
|
||||
match = re.search("ubridge version ([0-9a-z\.]+)", output)
|
||||
if match:
|
||||
self._version = match.group(1)
|
||||
@ -152,8 +151,7 @@ class Hypervisor(UBridgeHypervisor):
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
raise UbridgeError("Error while looking for uBridge version: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts the uBridge hypervisor process.
|
||||
"""
|
||||
@ -164,14 +162,14 @@ class Hypervisor(UBridgeHypervisor):
|
||||
system_root = os.path.join(os.path.expandvars("%SystemRoot%"), "System32", "Npcap")
|
||||
if os.path.isdir(system_root):
|
||||
env["PATH"] = system_root + ';' + env["PATH"]
|
||||
yield from self._check_ubridge_version(env)
|
||||
await self._check_ubridge_version(env)
|
||||
try:
|
||||
command = self._build_command()
|
||||
log.info("starting ubridge: {}".format(command))
|
||||
self._stdout_file = os.path.join(self._working_dir, "ubridge.log")
|
||||
log.info("logging to {}".format(self._stdout_file))
|
||||
with open(self._stdout_file, "w", encoding="utf-8") as fd:
|
||||
self._process = yield from asyncio.create_subprocess_exec(*command,
|
||||
self._process = await asyncio.create_subprocess_exec(*command,
|
||||
stdout=fd,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=self._working_dir,
|
||||
@ -194,17 +192,16 @@ class Hypervisor(UBridgeHypervisor):
|
||||
if returncode != 0:
|
||||
self._project.emit("log.error", {"message": "uBridge process has stopped, return code: {}\n{}".format(returncode, self.read_stdout())})
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the uBridge hypervisor process.
|
||||
"""
|
||||
|
||||
if self.is_running():
|
||||
log.info("Stopping uBridge process PID={}".format(self._process.pid))
|
||||
yield from UBridgeHypervisor.stop(self)
|
||||
await UBridgeHypervisor.stop(self)
|
||||
try:
|
||||
yield from wait_for_process_termination(self._process, timeout=3)
|
||||
await wait_for_process_termination(self._process, timeout=3)
|
||||
except asyncio.TimeoutError:
|
||||
if self._process and self._process.returncode is None:
|
||||
log.warning("uBridge process {} is still running... killing it".format(self._process.pid))
|
||||
|
@ -50,8 +50,7 @@ class UBridgeHypervisor:
|
||||
self._reader = None
|
||||
self._writer = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def connect(self, timeout=10):
|
||||
async def connect(self, timeout=10):
|
||||
"""
|
||||
Connects to the hypervisor.
|
||||
"""
|
||||
@ -69,9 +68,9 @@ class UBridgeHypervisor:
|
||||
connection_success = False
|
||||
last_exception = None
|
||||
while time.time() - begin < timeout:
|
||||
yield from asyncio.sleep(0.01)
|
||||
await asyncio.sleep(0.01)
|
||||
try:
|
||||
self._reader, self._writer = yield from asyncio.open_connection(host, self._port)
|
||||
self._reader, self._writer = await asyncio.open_connection(host, self._port)
|
||||
except OSError as e:
|
||||
last_exception = e
|
||||
continue
|
||||
@ -84,7 +83,7 @@ class UBridgeHypervisor:
|
||||
log.info("Connected to uBridge hypervisor on {}:{} after {:.4f} seconds".format(host, self._port, time.time() - begin))
|
||||
|
||||
try:
|
||||
version = yield from self.send("hypervisor version")
|
||||
version = await self.send("hypervisor version")
|
||||
self._version = version[0].split("-", 1)[0]
|
||||
except IndexError:
|
||||
self._version = "Unknown"
|
||||
@ -99,42 +98,39 @@ class UBridgeHypervisor:
|
||||
|
||||
return self._version
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes the connection to this hypervisor (but leave it running).
|
||||
"""
|
||||
|
||||
yield from self.send("hypervisor close")
|
||||
await self.send("hypervisor close")
|
||||
self._writer.close()
|
||||
self._reader, self._writer = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops this hypervisor (will no longer run).
|
||||
"""
|
||||
|
||||
try:
|
||||
# try to properly stop the hypervisor
|
||||
yield from self.send("hypervisor stop")
|
||||
await self.send("hypervisor stop")
|
||||
except UbridgeError:
|
||||
pass
|
||||
try:
|
||||
if self._writer is not None:
|
||||
yield from self._writer.drain()
|
||||
await self._writer.drain()
|
||||
self._writer.close()
|
||||
except OSError as e:
|
||||
log.debug("Stopping hypervisor {}:{} {}".format(self._host, self._port, e))
|
||||
self._reader = self._writer = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def reset(self):
|
||||
async def reset(self):
|
||||
"""
|
||||
Resets this hypervisor (used to get an empty configuration).
|
||||
"""
|
||||
|
||||
yield from self.send("hypervisor reset")
|
||||
await self.send("hypervisor reset")
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
@ -177,8 +173,7 @@ class UBridgeHypervisor:
|
||||
self._host = host
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def send(self, command):
|
||||
async def send(self, command):
|
||||
"""
|
||||
Sends commands to this hypervisor.
|
||||
|
||||
@ -207,7 +202,7 @@ class UBridgeHypervisor:
|
||||
command = command.strip() + '\n'
|
||||
log.debug("sending {}".format(command))
|
||||
self._writer.write(command.encode())
|
||||
yield from self._writer.drain()
|
||||
await self._writer.drain()
|
||||
except OSError as e:
|
||||
raise UbridgeError("Lost communication with {host}:{port} when sending command '{command}': {error}, uBridge process running: {run}"
|
||||
.format(host=self._host, port=self._port, command=command, error=e, run=self.is_running()))
|
||||
@ -220,7 +215,7 @@ class UBridgeHypervisor:
|
||||
while True:
|
||||
try:
|
||||
try:
|
||||
chunk = yield from self._reader.read(1024)
|
||||
chunk = await self._reader.read(1024)
|
||||
except asyncio.CancelledError:
|
||||
# task has been canceled but continue to read
|
||||
# any remaining data sent by the hypervisor
|
||||
@ -237,7 +232,7 @@ class UBridgeHypervisor:
|
||||
.format(host=self._host, port=self._port, command=command, run=self.is_running()))
|
||||
else:
|
||||
retries += 1
|
||||
yield from asyncio.sleep(0.1)
|
||||
await asyncio.sleep(0.1)
|
||||
continue
|
||||
retries = 0
|
||||
buf += chunk.decode("utf-8")
|
||||
|
@ -25,8 +25,7 @@ import threading
|
||||
from asyncio.futures import CancelledError
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def wait_run_in_executor(func, *args, **kwargs):
|
||||
async def wait_run_in_executor(func, *args, **kwargs):
|
||||
"""
|
||||
Run blocking code in a different thread and wait
|
||||
for the result.
|
||||
@ -39,12 +38,11 @@ def wait_run_in_executor(func, *args, **kwargs):
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
future = loop.run_in_executor(None, functools.partial(func, *args, **kwargs))
|
||||
yield from asyncio.wait([future])
|
||||
await asyncio.wait([future])
|
||||
return future.result()
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def cancellable_wait_run_in_executor(func, *args, **kwargs):
|
||||
async def cancellable_wait_run_in_executor(func, *args, **kwargs):
|
||||
"""
|
||||
Run blocking code in a different thread and wait
|
||||
for the result. Support cancellation.
|
||||
@ -57,13 +55,12 @@ def cancellable_wait_run_in_executor(func, *args, **kwargs):
|
||||
stopped_event = threading.Event()
|
||||
kwargs['stopped_event'] = stopped_event
|
||||
try:
|
||||
yield from wait_run_in_executor(func, *args, **kwargs)
|
||||
await wait_run_in_executor(func, *args, **kwargs)
|
||||
except CancelledError:
|
||||
stopped_event.set()
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def subprocess_check_output(*args, cwd=None, env=None, stderr=False):
|
||||
async def subprocess_check_output(*args, cwd=None, env=None, stderr=False):
|
||||
"""
|
||||
Run a command and capture output
|
||||
|
||||
@ -75,11 +72,11 @@ def subprocess_check_output(*args, cwd=None, env=None, stderr=False):
|
||||
"""
|
||||
|
||||
if stderr:
|
||||
proc = yield from asyncio.create_subprocess_exec(*args, stderr=asyncio.subprocess.PIPE, cwd=cwd, env=env)
|
||||
output = yield from proc.stderr.read()
|
||||
proc = await asyncio.create_subprocess_exec(*args, stderr=asyncio.subprocess.PIPE, cwd=cwd, env=env)
|
||||
output = await proc.stderr.read()
|
||||
else:
|
||||
proc = yield from asyncio.create_subprocess_exec(*args, stdout=asyncio.subprocess.PIPE, cwd=cwd, env=env)
|
||||
output = yield from proc.stdout.read()
|
||||
proc = await asyncio.create_subprocess_exec(*args, stdout=asyncio.subprocess.PIPE, cwd=cwd, env=env)
|
||||
output = await proc.stdout.read()
|
||||
if output is None:
|
||||
return ""
|
||||
# If we received garbage we ignore invalid characters
|
||||
@ -87,14 +84,13 @@ def subprocess_check_output(*args, cwd=None, env=None, stderr=False):
|
||||
# and the code of VPCS, dynamips... Will detect it's not the correct binary
|
||||
return output.decode("utf-8", errors="ignore")
|
||||
|
||||
@asyncio.coroutine
|
||||
def wait_for_process_termination(process, timeout=10):
|
||||
async def wait_for_process_termination(process, timeout=10):
|
||||
"""
|
||||
Wait for a process terminate, and raise asyncio.TimeoutError in case of
|
||||
timeout.
|
||||
|
||||
In theory this can be implemented by just:
|
||||
yield from asyncio.wait_for(self._iou_process.wait(), timeout=100)
|
||||
await asyncio.wait_for(self._iou_process.wait(), timeout=100)
|
||||
|
||||
But it's broken before Python 3.4:
|
||||
http://bugs.python.org/issue23140
|
||||
@ -105,24 +101,23 @@ def wait_for_process_termination(process, timeout=10):
|
||||
|
||||
if sys.version_info >= (3, 5):
|
||||
try:
|
||||
yield from asyncio.wait_for(process.wait(), timeout=timeout)
|
||||
await asyncio.wait_for(process.wait(), timeout=timeout)
|
||||
except ProcessLookupError:
|
||||
return
|
||||
else:
|
||||
while timeout > 0:
|
||||
if process.returncode is not None:
|
||||
return
|
||||
yield from asyncio.sleep(0.1)
|
||||
await asyncio.sleep(0.1)
|
||||
timeout -= 0.1
|
||||
raise asyncio.TimeoutError()
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_process(process, termination_callback):
|
||||
async def _check_process(process, termination_callback):
|
||||
if not hasattr(sys, "_called_from_test") or not sys._called_from_test:
|
||||
returncode = yield from process.wait()
|
||||
returncode = await process.wait()
|
||||
if asyncio.iscoroutinefunction(termination_callback):
|
||||
yield from termination_callback(returncode)
|
||||
await termination_callback(returncode)
|
||||
else:
|
||||
termination_callback(returncode)
|
||||
|
||||
@ -130,22 +125,20 @@ def _check_process(process, termination_callback):
|
||||
def monitor_process(process, termination_callback):
|
||||
"""Call termination_callback when a process dies"""
|
||||
|
||||
asyncio_ensure_future(_check_process(process, termination_callback))
|
||||
asyncio.ensure_future(_check_process(process, termination_callback))
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def wait_for_file_creation(path, timeout=10):
|
||||
async def wait_for_file_creation(path, timeout=10):
|
||||
|
||||
while timeout > 0:
|
||||
if os.path.exists(path):
|
||||
return
|
||||
yield from asyncio.sleep(0.5)
|
||||
await asyncio.sleep(0.5)
|
||||
timeout -= 0.5
|
||||
raise asyncio.TimeoutError()
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def wait_for_named_pipe_creation(pipe_path, timeout=60):
|
||||
async def wait_for_named_pipe_creation(pipe_path, timeout=60):
|
||||
|
||||
import win32pipe
|
||||
import pywintypes
|
||||
@ -154,38 +147,20 @@ def wait_for_named_pipe_creation(pipe_path, timeout=60):
|
||||
try:
|
||||
win32pipe.WaitNamedPipe(pipe_path, 1)
|
||||
except pywintypes.error:
|
||||
yield from asyncio.sleep(0.5)
|
||||
await asyncio.sleep(0.5)
|
||||
timeout -= 0.5
|
||||
else:
|
||||
return
|
||||
raise asyncio.TimeoutError()
|
||||
|
||||
#FIXME: Use the following wrapper when we drop Python 3.4 and use the async def syntax
|
||||
# def locking(f):
|
||||
#
|
||||
# @wraps(f)
|
||||
# async def wrapper(oself, *args, **kwargs):
|
||||
# lock_name = "__" + f.__name__ + "_lock"
|
||||
# if not hasattr(oself, lock_name):
|
||||
# setattr(oself, lock_name, asyncio.Lock())
|
||||
# async with getattr(oself, lock_name):
|
||||
# return await f(oself, *args, **kwargs)
|
||||
# return wrapper
|
||||
|
||||
def locking(f):
|
||||
|
||||
@functools.wraps(f)
|
||||
def wrapper(oself, *args, **kwargs):
|
||||
async def wrapper(oself, *args, **kwargs):
|
||||
lock_name = "__" + f.__name__ + "_lock"
|
||||
if not hasattr(oself, lock_name):
|
||||
setattr(oself, lock_name, asyncio.Lock())
|
||||
with (yield from getattr(oself, lock_name)):
|
||||
return (yield from f(oself, *args, **kwargs))
|
||||
async with getattr(oself, lock_name):
|
||||
return await f(oself, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
#FIXME: conservative approach to supported versions, please remove it when we drop the support to Python < 3.4.4
|
||||
try:
|
||||
from asyncio import ensure_future
|
||||
asyncio_ensure_future = asyncio.ensure_future
|
||||
except ImportError:
|
||||
asyncio_ensure_future = getattr(asyncio, 'async')
|
||||
|
@ -81,8 +81,7 @@ class EmbedShell:
|
||||
def welcome_message(self, welcome_message):
|
||||
self._welcome_message = welcome_message
|
||||
|
||||
@asyncio.coroutine
|
||||
def help(self, *args):
|
||||
async def help(self, *args):
|
||||
"""
|
||||
Show help
|
||||
"""
|
||||
@ -105,8 +104,7 @@ class EmbedShell:
|
||||
res += '\nhelp command for details about a command\n'
|
||||
return res
|
||||
|
||||
@asyncio.coroutine
|
||||
def _parse_command(self, text):
|
||||
async def _parse_command(self, text):
|
||||
cmd = text.split(' ')
|
||||
found = False
|
||||
if cmd[0] == '?':
|
||||
@ -119,22 +117,21 @@ class EmbedShell:
|
||||
for (name, meth) in inspect.getmembers(self):
|
||||
if name == cmd[0]:
|
||||
cmd.pop(0)
|
||||
res = yield from meth(*cmd)
|
||||
res = await meth(*cmd)
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
res = ('Command not found {}\n'.format(cmd[0]) + (yield from self.help()))
|
||||
res = ('Command not found {}\n'.format(cmd[0]) + (await self.help()))
|
||||
return res
|
||||
|
||||
@asyncio.coroutine
|
||||
def run(self):
|
||||
async def run(self):
|
||||
if self._welcome_message:
|
||||
self._writer.feed_data(self._welcome_message.encode())
|
||||
while True:
|
||||
self._writer.feed_data(self._prompt.encode())
|
||||
result = yield from self._reader.readline()
|
||||
result = await self._reader.readline()
|
||||
result = result.decode().strip('\n')
|
||||
res = yield from self._parse_command(result)
|
||||
res = await self._parse_command(result)
|
||||
self._writer.feed_data(res.encode())
|
||||
|
||||
def get_commands(self):
|
||||
@ -208,8 +205,7 @@ class ShellConnection(TelnetConnection):
|
||||
self.encoding = 'utf-8'
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def connected(self):
|
||||
async def connected(self):
|
||||
# prompt_toolkit internally checks if it's on windows during output rendering but
|
||||
# we need to force that we use Vt100_Output not Win32_Output
|
||||
from prompt_toolkit import renderer
|
||||
@ -235,16 +231,14 @@ class ShellConnection(TelnetConnection):
|
||||
|
||||
self._cli._redraw()
|
||||
|
||||
@asyncio.coroutine
|
||||
def disconnected(self):
|
||||
async def disconnected(self):
|
||||
pass
|
||||
|
||||
def window_size_changed(self, columns, rows):
|
||||
self._size = Size(rows=rows, columns=columns)
|
||||
self._cb.terminal_size_changed()
|
||||
|
||||
@asyncio.coroutine
|
||||
def feed(self, data):
|
||||
async def feed(self, data):
|
||||
data = data.decode()
|
||||
self._inputstream.feed(data)
|
||||
self._cli._redraw()
|
||||
@ -260,7 +254,7 @@ class ShellConnection(TelnetConnection):
|
||||
|
||||
command = returned_value.text
|
||||
|
||||
res = yield from self._shell._parse_command(command)
|
||||
res = await self._shell._parse_command(command)
|
||||
self.send(res.encode())
|
||||
self.reset()
|
||||
|
||||
@ -305,20 +299,18 @@ def create_stdin_shell(shell, loop=None):
|
||||
:param loop: The event loop
|
||||
:returns: Telnet server
|
||||
"""
|
||||
@asyncio.coroutine
|
||||
def feed_stdin(loop, reader, shell):
|
||||
async def feed_stdin(loop, reader, shell):
|
||||
history = InMemoryHistory()
|
||||
completer = WordCompleter([name for name, _ in shell.get_commands()], ignore_case=True)
|
||||
while True:
|
||||
line = yield from prompt(
|
||||
line = await prompt(
|
||||
">", patch_stdout=True, return_asyncio_coroutine=True, history=history, completer=completer)
|
||||
line += '\n'
|
||||
reader.feed_data(line.encode())
|
||||
|
||||
@asyncio.coroutine
|
||||
def read_stdout(writer):
|
||||
async def read_stdout(writer):
|
||||
while True:
|
||||
c = yield from writer.read(1)
|
||||
c = await writer.read(1)
|
||||
print(c.decode(), end='')
|
||||
sys.stdout.flush()
|
||||
|
||||
@ -339,22 +331,20 @@ if __name__ == '__main__':
|
||||
|
||||
class Demo(EmbedShell):
|
||||
|
||||
@asyncio.coroutine
|
||||
def hello(self, *args):
|
||||
async def hello(self, *args):
|
||||
"""
|
||||
Hello world
|
||||
|
||||
This command accept arguments: hello tutu will display tutu
|
||||
"""
|
||||
@asyncio.coroutine
|
||||
def world():
|
||||
yield from asyncio.sleep(2)
|
||||
async def world():
|
||||
await asyncio.sleep(2)
|
||||
if len(args):
|
||||
return ' '.join(args)
|
||||
else:
|
||||
return 'world\n'
|
||||
|
||||
return (yield from world())
|
||||
return (await world())
|
||||
|
||||
# Demo using telnet
|
||||
shell = Demo(welcome_message="Welcome!\n")
|
||||
|
@ -30,8 +30,7 @@ class Pool():
|
||||
def append(self, task, *args, **kwargs):
|
||||
self._tasks.append((task, args, kwargs))
|
||||
|
||||
@asyncio.coroutine
|
||||
def join(self):
|
||||
async def join(self):
|
||||
"""
|
||||
Wait for all task to finish
|
||||
"""
|
||||
@ -41,7 +40,7 @@ class Pool():
|
||||
while len(self._tasks) > 0 and len(pending) < self._concurrency:
|
||||
task, args, kwargs = self._tasks.pop(0)
|
||||
pending.add(task(*args, **kwargs))
|
||||
(done, pending) = yield from asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
|
||||
(done, pending) = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
|
||||
for task in done:
|
||||
if task.exception():
|
||||
exceptions.add(task.exception())
|
||||
@ -50,10 +49,9 @@ class Pool():
|
||||
|
||||
|
||||
def main():
|
||||
@asyncio.coroutine
|
||||
def task(id):
|
||||
async def task(id):
|
||||
print("Run", id)
|
||||
yield from asyncio.sleep(0.5)
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
pool = Pool(concurrency=5)
|
||||
for i in range(1, 20):
|
||||
|
@ -20,8 +20,6 @@ import copy
|
||||
import asyncio
|
||||
import asyncio.subprocess
|
||||
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -44,24 +42,22 @@ class AsyncioRawCommandServer:
|
||||
# We limit number of process
|
||||
self._lock = asyncio.Semaphore(value=4)
|
||||
|
||||
@asyncio.coroutine
|
||||
def run(self, network_reader, network_writer):
|
||||
yield from self._lock.acquire()
|
||||
process = yield from asyncio.subprocess.create_subprocess_exec(*self._command,
|
||||
async def run(self, network_reader, network_writer):
|
||||
await self._lock.acquire()
|
||||
process = await asyncio.subprocess.create_subprocess_exec(*self._command,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.STDOUT,
|
||||
stdin=asyncio.subprocess.PIPE)
|
||||
try:
|
||||
yield from self._process(network_reader, network_writer, process.stdout, process.stdin)
|
||||
await self._process(network_reader, network_writer, process.stdout, process.stdin)
|
||||
except ConnectionResetError:
|
||||
network_writer.close()
|
||||
if process.returncode is None:
|
||||
process.kill()
|
||||
yield from process.wait()
|
||||
await process.wait()
|
||||
self._lock.release()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _process(self, network_reader, network_writer, process_reader, process_writer):
|
||||
async def _process(self, network_reader, network_writer, process_reader, process_writer):
|
||||
replaces = []
|
||||
# Server host from the client point of view
|
||||
host = network_writer.transport.get_extra_info("sockname")[0]
|
||||
@ -71,12 +67,12 @@ class AsyncioRawCommandServer:
|
||||
else:
|
||||
replaces.append((replace[0], replace[1], ))
|
||||
|
||||
network_read = asyncio_ensure_future(network_reader.read(READ_SIZE))
|
||||
reader_read = asyncio_ensure_future(process_reader.read(READ_SIZE))
|
||||
network_read = asyncio.ensure_future(network_reader.read(READ_SIZE))
|
||||
reader_read = asyncio.ensure_future(process_reader.read(READ_SIZE))
|
||||
timeout = 30
|
||||
|
||||
while True:
|
||||
done, pending = yield from asyncio.wait(
|
||||
done, pending = await asyncio.wait(
|
||||
[
|
||||
network_read,
|
||||
reader_read
|
||||
@ -91,22 +87,22 @@ class AsyncioRawCommandServer:
|
||||
if network_reader.at_eof():
|
||||
raise ConnectionResetError()
|
||||
|
||||
network_read = asyncio_ensure_future(network_reader.read(READ_SIZE))
|
||||
network_read = asyncio.ensure_future(network_reader.read(READ_SIZE))
|
||||
|
||||
process_writer.write(data)
|
||||
yield from process_writer.drain()
|
||||
await process_writer.drain()
|
||||
elif coro == reader_read:
|
||||
if process_reader.at_eof():
|
||||
raise ConnectionResetError()
|
||||
|
||||
reader_read = asyncio_ensure_future(process_reader.read(READ_SIZE))
|
||||
reader_read = asyncio.ensure_future(process_reader.read(READ_SIZE))
|
||||
|
||||
for replace in replaces:
|
||||
data = data.replace(replace[0], replace[1])
|
||||
timeout = 2 # We reduce the timeout when the process start to return stuff to avoid problem with server not closing the connection
|
||||
|
||||
network_writer.write(data)
|
||||
yield from network_writer.drain()
|
||||
await network_writer.drain()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -47,8 +47,7 @@ class SerialReaderWriterProtocol(asyncio.Protocol):
|
||||
if self.transport:
|
||||
self.transport.write(data)
|
||||
|
||||
@asyncio.coroutine
|
||||
def drain(self):
|
||||
async def drain(self):
|
||||
pass
|
||||
|
||||
def connection_made(self, transport):
|
||||
@ -72,13 +71,12 @@ class WindowsPipe:
|
||||
self._handle = open(path, "a+b")
|
||||
self._pipe = msvcrt.get_osfhandle(self._handle.fileno())
|
||||
|
||||
@asyncio.coroutine
|
||||
def read(self, n=-1):
|
||||
async def read(self, n=-1):
|
||||
(read, num_avail, num_message) = win32pipe.PeekNamedPipe(self._pipe, 0)
|
||||
if num_avail > 0:
|
||||
(error_code, output) = win32file.ReadFile(self._pipe, num_avail, None)
|
||||
return output
|
||||
yield from asyncio.sleep(0.01)
|
||||
await asyncio.sleep(0.01)
|
||||
return b""
|
||||
|
||||
def at_eof(self):
|
||||
@ -87,16 +85,14 @@ class WindowsPipe:
|
||||
def write(self, data):
|
||||
win32file.WriteFile(self._pipe, data)
|
||||
|
||||
@asyncio.coroutine
|
||||
def drain(self):
|
||||
async def drain(self):
|
||||
return
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def _asyncio_open_serial_windows(path):
|
||||
async def _asyncio_open_serial_windows(path):
|
||||
"""
|
||||
Open a windows named pipe
|
||||
|
||||
@ -104,14 +100,13 @@ def _asyncio_open_serial_windows(path):
|
||||
"""
|
||||
|
||||
try:
|
||||
yield from wait_for_named_pipe_creation(path)
|
||||
await wait_for_named_pipe_creation(path)
|
||||
except asyncio.TimeoutError:
|
||||
raise NodeError('Pipe file "{}" is missing'.format(path))
|
||||
return WindowsPipe(path)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def _asyncio_open_serial_unix(path):
|
||||
async def _asyncio_open_serial_unix(path):
|
||||
"""
|
||||
Open a unix socket or a windows named pipe
|
||||
|
||||
@ -120,20 +115,19 @@ def _asyncio_open_serial_unix(path):
|
||||
|
||||
try:
|
||||
# wait for VM to create the pipe file.
|
||||
yield from wait_for_file_creation(path)
|
||||
await wait_for_file_creation(path)
|
||||
except asyncio.TimeoutError:
|
||||
raise NodeError('Pipe file "{}" is missing'.format(path))
|
||||
|
||||
output = SerialReaderWriterProtocol()
|
||||
try:
|
||||
yield from asyncio.get_event_loop().create_unix_connection(lambda: output, path)
|
||||
await asyncio.get_event_loop().create_unix_connection(lambda: output, path)
|
||||
except ConnectionRefusedError:
|
||||
raise NodeError('Can\'t open pipe file "{}"'.format(path))
|
||||
return output
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def asyncio_open_serial(path):
|
||||
async def asyncio_open_serial(path):
|
||||
"""
|
||||
Open a unix socket or a windows named pipe
|
||||
|
||||
@ -141,6 +135,6 @@ def asyncio_open_serial(path):
|
||||
"""
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
return (yield from _asyncio_open_serial_windows(path))
|
||||
return (await _asyncio_open_serial_windows(path))
|
||||
else:
|
||||
return (yield from _asyncio_open_serial_unix(path))
|
||||
return (await _asyncio_open_serial_unix(path))
|
||||
|
@ -15,13 +15,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import re
|
||||
import asyncio
|
||||
import asyncio.subprocess
|
||||
import struct
|
||||
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -75,13 +72,11 @@ class TelnetConnection(object):
|
||||
def writer(self):
|
||||
return self._writer
|
||||
|
||||
@asyncio.coroutine
|
||||
def connected(self):
|
||||
async def connected(self):
|
||||
"""Method called when client is connected"""
|
||||
pass
|
||||
|
||||
@asyncio.coroutine
|
||||
def disconnected(self):
|
||||
async def disconnected(self):
|
||||
"""Method called when client is disconnecting"""
|
||||
pass
|
||||
|
||||
@ -90,8 +85,7 @@ class TelnetConnection(object):
|
||||
`naws` flag is enable in server configuration."""
|
||||
pass
|
||||
|
||||
@asyncio.coroutine
|
||||
def feed(self, data):
|
||||
async def feed(self, data):
|
||||
"""
|
||||
Handles incoming data
|
||||
:return:
|
||||
@ -148,8 +142,7 @@ class AsyncioTelnetServer:
|
||||
self._connection_factory = connection_factory
|
||||
|
||||
@staticmethod
|
||||
@asyncio.coroutine
|
||||
def write_client_intro(writer, echo=False):
|
||||
async def write_client_intro(writer, echo=False):
|
||||
# Send initial telnet session opening
|
||||
if echo:
|
||||
writer.write(bytes([IAC, WILL, ECHO]))
|
||||
@ -157,10 +150,9 @@ class AsyncioTelnetServer:
|
||||
writer.write(bytes([
|
||||
IAC, WONT, ECHO,
|
||||
IAC, DONT, ECHO]))
|
||||
yield from writer.drain()
|
||||
await writer.drain()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _write_intro(self, writer, binary=False, echo=False, naws=False):
|
||||
async def _write_intro(self, writer, binary=False, echo=False, naws=False):
|
||||
# Send initial telnet session opening
|
||||
if echo:
|
||||
writer.write(bytes([IAC, WILL, ECHO]))
|
||||
@ -185,20 +177,19 @@ class AsyncioTelnetServer:
|
||||
writer.write(bytes([
|
||||
IAC, DO, NAWS
|
||||
]))
|
||||
yield from writer.drain()
|
||||
await writer.drain()
|
||||
|
||||
@asyncio.coroutine
|
||||
def run(self, network_reader, network_writer):
|
||||
async def run(self, network_reader, network_writer):
|
||||
# Keep track of connected clients
|
||||
connection = self._connection_factory(network_reader, network_writer)
|
||||
self._connections[network_writer] = connection
|
||||
|
||||
try:
|
||||
yield from self._write_intro(network_writer, echo=self._echo, binary=self._binary, naws=self._naws)
|
||||
yield from connection.connected()
|
||||
yield from self._process(network_reader, network_writer, connection)
|
||||
await self._write_intro(network_writer, echo=self._echo, binary=self._binary, naws=self._naws)
|
||||
await connection.connected()
|
||||
await self._process(network_reader, network_writer, connection)
|
||||
except ConnectionError:
|
||||
with (yield from self._lock):
|
||||
async with self._lock:
|
||||
network_writer.close()
|
||||
if self._reader_process == network_reader:
|
||||
self._reader_process = None
|
||||
@ -206,53 +197,49 @@ class AsyncioTelnetServer:
|
||||
if self._current_read is not None:
|
||||
self._current_read.cancel()
|
||||
|
||||
yield from connection.disconnected()
|
||||
await connection.disconnected()
|
||||
del self._connections[network_writer]
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
for writer, connection in self._connections.items():
|
||||
try:
|
||||
writer.write_eof()
|
||||
yield from writer.drain()
|
||||
await writer.drain()
|
||||
except (AttributeError, ConnectionError):
|
||||
continue
|
||||
|
||||
@asyncio.coroutine
|
||||
def client_connected_hook(self):
|
||||
async def client_connected_hook(self):
|
||||
pass
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_reader(self, network_reader):
|
||||
async def _get_reader(self, network_reader):
|
||||
"""
|
||||
Get a reader or None if another reader is already reading.
|
||||
"""
|
||||
with (yield from self._lock):
|
||||
async with self._lock:
|
||||
if self._reader_process is None:
|
||||
self._reader_process = network_reader
|
||||
if self._reader:
|
||||
if self._reader_process == network_reader:
|
||||
self._current_read = asyncio_ensure_future(self._reader.read(READ_SIZE))
|
||||
self._current_read = asyncio.ensure_future(self._reader.read(READ_SIZE))
|
||||
return self._current_read
|
||||
return None
|
||||
|
||||
@asyncio.coroutine
|
||||
def _process(self, network_reader, network_writer, connection):
|
||||
network_read = asyncio_ensure_future(network_reader.read(READ_SIZE))
|
||||
reader_read = yield from self._get_reader(network_reader)
|
||||
async def _process(self, network_reader, network_writer, connection):
|
||||
network_read = asyncio.ensure_future(network_reader.read(READ_SIZE))
|
||||
reader_read = await self._get_reader(network_reader)
|
||||
|
||||
while True:
|
||||
if reader_read is None:
|
||||
reader_read = yield from self._get_reader(network_reader)
|
||||
reader_read = await self._get_reader(network_reader)
|
||||
if reader_read is None:
|
||||
done, pending = yield from asyncio.wait(
|
||||
done, pending = await asyncio.wait(
|
||||
[
|
||||
network_read,
|
||||
],
|
||||
timeout=1,
|
||||
return_when=asyncio.FIRST_COMPLETED)
|
||||
else:
|
||||
done, pending = yield from asyncio.wait(
|
||||
done, pending = await asyncio.wait(
|
||||
[
|
||||
network_read,
|
||||
reader_read
|
||||
@ -264,10 +251,10 @@ class AsyncioTelnetServer:
|
||||
if network_reader.at_eof():
|
||||
raise ConnectionResetError()
|
||||
|
||||
network_read = asyncio_ensure_future(network_reader.read(READ_SIZE))
|
||||
network_read = asyncio.ensure_future(network_reader.read(READ_SIZE))
|
||||
|
||||
if IAC in data:
|
||||
data = yield from self._IAC_parser(data, network_reader, network_writer, connection)
|
||||
data = await self._IAC_parser(data, network_reader, network_writer, connection)
|
||||
|
||||
if len(data) == 0:
|
||||
continue
|
||||
@ -277,9 +264,9 @@ class AsyncioTelnetServer:
|
||||
|
||||
if self._writer:
|
||||
self._writer.write(data)
|
||||
yield from self._writer.drain()
|
||||
await self._writer.drain()
|
||||
|
||||
yield from connection.feed(data)
|
||||
await connection.feed(data)
|
||||
if connection.is_closing:
|
||||
raise ConnectionResetError()
|
||||
|
||||
@ -287,22 +274,21 @@ class AsyncioTelnetServer:
|
||||
if self._reader and self._reader.at_eof():
|
||||
raise ConnectionResetError()
|
||||
|
||||
reader_read = yield from self._get_reader(network_reader)
|
||||
reader_read = await self._get_reader(network_reader)
|
||||
|
||||
# Replicate the output on all clients
|
||||
for connection in self._connections.values():
|
||||
connection.writer.write(data)
|
||||
yield from connection.writer.drain()
|
||||
await connection.writer.drain()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _read(self, cmd, buffer, location, reader):
|
||||
async def _read(self, cmd, buffer, location, reader):
|
||||
""" Reads next op from the buffer or reader"""
|
||||
try:
|
||||
op = buffer[location]
|
||||
cmd.append(op)
|
||||
return op
|
||||
except IndexError:
|
||||
op = yield from reader.read(1)
|
||||
op = await reader.read(1)
|
||||
buffer.extend(op)
|
||||
cmd.append(buffer[location])
|
||||
return op
|
||||
@ -320,8 +306,7 @@ class AsyncioTelnetServer:
|
||||
else:
|
||||
log.debug("Not supported negotiation sequence, received {} bytes", len(data))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _IAC_parser(self, buf, network_reader, network_writer, connection):
|
||||
async def _IAC_parser(self, buf, network_reader, network_writer, connection):
|
||||
"""
|
||||
Processes and removes any Telnet commands from the buffer.
|
||||
|
||||
@ -342,7 +327,7 @@ class AsyncioTelnetServer:
|
||||
try:
|
||||
iac_cmd.append(buf[iac_loc + 1])
|
||||
except IndexError:
|
||||
d = yield from network_reader.read(1)
|
||||
d = await network_reader.read(1)
|
||||
buf.extend(d)
|
||||
iac_cmd.append(buf[iac_loc + 1])
|
||||
|
||||
@ -366,7 +351,7 @@ class AsyncioTelnetServer:
|
||||
elif iac_cmd[1] == SB: # starts negotiation commands
|
||||
negotiation = []
|
||||
for pos in range(2, self.MAX_NEGOTIATION_READ):
|
||||
op = yield from self._read(iac_cmd, buf, iac_loc + pos, network_reader)
|
||||
op = await self._read(iac_cmd, buf, iac_loc + pos, network_reader)
|
||||
negotiation.append(op)
|
||||
if op == SE:
|
||||
# ends negotiation commands
|
||||
@ -380,7 +365,7 @@ class AsyncioTelnetServer:
|
||||
try:
|
||||
iac_cmd.append(buf[iac_loc + 2])
|
||||
except IndexError:
|
||||
d = yield from network_reader.read(1)
|
||||
d = await network_reader.read(1)
|
||||
buf.extend(d)
|
||||
iac_cmd.append(buf[iac_loc + 2])
|
||||
# We do ECHO, SGA, and BINARY. Period.
|
||||
@ -413,7 +398,7 @@ class AsyncioTelnetServer:
|
||||
# Remove the entire TELNET command from the buffer
|
||||
buf = buf.replace(iac_cmd, b'', 1)
|
||||
|
||||
yield from network_writer.drain()
|
||||
await network_writer.drain()
|
||||
|
||||
# Return the new copy of the buffer, minus telnet commands
|
||||
return buf
|
||||
@ -422,7 +407,7 @@ if __name__ == '__main__':
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
process = loop.run_until_complete(asyncio_ensure_future(asyncio.subprocess.create_subprocess_exec("/bin/sh", "-i",
|
||||
process = loop.run_until_complete(asyncio.ensure_future(asyncio.subprocess.create_subprocess_exec("/bin/sh", "-i",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.STDOUT,
|
||||
stdin=asyncio.subprocess.PIPE)))
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user