Drop Python 3.4 and switch to async / await syntax for asyncio. Fixes #1425

This commit is contained in:
grossmj 2018-10-15 17:05:49 +07:00
parent 8baa480b79
commit 76af98404a
119 changed files with 2432 additions and 3084 deletions

View File

@ -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))

View File

@ -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):

View File

@ -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,

View File

@ -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.

View File

@ -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.

View File

@ -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>":

View File

@ -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()

View File

@ -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

View File

@ -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")

View File

@ -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))

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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):

View File

@ -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

View File

@ -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)

View File

@ -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))

View 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))

View 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))

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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)

View File

@ -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))

View File

@ -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))

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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))

View File

@ -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()

View File

@ -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:

View File

@ -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))

View File

@ -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,

View File

@ -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:

View File

@ -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,

View File

@ -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()))

View File

@ -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,

View File

@ -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):

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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()

View File

@ -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.
"""

View File

@ -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")

View File

@ -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.
"""

View File

@ -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))

View File

@ -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")

View File

@ -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):

View File

@ -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
"""

View File

@ -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):
"""

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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"]
)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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"))

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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:

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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",

View File

@ -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:

View File

@ -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],

View File

@ -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))

View File

@ -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")

View File

@ -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')

View File

@ -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")

View File

@ -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):

View File

@ -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__':

View File

@ -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))

View File

@ -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