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

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

No files matched your search

+23 -34
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))
+40 -52
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):
+49 -63
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,
@@ -44,8 +44,7 @@ class EthernetHub(BaseNode):
"node_id": self.id,
"project_id": self.project.id}
@asyncio.coroutine
def create(self):
async def create(self):
"""
Creates this hub.
"""
@@ -53,16 +52,14 @@ class EthernetHub(BaseNode):
super().create()
log.info('Ethernet hub "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
@asyncio.coroutine
def delete(self):
async def delete(self):
"""
Deletes this hub.
"""
raise NotImplementedError()
@asyncio.coroutine
def add_nio(self, nio, port_number):
async def add_nio(self, nio, port_number):
"""
Adds a NIO as new port on this hub.
@@ -72,8 +69,7 @@ class EthernetHub(BaseNode):
raise NotImplementedError()
@asyncio.coroutine
def remove_nio(self, port_number):
async def remove_nio(self, port_number):
"""
Removes the specified NIO as member of this hub.
@@ -84,8 +80,7 @@ class EthernetHub(BaseNode):
raise NotImplementedError()
@asyncio.coroutine
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
"""
Starts a packet capture.
@@ -96,8 +91,7 @@ class EthernetHub(BaseNode):
raise NotImplementedError()
@asyncio.coroutine
def stop_capture(self, port_number):
async def stop_capture(self, port_number):
"""
Stops a packet capture.
@@ -44,8 +44,7 @@ class EthernetSwitch(BaseNode):
"node_id": self.id,
"project_id": self.project.id}
@asyncio.coroutine
def create(self):
async def create(self):
"""
Creates this switch.
"""
@@ -53,16 +52,14 @@ class EthernetSwitch(BaseNode):
super().create()
log.info('Ethernet switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
@asyncio.coroutine
def delete(self):
async def delete(self):
"""
Deletes this switch.
"""
raise NotImplementedError()
@asyncio.coroutine
def add_nio(self, nio, port_number):
async def add_nio(self, nio, port_number):
"""
Adds a NIO as new port on this switch.
@@ -72,8 +69,7 @@ class EthernetSwitch(BaseNode):
raise NotImplementedError()
@asyncio.coroutine
def remove_nio(self, port_number):
async def remove_nio(self, port_number):
"""
Removes the specified NIO as member of this switch.
@@ -84,8 +80,7 @@ class EthernetSwitch(BaseNode):
raise NotImplementedError()
@asyncio.coroutine
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
"""
Starts a packet capture.
@@ -96,8 +91,7 @@ class EthernetSwitch(BaseNode):
raise NotImplementedError()
@asyncio.coroutine
def stop_capture(self, port_number):
async def stop_capture(self, port_number):
"""
Stops a packet capture.
+19 -26
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>":
+114 -147
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()
+59 -71
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
@@ -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")
+6 -8
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))
+21 -31
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
@@ -43,10 +43,9 @@ class NIOGenericEthernet(NIO):
self._ethernet_device = ethernet_device
super().__init__(name, hypervisor)
@asyncio.coroutine
def create(self):
async def create(self):
yield from self._hypervisor.send("nio create_gen_eth {name} {eth_device}".format(name=self._name,
await self._hypervisor.send("nio create_gen_eth {name} {eth_device}".format(name=self._name,
eth_device=self._ethernet_device))
log.info("NIO Generic Ethernet {name} created with device {device}".format(name=self._name,
@@ -42,10 +42,9 @@ class NIOLinuxEthernet(NIO):
self._ethernet_device = ethernet_device
super().__init__(name, hypervisor)
@asyncio.coroutine
def create(self):
async def create(self):
yield from self._hypervisor.send("nio create_linux_eth {name} {eth_device}".format(name=self._name,
await self._hypervisor.send("nio create_linux_eth {name} {eth_device}".format(name=self._name,
eth_device=self._ethernet_device))
log.info("NIO Linux Ethernet {name} created with device {device}".format(name=self._name,
+2 -3
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):
+2 -3
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
+8 -11
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)
+2 -3
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))
+2 -3
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))
+36 -49
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))
+11 -16
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
+7 -10
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,
+7 -10
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,
+2 -3
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,
+7 -10
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,
+2 -3
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,
+2 -3
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,
+18 -24
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)
@@ -93,10 +93,9 @@ class EthernetHub(Bridge):
self._ports = ports
@asyncio.coroutine
def create(self):
async def create(self):
yield from Bridge.create(self)
await Bridge.create(self)
log.info('Ethernet hub "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
@property
@@ -109,32 +108,29 @@ class EthernetHub(Bridge):
return self._mappings
@asyncio.coroutine
def delete(self):
return (yield from self.close())
async def delete(self):
return (await self.close())
@asyncio.coroutine
def close(self):
async def close(self):
"""
Deletes this hub.
"""
for nio in self._nios:
if nio:
yield from nio.close()
await nio.close()
try:
yield from Bridge.delete(self)
await Bridge.delete(self)
log.info('Ethernet hub "{name}" [{id}] has been deleted'.format(name=self._name, id=self._id))
except DynamipsError:
log.debug("Could not properly delete Ethernet hub {}".format(self._name))
if self._hypervisor and not self._hypervisor.devices:
yield from self.hypervisor.stop()
await self.hypervisor.stop()
self._hypervisor = None
return True
@asyncio.coroutine
def add_nio(self, nio, port_number):
async def add_nio(self, nio, port_number):
"""
Adds a NIO as new port on this hub.
@@ -148,7 +144,7 @@ class EthernetHub(Bridge):
if port_number in self._mappings:
raise DynamipsError("Port {} isn't free".format(port_number))
yield from Bridge.add_nio(self, nio)
await Bridge.add_nio(self, nio)
log.info('Ethernet hub "{name}" [{id}]: NIO {nio} bound to port {port}'.format(name=self._name,
id=self._id,
@@ -156,8 +152,7 @@ class EthernetHub(Bridge):
port=port_number))
self._mappings[port_number] = nio
@asyncio.coroutine
def remove_nio(self, port_number):
async def remove_nio(self, port_number):
"""
Removes the specified NIO as member of this hub.
@@ -172,7 +167,7 @@ class EthernetHub(Bridge):
nio = self._mappings[port_number]
if isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
yield from Bridge.remove_nio(self, nio)
await Bridge.remove_nio(self, nio)
log.info('Ethernet hub "{name}" [{id}]: NIO {nio} removed from port {port}'.format(name=self._name,
id=self._id,
@@ -182,8 +177,7 @@ class EthernetHub(Bridge):
del self._mappings[port_number]
return nio
@asyncio.coroutine
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
"""
Starts a packet capture.
@@ -204,15 +198,14 @@ class EthernetHub(Bridge):
if nio.input_filter[0] is not None and nio.output_filter[0] is not None:
raise DynamipsError("Port {} has already a filter applied".format(port_number))
yield from nio.bind_filter("both", "capture")
yield from nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
await nio.bind_filter("both", "capture")
await nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
log.info('Ethernet hub "{name}" [{id}]: starting packet capture on port {port}'.format(name=self._name,
id=self._id,
port=port_number))
@asyncio.coroutine
def stop_capture(self, port_number):
async def stop_capture(self, port_number):
"""
Stops a packet capture.
@@ -223,7 +216,7 @@ class EthernetHub(Bridge):
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._mappings[port_number]
yield from nio.unbind_filter("both")
await nio.unbind_filter("both")
log.info('Ethernet hub "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name,
id=self._id,
port=port_number))
@@ -43,13 +43,12 @@ class EthernetSwitchConsole(EmbedShell):
super().__init__(welcome_message="Welcome to GNS3 builtin Ethernet switch.\n\nType help for available commands\n")
self._node = node
@asyncio.coroutine
def mac(self):
async def mac(self):
"""
Show MAC address table
"""
res = 'Port Mac VLAN\n'
result = (yield from self._node._hypervisor.send('ethsw show_mac_addr_table {}'.format(self._node.name)))
result = (await self._node._hypervisor.send('ethsw show_mac_addr_table {}'.format(self._node.name)))
for line in result:
mac, vlan, nio = line.replace(' ', ' ').split(' ')
mac = mac.replace('.', '')
@@ -163,41 +162,38 @@ class EthernetSwitch(Device):
self._ports = ports
@asyncio.coroutine
def update_port_settings(self):
async def update_port_settings(self):
for port_settings in self._ports:
port_number = port_settings["port_number"]
if port_number in self._nios and self._nios[port_number] is not None:
yield from self.set_port_settings(port_number, port_settings)
await self.set_port_settings(port_number, port_settings)
@asyncio.coroutine
def create(self):
async def create(self):
if self._hypervisor is None:
module_workdir = self.project.module_working_directory(self.manager.module_name.lower())
self._hypervisor = yield from self.manager.start_new_hypervisor(working_dir=module_workdir)
self._hypervisor = await self.manager.start_new_hypervisor(working_dir=module_workdir)
yield from self._hypervisor.send('ethsw create "{}"'.format(self._name))
await self._hypervisor.send('ethsw create "{}"'.format(self._name))
log.info('Ethernet switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
self._telnet_shell = EthernetSwitchConsole(self)
self._telnet_shell.prompt = self._name + '> '
self._telnet = create_telnet_shell(self._telnet_shell)
try:
self._telnet_server = (yield from asyncio.start_server(self._telnet.run, self._manager.port_manager.console_host, self.console))
self._telnet_server = (await asyncio.start_server(self._telnet.run, self._manager.port_manager.console_host, self.console))
except OSError as e:
self.project.emit("log.warning", {"message": "Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e)})
self._hypervisor.devices.append(self)
@asyncio.coroutine
def set_name(self, new_name):
async def set_name(self, new_name):
"""
Renames this Ethernet switch.
:param new_name: New name for this switch
"""
yield from self._hypervisor.send('ethsw rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
await self._hypervisor.send('ethsw rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
log.info('Ethernet switch "{name}" [{id}]: renamed to "{new_name}"'.format(name=self._name,
id=self._id,
new_name=new_name))
@@ -223,39 +219,36 @@ class EthernetSwitch(Device):
return self._mappings
@asyncio.coroutine
def delete(self):
return (yield from self.close())
async def delete(self):
return (await self.close())
@asyncio.coroutine
def close(self):
async def close(self):
"""
Deletes this Ethernet switch.
"""
yield from self._telnet.close()
await self._telnet.close()
if self._telnet_server:
self._telnet_server.close()
for nio in self._nios.values():
if nio:
yield from nio.close()
await nio.close()
self.manager.port_manager.release_tcp_port(self._console, self._project)
if self._hypervisor:
try:
yield from self._hypervisor.send('ethsw delete "{}"'.format(self._name))
await self._hypervisor.send('ethsw delete "{}"'.format(self._name))
log.info('Ethernet switch "{name}" [{id}] has been deleted'.format(name=self._name, id=self._id))
except DynamipsError:
log.debug("Could not properly delete Ethernet switch {}".format(self._name))
if self._hypervisor and self in self._hypervisor.devices:
self._hypervisor.devices.remove(self)
if self._hypervisor and not self._hypervisor.devices:
yield from self.hypervisor.stop()
await self.hypervisor.stop()
self._hypervisor = None
return True
@asyncio.coroutine
def add_nio(self, nio, port_number):
async def add_nio(self, nio, port_number):
"""
Adds a NIO as new port on Ethernet switch.
@@ -266,7 +259,7 @@ class EthernetSwitch(Device):
if port_number in self._nios:
raise DynamipsError("Port {} isn't free".format(port_number))
yield from self._hypervisor.send('ethsw add_nio "{name}" {nio}'.format(name=self._name, nio=nio))
await self._hypervisor.send('ethsw add_nio "{name}" {nio}'.format(name=self._name, nio=nio))
log.info('Ethernet switch "{name}" [{id}]: NIO {nio} bound to port {port}'.format(name=self._name,
id=self._id,
@@ -275,11 +268,10 @@ class EthernetSwitch(Device):
self._nios[port_number] = nio
for port_settings in self._ports:
if port_settings["port_number"] == port_number:
yield from self.set_port_settings(port_number, port_settings)
await self.set_port_settings(port_number, port_settings)
break
@asyncio.coroutine
def remove_nio(self, port_number):
async def remove_nio(self, port_number):
"""
Removes the specified NIO as member of this Ethernet switch.
@@ -295,7 +287,7 @@ class EthernetSwitch(Device):
if isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
if self._hypervisor:
yield from self._hypervisor.send('ethsw remove_nio "{name}" {nio}'.format(name=self._name, nio=nio))
await self._hypervisor.send('ethsw remove_nio "{name}" {nio}'.format(name=self._name, nio=nio))
log.info('Ethernet switch "{name}" [{id}]: NIO {nio} removed from port {port}'.format(name=self._name,
id=self._id,
@@ -308,8 +300,7 @@ class EthernetSwitch(Device):
return nio
@asyncio.coroutine
def set_port_settings(self, port_number, settings):
async def set_port_settings(self, port_number, settings):
"""
Applies port settings to a specific port.
@@ -318,14 +309,13 @@ class EthernetSwitch(Device):
"""
if settings["type"] == "access":
yield from self.set_access_port(port_number, settings["vlan"])
await self.set_access_port(port_number, settings["vlan"])
elif settings["type"] == "dot1q":
yield from self.set_dot1q_port(port_number, settings["vlan"])
await self.set_dot1q_port(port_number, settings["vlan"])
elif settings["type"] == "qinq":
yield from self.set_qinq_port(port_number, settings["vlan"], settings.get("ethertype"))
await self.set_qinq_port(port_number, settings["vlan"], settings.get("ethertype"))
@asyncio.coroutine
def set_access_port(self, port_number, vlan_id):
async def set_access_port(self, port_number, vlan_id):
"""
Sets the specified port as an ACCESS port.
@@ -337,7 +327,7 @@ class EthernetSwitch(Device):
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._nios[port_number]
yield from self._hypervisor.send('ethsw set_access_port "{name}" {nio} {vlan_id}'.format(name=self._name,
await self._hypervisor.send('ethsw set_access_port "{name}" {nio} {vlan_id}'.format(name=self._name,
nio=nio,
vlan_id=vlan_id))
@@ -347,8 +337,7 @@ class EthernetSwitch(Device):
vlan_id=vlan_id))
self._mappings[port_number] = ("access", vlan_id)
@asyncio.coroutine
def set_dot1q_port(self, port_number, native_vlan):
async def set_dot1q_port(self, port_number, native_vlan):
"""
Sets the specified port as a 802.1Q trunk port.
@@ -360,7 +349,7 @@ class EthernetSwitch(Device):
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._nios[port_number]
yield from self._hypervisor.send('ethsw set_dot1q_port "{name}" {nio} {native_vlan}'.format(name=self._name,
await self._hypervisor.send('ethsw set_dot1q_port "{name}" {nio} {native_vlan}'.format(name=self._name,
nio=nio,
native_vlan=native_vlan))
@@ -371,8 +360,7 @@ class EthernetSwitch(Device):
self._mappings[port_number] = ("dot1q", native_vlan)
@asyncio.coroutine
def set_qinq_port(self, port_number, outer_vlan, ethertype):
async def set_qinq_port(self, port_number, outer_vlan, ethertype):
"""
Sets the specified port as a trunk (QinQ) port.
@@ -387,7 +375,7 @@ class EthernetSwitch(Device):
if ethertype != "0x8100" and parse_version(self.hypervisor.version) < parse_version('0.2.16'):
raise DynamipsError("Dynamips version required is >= 0.2.16 to change the default QinQ Ethernet type, detected version is {}".format(self.hypervisor.version))
yield from self._hypervisor.send('ethsw set_qinq_port "{name}" {nio} {outer_vlan} {ethertype}'.format(name=self._name,
await self._hypervisor.send('ethsw set_qinq_port "{name}" {nio} {outer_vlan} {ethertype}'.format(name=self._name,
nio=nio,
outer_vlan=outer_vlan,
ethertype=ethertype if ethertype != "0x8100" else ""))
@@ -399,27 +387,24 @@ class EthernetSwitch(Device):
ethertype=ethertype))
self._mappings[port_number] = ("qinq", outer_vlan, ethertype)
@asyncio.coroutine
def get_mac_addr_table(self):
async def get_mac_addr_table(self):
"""
Returns the MAC address table for this Ethernet switch.
:returns: list of entries (Ethernet address, VLAN, NIO)
"""
mac_addr_table = yield from self._hypervisor.send('ethsw show_mac_addr_table "{}"'.format(self._name))
mac_addr_table = await self._hypervisor.send('ethsw show_mac_addr_table "{}"'.format(self._name))
return mac_addr_table
@asyncio.coroutine
def clear_mac_addr_table(self):
async def clear_mac_addr_table(self):
"""
Clears the MAC address table for this Ethernet switch.
"""
yield from self._hypervisor.send('ethsw clear_mac_addr_table "{}"'.format(self._name))
await self._hypervisor.send('ethsw clear_mac_addr_table "{}"'.format(self._name))
@asyncio.coroutine
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
"""
Starts a packet capture.
@@ -443,15 +428,14 @@ class EthernetSwitch(Device):
if nio.input_filter[0] is not None and nio.output_filter[0] is not None:
raise DynamipsError("Port {} has already a filter applied".format(port_number))
yield from nio.bind_filter("both", "capture")
yield from nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
await nio.bind_filter("both", "capture")
await nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
log.info('Ethernet switch "{name}" [{id}]: starting packet capture on port {port}'.format(name=self._name,
id=self._id,
port=port_number))
@asyncio.coroutine
def stop_capture(self, port_number):
async def stop_capture(self, port_number):
"""
Stops a packet capture.
@@ -466,7 +450,7 @@ class EthernetSwitch(Device):
if not nio:
raise DynamipsError("Port {} is not connected".format(port_number))
yield from nio.unbind_filter("both")
await nio.unbind_filter("both")
log.info('Ethernet switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name,
id=self._id,
port=port_number))
@@ -63,26 +63,24 @@ class FrameRelaySwitch(Device):
"mappings": mappings,
"status": "started"}
@asyncio.coroutine
def create(self):
async def create(self):
if self._hypervisor is None:
module_workdir = self.project.module_working_directory(self.manager.module_name.lower())
self._hypervisor = yield from self.manager.start_new_hypervisor(working_dir=module_workdir)
self._hypervisor = await self.manager.start_new_hypervisor(working_dir=module_workdir)
yield from self._hypervisor.send('frsw create "{}"'.format(self._name))
await self._hypervisor.send('frsw create "{}"'.format(self._name))
log.info('Frame Relay switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
self._hypervisor.devices.append(self)
@asyncio.coroutine
def set_name(self, new_name):
async def set_name(self, new_name):
"""
Renames this Frame Relay switch.
:param new_name: New name for this switch
"""
yield from self._hypervisor.send('frsw rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
await self._hypervisor.send('frsw rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
log.info('Frame Relay switch "{name}" [{id}]: renamed to "{new_name}"'.format(name=self._name,
id=self._id,
new_name=new_name))
@@ -118,15 +116,14 @@ class FrameRelaySwitch(Device):
self._mappings = mappings
@asyncio.coroutine
def close(self):
async def close(self):
for nio in self._nios.values():
if nio:
yield from nio.close()
await nio.close()
if self._hypervisor:
try:
yield from self._hypervisor.send('frsw delete "{}"'.format(self._name))
await self._hypervisor.send('frsw delete "{}"'.format(self._name))
log.info('Frame Relay switch "{name}" [{id}] has been deleted'.format(name=self._name, id=self._id))
except DynamipsError:
log.debug("Could not properly delete Frame relay switch {}".format(self._name))
@@ -134,15 +131,14 @@ class FrameRelaySwitch(Device):
if self._hypervisor and self in self._hypervisor.devices:
self._hypervisor.devices.remove(self)
if self._hypervisor and not self._hypervisor.devices:
yield from self.hypervisor.stop()
await self.hypervisor.stop()
self._hypervisor = None
@asyncio.coroutine
def delete(self):
async def delete(self):
"""
Deletes this Frame Relay switch.
"""
yield from self.close()
await self.close()
return True
def has_port(self, port):
@@ -156,8 +152,7 @@ class FrameRelaySwitch(Device):
return True
return False
@asyncio.coroutine
def add_nio(self, nio, port_number):
async def add_nio(self, nio, port_number):
"""
Adds a NIO as new port on Frame Relay switch.
@@ -174,10 +169,9 @@ class FrameRelaySwitch(Device):
port=port_number))
self._nios[port_number] = nio
yield from self.set_mappings(self._mappings)
await self.set_mappings(self._mappings)
@asyncio.coroutine
def remove_nio(self, port_number):
async def remove_nio(self, port_number):
"""
Removes the specified NIO as member of this Frame Relay switch.
@@ -200,8 +194,8 @@ class FrameRelaySwitch(Device):
source_dlci=source_dlci,
destination_port=destination_port,
destination_dlci=destination_dlci))
yield from self.unmap_vc(source_port, source_dlci, destination_port, destination_dlci)
yield from self.unmap_vc(destination_port, destination_dlci, source_port, source_dlci)
await self.unmap_vc(source_port, source_dlci, destination_port, destination_dlci)
await self.unmap_vc(destination_port, destination_dlci, source_port, source_dlci)
nio = self._nios[port_number]
if isinstance(nio, NIOUDP):
@@ -215,8 +209,7 @@ class FrameRelaySwitch(Device):
del self._nios[port_number]
return nio
@asyncio.coroutine
def set_mappings(self, mappings):
async def set_mappings(self, mappings):
"""
Applies VC mappings
@@ -237,11 +230,10 @@ class FrameRelaySwitch(Device):
destination_port=destination_port,
destination_dlci=destination_dlci))
yield from self.map_vc(source_port, source_dlci, destination_port, destination_dlci)
yield from self.map_vc(destination_port, destination_dlci, source_port, source_dlci)
await self.map_vc(source_port, source_dlci, destination_port, destination_dlci)
await self.map_vc(destination_port, destination_dlci, source_port, source_dlci)
@asyncio.coroutine
def map_vc(self, port1, dlci1, port2, dlci2):
async def map_vc(self, port1, dlci1, port2, dlci2):
"""
Creates a new Virtual Circuit connection (unidirectional).
@@ -260,7 +252,7 @@ class FrameRelaySwitch(Device):
nio1 = self._nios[port1]
nio2 = self._nios[port2]
yield from self._hypervisor.send('frsw create_vc "{name}" {input_nio} {input_dlci} {output_nio} {output_dlci}'.format(name=self._name,
await self._hypervisor.send('frsw create_vc "{name}" {input_nio} {input_dlci} {output_nio} {output_dlci}'.format(name=self._name,
input_nio=nio1,
input_dlci=dlci1,
output_nio=nio2,
@@ -275,8 +267,7 @@ class FrameRelaySwitch(Device):
self._active_mappings[(port1, dlci1)] = (port2, dlci2)
@asyncio.coroutine
def unmap_vc(self, port1, dlci1, port2, dlci2):
async def unmap_vc(self, port1, dlci1, port2, dlci2):
"""
Deletes a Virtual Circuit connection (unidirectional).
@@ -295,7 +286,7 @@ class FrameRelaySwitch(Device):
nio1 = self._nios[port1]
nio2 = self._nios[port2]
yield from self._hypervisor.send('frsw delete_vc "{name}" {input_nio} {input_dlci} {output_nio} {output_dlci}'.format(name=self._name,
await self._hypervisor.send('frsw delete_vc "{name}" {input_nio} {input_dlci} {output_nio} {output_dlci}'.format(name=self._name,
input_nio=nio1,
input_dlci=dlci1,
output_nio=nio2,
@@ -309,8 +300,7 @@ class FrameRelaySwitch(Device):
dlci2=dlci2))
del self._active_mappings[(port1, dlci1)]
@asyncio.coroutine
def start_capture(self, port_number, output_file, data_link_type="DLT_FRELAY"):
async def start_capture(self, port_number, output_file, data_link_type="DLT_FRELAY"):
"""
Starts a packet capture.
@@ -331,15 +321,14 @@ class FrameRelaySwitch(Device):
if nio.input_filter[0] is not None and nio.output_filter[0] is not None:
raise DynamipsError("Port {} has already a filter applied".format(port_number))
yield from nio.bind_filter("both", "capture")
yield from nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
await nio.bind_filter("both", "capture")
await nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
log.info('Frame relay switch "{name}" [{id}]: starting packet capture on port {port}'.format(name=self._name,
id=self._id,
port=port_number))
@asyncio.coroutine
def stop_capture(self, port_number):
async def stop_capture(self, port_number):
"""
Stops a packet capture.
@@ -350,7 +339,7 @@ class FrameRelaySwitch(Device):
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._nios[port_number]
yield from nio.unbind_filter("both")
await nio.unbind_filter("both")
log.info('Frame relay switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name,
id=self._id,
port=port_number))
File diff suppressed because it is too large. Load diff
+3 -4
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
+52 -69
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))
+19 -24
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)
+16 -21
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))
+3 -4
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()
+96 -121
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:
+2 -3
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))
+27 -37
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,
+14 -18
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:
+134 -165
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,
+31 -41
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()))
+69 -90
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,
+4 -6
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):
+30 -41
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,